[otbn, rtl, dv] Add prefetch stage
The prefetch stage fetches instructions a cycle ahead of when they are
required. This is to enable flopped register file inputs to implement
blanking.
The prefetch stage isn't really speculative. It only prefetches where it
can be confident of the address, in situations where it can't
(specifically branches and jumps) it does not prefetch.
Signed-off-by: Greg Chadwick <gac@lowrisc.org>
diff --git a/hw/ip/otbn/dv/otbnsim/sim/insn.py b/hw/ip/otbn/dv/otbnsim/sim/insn.py
index 33cf51a..d3a15bd 100644
--- a/hw/ip/otbn/dv/otbnsim/sim/insn.py
+++ b/hw/ip/otbn/dv/otbnsim/sim/insn.py
@@ -301,6 +301,7 @@
class BEQ(OTBNInsn):
insn = insn_for_mnemonic('beq', 3)
affects_control = True
+ has_fetch_stall = True
def __init__(self, raw: int, op_vals: Dict[str, int]):
super().__init__(raw, op_vals)
@@ -326,6 +327,7 @@
class BNE(OTBNInsn):
insn = insn_for_mnemonic('bne', 3)
affects_control = True
+ has_fetch_stall = True
def __init__(self, raw: int, op_vals: Dict[str, int]):
super().__init__(raw, op_vals)
@@ -352,6 +354,7 @@
class JAL(OTBNInsn):
insn = insn_for_mnemonic('jal', 2)
affects_control = True
+ has_fetch_stall = True
def __init__(self, raw: int, op_vals: Dict[str, int]):
super().__init__(raw, op_vals)
@@ -373,6 +376,7 @@
class JALR(OTBNInsn):
insn = insn_for_mnemonic('jalr', 3)
affects_control = True
+ has_fetch_stall = True
def __init__(self, raw: int, op_vals: Dict[str, int]):
super().__init__(raw, op_vals)
diff --git a/hw/ip/otbn/dv/otbnsim/sim/isa.py b/hw/ip/otbn/dv/otbnsim/sim/isa.py
index 085016d..ff844f3 100644
--- a/hw/ip/otbn/dv/otbnsim/sim/isa.py
+++ b/hw/ip/otbn/dv/otbnsim/sim/isa.py
@@ -65,6 +65,10 @@
# false by the EmptyInsn subclass)
has_bits = True
+ # A class variable that is true if there will be a cycle of fetch stall
+ # after the instruction executes.
+ has_fetch_stall = False
+
def __init__(self, raw: int, op_vals: Dict[str, int]):
self.raw = raw
self.op_vals = op_vals
diff --git a/hw/ip/otbn/dv/otbnsim/sim/sim.py b/hw/ip/otbn/dv/otbnsim/sim/sim.py
index 6b44b78..be55444 100644
--- a/hw/ip/otbn/dv/otbnsim/sim/sim.py
+++ b/hw/ip/otbn/dv/otbnsim/sim/sim.py
@@ -109,8 +109,10 @@
pc_before = self.state.pc
self.state.commit(sim_stalled=False)
- # Fetch the next instruction
- self._next_insn = self._fetch(self.state.pc)
+ # Fetch the next instruction unless this instruction had
+ # `has_fetch_stall` set, in which case we inject a single cycle stall.
+ self._next_insn = (None if insn.has_fetch_stall
+ else self._fetch(self.state.pc))
disasm = insn.disassemble(pc_before)
if verbose:
@@ -129,7 +131,7 @@
if not self.state.running():
return (None, [])
- if self.state.fsm_state == FsmState.PRE_EXEC:
+ if self.state.fsm_state in [FsmState.PRE_EXEC, FsmState.FETCH_WAIT]:
# Zero INSN_CNT the cycle after we are told to start (and every
# cycle after that until we start executing instructions, but that
# doesn't really matter)
diff --git a/hw/ip/otbn/dv/otbnsim/sim/state.py b/hw/ip/otbn/dv/otbnsim/sim/state.py
index 97d2ef8..d8ec83a 100644
--- a/hw/ip/otbn/dv/otbnsim/sim/state.py
+++ b/hw/ip/otbn/dv/otbnsim/sim/state.py
@@ -24,13 +24,13 @@
The FSM diagram looks like:
- /---------------------------------------------\
- | |
- v /-> POST_EXEC --/
- IDLE -> PRE_EXEC -> EXEC <
- \-> LOCKING --\
- |
- /---------------------------------------------/
+ /----------------------------------------------------------\
+ | |
+ v /-> POST_EXEC --/
+ IDLE -> PRE_EXEC -> FETCH_WAIT -> EXEC <
+ \-> LOCKING --\
+ |
+ /----------------------------------------------------------/
|
v
LOCKED
@@ -39,10 +39,11 @@
fatal errors. It matches Status.IDLE. LOCKED represents the state when
there has been a fatal error. It matches Status.LOCKED.
- PRE_EXEC, EXEC, POST_EXEC and LOCKING correspond to Status.BUSY_EXECUTE.
- PRE_EXEC is the period after starting OTBN where we're still waiting for an
- EDN value to seed URND. EXEC is the period where we start fetching and
- executing instructions.
+ PRE_EXEC, FETCH_WAIT, EXEC, POST_EXEC and LOCKING correspond to
+ Status.BUSY_EXECUTE. PRE_EXEC is the period after starting OTBN where we're
+ still waiting for an EDN value to seed URND. FETCH_WAIT is the single cycle
+ delay after seeding URND to fill the prefetch stage. EXEC is the period
+ where we start fetching and executing instructions.
POST_EXEC and LOCKING are both used for the single cycle after we finish
executing where the STATUS register gets updated. The difference between
@@ -56,8 +57,9 @@
'''
IDLE = 0
PRE_EXEC = 10
- EXEC = 11
- POST_EXEC = 12
+ FETCH_WAIT = 11
+ EXEC = 12
+ POST_EXEC = 13
LOCKING = 13
LOCKED = 2550
@@ -286,7 +288,16 @@
if self.wsrs.URND.running:
# This part is strictly for standalone simulation. Otherwise
# we would set fsm_state before commit (at urnd_completed)
- self.fsm_state = FsmState.EXEC
+ self.fsm_state = FsmState.FETCH_WAIT
+
+ return
+
+ # FETCH_WAIT works like PRE_EXEC, but it's only ever a single cycle
+ # wait.
+ if self.fsm_state == FsmState.FETCH_WAIT:
+ self.ext_regs.commit()
+ self.fsm_state = FsmState.EXEC
+
return
# If we are in POST_EXEC mode, this is the single cycle after the end
diff --git a/hw/ip/otbn/dv/otbnsim/test/stats_test.py b/hw/ip/otbn/dv/otbnsim/test/stats_test.py
index 340e5f6..9b169bb 100644
--- a/hw/ip/otbn/dv/otbnsim/test/stats_test.py
+++ b/hw/ip/otbn/dv/otbnsim/test/stats_test.py
@@ -136,7 +136,7 @@
stats = _simulate_asm_file(asm_file, tmpdir)
# General statistics
- assert stats.stall_count == 3
+ assert stats.stall_count == 4
assert stats.get_insn_count() == 28
assert stats.insn_histo == {'addi': 22, 'loop': 4, 'loopi': 1, 'ecall': 1}
assert stats.func_calls == []
diff --git a/hw/ip/otbn/dv/verilator/otbn_top_sim.sv b/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
index 9195e18..cf96102 100644
--- a/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
+++ b/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
@@ -34,7 +34,6 @@
logic [ImemAddrWidth-1:0] imem_addr;
logic [38:0] imem_rdata;
logic imem_rvalid;
- logic imem_rerror;
// Data memory (DMEM) signals
logic dmem_req;
@@ -76,10 +75,10 @@
.imem_req_o ( imem_req ),
.imem_addr_o ( imem_addr ),
- .imem_wdata_o ( ),
- .imem_rdata_i ( imem_rdata[31:0] ),
+ .imem_rdata_i ( imem_rdata ),
.imem_rvalid_i ( imem_rvalid ),
- .imem_rerror_i ( imem_rerror ),
+
+ .insn_fetch_err_o ( ),
.dmem_req_o ( dmem_req ),
.dmem_write_o ( dmem_write ),
@@ -110,11 +109,6 @@
.sideload_key_shares_valid_i ( 2'b11 )
);
- // The top bits of IMEM rdata aren't currently used (they will eventually be used for integrity
- // checks both on the bus and within the core)
- logic unused_imem_top_rdata;
- assign unused_imem_top_rdata = &{1'b0, imem_rdata[38:32]};
-
localparam logic [WLEN-1:0] FixedEdnVal = {{(WLEN / 4){4'h9}}};
edn_req_t rnd_req;
@@ -265,9 +259,6 @@
.cfg_i ( '0 )
);
- // No integrity errors in Verilator testbench
- assign imem_rerror = 1'b0;
-
// When OTBN is done let a few more cycles run then finish simulation
logic [1:0] finish_counter;
diff --git a/hw/ip/otbn/rtl/otbn.sv b/hw/ip/otbn/rtl/otbn.sv
index 256b58f..eccc639 100644
--- a/hw/ip/otbn/rtl/otbn.sv
+++ b/hw/ip/otbn/rtl/otbn.sv
@@ -181,9 +181,9 @@
logic imem_req_core;
logic imem_write_core;
logic [ImemIndexWidth-1:0] imem_index_core;
- logic [31:0] imem_rdata_core;
+ logic [38:0] imem_rdata_core;
logic imem_rvalid_core;
- logic imem_rerror_core;
+ logic insn_fetch_err;
logic imem_req_bus;
logic imem_dummy_response_q, imem_dummy_response_d;
@@ -356,7 +356,7 @@
// the currently executed instruction from IMEM through the bus
// unintentionally.
assign imem_rdata_bus = !imem_access_core && !illegal_bus_access_q ? imem_rdata : 39'b0;
- assign imem_rdata_core = imem_rdata[31:0];
+ assign imem_rdata_core = imem_rdata;
// When an illegal bus access is seen, always return a dummy response the follow cycle.
assign imem_rvalid_bus = (~imem_access_core & imem_rvalid) | imem_dummy_response_q;
@@ -365,7 +365,6 @@
// No imem errors reported for bus reads. Integrity is carried through on the bus so integrity
// checking on TL responses will pick up any errors.
assign imem_rerror_bus = 1'b0;
- assign imem_rerror_core = imem_rerror;
// Data Memory (DMEM) ========================================================
@@ -662,8 +661,8 @@
// FATAL_ALERT_CAUSE register. The .de and .d values are equal for each bit, so that it can only
// be set, not cleared.
- assign hw2reg.fatal_alert_cause.imem_intg_violation.de = imem_rerror;
- assign hw2reg.fatal_alert_cause.imem_intg_violation.d = imem_rerror;
+ assign hw2reg.fatal_alert_cause.imem_intg_violation.de = insn_fetch_err;
+ assign hw2reg.fatal_alert_cause.imem_intg_violation.d = insn_fetch_err;
assign hw2reg.fatal_alert_cause.dmem_intg_violation.de = dmem_rerror;
assign hw2reg.fatal_alert_cause.dmem_intg_violation.d = dmem_rerror;
// TODO: Register file errors
@@ -693,7 +692,7 @@
reg2hw.alert_test.recov.qe;
logic [NumAlerts-1:0] alerts;
- assign alerts[AlertFatal] = imem_rerror |
+ assign alerts[AlertFatal] = insn_fetch_err |
dmem_rerror |
bus_intg_violation |
illegal_bus_access_d |
@@ -866,7 +865,8 @@
.imem_addr_o (imem_addr_core),
.imem_rdata_i (imem_rdata_core),
.imem_rvalid_i (imem_rvalid_core),
- .imem_rerror_i (imem_rerror_core),
+
+ .insn_fetch_err_o (insn_fetch_err),
.dmem_req_o (dmem_req_core),
.dmem_write_o (dmem_write_core),
@@ -917,7 +917,8 @@
.imem_addr_o (imem_addr_core),
.imem_rdata_i (imem_rdata_core),
.imem_rvalid_i (imem_rvalid_core),
- .imem_rerror_i (imem_rerror_core),
+
+ .insn_fetch_err_o (insn_fetch_err),
.dmem_req_o (dmem_req_core),
.dmem_write_o (dmem_write_core),
diff --git a/hw/ip/otbn/rtl/otbn_controller.sv b/hw/ip/otbn/rtl/otbn_controller.sv
index c8bedc6..91735a9 100644
--- a/hw/ip/otbn/rtl/otbn_controller.sv
+++ b/hw/ip/otbn/rtl/otbn_controller.sv
@@ -31,6 +31,7 @@
// Next instruction selection (to instruction fetch)
output logic insn_fetch_req_valid_o,
output logic [ImemAddrWidth-1:0] insn_fetch_req_addr_o,
+ output logic insn_fetch_resp_clear_o,
// Error from fetch requested last cycle
input logic insn_fetch_err_i,
@@ -135,7 +136,14 @@
input logic lifecycle_escalation_i,
input logic software_errs_fatal_i,
- input logic [1:0] sideload_key_shares_valid_i
+ input logic [1:0] sideload_key_shares_valid_i,
+
+ // Prefetch stage control
+ output logic prefetch_en_o,
+ output logic prefetch_loop_active_o,
+ output logic [31:0] prefetch_loop_iterations_o,
+ output logic [ImemAddrWidth-1:0] prefetch_loop_end_addr_o,
+ output logic [ImemAddrWidth-1:0] prefetch_loop_jump_addr_o
);
otbn_state_e state_q, state_d;
@@ -146,7 +154,8 @@
logic done_complete;
logic executing;
- logic insn_fetch_req_valid_raw;
+ logic insn_fetch_req_valid_raw;
+ logic [ImemAddrWidth-1:0] insn_fetch_req_addr_last;
logic stall;
logic ispr_stall;
@@ -274,13 +283,22 @@
assign next_insn_addr_wide = {1'b0, insn_addr_i} + 'd4;
assign next_insn_addr = next_insn_addr_wide[ImemAddrWidth-1:0];
+ // Record address for fetch request so it can be retried when an invalid response is received
+ always_ff @(posedge clk_i) begin
+ if (insn_fetch_req_valid_raw) begin
+ insn_fetch_req_addr_last <= insn_fetch_req_addr_o;
+ end
+ end
+
always_comb begin
state_d = state_q;
// `insn_fetch_req_valid_raw` is the value `insn_fetch_req_valid_o` before any errors are
// considered.
insn_fetch_req_valid_raw = 1'b0;
insn_fetch_req_addr_o = '0;
+ insn_fetch_resp_clear_o = 1'b1;
err_bits_en = 1'b0;
+ prefetch_en_o = 1'b0;
// TODO: Harden state machine
// TODO: Jumps/branches
@@ -291,6 +309,7 @@
insn_fetch_req_addr_o = '0;
insn_fetch_req_valid_raw = 1'b1;
+ prefetch_en_o = 1'b1;
// Enable error bits to zero them on start
err_bits_en = 1'b1;
@@ -298,15 +317,21 @@
end
OtbnStateRun: begin
insn_fetch_req_valid_raw = 1'b1;
+ prefetch_en_o = 1'b1;
- if (done_complete) begin
+ if (!insn_valid_i) begin
+ insn_fetch_req_addr_o = insn_fetch_req_addr_last;
+ end else if (done_complete) begin
state_d = OtbnStateHalt;
insn_fetch_req_valid_raw = 1'b0;
+ prefetch_en_o = 1'b0;
end else begin
- // When stalling refetch the same instruction to keep decode inputs constant
if (stall) begin
+ // When stalling don't request a new fetch and don't clear response either to keep
+ // current instruction.
state_d = OtbnStateStall;
- insn_fetch_req_addr_o = insn_addr_i;
+ insn_fetch_req_valid_raw = 1'b0;
+ insn_fetch_resp_clear_o = 1'b0;
end else begin
if (branch_taken) begin
insn_fetch_req_addr_o = branch_target;
@@ -319,13 +344,16 @@
end
end
OtbnStateStall: begin
- insn_fetch_req_valid_raw = 1'b1;
-
+ prefetch_en_o = 1'b1;
// When stalling refetch the same instruction to keep decode inputs constant
if (stall) begin
- state_d = OtbnStateStall;
- insn_fetch_req_addr_o = insn_addr_i;
+ state_d = OtbnStateStall;
+ //insn_fetch_req_addr_o = insn_addr_i;
+ insn_fetch_req_valid_raw = 1'b0;
+ insn_fetch_resp_clear_o = 1'b0;
end else begin
+ insn_fetch_req_valid_raw = 1'b1;
+
if (loop_jump) begin
insn_fetch_req_addr_o = loop_jump_addr;
end else begin
@@ -345,6 +373,9 @@
// On any error immediately halt, either going to OtbnStateLocked or OtbnStateHalt depending on
// whether it was a fatal error.
if (err) begin
+ prefetch_en_o = 1'b0;
+ insn_fetch_resp_clear_o = 1'b1;
+
if (!secure_wipe_running_i) begin
// Capture error bits on error unless a secure wipe is in progress
err_bits_en = 1'b1;
@@ -363,6 +394,8 @@
end
end
+ `ASSERT(InsnAlwaysValidInStall, state_q == OtbnStateStall |-> insn_valid_i)
+
// Anything that moves us or keeps us in the stall state should cause `stall` to be asserted
`ASSERT(StallIfNextStateStall, insn_valid_i & (state_d == OtbnStateStall) |-> stall)
@@ -379,7 +412,7 @@
end else if (branch_taken) begin
imem_addr_err = branch_target_overflow;
end else begin
- imem_addr_err = next_insn_addr_wide[ImemAddrWidth];
+ imem_addr_err = next_insn_addr_wide[ImemAddrWidth] & insn_valid_i;
end
end
end
@@ -531,7 +564,12 @@
.loop_err_o (loop_err),
.jump_or_branch_i (jump_or_branch),
- .otbn_stall_i (stall)
+ .otbn_stall_i (stall),
+
+ .prefetch_loop_active_o,
+ .prefetch_loop_iterations_o,
+ .prefetch_loop_end_addr_o,
+ .prefetch_loop_jump_addr_o
);
// loop_start_req indicates the instruction wishes to start a loop, loop_start_commit confirms it
diff --git a/hw/ip/otbn/rtl/otbn_core.sv b/hw/ip/otbn/rtl/otbn_core.sv
index ca377ed..fb87dd6 100644
--- a/hw/ip/otbn/rtl/otbn_core.sv
+++ b/hw/ip/otbn/rtl/otbn_core.sv
@@ -40,9 +40,10 @@
// Instruction memory (IMEM)
output logic imem_req_o,
output logic [ImemAddrWidth-1:0] imem_addr_o,
- input logic [31:0] imem_rdata_i,
+ input logic [38:0] imem_rdata_i,
input logic imem_rvalid_i,
- input logic imem_rerror_i,
+
+ output logic insn_fetch_err_o,
// Data memory (DMEM)
output logic dmem_req_o,
@@ -91,6 +92,7 @@
logic insn_fetch_resp_valid;
logic [ImemAddrWidth-1:0] insn_fetch_resp_addr;
logic [31:0] insn_fetch_resp_data;
+ logic insn_fetch_resp_clear;
logic insn_fetch_err;
// The currently executed instruction.
@@ -203,6 +205,12 @@
logic sec_wipe_mod_urnd;
logic sec_wipe_zero;
+ logic prefetch_en;
+ logic prefetch_loop_active;
+ logic [31:0] prefetch_loop_iterations;
+ logic [ImemAddrWidth-1:0] prefetch_loop_end_addr;
+ logic [ImemAddrWidth-1:0] prefetch_loop_jump_addr;
+
// Start stop control start OTBN execution when requested and deals with any pre start or post
// stop actions.
otbn_start_stop_control #(
@@ -255,7 +263,6 @@
.imem_addr_o,
.imem_rdata_i,
.imem_rvalid_i,
- .imem_rerror_i,
// Instruction to fetch
.insn_fetch_req_addr_i (insn_fetch_req_addr),
@@ -265,9 +272,17 @@
.insn_fetch_resp_addr_o (insn_fetch_resp_addr),
.insn_fetch_resp_valid_o (insn_fetch_resp_valid),
.insn_fetch_resp_data_o (insn_fetch_resp_data),
- .insn_fetch_err_o (insn_fetch_err)
+ .insn_fetch_resp_clear_i (insn_fetch_resp_clear),
+ .insn_fetch_err_o (insn_fetch_err),
+
+ .prefetch_en_i (prefetch_en),
+ .prefetch_loop_active_i (prefetch_loop_active),
+ .prefetch_loop_iterations_i (prefetch_loop_iterations),
+ .prefetch_loop_end_addr_i (prefetch_loop_end_addr),
+ .prefetch_loop_jump_addr_i (prefetch_loop_jump_addr)
);
+ assign insn_fetch_err_o = insn_fetch_err;
// Instruction decoder
otbn_decoder u_otbn_decoder (
@@ -303,8 +318,9 @@
.err_bits_o,
// Next instruction selection (to instruction fetch)
- .insn_fetch_req_addr_o (insn_fetch_req_addr),
- .insn_fetch_req_valid_o (insn_fetch_req_valid),
+ .insn_fetch_req_addr_o (insn_fetch_req_addr),
+ .insn_fetch_req_valid_o (insn_fetch_req_valid),
+ .insn_fetch_resp_clear_o (insn_fetch_resp_clear),
// Error from fetch requested last cycle
.insn_fetch_err_i (insn_fetch_err),
@@ -402,9 +418,18 @@
.lifecycle_escalation_i,
.software_errs_fatal_i,
- .sideload_key_shares_valid_i
+ .sideload_key_shares_valid_i,
+
+ .prefetch_en_o (prefetch_en),
+ .prefetch_loop_active_o (prefetch_loop_active),
+ .prefetch_loop_iterations_o (prefetch_loop_iterations),
+ .prefetch_loop_end_addr_o (prefetch_loop_end_addr),
+ .prefetch_loop_jump_addr_o (prefetch_loop_jump_addr)
);
+ `ASSERT(InsnDataStableInStall, u_otbn_controller.state_q == OtbnStateStall |->
+ insn_fetch_resp_data == $past(insn_fetch_resp_data))
+
assign insn_cnt_o = insn_cnt;
// Load store unit: read and write data from data memory
diff --git a/hw/ip/otbn/rtl/otbn_instruction_fetch.sv b/hw/ip/otbn/rtl/otbn_instruction_fetch.sv
index 92033ad..873c885 100644
--- a/hw/ip/otbn/rtl/otbn_instruction_fetch.sv
+++ b/hw/ip/otbn/rtl/otbn_instruction_fetch.sv
@@ -22,9 +22,8 @@
// Instruction memory (IMEM) interface. Read-only.
output logic imem_req_o,
output logic [ImemAddrWidth-1:0] imem_addr_o,
- input logic [31:0] imem_rdata_i,
+ input logic [38:0] imem_rdata_i,
input logic imem_rvalid_i,
- input logic imem_rerror_i,
// Next instruction selection (to instruction fetch)
input logic insn_fetch_req_valid_i,
@@ -34,24 +33,113 @@
output logic insn_fetch_resp_valid_o,
output logic [ImemAddrWidth-1:0] insn_fetch_resp_addr_o,
output logic [31:0] insn_fetch_resp_data_o,
+ input logic insn_fetch_resp_clear_i,
- output logic insn_fetch_err_o // ECC error seen in instruction fetch
+ output logic insn_fetch_err_o, // ECC error seen in instruction fetch
+
+ input logic prefetch_en_i,
+ input logic prefetch_loop_active_i,
+ input logic [31:0] prefetch_loop_iterations_i,
+ input logic [ImemAddrWidth-1:0] prefetch_loop_end_addr_i,
+ input logic [ImemAddrWidth-1:0] prefetch_loop_jump_addr_i
);
- assign imem_req_o = insn_fetch_req_valid_i;
- assign imem_addr_o = insn_fetch_req_addr_i;
+ function automatic logic insn_is_branch(logic [31:0] insn_data);
+ logic [31:7] unused_insn_data;
- assign insn_fetch_resp_valid_o = imem_rvalid_i;
- assign insn_fetch_resp_data_o = imem_rdata_i;
+ unused_insn_data = insn_data[31:7];
- always_ff @(posedge clk_i) begin
- insn_fetch_resp_addr_o <= insn_fetch_req_addr_i;
+ return insn_data[6:0] inside {InsnOpcodeBaseBranch, InsnOpcodeBaseJal, InsnOpcodeBaseJalr};
+ endfunction
+
+ logic [ImemAddrWidth-1:0] insn_prefetch_addr;
+ logic [38:0] insn_fetch_resp_data_intg_q;
+ logic [ImemAddrWidth-1:0] insn_fetch_resp_addr_q;
+ logic insn_fetch_resp_valid_q, insn_fetch_resp_valid_d;
+ logic [1:0] insn_fetch_resp_intg_error_vec;
+ logic insn_fetch_en;
+
+ logic insn_prefetch;
+ logic insn_prefetch_fail;
+
+ // The prefetch has failed if a fetch is requested and either no prefetch has done or was done to
+ // the wrong address.
+ assign insn_prefetch_fail = insn_fetch_req_valid_i &
+ (~imem_rvalid_i || (insn_fetch_req_addr_i != insn_prefetch_addr));
+
+ // Fetch response is valid when prefetch has matched what was requested. Otherwise if no fetch is
+ // requested keep fetch response validity constant unless a clear is commanded.
+ assign insn_fetch_resp_valid_d =
+ insn_fetch_req_valid_i ? imem_rvalid_i & (insn_fetch_req_addr_i == insn_prefetch_addr) :
+ insn_fetch_resp_valid_q & ~insn_fetch_resp_clear_i;
+
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if (!rst_ni) begin
+ insn_fetch_resp_valid_q <= 1'b0;
+ end else begin
+ insn_fetch_resp_valid_q <= insn_fetch_resp_valid_d;
+ end
end
- assign insn_fetch_err_o = imem_rvalid_i & imem_rerror_i;
+ assign insn_fetch_en = imem_rvalid_i & insn_fetch_req_valid_i;
- // Nothing is reset in this module so rst_ni is unused. Leaving it in so adding resettable flops
- // (or an assertion which will use the reset) is straight forward.
- logic unused_rst_n;
- assign unused_rst_n = rst_ni;
+ always_ff @(posedge clk_i) begin
+ if (insn_fetch_en) begin
+ insn_fetch_resp_data_intg_q <= imem_rdata_i;
+ insn_fetch_resp_addr_q <= insn_prefetch_addr;
+ end
+ end
+
+ always_ff @(posedge clk_i) begin
+ if (insn_prefetch) begin
+ insn_prefetch_addr <= imem_addr_o;
+ end
+ end
+
+ // Prefetch control
+ always_comb begin
+ // Only prefetch if controller tells us to
+ insn_prefetch = prefetch_en_i;
+ // By default prefetch the next instruction
+ imem_addr_o = insn_prefetch_addr + 'd4;
+
+ if (!insn_fetch_req_valid_i) begin
+ // Keep prefetching the same instruction when a new one isn't being requested. In this
+ // scenario OTBN is stalled and will eventually want the prefetched instruction.
+ imem_addr_o = insn_prefetch_addr;
+ end else if (insn_prefetch_fail) begin
+ // When prefetching has failed prefetch the requested address
+ imem_addr_o = insn_fetch_req_addr_i;
+ end else if (insn_is_branch(imem_rdata_i[31:0])) begin
+ // For a branch we do not know if it will be taken or untaken. So never prefetch to keep
+ // timing consistent regardless of taken/not-taken.
+ // This also applies to jumps, this avoids the need to calculate the jump address here.
+ insn_prefetch = 1'b0;
+ end else if (insn_prefetch_addr == prefetch_loop_end_addr_i && prefetch_loop_active_i &&
+ prefetch_loop_iterations_i > 32'd1) begin
+ // When in a loop prefetch the loop beginning when execution reaches the end.
+ imem_addr_o = prefetch_loop_jump_addr_i;
+ end
+ end
+
+ // Check integrity on prefetched instruction
+ prim_secded_39_32_dec u_insn_intg_check (
+ .data_i (insn_fetch_resp_data_intg_q),
+ .data_o (),
+ .syndrome_o (),
+ .err_o (insn_fetch_resp_intg_error_vec)
+ );
+
+ assign imem_req_o = insn_prefetch;
+
+ assign insn_fetch_resp_valid_o = insn_fetch_resp_valid_q;
+ assign insn_fetch_resp_addr_o = insn_fetch_resp_addr_q;
+ // Strip integrity bits before passing instruction to decoder
+ assign insn_fetch_resp_data_o = insn_fetch_resp_data_intg_q[31:0];
+
+ assign insn_fetch_err_o = |insn_fetch_resp_intg_error_vec & insn_fetch_resp_valid_q;
+
+ // We should always get prefetches correct, the check exists as an integrity check only
+ `ASSERT(NoAddressMismatch,
+ imem_rvalid_i && insn_fetch_req_valid_i |-> insn_fetch_req_addr_i == insn_prefetch_addr)
endmodule
diff --git a/hw/ip/otbn/rtl/otbn_loop_controller.sv b/hw/ip/otbn/rtl/otbn_loop_controller.sv
index 49d2beb..a5bad2e 100644
--- a/hw/ip/otbn/rtl/otbn_loop_controller.sv
+++ b/hw/ip/otbn/rtl/otbn_loop_controller.sv
@@ -25,6 +25,11 @@
output [ImemAddrWidth-1:0] loop_jump_addr_o,
output loop_err_o,
+ output prefetch_loop_active_o,
+ output [31:0] prefetch_loop_iterations_o,
+ output [ImemAddrWidth-1:0] prefetch_loop_end_addr_o,
+ output [ImemAddrWidth-1:0] prefetch_loop_jump_addr_o,
+
input jump_or_branch_i,
input otbn_stall_i
);
@@ -203,5 +208,11 @@
.top_valid_o (next_loop_valid)
);
+ // Forward info about loop state for next cycle to prefetch stage
+ assign prefetch_loop_active_o = loop_active_d;
+ assign prefetch_loop_iterations_o = current_loop_d.loop_iterations;
+ assign prefetch_loop_end_addr_o = current_loop_d.loop_end[ImemAddrWidth-1:0];
+ assign prefetch_loop_jump_addr_o = current_loop_d.loop_start;
+
`ASSERT(NoLoopStackPushAndPop, !(loop_stack_push && loop_stack_pop))
endmodule