[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/data/otbn.hjson b/hw/ip/otbn/data/otbn.hjson
index 99b8c81..ac5f7a1 100644
--- a/hw/ip/otbn/data/otbn.hjson
+++ b/hw/ip/otbn/data/otbn.hjson
@@ -20,7 +20,23 @@
desc: "Selection of the register file implementation. See otbn_pkg.sv."
local: "false",
expose: "true"
- }
+ },
+ { name: "RndCnstUrndLfsrSeed",
+ type: "otbn_pkg::urnd_lfsr_seed_t",
+ desc: '''
+ Default seed of the PRNG used for URND.
+ '''
+ randcount: "256",
+ randtype: "data"
+ },
+ { name: "RndCnstUrndChunkLfsrPerm",
+ type: "otbn_pkg::urnd_chunk_lfsr_perm_t",
+ desc: '''
+ Permutation applied to the LFSR chunks of the PRNG used for URND.
+ '''
+ randcount: "64",
+ randtype: "perm"
+ },
]
interrupt_list: [
{ name: "done"
diff --git a/hw/ip/otbn/dv/model/iss_wrapper.cc b/hw/ip/otbn/dv/model/iss_wrapper.cc
index f5dc918..5357c77 100644
--- a/hw/ip/otbn/dv/model/iss_wrapper.cc
+++ b/hw/ip/otbn/dv/model/iss_wrapper.cc
@@ -8,6 +8,7 @@
#include <cstring>
#include <fcntl.h>
#include <ftw.h>
+#include <iomanip>
#include <iostream>
#include <memory>
#include <regex>
@@ -218,6 +219,17 @@
return val;
}
+static std::string wlen_val_to_hex_str(uint32_t val[8]) {
+ std::ostringstream oss;
+
+ oss << std::hex << "0x";
+ for (int i = 7; i >= 0; --i) {
+ oss << std::setfill('0') << std::setw(8) << val[i];
+ }
+
+ return oss.str();
+}
+
ISSWrapper::ISSWrapper() : tmpdir(new TmpDir()) {
std::string model_path(find_otbn_model());
@@ -322,6 +334,16 @@
run_command(oss.str(), nullptr);
}
+void ISSWrapper::edn_rnd_data(uint32_t edn_rnd_data[8]) {
+ std::ostringstream oss;
+ oss << "edn_rnd_data " << wlen_val_to_hex_str(edn_rnd_data) << "\n";
+ run_command(oss.str(), nullptr);
+}
+
+void ISSWrapper::edn_urnd_reseed_complete() {
+ run_command("edn_urnd_reseed_complete\n", nullptr);
+}
+
std::pair<int, uint32_t> ISSWrapper::step(bool gen_trace) {
std::vector<std::string> lines;
bool mismatch = false;
diff --git a/hw/ip/otbn/dv/model/iss_wrapper.h b/hw/ip/otbn/dv/model/iss_wrapper.h
index a5383f4..9143c23 100644
--- a/hw/ip/otbn/dv/model/iss_wrapper.h
+++ b/hw/ip/otbn/dv/model/iss_wrapper.h
@@ -36,6 +36,13 @@
// Jump to a new address and start running
void start(uint32_t addr);
+ // Provide data for RND. ISS will stall when RND is read and RND data isn't
+ // available
+ void edn_rnd_data(uint32_t edn_rnd_data[8]);
+
+ // Signal URND reseed at beginning of execution is complete
+ void edn_urnd_reseed_complete();
+
// Run simulation for a single cycle. Returns a pair (ret_code, err_bits).
//
// If gen_trace is true, pass trace data to the (singleton)
diff --git a/hw/ip/otbn/dv/model/otbn_core_model.sv b/hw/ip/otbn/dv/model/otbn_core_model.sv
index e5bda2c..5e27730 100644
--- a/hw/ip/otbn/dv/model/otbn_core_model.sv
+++ b/hw/ip/otbn/dv/model/otbn_core_model.sv
@@ -39,7 +39,11 @@
input logic [ImemAddrWidth-1:0] start_addr_i, // start byte address in IMEM
- output bit err_o // something went wrong
+ output bit err_o, // something went wrong
+
+ input logic edn_rnd_data_valid_i, // provide RND data from EDN
+ input logic [WLEN-1:0] edn_rnd_data_i,
+ input logic edn_urnd_data_valid_i // URND reseed from EDN is valid
);
import "DPI-C" context function chandle otbn_model_init(string mem_scope,
@@ -48,11 +52,15 @@
int unsigned dmem_words);
import "DPI-C" function void otbn_model_destroy(chandle handle);
import "DPI-C" context function
- int unsigned otbn_model_step(chandle model,
- logic start_i,
- int unsigned start_addr,
- int unsigned status,
- inout bit [31:0] err_code);
+ int unsigned otbn_model_step(chandle model,
+ logic start,
+ int unsigned start_addr,
+ int unsigned status,
+ logic edn_rnd_data_valid,
+ logic [WLEN-1:0] edn_rnd_data,
+ logic edn_urnd_data_valid,
+ inout bit [31:0] err_code);
+
localparam int ImemSizeWords = ImemSizeByte / 4;
localparam int DmemSizeWords = DmemSizeByte / (WLEN / 8);
@@ -107,7 +115,10 @@
if (start_i | running | check_due) begin
status <= otbn_model_step(model_handle,
start_i, start_addr_32,
- status, raw_err_bits_d);
+ status,
+ edn_rnd_data_valid_i, edn_rnd_data_i,
+ edn_urnd_data_valid_i,
+ raw_err_bits_d);
raw_err_bits_q <= raw_err_bits_d;
end else begin
// If we're not running and we're not being told to start, there's nothing to do.
diff --git a/hw/ip/otbn/dv/model/otbn_model.cc b/hw/ip/otbn/dv/model/otbn_model.cc
index 08061f9..dd45504 100644
--- a/hw/ip/otbn/dv/model/otbn_model.cc
+++ b/hw/ip/otbn/dv/model/otbn_model.cc
@@ -104,28 +104,28 @@
// Bit 3: failed_cmp Consistency check at end of run failed
//
// The otbn_model_step function should only be called when either the model is
-// running (bit 0 of status), has a check due (bit 1 of status), or when
-// start_i is asserted. At other times, it will return immediately (but wastes
-// a DPI call).
+// running (bit 0 of status), has a check due (bit 1 of status), or when start
+// is asserted. At other times, it will return immediately (but wastes a DPI
+// call).
//
-// If the model is running and start_i is false, otbn_model_step steps the ISS
-// by a single cycle. If something goes wrong, it will set failed_step to true
-// and running to false.
+// If the model is running and start is false, otbn_model_step steps the ISS by
+// a single cycle. If something goes wrong, it will set failed_step to true and
+// running to false.
//
-// If nothing goes wrong, but the ISS finishes its run, we set running to
-// false, write out err_bits and do the post-run task. If the model's
-// design_scope is non-empty, it should be the scope of an RTL implementation.
-// In that case, we compare register and memory contents with that
-// implementation, printing to stderr and setting the failed_cmp bit if there
-// are any mismatches. If the model's design_scope is the empty string, we grab
-// the contents of DMEM from the ISS and inject them into the simulation
-// memory.
+// If nothing goes wrong, but the ISS finishes its run, we set running to false,
+// write out err_bits and do the post-run task. If the model's design_scope is
+// non-empty, it should be the scope of an RTL implementation. In that case, we
+// compare register and memory contents with that implementation, printing to
+// stderr and setting the failed_cmp bit if there are any mismatches. If the
+// model's design_scope is the empty string, we grab the contents of DMEM from
+// the ISS and inject them into the simulation memory.
//
-// If start_i is true, we start the model at start_addr and then step once (as
+// If start is true, we start the model at start_addr and then step once (as
// described above).
-extern "C" unsigned otbn_model_step(OtbnModel *model, svLogic start_i,
- unsigned start_addr, unsigned status,
- svBitVecVal *err_bits /* bit [31:0] */);
+extern "C" unsigned otbn_model_step(
+ OtbnModel *model, svLogic start, unsigned start_addr, unsigned status,
+ svLogic edn_rnd_data_valid, svLogicVecVal *edn_rnd_data, /* logic [255:0] */
+ svLogic edn_urnd_data_valid, svBitVecVal *err_bits /* bit [31:0] */);
static std::vector<uint8_t> read_vector_from_file(const std::string &path,
size_t num_bytes) {
@@ -216,17 +216,54 @@
return 0;
}
+// Extract 256-bit RND EDN data from a 4 state logic value. RND data is placed
+// into 8 element uint32_t array dst.
+static void set_rnd_data(uint32_t dst[8], const svLogicVecVal src[8]) {
+ for (int i = 0; i < 8; ++i) {
+ // All bits should be known
+ assert(src[i].bval == 0);
+ dst[i] = src[i].aval;
+ }
+}
+
+// Pack uint32_t-based error bitfield into a SystemVerilog bit vector that
+// represents a "bit [31:0]" (as in the SV prototype of otbn_model_step)
+static void set_err_bits(svBitVecVal *dst, uint32_t src) {
+ for (int i = 0; i < 32; ++i) {
+ svBit bit = (src >> i) & 1;
+ svPutBitselBit(dst, i, bit);
+ }
+}
+
+static bool is_xz(svLogic l) { return l == sv_x || l == sv_z; }
+
// Step once in the model. Returns 1 if the model has finished, 0 if not and -1
// on failure. If gen_trace is true, pass trace entries to the trace checker.
// If the model has finished, writes otbn.ERR_BITS to *err_bits.
-static int step_model(OtbnModel &model, bool gen_trace, uint32_t *err_bits) {
+static int step_model(OtbnModel &model, svLogic edn_rnd_data_valid,
+ svLogicVecVal *edn_rnd_data, /* logic [255:0] */
+ svLogic edn_urnd_data_valid, bool gen_trace,
+ svBitVecVal *err_bits /* bit [31:0] */) {
assert(err_bits);
ISSWrapper *iss = model.get_wrapper(true);
if (!iss)
return -1;
+ assert(!is_xz(edn_rnd_data_valid));
+ assert(!is_xz(edn_urnd_data_valid));
+
try {
+ if (edn_rnd_data_valid) {
+ uint32_t int_edn_rnd_data[8];
+ set_rnd_data(int_edn_rnd_data, edn_rnd_data);
+ iss->edn_rnd_data(int_edn_rnd_data);
+ }
+
+ if (edn_urnd_data_valid) {
+ iss->edn_urnd_reseed_complete();
+ }
+
std::pair<int, uint32_t> ret = iss->step(gen_trace);
switch (ret.first) {
case -1:
@@ -236,7 +273,7 @@
case 1:
// The simulation has stopped. Extract err_bits
- *err_bits = ret.second;
+ set_err_bits(err_bits, ret.second);
return 1;
case 0:
@@ -532,19 +569,11 @@
return good ? 1 : 0;
}
-// Pack uint32_t-based error bitfield (as generated by step_model) into a
-// SystemVerilog bit vector that represents a "bit [31:0]" (as in the SV
-// prototype of otbn_model_step)
-static void set_err_bits(svBitVecVal *dst, uint32_t src) {
- for (int i = 0; i < 32; ++i) {
- svBit bit = (src >> i) & 1;
- svPutBitselBit(dst, i, bit);
- }
-}
-
-extern "C" unsigned otbn_model_step(OtbnModel *model, svLogic start_i,
- unsigned start_addr, unsigned status,
- svBitVecVal *err_bits /* bit [31:0] */) {
+extern "C" unsigned otbn_model_step(
+ OtbnModel *model, svLogic start, unsigned start_addr, unsigned status,
+ svLogic edn_rnd_data_valid, svLogicVecVal *edn_rnd_data, /* logic [255:0] */
+ svLogic edn_urnd_data_valid, svBitVecVal *err_bits /* bit [31:0] */
+) {
assert(model && err_bits);
// Run model checks if needed. This usually happens just after an operation
@@ -568,8 +597,10 @@
status &= ~CHECK_DUE_BIT;
}
+ assert(!is_xz(start));
+
// Start the model if requested
- if (start_i) {
+ if (start) {
switch (start_model(*model, start_addr)) {
case 0:
// All good
@@ -587,8 +618,8 @@
return status;
// Step the model once
- uint32_t int_err_bits;
- switch (step_model(*model, check_rtl, &int_err_bits)) {
+ switch (step_model(*model, edn_rnd_data_valid, edn_rnd_data,
+ edn_urnd_data_valid, check_rtl, err_bits)) {
case 0:
// Still running: no change
break;
@@ -596,7 +627,6 @@
case 1:
// Finished
status = (status & ~RUNNING_BIT) | CHECK_DUE_BIT;
- set_err_bits(err_bits, int_err_bits);
break;
default:
diff --git a/hw/ip/otbn/dv/otbnsim/sim/csr.py b/hw/ip/otbn/dv/otbnsim/sim/csr.py
index 7be2891..a1a7352 100644
--- a/hw/ip/otbn/dv/otbnsim/sim/csr.py
+++ b/hw/ip/otbn/dv/otbnsim/sim/csr.py
@@ -43,6 +43,10 @@
# RND register
return wsrs.RND.read_u32()
+ if idx == 0xfc1:
+ # RND_PREFETCH register
+ return 0
+
raise RuntimeError('Unknown CSR index: {:#x}'.format(idx))
def write_unsigned(self, wsrs: WSRFile, idx: int, value: int) -> None:
@@ -67,8 +71,8 @@
wsrs.MOD.write_unsigned(self._set_field(mod_n, 32, value, old))
return
- if idx == 0xfc0:
- # RND register (writes are ignored)
+ if idx == 0xfc0 or idx == 0xfc1:
+ # RND/RND_PREFETCH register (writes are ignored)
return
raise RuntimeError('Unknown CSR index: {:#x}'.format(idx))
diff --git a/hw/ip/otbn/dv/otbnsim/sim/insn.py b/hw/ip/otbn/dv/otbnsim/sim/insn.py
index 2d88482..d09cb33 100644
--- a/hw/ip/otbn/dv/otbnsim/sim/insn.py
+++ b/hw/ip/otbn/dv/otbnsim/sim/insn.py
@@ -280,6 +280,14 @@
self.csr = op_vals['csr']
self.grs1 = op_vals['grs1']
+ def pre_execute(self, state: OTBNState) -> bool:
+ if self.csr == 0xfc0:
+ # Will return False if RND value not available, causing instruction
+ # to stall
+ return state.wsrs.RND.request_value()
+
+ return True
+
def execute(self, state: OTBNState) -> None:
old_val = state.read_csr(self.csr)
bits_to_set = state.gprs.get_reg(self.grs1).read_unsigned()
@@ -298,6 +306,14 @@
self.csr = op_vals['csr']
self.grs1 = op_vals['grs1']
+ def pre_execute(self, state: OTBNState) -> bool:
+ if self.csr == 0xfc0 and self.grd != 0:
+ # Will return False if RND value not available, causing instruction
+ # to stall
+ return state.wsrs.RND.request_value()
+
+ return True
+
def execute(self, state: OTBNState) -> None:
new_val = state.gprs.get_reg(self.grs1).read_unsigned()
@@ -977,6 +993,14 @@
self.wrd = op_vals['wrd']
self.wsr = op_vals['wsr']
+ def pre_execute(self, state: OTBNState) -> bool:
+ if self.wsr == 0x1:
+ # Will return False if RND value not available, causing instruction
+ # to stall
+ return state.wsrs.RND.request_value()
+
+ return True
+
def execute(self, state: OTBNState) -> None:
val = state.wsrs.read_at_idx(self.wsr)
state.wdrs.get_reg(self.wrd).write_unsigned(val)
diff --git a/hw/ip/otbn/dv/otbnsim/sim/sim.py b/hw/ip/otbn/dv/otbnsim/sim/sim.py
index 325f639..783eb76 100644
--- a/hw/ip/otbn/dv/otbnsim/sim/sim.py
+++ b/hw/ip/otbn/dv/otbnsim/sim/sim.py
@@ -8,6 +8,9 @@
from .state import OTBNState
from .trace import Trace
+_TEST_RND_DATA = \
+ 0x99999999_99999999_99999999_99999999_99999999_99999999_99999999_99999999
+
class OTBNSim:
def __init__(self) -> None:
@@ -27,10 +30,18 @@
'''
insn_count = 0
+ # ISS will stall at start until URND data is valid, immediately set it
+ # valid when in free running mode as nothing else will.
+ self.state.set_urnd_reseed_complete()
while self.state.running:
self.step(verbose)
insn_count += 1
+ if self.state.wsrs.RND.pending_request:
+ # If an instruction requests RND data, make it available
+ # immediately.
+ self.state.wsrs.RND.set_unsigned(_TEST_RND_DATA)
+
return insn_count
def step(self, verbose: bool) -> Tuple[Optional[OTBNInsn], List[Trace]]:
diff --git a/hw/ip/otbn/dv/otbnsim/sim/state.py b/hw/ip/otbn/dv/otbnsim/sim/state.py
index 303979f..8033a23 100644
--- a/hw/ip/otbn/dv/otbnsim/sim/state.py
+++ b/hw/ip/otbn/dv/otbnsim/sim/state.py
@@ -38,11 +38,13 @@
# returning false from OTBNInsn.pre_execute. For non instruction related
# stalls setting self.non_insn_stall will produce a stall.
#
- # As a special case, we stall for one cycle before fetching the first
- # instruction (to match the behaviour of the RTL). This is modelled by
- # setting self._start_stall and self.non_insn_stall
+ # As a special case, we stall until the URND reseed is completed then
+ # stall for one more cycle before fetching the first instruction (to
+ # match the behaviour of the RTL). This is modelled by setting
+ # self._start_stall, self._urnd_stall and self.non_insn_stall
self.non_insn_stall = False
self._start_stall = False
+ self._urnd_stall = False
self.loop_stack = LoopStack()
self.ext_regs = OTBNExtRegs()
@@ -51,6 +53,15 @@
self._err_bits = 0
self.pending_halt = False
+ self._new_rnd_data = None # type: Optional[int]
+ self._urnd_reseed_complete = False
+
+ def set_rnd_data(self, rnd_data: int) -> None:
+ self._new_rnd_data = rnd_data
+
+ def set_urnd_reseed_complete(self) -> None:
+ self._urnd_reseed_complete = True
+
def loop_start(self, iterations: int, bodysize: int) -> None:
next_pc = int(self.pc) + 4
self.loop_stack.start_loop(next_pc, iterations, bodysize)
@@ -79,6 +90,16 @@
assert not self.pending_halt
assert self._err_bits == 0
+ if self._new_rnd_data:
+ self.wsrs.RND.set_unsigned(self._new_rnd_data)
+ self._new_rnd_data = None
+
+ if self._urnd_stall:
+ if self._urnd_reseed_complete:
+ self._urnd_stall = False
+
+ return
+
# If self._start_stall, this is the end of the stall cycle at the start
# of a run. Clear self.non_insn_stall and self._start_stall
# and commit self.ext_regs (so the start flag becomes visible).
@@ -119,9 +140,11 @@
self.ext_regs.set_bits('STATUS', 1 << 0)
self.running = True
self._start_stall = True
+ self._urnd_stall = True
self.non_insn_stall = True
self.pending_halt = False
self._err_bits = 0
+ self._urnd_reseed_complete = False
self.pc = addr
diff --git a/hw/ip/otbn/dv/otbnsim/sim/wsr.py b/hw/ip/otbn/dv/otbnsim/sim/wsr.py
index 2157aae..0fc2aca 100644
--- a/hw/ip/otbn/dv/otbnsim/sim/wsr.py
+++ b/hw/ip/otbn/dv/otbnsim/sim/wsr.py
@@ -86,33 +86,68 @@
class RandWSR(WSR):
- '''The magic RND WSR'''
+ '''The magic RND WSR
+
+ RND is special as OTBN can stall on reads to it. A read from RND either
+ immediately returns data from a cache of a previous EDN request (triggered
+ by writing to the RND_PREFETCH CSR) or waits for data from the EDN. To model
+ this anything reading from RND must first call `request_value` which returns
+ True if the value is available.
+ '''
def __init__(self, name: str):
super().__init__(name)
- # For now, the RTL doesn't have a real "random number generator".
- # Eventually, it will have an LFSR of some sort, seeded by the
- # CSRNG/EDN. We'll model that properly when we've specced it out. Until
- # then, random numbers are constant. This constant must match the one
- # in the RTL (the `rnd` signal in the `otbn_core` module found in
- # rtl/otbn_core.sv). If changed here it must be changed there to match.
- # Constant for RND is the binary bit pattern 1001 (0x9 hex) repeated to
- # fill a 256-bit word.
- u32 = 0x99999999
- u64 = (u32 << 32) | u32
- u128 = (u64 << 64) | u64
- self._random_value = (u128 << 128) | u128
+ self._random_value = None # type: Optional[int]
+ self._random_value_read = False
+ self.pending_request = False
def read_unsigned(self) -> int:
+ assert self._random_value is not None
+
+ self._random_value_read = True
+
return self._random_value
def read_u32(self) -> int:
'''Read a 32-bit unsigned result'''
- return self._random_value & ((1 << 32) - 1)
+ return self.read_unsigned() & ((1 << 32) - 1)
def write_unsigned(self, value: int) -> None:
+ '''Writes to RND are ignored
+
+ Note this is different to `set_unsigned`. This is used by executing
+ instruction, see `set_unsigned` docstring for more details
+ '''
return
+ def commit(self) -> None:
+ if self._random_value_read:
+ self._random_value = None
+ self.pending_request = False
+
+ self._random_value_read = False
+
+ def request_value(self) -> bool:
+ '''Signals intent to read RND, returns True if a value is available'''
+ if self._random_value:
+ return True
+
+ self.pending_request = True
+ return False
+
+ def set_unsigned(self, value: int) -> None:
+ '''Sets a random value that can be read by a future `read_unsigned`
+
+ This is different to `write_unsigned`, that is used by an executing
+ instruction to write to RND. This is used by the simulation environment
+ to provide a value that is later read by `read_unsigned` and doesn't
+ relate to instruction execution (e.g. in an RTL simulation it monitors
+ the EDN bus and supplies the simulator with an RND value when a fresh
+ one is seen on the EDN bus).
+ '''
+ assert 0 <= value < (1 << 256)
+ self._random_value = value
+
class WSRFile:
'''A model of the WSR file'''
diff --git a/hw/ip/otbn/dv/otbnsim/stepped.py b/hw/ip/otbn/dv/otbnsim/stepped.py
index 57ba293..44edeb3 100755
--- a/hw/ip/otbn/dv/otbnsim/stepped.py
+++ b/hw/ip/otbn/dv/otbnsim/stepped.py
@@ -44,17 +44,17 @@
from sim.sim import OTBNSim
-def read_word(arg_name: str, word_data: str) -> int:
- '''Try to read a 32-bit unsigned word'''
+def read_word(arg_name: str, word_data: str, bits: int) -> int:
+ '''Try to read an unsigned word of the specified bit length'''
try:
value = int(word_data, 0)
except ValueError:
raise ValueError('Failed to read {!r} as an integer for <{}> argument.'
.format(word_data, arg_name)) from None
- if value < 0 or value >> 32:
- raise ValueError('<{}> argument is {!r}: not representable as a u32.'
- .format(arg_name, word_data))
+ if value < 0 or value >> bits:
+ raise ValueError('<{}> argument is {!r}: not representable in {!r} bits.'
+ .format(arg_name, word_data, bits))
return value
@@ -71,7 +71,7 @@
raise ValueError('start expects exactly 1 argument. Got {}.'
.format(args))
- addr = read_word('addr', args[0])
+ addr = read_word('addr', args[0], 32)
if addr & 3:
raise ValueError('start address must be word-aligned. Got {:#08x}.'
.format(addr))
@@ -183,6 +183,23 @@
print('0x{:08x}'.format(value))
+def on_edn_rnd_data(sim: OTBNSim, args: List[str]) -> None:
+ if len(args) != 1:
+ raise ValueError('edn_rnd_data expects exactly 1 argument. Got {}.'
+ .format(args))
+
+ edn_rnd_data = read_word('edn_rnd_data', args[0], 256)
+ sim.state.set_rnd_data(edn_rnd_data)
+
+
+def on_edn_urnd_reseed_complete(sim: OTBNSim, args: List[str]) -> None:
+ if args:
+ raise ValueError('edn_urnd_reseed_complete expects zero arguments. Got {}.'
+ .format(args))
+
+ sim.state.set_urnd_reseed_complete()
+
+
_HANDLERS = {
'start': on_start,
'step': on_step,
@@ -192,7 +209,9 @@
'load_i': on_load_i,
'dump_d': on_dump_d,
'print_regs': on_print_regs,
- 'print_call_stack': on_print_call_stack
+ 'print_call_stack': on_print_call_stack,
+ 'edn_rnd_data': on_edn_rnd_data,
+ 'edn_urnd_reseed_complete': on_edn_urnd_reseed_complete
}
diff --git a/hw/ip/otbn/dv/tracer/rtl/otbn_trace_if.sv b/hw/ip/otbn/dv/tracer/rtl/otbn_trace_if.sv
index a5162a5..de0ed74 100644
--- a/hw/ip/otbn/dv/tracer/rtl/otbn_trace_if.sv
+++ b/hw/ip/otbn/dv/tracer/rtl/otbn_trace_if.sv
@@ -70,7 +70,11 @@
input otbn_pkg::alu_bignum_operation_t alu_bignum_operation,
input logic mac_bignum_en,
- input logic [otbn_pkg::WLEN-1:0] rnd
+ input logic [otbn_pkg::WLEN-1:0] rnd_data,
+ input logic rnd_req,
+ input logic rnd_valid,
+
+ input logic [otbn_pkg::WLEN-1:0] urnd_data
);
import otbn_pkg::*;
@@ -196,9 +200,14 @@
assign ispr_write[IsprRnd] = 1'b0;
assign ispr_write_data[IsprRnd] = '0;
+ assign ispr_write[IsprUrnd] = 1'b0;
+ assign ispr_write_data[IsprUrnd] = '0;
- assign ispr_read[IsprRnd] = any_ispr_read & (ispr_addr == IsprRnd);
- assign ispr_read_data[IsprRnd] = rnd;
+ assign ispr_read[IsprRnd] = any_ispr_read & (ispr_addr == IsprRnd) & rnd_req & rnd_valid;
+ assign ispr_read_data[IsprRnd] = rnd_data;
+
+ assign ispr_read[IsprUrnd] = any_ispr_read & (ispr_addr == IsprUrnd);
+ assign ispr_read_data[IsprUrnd] = urnd_data;
// Seperate per flag group tracking using the flags_t struct so tracer can cleanly present flag
// accesses.
diff --git a/hw/ip/otbn/dv/tracer/rtl/otbn_tracer.sv b/hw/ip/otbn/dv/tracer/rtl/otbn_tracer.sv
index 094dbaf..ed753d1 100644
--- a/hw/ip/otbn/dv/tracer/rtl/otbn_tracer.sv
+++ b/hw/ip/otbn/dv/tracer/rtl/otbn_tracer.sv
@@ -89,6 +89,7 @@
IsprAcc: return "ACC";
IsprRnd: return "RND";
IsprFlags: return "FLAGS";
+ IsprUrnd: return "URND";
default: return "UNKNOWN_ISPR";
endcase
endfunction
diff --git a/hw/ip/otbn/dv/uvm/otbn_sim.core b/hw/ip/otbn/dv/uvm/otbn_sim.core
index da09dec..5cd960e 100644
--- a/hw/ip/otbn/dv/uvm/otbn_sim.core
+++ b/hw/ip/otbn/dv/uvm/otbn_sim.core
@@ -14,6 +14,7 @@
depend:
- lowrisc:dv:otbn_test
- lowrisc:dv:otbn_sva
+ - lowrisc:ip:edn_pkg
files:
- tb.sv
file_type: systemVerilogSource
diff --git a/hw/ip/otbn/dv/uvm/tb.sv b/hw/ip/otbn/dv/uvm/tb.sv
index 46af9f3..db72f9d 100644
--- a/hw/ip/otbn/dv/uvm/tb.sv
+++ b/hw/ip/otbn/dv/uvm/tb.sv
@@ -11,6 +11,7 @@
// dep packages (rtl)
import otbn_reg_pkg::*;
+ import edn_pkg::*;
// macro includes
`include "uvm_macros.svh"
@@ -36,6 +37,22 @@
`DV_ALERT_IF_CONNECT
+ // Mock up EDN that just instantly returns fixed data when requested
+ // TODO: Provide a proper EDN agent
+ edn_req_t edn_rnd_req;
+ edn_rsp_t edn_rnd_rsp;
+
+ edn_req_t edn_urnd_req;
+ edn_rsp_t edn_urnd_rsp;
+
+ assign edn_rnd_rsp.edn_ack = edn_rnd_req.edn_req;
+ assign edn_rnd_rsp.edn_fips = 1'b0;
+ assign edn_rnd_rsp.edn_bus = 32'h99999999;
+
+ assign edn_urnd_rsp.edn_ack = edn_urnd_req.edn_req;
+ assign edn_urnd_rsp.edn_fips = 1'b0;
+ assign edn_urnd_rsp.edn_bus = 32'h99999999;
+
// dut
otbn dut (
.clk_i (clk),
@@ -49,8 +66,15 @@
.intr_done_o (intr_done),
.alert_rx_i (alert_rx),
- .alert_tx_o (alert_tx)
+ .alert_tx_o (alert_tx),
+ .clk_edn_i (clk),
+ .rst_edn_ni (rst_n),
+ .edn_rnd_o ( edn_rnd_req ),
+ .edn_rnd_i ( edn_rnd_rsp ),
+
+ .edn_urnd_o ( edn_urnd_req ),
+ .edn_urnd_i ( edn_urnd_rsp )
);
bind otbn_core otbn_trace_if #(
@@ -60,6 +84,7 @@
bind otbn_core otbn_tracer u_otbn_tracer(.*, .otbn_trace(i_otbn_trace_if));
+
// OTBN model, wrapping an ISS.
//
// Note that we pull the "start" signal out of the DUT. This is because it's much more difficult
@@ -68,6 +93,15 @@
// decoding errors).
assign model_if.start = dut.start;
+ // Internally otbn_core uses a 256-bit width interface for EDN data. This maps to muliple EDN
+ // requests at this level (via a packing FIFO internal to otbn). The model works with the internal
+ // otbn_core interface so probe into it here to provide the relevant signals to the model.
+ logic edn_rnd_data_valid;
+ logic edn_urnd_data_valid;
+
+ assign edn_rnd_data_valid = dut.edn_rnd_req & dut.edn_rnd_ack;
+ assign edn_urnd_data_valid = dut.edn_urnd_req & dut.edn_urnd_ack;
+
otbn_core_model #(
.DmemSizeByte (otbn_reg_pkg::OTBN_DMEM_SIZE),
.ImemSizeByte (otbn_reg_pkg::OTBN_IMEM_SIZE),
@@ -80,7 +114,11 @@
.start_i (model_if.start),
.done_o (model_if.done),
.start_addr_i (model_if.start_addr),
- .err_o (model_if.err)
+ .err_o (model_if.err),
+
+ .edn_rnd_data_valid_i (edn_rnd_data_valid),
+ .edn_rnd_data_i (dut.edn_rnd_data),
+ .edn_urnd_data_valid_i (edn_urnd_data_valid)
);
initial begin
diff --git a/hw/ip/otbn/dv/verilator/otbn_mock_edn.sv b/hw/ip/otbn/dv/verilator/otbn_mock_edn.sv
new file mode 100644
index 0000000..a9ecd53
--- /dev/null
+++ b/hw/ip/otbn/dv/verilator/otbn_mock_edn.sv
@@ -0,0 +1,50 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+/**
+ * Mock EDN end point that returns a fixed value after a fixed delay used for
+ * OTBN simulation purposes.
+ */
+module otbn_mock_edn #(
+ parameter int Width = 256,
+ parameter logic [Width-1:0] FixedEdnVal = '0,
+ parameter int Delay = 16,
+
+ localparam int DelayWidth = $clog2(Delay)
+) (
+ input clk_i,
+ input rst_ni,
+
+ input edn_req_i,
+ output edn_ack_o,
+ output [Width-1:0] edn_data_o
+);
+ parameter int MaxDelay = Delay - 1;
+ logic [DelayWidth-1:0] edn_req_counter;
+ logic edn_req_active;
+ logic edn_req_complete;
+
+ assign edn_req_complete = edn_req_counter == MaxDelay[DelayWidth-1:0];
+ assign edn_ack_o = edn_req_complete;
+ assign edn_data_o = edn_req_complete ? FixedEdnVal : '0;
+
+ always @(posedge clk_i or negedge rst_ni) begin
+ if (!rst_ni) begin
+ edn_req_counter <= 0;
+ edn_req_active <= 0;
+ end else begin
+ if (edn_req_active) begin
+ edn_req_counter <= edn_req_counter + 4'b1;
+ end
+
+ if (edn_req_i & ~edn_req_active) begin
+ edn_req_active <= 1;
+ end
+
+ if (edn_req_complete) begin
+ edn_req_active <= 0;
+ end
+ end
+ end
+endmodule
diff --git a/hw/ip/otbn/dv/verilator/otbn_top_sim.core b/hw/ip/otbn/dv/verilator/otbn_top_sim.core
index c498257..39a96e0 100644
--- a/hw/ip/otbn/dv/verilator/otbn_top_sim.core
+++ b/hw/ip/otbn/dv/verilator/otbn_top_sim.core
@@ -19,6 +19,7 @@
files:
- otbn_top_sim.cc: { file_type: cppSource }
- otbn_top_sim.sv: { file_type: systemVerilogSource }
+ - otbn_mock_edn.sv: { file_type: systemVerilogSource }
files_verilator_waiver:
files:
- otbn_top_sim_waivers.vlt
diff --git a/hw/ip/otbn/dv/verilator/otbn_top_sim.sv b/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
index 8600de3..d057beb 100644
--- a/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
+++ b/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
@@ -52,6 +52,8 @@
logic edn_rnd_req, edn_urnd_req;
logic edn_rnd_ack, edn_urnd_ack;
logic [EdnDataWidth-1:0] edn_rnd_data, edn_urnd_data;
+ logic edn_rnd_data_valid;
+ logic edn_urnd_data_valid;
otbn_core #(
.ImemSizeByte ( ImemSizeByte ),
@@ -107,14 +109,35 @@
logic unused_imem_top_rdata;
assign unused_imem_top_rdata = &{1'b0, imem_rdata[38:32]};
- // Tie-off EDN signals, eventually simulation will provide something here for testing RND
- logic unused_edn_rnd_req, unused_edn_urnd_req;
- assign unused_edn_rnd_req = edn_rnd_req;
- assign unused_edn_urnd_req = edn_urnd_req;
- assign edn_rnd_ack = 1'b0;
- assign edn_rnd_data = '0;
- assign edn_urnd_ack = 1'b0;
- assign edn_urnd_data = '0;
+ localparam logic [WLEN-1:0] FixedEdnVal = {{(WLEN / 4){4'h9}}};
+
+ otbn_mock_edn #(
+ .Width ( WLEN ),
+ .FixedEdnVal ( FixedEdnVal )
+ ) u_mock_rnd_edn(
+ .clk_i ( IO_CLK ),
+ .rst_ni ( IO_RST_N ),
+
+ .edn_req_i ( edn_rnd_req ),
+ .edn_ack_o ( edn_rnd_ack ),
+ .edn_data_o ( edn_rnd_data )
+ );
+
+ assign edn_rnd_data_valid = edn_rnd_req & edn_rnd_ack;
+
+ otbn_mock_edn #(
+ .Width ( WLEN ),
+ .FixedEdnVal ( FixedEdnVal )
+ ) u_mock_urnd_edn(
+ .clk_i ( IO_CLK ),
+ .rst_ni ( IO_RST_N ),
+
+ .edn_req_i ( edn_urnd_req ),
+ .edn_ack_o ( edn_urnd_ack ),
+ .edn_data_o ( edn_urnd_data )
+ );
+
+ assign edn_urnd_data_valid = edn_urnd_req & edn_urnd_ack;
bind otbn_core otbn_trace_if #(.ImemAddrWidth, .DmemAddrWidth) i_otbn_trace_if (.*);
bind otbn_core otbn_tracer u_otbn_tracer(.*, .otbn_trace(i_otbn_trace_if));
@@ -237,17 +260,21 @@
.MemScope ( ".." ),
.DesignScope ( DesignScope )
) u_otbn_core_model (
- .clk_i ( IO_CLK ),
- .rst_ni ( IO_RST_N ),
+ .clk_i ( IO_CLK ),
+ .rst_ni ( IO_RST_N ),
- .start_i ( otbn_start ),
- .done_o ( otbn_model_done ),
+ .start_i ( otbn_start ),
+ .done_o ( otbn_model_done ),
- .start_addr_i ( ImemStartAddr ),
+ .start_addr_i ( ImemStartAddr ),
- .err_bits_o ( otbn_model_err_bits ),
+ .err_bits_o ( otbn_model_err_bits ),
- .err_o ( otbn_model_err )
+ .err_o ( otbn_model_err ),
+
+ .edn_rnd_data_valid_i ( edn_rnd_data_valid ),
+ .edn_rnd_data_i ( edn_rnd_data ),
+ .edn_urnd_data_valid_i ( edn_urnd_data_valid )
);
bit done_mismatch_latched, err_bits_mismatch_latched;
diff --git a/hw/ip/otbn/otbn.core b/hw/ip/otbn/otbn.core
index cca4fed..6d7de0e 100644
--- a/hw/ip/otbn/otbn.core
+++ b/hw/ip/otbn/otbn.core
@@ -10,6 +10,9 @@
depend:
- lowrisc:prim:assert
- lowrisc:prim:util
+ - lowrisc:prim:lfsr
+ - lowrisc:prim:cipher_pkg
+ - lowrisc:ip:edn_pkg
- lowrisc:ip:otbn_pkg
files:
- rtl/otbn_controller.sv
@@ -26,6 +29,8 @@
- rtl/otbn_mac_bignum.sv
- rtl/otbn_loop_controller.sv
- rtl/otbn_stack.sv
+ - rtl/otbn_rnd.sv
+ - rtl/otbn_start_stop_control.sv
- rtl/otbn_core.sv
file_type: systemVerilogSource
diff --git a/hw/ip/otbn/rtl/otbn.sv b/hw/ip/otbn/rtl/otbn.sv
index 9f049c8..3cf29de 100644
--- a/hw/ip/otbn/rtl/otbn.sv
+++ b/hw/ip/otbn/rtl/otbn.sv
@@ -13,7 +13,11 @@
import otbn_reg_pkg::*;
#(
parameter regfile_e RegFile = RegFileFF,
- parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}
+ parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}},
+
+ // Default seed and permutation for URND LFSR
+ parameter urnd_lfsr_seed_t RndCnstUrndLfsrSeed = RndCnstUrndLfsrSeedDefault,
+ parameter urnd_chunk_lfsr_perm_t RndCnstUrndChunkLfsrPerm = RndCnstUrndChunkLfsrPermDefault
) (
input clk_i,
input rst_ni,
@@ -531,9 +535,12 @@
end
// Mux between model and RTL implementation at runtime.
- logic done_model, done_rtl;
- logic start_model, start_rtl;
- err_bits_t err_bits_model, err_bits_rtl;
+ logic done_model, done_rtl;
+ logic start_model, start_rtl;
+ err_bits_t err_bits_model, err_bits_rtl;
+ logic edn_rnd_data_valid;
+ logic edn_urnd_data_valid;
+ logic [255:0] edn_rnd_data_model;
assign done = otbn_use_model ? done_model : done_rtl;
assign err_bits = otbn_use_model ? err_bits_model : err_bits_rtl;
@@ -541,6 +548,15 @@
assign start_rtl = start & ~otbn_use_model;
// Model (Instruction Set Simulator)
+ // In model only runs, leave valid signals high and supply constant RND data for EDN which will
+ // allow the model to continue without RND/URND related stalls.
+ // TODO: Implement proper EDN requests in model only runs
+ localparam logic [WLEN-1:0] ModelOnlyEdnVal = {{(WLEN / 4){4'h9}}};
+
+ assign edn_rnd_data_valid = otbn_use_model ? 1'b1 : edn_rnd_req & edn_rnd_ack;
+ assign edn_urnd_data_valid = otbn_use_model ? 1'b1 : edn_urnd_req & edn_urnd_ack;
+ assign edn_rnd_data_model = otbn_use_model ? ModelOnlyEdnVal : edn_rnd_data;
+
otbn_core_model #(
.DmemSizeByte(DmemSizeByte),
.ImemSizeByte(ImemSizeByte),
@@ -557,14 +573,20 @@
.start_addr_i (start_addr),
- .err_o ()
+ .err_o (),
+
+ .edn_rnd_data_valid_i ( edn_rnd_data_valid ),
+ .edn_rnd_data_i ( edn_rnd_data ),
+ .edn_urnd_data_valid_i ( edn_urnd_data_valid )
);
// RTL implementation
otbn_core #(
.RegFile(RegFile),
.DmemSizeByte(DmemSizeByte),
- .ImemSizeByte(ImemSizeByte)
+ .ImemSizeByte(ImemSizeByte),
+ .RndCnstUrndLfsrSeed(RndCnstUrndLfsrSeed),
+ .RndCnstUrndChunkLfsrPerm(RndCnstUrndChunkLfsrPerm)
) u_otbn_core (
.clk_i,
.rst_ni,
@@ -590,7 +612,15 @@
.dmem_wmask_o (dmem_wmask_core_nointeg),
.dmem_rdata_i (dmem_rdata_core_nointeg),
.dmem_rvalid_i (dmem_rvalid_core),
- .dmem_rerror_i (dmem_rerror_core)
+ .dmem_rerror_i (dmem_rerror_core),
+
+ .edn_rnd_req_o (edn_rnd_req),
+ .edn_rnd_ack_i (edn_rnd_ack),
+ .edn_rnd_data_i (edn_rnd_data),
+
+ .edn_urnd_req_o (edn_urnd_req),
+ .edn_urnd_ack_i (edn_urnd_ack),
+ .edn_urnd_data_i (edn_urnd_data)
);
`else
otbn_core #(
diff --git a/hw/ip/otbn/rtl/otbn_alu_bignum.sv b/hw/ip/otbn/rtl/otbn_alu_bignum.sv
index 1f6afcf..eeb207d 100644
--- a/hw/ip/otbn/rtl/otbn_alu_bignum.sv
+++ b/hw/ip/otbn/rtl/otbn_alu_bignum.sv
@@ -88,7 +88,8 @@
input flags_t mac_operation_flags_i,
input flags_t mac_operation_flags_en_i,
- input logic [WLEN-1:0] rnd_i
+ input logic [WLEN-1:0] rnd_data_i,
+ input logic [WLEN-1:0] urnd_data_i
);
///////////
// ISPRs //
@@ -195,7 +196,8 @@
unique case (ispr_addr_i)
IsprMod: ispr_rdata_o = mod_q;
- IsprRnd: ispr_rdata_o = rnd_i;
+ IsprRnd: ispr_rdata_o = rnd_data_i;
+ IsprUrnd: ispr_rdata_o = urnd_data_i;
IsprAcc: ispr_rdata_o = ispr_acc_i;
IsprFlags: ispr_rdata_o = {{(WLEN - (NFlagGroups * FlagsWidth)){1'b0}}, flags_flattened};
default: ;
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;
diff --git a/hw/ip/otbn/rtl/otbn_core.sv b/hw/ip/otbn/rtl/otbn_core.sv
index 48013c4..9a92c3f 100644
--- a/hw/ip/otbn/rtl/otbn_core.sv
+++ b/hw/ip/otbn/rtl/otbn_core.sv
@@ -20,6 +20,10 @@
// Size of the data memory, in bytes
parameter int DmemSizeByte = 4096,
+ // Default seed and permutation for URND LFSR
+ parameter urnd_lfsr_seed_t RndCnstUrndLfsrSeed = RndCnstUrndLfsrSeedDefault,
+ parameter urnd_chunk_lfsr_perm_t RndCnstUrndChunkLfsrPerm = RndCnstUrndChunkLfsrPermDefault,
+
localparam int ImemAddrWidth = prim_util_pkg::vbits(ImemSizeByte),
localparam int DmemAddrWidth = prim_util_pkg::vbits(DmemSizeByte)
)(
@@ -61,17 +65,6 @@
input logic edn_urnd_ack_i,
input logic [EdnDataWidth-1:0] edn_urnd_data_i
);
- // Random number
- // TODO: Hook up to RNG distribution network
- // TODO: Decide what guarantees we make for random numbers on CSRs/WSRs, and how they might or
- // might not come from the same source.
- logic [WLEN-1:0] rnd;
-
- // Constant for now until RNG is set up. This constant is the same in the model and must be
- // altered there to match is altered here (the `_random_value` variable in the `RandWSR` class in
- // dv/otbn/sim/wsr.py).
- assign rnd = 256'h9999999999999999999999999999999999999999999999999999999999999999;
-
// Fetch request (the next instruction)
logic [ImemAddrWidth-1:0] insn_fetch_req_addr;
logic insn_fetch_req_valid;
@@ -148,6 +141,41 @@
logic ispr_acc_wr_en;
logic ispr_init;
+ logic rnd_req;
+ logic rnd_prefetch_req;
+ logic rnd_valid;
+ logic [WLEN-1:0] rnd_data;
+
+ logic urnd_reseed_req;
+ logic urnd_reseed_busy;
+ logic urnd_advance;
+ logic [WLEN-1:0] urnd_data;
+
+ logic controller_start;
+ logic [ImemAddrWidth-1:0] controller_start_addr;
+
+ // Start stop control start OTBN execution when requested and deals with any pre start or post
+ // stop actions.
+ otbn_start_stop_control #(
+ .ImemSizeByte(ImemSizeByte)
+ ) u_otbn_start_stop_control (
+ .clk_i,
+ .rst_ni,
+
+ .start_i,
+ .start_addr_i,
+
+ .controller_start_o (controller_start),
+ .controller_start_addr_o (controller_start_addr),
+ .controller_done_i (done_o),
+
+ .urnd_reseed_req_o (urnd_reseed_req),
+ .urnd_reseed_busy_i (urnd_reseed_busy),
+ .urnd_advance_o (urnd_advance),
+
+ .ispr_init_o (ispr_init)
+ );
+
// Depending on its usage, the instruction address (program counter) is qualified by two valid
// signals: insn_fetch_resp_valid (together with the undecoded instruction data), and insn_valid
// for valid decoded (i.e. legal) instructions. Duplicate the signal in the source code for
@@ -208,12 +236,12 @@
.clk_i,
.rst_ni,
- .start_i,
+ .start_i (controller_start),
.done_o,
.err_bits_o,
- .start_addr_i,
+ .start_addr_i (controller_start_addr),
// Next instruction selection (to instruction fetch)
.insn_fetch_req_addr_o (insn_fetch_req_addr),
@@ -288,8 +316,11 @@
.ispr_base_wr_en_o (ispr_base_wr_en),
.ispr_bignum_wdata_o (ispr_bignum_wdata),
.ispr_bignum_wr_en_o (ispr_bignum_wr_en),
- .ispr_init_o (ispr_init),
- .ispr_rdata_i (ispr_rdata)
+ .ispr_rdata_i (ispr_rdata),
+
+ .rnd_req_o (rnd_req),
+ .rnd_prefetch_req_o (rnd_prefetch_req),
+ .rnd_valid_i (rnd_valid)
);
// Load store unit: read and write data from data memory
@@ -406,7 +437,8 @@
.mac_operation_flags_i (mac_bignum_operation_flags),
.mac_operation_flags_en_i (mac_bignum_operation_flags_en),
- .rnd_i (rnd)
+ .rnd_data_i (rnd_data),
+ .urnd_data_i (urnd_data)
);
otbn_mac_bignum u_otbn_mac_bignum (
@@ -425,17 +457,31 @@
.ispr_acc_wr_en_i (ispr_acc_wr_en)
);
- logic unused_edn_rnd_ack;
- logic [EdnDataWidth-1:0] unused_edn_rnd_data;
- logic unused_edn_urnd_ack;
- logic [EdnDataWidth-1:0] unused_edn_urnd_data;
+ otbn_rnd #(
+ .RndCnstUrndLfsrSeed (RndCnstUrndLfsrSeed),
+ .RndCnstUrndChunkLfsrPerm (RndCnstUrndChunkLfsrPerm)
+ ) u_otbn_rnd (
+ .clk_i,
+ .rst_ni,
- // Tie-off EDN interface
- assign unused_edn_rnd_ack = edn_rnd_ack_i;
- assign unused_edn_rnd_data = edn_rnd_data_i;
- assign edn_rnd_req_o = 1'b0;
+ .rnd_req_i (rnd_req),
+ .rnd_prefetch_req_i (rnd_prefetch_req),
+ .rnd_valid_o (rnd_valid),
+ .rnd_data_o (rnd_data),
- assign unused_edn_urnd_ack = edn_urnd_ack_i;
- assign unused_edn_urnd_data = edn_urnd_data_i;
- assign edn_urnd_req_o = 1'b0;
+ .urnd_reseed_req_i (urnd_reseed_req),
+ .urnd_reseed_busy_o (urnd_reseed_busy),
+ .urnd_advance_i (urnd_advance),
+ .urnd_data_o (urnd_data),
+
+ .edn_rnd_req_o,
+ .edn_rnd_ack_i,
+ .edn_rnd_data_i,
+
+ .edn_urnd_req_o,
+ .edn_urnd_ack_i,
+ .edn_urnd_data_i
+ );
+
+ `ASSERT(edn_req_stable, edn_rnd_req_o & ~edn_rnd_ack_i |=> edn_rnd_req_o)
endmodule
diff --git a/hw/ip/otbn/rtl/otbn_pkg.sv b/hw/ip/otbn/rtl/otbn_pkg.sv
index c1b1c7b..a0dea0b 100644
--- a/hw/ip/otbn/rtl/otbn_pkg.sv
+++ b/hw/ip/otbn/rtl/otbn_pkg.sv
@@ -173,39 +173,43 @@
// Control and Status Registers (CSRs)
parameter int CsrNumWidth = 12;
typedef enum logic [CsrNumWidth-1:0] {
- CsrFg0 = 12'h7C0,
- CsrFg1 = 12'h7C1,
- CsrFlags = 12'h7C8,
- CsrMod0 = 12'h7D0,
- CsrMod1 = 12'h7D1,
- CsrMod2 = 12'h7D2,
- CsrMod3 = 12'h7D3,
- CsrMod4 = 12'h7D4,
- CsrMod5 = 12'h7D5,
- CsrMod6 = 12'h7D6,
- CsrMod7 = 12'h7D7,
- CsrRnd = 12'hFC0
+ CsrFg0 = 12'h7C0,
+ CsrFg1 = 12'h7C1,
+ CsrFlags = 12'h7C8,
+ CsrMod0 = 12'h7D0,
+ CsrMod1 = 12'h7D1,
+ CsrMod2 = 12'h7D2,
+ CsrMod3 = 12'h7D3,
+ CsrMod4 = 12'h7D4,
+ CsrMod5 = 12'h7D5,
+ CsrMod6 = 12'h7D6,
+ CsrMod7 = 12'h7D7,
+ CsrRnd = 12'hFC0,
+ CsrRndPrefetch = 12'hFC1,
+ CsrUrnd = 12'hFC2
} csr_e;
// Wide Special Purpose Registers (WSRs)
- parameter int NWsr = 3; // Number of WSRs
+ parameter int NWsr = 4; // Number of WSRs
parameter int WsrNumWidth = $clog2(NWsr);
typedef enum logic [WsrNumWidth-1:0] {
WsrMod = 'd0,
WsrRnd = 'd1,
- WsrAcc = 'd2
+ WsrAcc = 'd2,
+ WsrUrnd = 'd3
} wsr_e;
// Internal Special Purpose Registers (ISPRs)
// CSRs and WSRs have some overlap into what they map into. ISPRs are the actual registers in the
// design which CSRs and WSRs are mapped on to.
- parameter int NIspr = NWsr + 1;
+ parameter int NIspr = 5;
parameter int IsprNumWidth = $clog2(NIspr);
typedef enum logic [IsprNumWidth-1:0] {
IsprMod = 'd0,
IsprRnd = 'd1,
IsprAcc = 'd2,
- IsprFlags = 'd3
+ IsprFlags = 'd3,
+ IsprUrnd = 'd4
} ispr_e;
typedef logic [$clog2(NFlagGroups)-1:0] flag_group_t;
@@ -352,8 +356,33 @@
// States for controller state machine
typedef enum logic [1:0] {
OtbnStateHalt,
+ OtbnStateUrndRefresh,
OtbnStateRun,
OtbnStateStall
} otbn_state_e;
+ typedef enum logic [1:0] {
+ OtbnStartStopStateHalt,
+ OtbnStartStopStateUrndRefresh,
+ OtbnStartStopStateRunning
+ } otbn_start_stop_state_e;
+
+ // URNG PRNG default LFSR seed and permutation
+ // A single default seed is split into 64 bit chunks, a seperate LFSR is used for each chunk. All
+ // LFSR chunks use the same permutation.
+ // These LFSR parameters have been generated with
+ // $ ./util/design/gen-lfsr-seed.py --width 256 --seed 2840984437 --prefix "Urnd"
+ parameter int UrndLfsrWidth = 256;
+ typedef logic [UrndLfsrWidth-1:0] urnd_lfsr_seed_t;
+ parameter urnd_lfsr_seed_t RndCnstUrndLfsrSeedDefault = 256'h84ddfadaf7e1134d70aa1c59de6197ff25a4fe335d095f1e2cba89acbe4a07e9;
+
+ // These LFSR parameters have been generated with
+ // $ ./util/design/gen-lfsr-seed.py --width 64 --seed 2840984437 --prefix "UrndChunk"
+ parameter int UrndChunkLfsrWidth = 64;
+ typedef logic [UrndChunkLfsrWidth-1:0][$clog2(UrndChunkLfsrWidth)-1:0] urnd_chunk_lfsr_perm_t;
+ parameter urnd_chunk_lfsr_perm_t RndCnstUrndChunkLfsrPermDefault = {
+ 128'h911992e063cafd4f3b13ee5682b969be,
+ 256'h86c701ecc39d9d483bdcacb5a15340b0988e2336e955ddd0dc01ab17e173726e
+ };
+
endpackage
diff --git a/hw/ip/otbn/rtl/otbn_rnd.sv b/hw/ip/otbn/rtl/otbn_rnd.sv
new file mode 100644
index 0000000..aae1399
--- /dev/null
+++ b/hw/ip/otbn/rtl/otbn_rnd.sv
@@ -0,0 +1,175 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+`include "prim_assert.sv"
+
+/**
+ * OTBN random number coordination
+ *
+ * This module implements the RND, RND_PREFETCH and URND CSRs/WSRs. The EDN (entropy distribution
+ * network) provides the bits for random numbers. RND gives direct access to EDN bits. URND provides
+ * bits from an LFSR that is seeded with bits from the EDN.
+ */
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// IMPORTANT NOTE: //
+// DO NOT USE THIS BLINDLY! //
+// //
+// This is an initial prototype of the random number functionality in OTBN. Details are still //
+// under discussion and subject to change. It has not yet been verified this provides the //
+// necessary guarantees required for the various uses of random numbers in OTBN software. //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+module otbn_rnd import otbn_pkg::*;
+#(
+ parameter urnd_lfsr_seed_t RndCnstUrndLfsrSeed = RndCnstUrndLfsrSeedDefault,
+ parameter urnd_chunk_lfsr_perm_t RndCnstUrndChunkLfsrPerm = RndCnstUrndChunkLfsrPermDefault
+) (
+ input logic clk_i,
+ input logic rst_ni,
+
+ input logic rnd_req_i,
+ input logic rnd_prefetch_req_i,
+ output logic rnd_valid_o,
+ output logic [WLEN-1:0] rnd_data_o,
+
+ // Request URND LFSR reseed from the EDN
+ input logic urnd_reseed_req_i,
+ // Remains asserted whilst reseed is in progress
+ output logic urnd_reseed_busy_o,
+ // When asserted URND LFSR state advances. It is permissible to advance the state whilst
+ // reseeding.
+ input logic urnd_advance_i,
+ // URND data from LFSR
+ output logic [WLEN-1:0] urnd_data_o,
+
+ // Entropy distribution network (EDN)
+ output logic edn_rnd_req_o,
+ input logic edn_rnd_ack_i,
+ input logic [EdnDataWidth-1:0] edn_rnd_data_i,
+
+ output logic edn_urnd_req_o,
+ input logic edn_urnd_ack_i,
+ input logic [EdnDataWidth-1:0] edn_urnd_data_i
+);
+ // Determine how many LFSR chunks are required to fill a full WLEN register
+ localparam int LfsrChunksPerWLEN = WLEN / UrndChunkLfsrWidth;
+ localparam int BytesPerLfsrChunk = UrndChunkLfsrWidth / 8;
+
+ logic rnd_valid_q, rnd_valid_d;
+ logic [WLEN-1:0] rnd_data_q, rnd_data_d;
+ logic rnd_data_en;
+ logic rnd_req_complete;
+ logic edn_rnd_req_complete;
+ logic edn_rnd_req_start;
+
+ logic edn_rnd_req_q, edn_rnd_req_d;
+
+ ////////////////////////
+ // RND Implementation //
+ ////////////////////////
+
+ assign rnd_req_complete = rnd_req_i & rnd_valid_o;
+ assign edn_rnd_req_complete = edn_rnd_req_o & edn_rnd_ack_i;
+
+ assign rnd_data_en = edn_rnd_req_complete;
+ // RND becomes valid when EDN request completes and provides new bits. Valid is cleared when OTBN
+ // reads RND
+ assign rnd_valid_d = (rnd_valid_q & ~rnd_req_complete) | edn_rnd_req_complete;
+ assign rnd_data_d = edn_rnd_data_i;
+
+ // Start an EDN request when there is a prefetch or an attempt at reading RND when RND data is not
+ // available. Signalling `edn_rnd_req_start` whilst there is an outstanding request has no effect and
+ // is harmless.
+ assign edn_rnd_req_start = rnd_prefetch_req_i | (rnd_req_i & ~rnd_valid_q);
+
+ // Assert `edn_rnd_req_o` when a request is started and keep it asserted until the request is done
+ assign edn_rnd_req_d = (edn_rnd_req_q | edn_rnd_req_start) & ~edn_rnd_req_complete;
+
+ assign edn_rnd_req_o = edn_rnd_req_q;
+
+ always_ff @(posedge clk_i) begin
+ if (rnd_data_en) begin
+ rnd_data_q <= rnd_data_d;
+ end
+ end
+
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if (!rst_ni) begin
+ rnd_valid_q <= 1'b0;
+ edn_rnd_req_q <= 1'b0;
+ end else begin
+ rnd_valid_q <= rnd_valid_d;
+ edn_rnd_req_q <= edn_rnd_req_d;
+ end
+ end
+
+ assign rnd_valid_o = rnd_valid_q;
+ assign rnd_data_o = rnd_data_q;
+
+ /////////////////////////
+ // URND Implementation //
+ /////////////////////////
+
+ logic edn_urnd_req_complete;
+ logic edn_urnd_req_q, edn_urnd_req_d;
+
+ assign edn_urnd_req_complete = edn_urnd_req_o & edn_urnd_ack_i;
+ assign edn_urnd_req_d = (edn_urnd_req_q | urnd_reseed_req_i) & ~edn_urnd_req_complete;
+
+ assign edn_urnd_req_o = edn_urnd_req_q;
+ assign urnd_reseed_busy_o = edn_urnd_req_q;
+
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if (!rst_ni) begin
+ edn_urnd_req_q <= 1'b0;
+ end else begin
+ edn_urnd_req_q <= edn_urnd_req_d;
+ end
+ end
+
+ logic lfsr_seed_en;
+ logic [UrndChunkLfsrWidth-1:0] lfsr_seed [LfsrChunksPerWLEN];
+ logic [UrndChunkLfsrWidth-1:0] lfsr_state [LfsrChunksPerWLEN];
+
+ assign lfsr_seed_en = edn_urnd_req_complete;
+
+ // We use multiple LFSR instances each having a width of ChunkSize.
+ // This is a functional prototype of the final URND functionality and is subject to change
+ // https://github.com/lowRISC/opentitan/issues/6083
+ for (genvar c = 0; c < LfsrChunksPerWLEN; c++) begin : gen_lfsr_chunks
+ localparam logic [UrndChunkLfsrWidth-1:0] LfsrChunkSeed =
+ RndCnstUrndLfsrSeed[c * UrndChunkLfsrWidth +: UrndChunkLfsrWidth];
+
+ assign lfsr_seed[c] = edn_urnd_data_i[c * UrndChunkLfsrWidth+: UrndChunkLfsrWidth];
+
+ prim_lfsr #(
+ .LfsrType ( "GAL_XOR" ),
+ .LfsrDw ( UrndChunkLfsrWidth ),
+ .StateOutDw ( UrndChunkLfsrWidth ),
+ .DefaultSeed ( LfsrChunkSeed ),
+ .StatePermEn ( 1'b1 ),
+ .StatePerm ( RndCnstUrndChunkLfsrPerm )
+ ) u_lfsr_chunk (
+ .clk_i ( clk_i ),
+ .rst_ni ( rst_ni ),
+ .seed_en_i ( lfsr_seed_en ),
+ .seed_i ( lfsr_seed[c] ),
+ .lfsr_en_i ( urnd_advance_i ),
+ .entropy_i ( '0 ),
+ .state_o ( lfsr_state[c] )
+ );
+ end
+
+ // Further "scramble" the LFSR state at the byte level to break linear shift patterns.
+ for (genvar c = 0; c < LfsrChunksPerWLEN; c++) begin : gen_lfsr_state_scramble_outer
+ for (genvar b = 0;b < BytesPerLfsrChunk; b++) begin : gen_lfsr_start_scramble_inner
+ assign urnd_data_o[b * 8 + c * UrndChunkLfsrWidth +: 8] =
+ prim_cipher_pkg::sbox4_8bit(lfsr_state[c][b*8 +: 8], prim_cipher_pkg::PRINCE_SBOX4);
+ end
+ end
+
+ `ASSERT(rnd_req_stable, rnd_req_i & ~rnd_valid_o |=> rnd_req_i)
+ `ASSERT(rnd_clear_on_req_complete, rnd_req_complete |=> ~rnd_valid_q)
+endmodule
diff --git a/hw/ip/otbn/rtl/otbn_start_stop_control.sv b/hw/ip/otbn/rtl/otbn_start_stop_control.sv
new file mode 100644
index 0000000..b6d7c5b
--- /dev/null
+++ b/hw/ip/otbn/rtl/otbn_start_stop_control.sv
@@ -0,0 +1,98 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+/**
+ * State machine to handle actions that occur around the start and stop of OTBN.
+ *
+ * This recieves the start signals from the top-level and passes them on to the
+ * controller to begin execution when pre-start actions have finished.
+ *
+ * pre-start actions:
+ * - Seed LFSR for URND
+ */
+
+`include "prim_assert.sv"
+
+module otbn_start_stop_control
+ import otbn_pkg::*;
+#(
+ // Size of the instruction memory, in bytes
+ parameter int ImemSizeByte = 4096,
+ localparam int ImemAddrWidth = prim_util_pkg::vbits(ImemSizeByte)
+) (
+ input logic clk_i,
+ input logic rst_ni,
+
+ input logic start_i,
+ input logic [ImemAddrWidth-1:0] start_addr_i,
+
+ output logic controller_start_o,
+ output logic [ImemAddrWidth-1:0] controller_start_addr_o,
+
+ input logic controller_done_i,
+
+ output logic urnd_reseed_req_o,
+ input logic urnd_reseed_busy_i,
+ output logic urnd_advance_o,
+
+ output logic ispr_init_o
+);
+ otbn_start_stop_state_e state_q, state_d;
+
+ logic [ImemAddrWidth-1:0] start_addr_q;
+ logic start_addr_en;
+
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if (!rst_ni) begin
+ state_q <= OtbnStartStopStateHalt;
+ end else begin
+ state_q <= state_d;
+ end
+ end
+
+ always_ff @(posedge clk_i) begin
+ if (start_addr_en) begin
+ start_addr_q <= start_addr_i;
+ end
+ end
+
+ always_comb begin
+ urnd_reseed_req_o = 1'b0;
+ urnd_advance_o = 1'b0;
+ start_addr_en = 1'b0;
+ state_d = state_q;
+ ispr_init_o = 1'b0;
+
+ unique case(state_q)
+ OtbnStartStopStateHalt: begin
+ if (start_i) begin
+ start_addr_en = 1'b1;
+ urnd_reseed_req_o = 1'b1;
+ ispr_init_o = 1'b1;
+ state_d = OtbnStartStopStateUrndRefresh;
+ end
+ end
+ OtbnStartStopStateUrndRefresh: begin
+ if (!urnd_reseed_busy_i) begin
+ state_d = OtbnStartStopStateRunning;
+ end
+ end
+ OtbnStartStopStateRunning: begin
+ urnd_advance_o = 1'b1;
+ if (controller_done_i) begin
+ state_d = OtbnStartStopStateHalt;
+ end
+ end
+ default: ;
+ endcase
+ end
+
+ // Logic seperate from main FSM code to avoid false combinational loop warning from verilator
+ assign controller_start_o = (state_q == OtbnStartStopStateUrndRefresh) & !urnd_reseed_busy_i;
+
+ `ASSERT(StartStopStateValid,
+ state_q inside {OtbnStartStopStateHalt, OtbnStartStopStateUrndRefresh, OtbnStartStopStateRunning})
+
+ assign controller_start_addr_o = start_addr_q;
+endmodule
diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
index d05e7ea..22fb39a 100644
--- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
+++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
@@ -4918,6 +4918,26 @@
expose: "true"
name_top: OtbnRegFile
}
+ {
+ name: RndCnstUrndLfsrSeed
+ desc: Default seed of the PRNG used for URND.
+ type: otbn_pkg::urnd_lfsr_seed_t
+ randcount: 256
+ randtype: data
+ name_top: RndCnstOtbnUrndLfsrSeed
+ default: 0x1102be301eb1a8d04814ac33b45de425069e5959df06d42254a25afae52a5963
+ randwidth: 256
+ }
+ {
+ name: RndCnstUrndChunkLfsrPerm
+ desc: Permutation applied to the LFSR chunks of the PRNG used for URND.
+ type: otbn_pkg::urnd_chunk_lfsr_perm_t
+ randcount: 64
+ randtype: perm
+ name_top: RndCnstOtbnUrndChunkLfsrPerm
+ default: 0x96c27a70b8a45cfd2d3b52b1f040db79a46ed80e5942bc02513fbdfd5a98a66805bc17dded6ccd3271a3e37a08c92847
+ randwidth: 384
+ }
]
inter_signal_list:
[
diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
index fcbc3dc..a93e716 100644
--- a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
+++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
@@ -2243,7 +2243,9 @@
otbn #(
.AlertAsyncOn(alert_handler_reg_pkg::AsyncOn[29:28]),
- .RegFile(OtbnRegFile)
+ .RegFile(OtbnRegFile),
+ .RndCnstUrndLfsrSeed(RndCnstOtbnUrndLfsrSeed),
+ .RndCnstUrndChunkLfsrPerm(RndCnstOtbnUrndChunkLfsrPerm)
) u_otbn (
// Interrupt
diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey_rnd_cnst_pkg.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey_rnd_cnst_pkg.sv
index 2716749..12dbfc5 100644
--- a/hw/top_earlgrey/rtl/autogen/top_earlgrey_rnd_cnst_pkg.sv
+++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey_rnd_cnst_pkg.sv
@@ -214,4 +214,18 @@
160'h33D0BEDD4D8C36CB029C0CFD5CB87F2170991F42
};
+ ////////////////////////////////////////////
+ // otbn
+ ////////////////////////////////////////////
+ // Default seed of the PRNG used for URND.
+ parameter otbn_pkg::urnd_lfsr_seed_t RndCnstOtbnUrndLfsrSeed = {
+ 256'h1102BE301EB1A8D04814AC33B45DE425069E5959DF06D42254A25AFAE52A5963
+ };
+
+ // Permutation applied to the LFSR chunks of the PRNG used for URND.
+ parameter otbn_pkg::urnd_chunk_lfsr_perm_t RndCnstOtbnUrndChunkLfsrPerm = {
+ 128'h96C27A70B8A45CFD2D3B52B1F040DB79,
+ 256'hA46ED80E5942BC02513FBDFD5A98A66805BC17DDED6CCD3271A3E37A08C92847
+ };
+
endpackage : top_earlgrey_rnd_cnst_pkg
diff --git a/sw/device/tests/dif/dif_otbn_smoketest.c b/sw/device/tests/dif/dif_otbn_smoketest.c
index 57268d0..7ec647b 100644
--- a/sw/device/tests/dif/dif_otbn_smoketest.c
+++ b/sw/device/tests/dif/dif_otbn_smoketest.c
@@ -11,6 +11,23 @@
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+// Temporary solution to configue/enable the EDN and CSRNG to allow OTBN to run
+// before a DIF is available, https://github.com/lowRISC/opentitan/issues/6082
+static const uint32_t kEntropySrcConfRegOffset = 0x18;
+static const uint32_t kCsrngCtrlRegOffset = 0x14;
+static const uint32_t kEdnCtrlRegOffset = 0x14;
+
+static void setup_edn(void) {
+ mmio_region_write32(mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR),
+ kEntropySrcConfRegOffset, 0x2);
+ mmio_region_write32(mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR),
+ kCsrngCtrlRegOffset, 0x1);
+ mmio_region_write32(mmio_region_from_addr(TOP_EARLGREY_EDN0_BASE_ADDR),
+ kEdnCtrlRegOffset, 0x9);
+ mmio_region_write32(mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR),
+ kEdnCtrlRegOffset, 0x9);
+}
+
OTBN_DECLARE_APP_SYMBOLS(barrett384);
OTBN_DECLARE_PTR_SYMBOL(barrett384, wrap_barrett384);
@@ -129,6 +146,8 @@
}
bool test_main() {
+ setup_edn();
+
dif_otbn_config_t otbn_config = {
.base_addr = mmio_region_from_addr(TOP_EARLGREY_OTBN_BASE_ADDR),
};
diff --git a/sw/device/tests/otbn/otbn_ecdsa_p256_test.c b/sw/device/tests/otbn/otbn_ecdsa_p256_test.c
index a54f536..550dfd9 100644
--- a/sw/device/tests/otbn/otbn_ecdsa_p256_test.c
+++ b/sw/device/tests/otbn/otbn_ecdsa_p256_test.c
@@ -11,6 +11,23 @@
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+// Temporary solution to configue/enable the EDN and CSRNG to allow OTBN to run
+// before a DIF is available, https://github.com/lowRISC/opentitan/issues/6082
+static const uint32_t kEntropySrcConfRegOffset = 0x18;
+static const uint32_t kCsrngCtrlRegOffset = 0x14;
+static const uint32_t kEdnCtrlRegOffset = 0x14;
+
+static void setup_edn(void) {
+ mmio_region_write32(mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR),
+ kEntropySrcConfRegOffset, 0x2);
+ mmio_region_write32(mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR),
+ kCsrngCtrlRegOffset, 0x1);
+ mmio_region_write32(mmio_region_from_addr(TOP_EARLGREY_EDN0_BASE_ADDR),
+ kEdnCtrlRegOffset, 0x9);
+ mmio_region_write32(mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR),
+ kEdnCtrlRegOffset, 0x9);
+}
+
/**
* ECDSA sign and verify test with the NIST P-256 curve using OTBN.
*
@@ -270,6 +287,8 @@
}
bool test_main() {
+ setup_edn();
+
test_ecdsa_p256_roundtrip();
return true;
diff --git a/sw/device/tests/otbn/otbn_rsa_test.c b/sw/device/tests/otbn/otbn_rsa_test.c
index c22fd0c..b0be43e 100644
--- a/sw/device/tests/otbn/otbn_rsa_test.c
+++ b/sw/device/tests/otbn/otbn_rsa_test.c
@@ -11,6 +11,23 @@
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+// Temporary solution to configue/enable the EDN and CSRNG to allow OTBN to run
+// before a DIF is available, https://github.com/lowRISC/opentitan/issues/6082
+static const uint32_t kEntropySrcConfRegOffset = 0x18;
+static const uint32_t kCsrngCtrlRegOffset = 0x14;
+static const uint32_t kEdnCtrlRegOffset = 0x14;
+
+static void setup_edn(void) {
+ mmio_region_write32(mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR),
+ kEntropySrcConfRegOffset, 0x2);
+ mmio_region_write32(mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR),
+ kCsrngCtrlRegOffset, 0x1);
+ mmio_region_write32(mmio_region_from_addr(TOP_EARLGREY_EDN0_BASE_ADDR),
+ kEdnCtrlRegOffset, 0x9);
+ mmio_region_write32(mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR),
+ kEdnCtrlRegOffset, 0x9);
+}
+
/**
* End-to-end RSA encryption and decryption test using OTBN.
*
@@ -696,6 +713,8 @@
}
bool test_main() {
+ setup_edn();
+
test_rsa512_roundtrip();
test_rsa1024_roundtrip();