[otbn] Implement RND/URND CSR/WSR and RND_PREFETCH CSR This adds logic to read RND values from the EDN, removing the test RND dummy value. A small cache to hold a single 256-bit random number from an EDN request is provided. A RND_PREFETCH CSR is introduced which prefetches to the cache when any value is written to it. Any RND read will take the value from the cache, emptying it. If a value isn't available in the cache the RND read will stall until one is available, starting a new EDN request to fetch one if required. An LFSR is added to supply values to URND. This is seeded via a request to the EDN that occurs when OTBN starts. Execution cannot proceed until the LFSR has been seeded. Two dummy EDNs are provided in `otbn_top_sim.sv`. These provides the existing test RND dummy value on every request after a fixed delay. The ISS has been altered to support the new stalling behaviour for RND reads. In a testbench the EDN interface is monitored and the ISS is provided with RND values as they appear on the interface. ISS support for URND has not yet been implemented, though it does implement the stall at the beginning of execution whilst OTBN awaits a URND reseed. Signed-off-by: Greg Chadwick <gac@lowrisc.org>
diff --git a/hw/ip/otbn/rtl/otbn_controller.sv b/hw/ip/otbn/rtl/otbn_controller.sv index 536807b..95144a0 100644 --- a/hw/ip/otbn/rtl/otbn_controller.sv +++ b/hw/ip/otbn/rtl/otbn_controller.sv
@@ -107,8 +107,11 @@ output logic [BaseWordsPerWLEN-1:0] ispr_base_wr_en_o, output logic [WLEN-1:0] ispr_bignum_wdata_o, output logic ispr_bignum_wr_en_o, - output logic ispr_init_o, - input logic [WLEN-1:0] ispr_rdata_i + input logic [WLEN-1:0] ispr_rdata_i, + + output logic rnd_req_o, + output logic rnd_prefetch_req_o, + input logic rnd_valid_i ); otbn_state_e state_q, state_d, state_raw; @@ -118,6 +121,7 @@ logic insn_fetch_req_valid_raw; logic stall; + logic ispr_stall; logic mem_stall; logic branch_taken; logic insn_executing; @@ -143,7 +147,7 @@ ispr_e ispr_addr_bignum; - logic ispr_wr_insn; + logic ispr_wr_insn, ispr_rd_insn; logic ispr_wr_base_insn; logic ispr_wr_bignum_insn; @@ -185,7 +189,10 @@ // just ensure incoming store error stops anything else happening. assign mem_stall = lsu_load_req_raw; - assign stall = mem_stall; + // Reads to RND must stall until data is available + assign ispr_stall = rnd_req_o & ~rnd_valid_i; + + assign stall = mem_stall | ispr_stall; // OTBN is done (raising the 'done' interrupt) either when it executes an ecall or an error // occurs. The ecall triggered done is factored out as `done_complete` to avoid logic loops in the @@ -211,7 +218,6 @@ state_raw = state_q; insn_fetch_req_valid_raw = 1'b0; insn_fetch_req_addr_o = start_addr_i; - ispr_init_o = 1'b0; // TODO: Harden state machine // TODO: Jumps/branches @@ -222,8 +228,6 @@ insn_fetch_req_addr_o = start_addr_i; insn_fetch_req_valid_raw = 1'b1; - - ispr_init_o = 1'b1; end end OtbnStateRun: begin @@ -309,7 +313,8 @@ `ASSERT(ErrBitSetOnErr, err |-> |err_bits_o) - `ASSERT(ControllerStateValid, state_q inside {OtbnStateHalt, OtbnStateRun, OtbnStateStall}) + `ASSERT(ControllerStateValid, state_q inside {OtbnStateHalt, OtbnStateRun, + OtbnStateStall}) // Branch only takes effect in OtbnStateRun so must not go into stall state for branch // instructions. `ASSERT(NoStallOnBranch, @@ -579,6 +584,14 @@ ispr_addr_base = IsprRnd; ispr_word_addr_base = '0; end + CsrRndPrefetch: begin + // Reading from RND_PREFETCH results in 0, there is no ISPR to read so no address is set. + // The csr_rdata mux logic takes care of producing the 0. + end + CsrUrnd: begin + ispr_addr_base = IsprUrnd; + ispr_word_addr_base = '0; + end default: csr_illegal_addr = 1'b1; endcase end @@ -600,10 +613,11 @@ always_comb begin csr_rdata = csr_rdata_raw; - unique case(csr_addr) + unique case (csr_addr) // For FG0/FG1 select out appropriate bits from FLAGS ISPR and pad the rest with zeros. - CsrFg0: csr_rdata = {28'b0, csr_rdata_raw[3:0]}; - CsrFg1: csr_rdata = {28'b0, csr_rdata_raw[7:4]}; + CsrFg0: csr_rdata = {28'b0, csr_rdata_raw[3:0]}; + CsrFg1: csr_rdata = {28'b0, csr_rdata_raw[7:4]}; + CsrRndPrefetch: csr_rdata = '0; default: ; endcase end @@ -615,7 +629,7 @@ always_comb begin csr_wdata = csr_wdata_raw; - unique case(csr_addr) + unique case (csr_addr) // For FG0/FG1 only modify relevant part of FLAGS ISPR. CsrFg0: csr_wdata = {24'b0, csr_rdata_raw[7:4], csr_wdata_raw[3:0]}; CsrFg1: csr_wdata = {24'b0, csr_wdata_raw[3:0], csr_rdata_raw[3:0]}; @@ -637,9 +651,10 @@ wsr_illegal_addr = 1'b0; unique case (wsr_addr) - WsrMod: ispr_addr_bignum = IsprMod; - WsrRnd: ispr_addr_bignum = IsprRnd; - WsrAcc: ispr_addr_bignum = IsprAcc; + WsrMod: ispr_addr_bignum = IsprMod; + WsrRnd: ispr_addr_bignum = IsprRnd; + WsrAcc: ispr_addr_bignum = IsprAcc; + WsrUrnd: ispr_addr_bignum = IsprUrnd; default: wsr_illegal_addr = 1'b1; endcase end @@ -655,7 +670,12 @@ insn_dec_shared_i.ispr_rs_insn); assign ispr_wr_insn = insn_dec_shared_i.ispr_wr_insn | insn_dec_shared_i.ispr_rs_insn; - assign ispr_wr_base_insn = ispr_wr_insn & (insn_dec_shared_i.subset == InsnSubsetBase); + assign ispr_rd_insn = insn_dec_shared_i.ispr_rd_insn | insn_dec_shared_i.ispr_rs_insn; + + // Write to RND_PREFETCH must not produce ISR write + assign ispr_wr_base_insn = + ispr_wr_insn & (insn_dec_shared_i.subset == InsnSubsetBase) & (csr_addr != CsrRndPrefetch); + assign ispr_wr_bignum_insn = ispr_wr_insn & (insn_dec_shared_i.subset == InsnSubsetBignum); assign ispr_addr_o = insn_dec_shared_i.subset == InsnSubsetBase ? ispr_addr_base : @@ -693,6 +713,11 @@ dmem_addr_unaligned_bignum | dmem_addr_unaligned_base); + assign rnd_req_o = insn_valid_i & ispr_rd_insn & (ispr_addr_o == IsprRnd); + + assign rnd_prefetch_req_o = insn_valid_i & ispr_wr_insn & + (insn_dec_shared_i.subset == InsnSubsetBase) & (csr_addr == CsrRndPrefetch); + // RF Read enables for bignum RF are unused for now. Future security hardening work may make use // of them. logic unused_rf_ren_a_bignum;