blob: 13f7c5a05ff83f04840aeee449d66da438b7e72c [file] [log] [blame]
// 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 Instruction Fetch Unit
*
* Fetch an instruction from the instruction memory.
*/
module otbn_instruction_fetch
import otbn_pkg::*;
#(
parameter int ImemSizeByte = 4096,
localparam int ImemAddrWidth = prim_util_pkg::vbits(ImemSizeByte)
) (
input logic clk_i,
input logic rst_ni,
// Instruction memory (IMEM) interface. Read-only.
output logic imem_req_o,
output logic [ImemAddrWidth-1:0] imem_addr_o,
input logic [38:0] imem_rdata_i,
input logic imem_rvalid_i,
// Next instruction selection (to instruction fetch)
input logic insn_fetch_req_valid_i,
input logic [ImemAddrWidth-1:0] insn_fetch_req_addr_i,
// Decoded instruction
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
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
);
function automatic logic insn_is_branch(logic [31:0] insn_data);
logic [31:7] unused_insn_data;
unused_insn_data = insn_data[31:7];
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_en = imem_rvalid_i & insn_fetch_req_valid_i;
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)
`ASSERT(FetchEnOnlyIfValidIMem, insn_fetch_en |-> imem_rvalid_i)
endmodule