blob: bb67c72ffdf8c45c12dcf45065f7b10a5625d00e [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
/**
* Ibex RISC-V core
*
* 32 bit RISC-V core supporting the RV32I + optionally EMC instruction sets.
* Instruction and data bus are 32 bit wide TileLink-UL (TL-UL).
*/
module rv_core_ibex
import rv_core_ibex_pkg::*;
import rv_core_ibex_reg_pkg::*;
#(
parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}},
parameter bit PMPEnable = 1'b1,
parameter int unsigned PMPGranularity = 0,
parameter int unsigned PMPNumRegions = 16,
parameter int unsigned MHPMCounterNum = 10,
parameter int unsigned MHPMCounterWidth = 32,
parameter bit RV32E = 0,
parameter ibex_pkg::rv32m_e RV32M = ibex_pkg::RV32MSingleCycle,
parameter ibex_pkg::rv32b_e RV32B = ibex_pkg::RV32BOTEarlGrey,
parameter ibex_pkg::regfile_e RegFile = ibex_pkg::RegFileFF,
parameter bit BranchTargetALU = 1'b1,
parameter bit WritebackStage = 1'b1,
parameter bit ICache = 1'b1,
parameter bit ICacheECC = 1'b1,
parameter bit ICacheScramble = 1'b1,
parameter bit BranchPredictor = 1'b0,
parameter bit DbgTriggerEn = 1'b1,
parameter int unsigned DbgHwBreakNum = 4,
parameter bit SecureIbex = 1'b1,
parameter ibex_pkg::lfsr_seed_t RndCnstLfsrSeed = ibex_pkg::RndCnstLfsrSeedDefault,
parameter ibex_pkg::lfsr_perm_t RndCnstLfsrPerm = ibex_pkg::RndCnstLfsrPermDefault,
parameter int unsigned DmHaltAddr = 32'h1A110800,
parameter int unsigned DmExceptionAddr = 32'h1A110808,
parameter bit PipeLine = 1'b0,
parameter logic [ibex_pkg::SCRAMBLE_KEY_W-1:0] RndCnstIbexKeyDefault =
ibex_pkg::RndCnstIbexKeyDefault,
parameter logic [ibex_pkg::SCRAMBLE_NONCE_W-1:0] RndCnstIbexNonceDefault =
ibex_pkg::RndCnstIbexNonceDefault
) (
// Clock and Reset
input logic clk_i,
input logic rst_ni,
// Clock domain for edn
input logic clk_edn_i,
input logic rst_edn_ni,
// Clock domain for escalation receiver
input logic clk_esc_i,
input logic rst_esc_ni,
// Reset feedback to rstmgr
output logic rst_cpu_n_o,
input prim_ram_1p_pkg::ram_1p_cfg_t ram_cfg_i,
input logic [31:0] hart_id_i,
input logic [31:0] boot_addr_i,
// Instruction memory interface
output tlul_pkg::tl_h2d_t corei_tl_h_o,
input tlul_pkg::tl_d2h_t corei_tl_h_i,
// Data memory interface
output tlul_pkg::tl_h2d_t cored_tl_h_o,
input tlul_pkg::tl_d2h_t cored_tl_h_i,
// Interrupt inputs
input logic irq_software_i,
input logic irq_timer_i,
input logic irq_external_i,
// Escalation input for NMI
input prim_esc_pkg::esc_tx_t esc_tx_i,
output prim_esc_pkg::esc_rx_t esc_rx_o,
// watchdog NMI input
input logic nmi_wdog_i,
// Debug Interface
input logic debug_req_i,
// Crash dump information
output cpu_crash_dump_t crash_dump_o,
// CPU Control Signals
input lc_ctrl_pkg::lc_tx_t lc_cpu_en_i,
input lc_ctrl_pkg::lc_tx_t pwrmgr_cpu_en_i,
output pwrmgr_pkg::pwr_cpu_t pwrmgr_o,
// dft bypass
input scan_rst_ni,
input prim_mubi_pkg::mubi4_t scanmode_i,
// peripheral interface access
input tlul_pkg::tl_h2d_t cfg_tl_d_i,
output tlul_pkg::tl_d2h_t cfg_tl_d_o,
// connection to edn
output edn_pkg::edn_req_t edn_o,
input edn_pkg::edn_rsp_t edn_i,
// connection to otp scramble interface
input clk_otp_i,
input rst_otp_ni,
output otp_ctrl_pkg::sram_otp_key_req_t icache_otp_key_o,
input otp_ctrl_pkg::sram_otp_key_rsp_t icache_otp_key_i,
// fpga build info
input [31:0] fpga_info_i,
// interrupts and alerts
input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i,
output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o
);
import top_pkg::*;
import tlul_pkg::*;
// Register module
rv_core_ibex_cfg_reg2hw_t reg2hw;
rv_core_ibex_cfg_hw2reg_t hw2reg;
// if pipeline=1, do not allow pass through and always break the path
// if pipeline is 0, passthrough the fifo completely
localparam bit FifoPass = PipeLine ? 1'b0 : 1'b1;
localparam int unsigned FifoDepth = PipeLine ? 2 : 0;
// ICache creates more outstanding transactions
localparam int NumOutstandingReqs = ICache ? 8 : 2;
// Instruction interface (internal)
logic instr_req;
logic instr_gnt;
logic instr_rvalid;
logic [31:0] instr_addr;
logic [31:0] instr_rdata;
logic [6:0] instr_rdata_intg;
logic instr_err;
// Data interface (internal)
logic data_req;
logic data_gnt;
logic data_rvalid;
logic data_we;
logic [3:0] data_be;
logic [31:0] data_addr;
logic [31:0] data_wdata;
logic [6:0] data_wdata_intg;
logic [31:0] data_rdata;
logic [6:0] data_rdata_intg;
logic data_err;
// Pipeline interfaces
tl_h2d_t tl_i_ibex2fifo;
tl_d2h_t tl_i_fifo2ibex;
tl_h2d_t tl_d_ibex2fifo;
tl_d2h_t tl_d_fifo2ibex;
`ifdef RVFI
logic rvfi_valid;
logic [63:0] rvfi_order;
logic [31:0] rvfi_insn;
logic rvfi_trap;
logic rvfi_halt;
logic rvfi_intr;
logic [ 1:0] rvfi_mode;
logic [ 1:0] rvfi_ixl;
logic [ 4:0] rvfi_rs1_addr;
logic [ 4:0] rvfi_rs2_addr;
logic [ 4:0] rvfi_rs3_addr;
logic [31:0] rvfi_rs1_rdata;
logic [31:0] rvfi_rs2_rdata;
logic [31:0] rvfi_rs3_rdata;
logic [ 4:0] rvfi_rd_addr;
logic [31:0] rvfi_rd_wdata;
logic [31:0] rvfi_pc_rdata;
logic [31:0] rvfi_pc_wdata;
logic [31:0] rvfi_mem_addr;
logic [ 3:0] rvfi_mem_rmask;
logic [ 3:0] rvfi_mem_wmask;
logic [31:0] rvfi_mem_rdata;
logic [31:0] rvfi_mem_wdata;
`endif
// core sleeping
logic core_sleep;
// The following intermediate signals are created to aid in simulations.
//
// If a parent port is connected directly to a port of sub-modules, the implicit wire connection
// can have only one procedural driver (assign, force etc). What that means is, it prevents us
// from forcing the sub-module port without impacting the same port on other sub-modules. The
// reason for this is, regardless of which hierarchy the port signal is forced, it is a singular
// wire-connected entity - the effect of the force ends up getting reflected on the same port of
// sub-modules, as well as the parent port. To achieve the behavior of a force on a sub-module
// port not impacting (i.e. back-propagating to) the same port on parent / peer sub-modules, we
// need to add an extra `logic` type variables between the port-port connections.
logic ibex_top_clk_i;
logic addr_trans_rst_ni;
assign ibex_top_clk_i = clk_i;
assign addr_trans_rst_ni = rst_ni;
// errors and core alert events
logic ibus_intg_err, dbus_intg_err;
logic alert_minor, alert_major_internal, alert_major_bus;
logic double_fault;
logic fatal_intg_err, fatal_core_err, recov_core_err;
// alert events to peripheral module
logic fatal_intg_event;
logic fatal_core_event;
logic recov_core_event;
// SEC_CM: BUS.INTEGRITY
assign fatal_intg_event = ibus_intg_err | dbus_intg_err | alert_major_bus;
assign fatal_core_event = alert_major_internal | double_fault;
assign recov_core_event = alert_minor;
// configurations for address translation
region_cfg_t [NumRegions-1:0] ibus_region_cfg;
region_cfg_t [NumRegions-1:0] dbus_region_cfg;
// Reset feedback to clkmgr
assign rst_cpu_n_o = rst_ni;
// Escalation receiver that converts differential
// protocol into single ended signal.
logic esc_irq_nm;
prim_esc_receiver #(
.N_ESC_SEV (alert_handler_reg_pkg::N_ESC_SEV),
.PING_CNT_DW (alert_handler_reg_pkg::PING_CNT_DW)
) u_prim_esc_receiver (
.clk_i ( clk_esc_i ),
.rst_ni ( rst_esc_ni ),
.esc_req_o ( esc_irq_nm ),
.esc_rx_o,
.esc_tx_i
);
// Synchronize to fast Ibex clock domain.
logic alert_irq_nm;
prim_flop_2sync #(
.Width(1)
) u_alert_nmi_sync (
.clk_i,
.rst_ni,
.d_i(esc_irq_nm),
.q_o(alert_irq_nm)
);
logic wdog_irq_nm;
prim_flop_2sync #(
.Width(1)
) u_wdog_nmi_sync (
.clk_i,
.rst_ni,
.d_i(nmi_wdog_i),
.q_o(wdog_irq_nm)
);
assign hw2reg.nmi_state.alert.d = 1'b1;
assign hw2reg.nmi_state.alert.de = alert_irq_nm;
assign hw2reg.nmi_state.wdog.d = 1'b1;
assign hw2reg.nmi_state.wdog.de = wdog_irq_nm;
logic irq_nm;
assign irq_nm = |(reg2hw.nmi_state & reg2hw.nmi_enable);
lc_ctrl_pkg::lc_tx_t [0:0] lc_cpu_en;
prim_lc_sync u_lc_sync (
.clk_i,
.rst_ni,
.lc_en_i(lc_cpu_en_i),
.lc_en_o(lc_cpu_en)
);
lc_ctrl_pkg::lc_tx_t [0:0] pwrmgr_cpu_en;
prim_lc_sync u_pwrmgr_sync (
.clk_i,
.rst_ni,
.lc_en_i(pwrmgr_cpu_en_i),
.lc_en_o(pwrmgr_cpu_en)
);
// TODO: This is a hoaky fix, we really should converge everthing
// through rv_plic.
// timer interrupts do not come from
// rv_plic and may not be synchronous to the ibex core
logic irq_timer_sync;
prim_flop_2sync #(
.Width(1)
) u_intr_timer_sync (
.clk_i,
.rst_ni,
.d_i(irq_timer_i),
.q_o(irq_timer_sync)
);
logic irq_software;
logic irq_timer;
logic irq_external;
prim_sec_anchor_buf #(
.Width(3)
) u_prim_buf_irq (
.in_i({irq_software_i,
irq_timer_sync,
irq_external_i}),
.out_o({irq_software,
irq_timer,
irq_external})
);
logic key_req, key_ack;
logic [ibex_pkg::SCRAMBLE_KEY_W-1:0] key;
logic [ibex_pkg::SCRAMBLE_NONCE_W-1:0] nonce;
logic unused_seed_valid;
localparam int PayLoadW = ibex_pkg::SCRAMBLE_KEY_W + ibex_pkg::SCRAMBLE_NONCE_W + 1;
prim_sync_reqack_data #(
.Width(PayLoadW),
.DataSrc2Dst(1'b0)
) u_prim_sync_reqack_data (
.clk_src_i ( clk_i ),
.rst_src_ni ( rst_ni ),
.clk_dst_i ( clk_otp_i ),
.rst_dst_ni ( rst_otp_ni ),
.req_chk_i ( 1'b1 ),
.src_req_i ( key_req ),
.src_ack_o ( key_ack ),
.dst_req_o ( icache_otp_key_o.req ),
.dst_ack_i ( icache_otp_key_i.ack ),
.data_i ( {icache_otp_key_i.key,
icache_otp_key_i.nonce[ibex_pkg::SCRAMBLE_NONCE_W-1:0],
icache_otp_key_i.seed_valid} ),
.data_o ( {key,
nonce,
unused_seed_valid} )
);
logic unused_nonce;
assign unused_nonce = |icache_otp_key_i.nonce;
// Local fetch enable control.
// Whenever a fatal core error is seen disable local fetch enable.
lc_ctrl_pkg::lc_tx_t local_fetch_enable_d, local_fetch_enable_q;
assign local_fetch_enable_d = fatal_core_err ? lc_ctrl_pkg::Off : local_fetch_enable_q;
prim_lc_sender #(
.AsyncOn(1), // this instantiates a register
.ResetValueIsOn(1)
) u_prim_lc_sender (
.clk_i,
.rst_ni,
.lc_en_i(local_fetch_enable_d),
.lc_en_o(local_fetch_enable_q)
);
// Multibit AND computation for fetch enable. Fetch is only enabled when local fetch enable,
// lifecycle CPU enable and power manager CPU enable are all enabled.
lc_ctrl_pkg::lc_tx_t fetch_enable;
assign fetch_enable = lc_ctrl_pkg::lc_tx_and_hi(local_fetch_enable_q,
lc_ctrl_pkg::lc_tx_and_hi(lc_cpu_en[0],
pwrmgr_cpu_en[0]));
ibex_pkg::crash_dump_t crash_dump;
ibex_top #(
.PMPEnable ( PMPEnable ),
.PMPGranularity ( PMPGranularity ),
.PMPNumRegions ( PMPNumRegions ),
.MHPMCounterNum ( MHPMCounterNum ),
.MHPMCounterWidth ( MHPMCounterWidth ),
.RV32E ( RV32E ),
.RV32M ( RV32M ),
.RV32B ( RV32B ),
.RegFile ( RegFile ),
.BranchTargetALU ( BranchTargetALU ),
.WritebackStage ( WritebackStage ),
.ICache ( ICache ),
// Our automatic SEC_CM label check doesn't look at vendored code so the SEC_CM labels need
// to be mentioned here. The real locations can be found by grepping the vendored code.
// TODO(#10071): this should be fixed.
// SEC_CM: ICACHE.MEM.INTEGRITY
.ICacheECC ( ICacheECC ),
// SEC_CM: ICACHE.MEM.SCRAMBLE, SCRAMBLE.KEY.SIDELOAD
.ICacheScramble ( ICacheScramble ),
.BranchPredictor ( BranchPredictor ),
.DbgTriggerEn ( DbgTriggerEn ),
.DbgHwBreakNum ( DbgHwBreakNum ),
// SEC_CM: LOGIC.SHADOW
// SEC_CM: PC.CTRL_FLOW.CONSISTENCY, CTRL_FLOW.UNPREDICTABLE, CORE.DATA_REG_SW.SCA
// SEC_CM: EXCEPTION.CTRL_FLOW.GLOBAL_ESC, EXCEPTION.CTRL_FLOW.LOCAL_ESC
// SEC_CM: DATA_REG_SW.INTEGRITY, DATA_REG_SW.GLITCH_DETECT
.SecureIbex ( SecureIbex ),
.RndCnstLfsrSeed ( RndCnstLfsrSeed ),
.RndCnstLfsrPerm ( RndCnstLfsrPerm ),
.RndCnstIbexKey ( RndCnstIbexKeyDefault ),
.RndCnstIbexNonce ( RndCnstIbexNonceDefault ),
.DmHaltAddr ( DmHaltAddr ),
.DmExceptionAddr ( DmExceptionAddr )
) u_core (
.clk_i (ibex_top_clk_i),
.rst_ni,
.test_en_i (prim_mubi_pkg::mubi4_test_true_strict(scanmode_i)),
.scan_rst_ni,
.ram_cfg_i,
.hart_id_i,
.boot_addr_i,
.instr_req_o ( instr_req ),
.instr_gnt_i ( instr_gnt ),
.instr_rvalid_i ( instr_rvalid ),
.instr_addr_o ( instr_addr ),
.instr_rdata_i ( instr_rdata ),
.instr_rdata_intg_i ( instr_rdata_intg ),
.instr_err_i ( instr_err ),
.data_req_o ( data_req ),
.data_gnt_i ( data_gnt ),
.data_rvalid_i ( data_rvalid ),
.data_we_o ( data_we ),
.data_be_o ( data_be ),
.data_addr_o ( data_addr ),
.data_wdata_o ( data_wdata ),
.data_wdata_intg_o ( data_wdata_intg ),
.data_rdata_i ( data_rdata ),
.data_rdata_intg_i ( data_rdata_intg ),
.data_err_i ( data_err ),
.irq_software_i ( irq_software ),
.irq_timer_i ( irq_timer ),
.irq_external_i ( irq_external ),
.irq_fast_i ( '0 ),
.irq_nm_i ( irq_nm ),
.debug_req_i,
.crash_dump_o ( crash_dump ),
// icache scramble interface
.scramble_key_valid_i (key_ack),
.scramble_key_i (key),
.scramble_nonce_i (nonce),
.scramble_req_o (key_req),
// double fault
.double_fault_seen_o (double_fault),
`ifdef RVFI
.rvfi_valid,
.rvfi_order,
.rvfi_insn,
.rvfi_trap,
.rvfi_halt,
.rvfi_intr,
.rvfi_mode,
.rvfi_ixl,
.rvfi_rs1_addr,
.rvfi_rs2_addr,
.rvfi_rs3_addr,
.rvfi_rs1_rdata,
.rvfi_rs2_rdata,
.rvfi_rs3_rdata,
.rvfi_rd_addr,
.rvfi_rd_wdata,
.rvfi_pc_rdata,
.rvfi_pc_wdata,
.rvfi_mem_addr,
.rvfi_mem_rmask,
.rvfi_mem_wmask,
.rvfi_mem_rdata,
.rvfi_mem_wdata,
`endif
// SEC_CM: FETCH.CTRL.LC_GATED
.fetch_enable_i (fetch_enable),
.alert_minor_o (alert_minor),
.alert_major_internal_o (alert_major_internal),
.alert_major_bus_o (alert_major_bus),
.core_sleep_o (core_sleep)
);
logic core_sleep_q;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
core_sleep_q <= '0;
end else begin
core_sleep_q <= core_sleep;
end
end
prim_buf #(
.Width(1)
) u_core_sleeping_buf (
.in_i(core_sleep_q),
.out_o(pwrmgr_o.core_sleeping)
);
logic prev_valid;
logic [31:0] prev_exception_pc;
logic [31:0] prev_exception_addr;
assign crash_dump_o.current = crash_dump;
assign crash_dump_o.prev_valid = prev_valid;
assign crash_dump_o.prev_exception_pc = prev_exception_pc;
assign crash_dump_o.prev_exception_addr = prev_exception_addr;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
prev_valid <= '0;
prev_exception_pc <= '0;
prev_exception_addr <= '0;
end else if (double_fault) begin
prev_valid <= 1'b1;
prev_exception_pc <= crash_dump.exception_pc;
prev_exception_addr <= crash_dump.exception_addr;
end
end
//
// Convert ibex data/instruction bus to TL-UL
//
logic [31:0] instr_addr_trans;
rv_core_addr_trans #(
.AddrWidth(32),
.NumRegions(NumRegions)
) u_ibus_trans (
.clk_i,
.rst_ni(addr_trans_rst_ni),
.region_cfg_i(ibus_region_cfg),
.addr_i(instr_addr),
.addr_o(instr_addr_trans)
);
logic [6:0] instr_wdata_intg;
logic [top_pkg::TL_DW-1:0] unused_data;
// tl_adapter_host_i_ibex only reads instruction. a_data is always 0
assign {instr_wdata_intg, unused_data} = prim_secded_pkg::prim_secded_inv_39_32_enc('0);
// SEC_CM: BUS.INTEGRITY
tlul_adapter_host #(
.MAX_REQS(NumOutstandingReqs),
// if secure ibex is not set, data integrity is not generated
// from ibex, therefore generate it in the gasket instead.
.EnableDataIntgGen(~SecureIbex)
) tl_adapter_host_i_ibex (
.clk_i,
.rst_ni,
.req_i (instr_req),
.instr_type_i (prim_mubi_pkg::MuBi4True),
.gnt_o (instr_gnt),
.addr_i (instr_addr_trans),
.we_i (1'b0),
.wdata_i (32'b0),
.wdata_intg_i (instr_wdata_intg),
.be_i (4'hF),
.valid_o (instr_rvalid),
.rdata_o (instr_rdata),
.rdata_intg_o (instr_rdata_intg),
.err_o (instr_err),
.intg_err_o (ibus_intg_err),
.tl_o (tl_i_ibex2fifo),
.tl_i (tl_i_fifo2ibex)
);
tlul_fifo_sync #(
.ReqPass(FifoPass),
.RspPass(FifoPass),
.ReqDepth(FifoDepth),
.RspDepth(FifoDepth)
) fifo_i (
.clk_i,
.rst_ni,
.tl_h_i (tl_i_ibex2fifo),
.tl_h_o (tl_i_fifo2ibex),
.tl_d_o (corei_tl_h_o),
.tl_d_i (corei_tl_h_i),
.spare_req_i (1'b0),
.spare_req_o (),
.spare_rsp_i (1'b0),
.spare_rsp_o ());
logic [31:0] data_addr_trans;
rv_core_addr_trans #(
.AddrWidth(32),
.NumRegions(NumRegions)
) u_dbus_trans (
.clk_i,
.rst_ni(addr_trans_rst_ni),
.region_cfg_i(dbus_region_cfg),
.addr_i(data_addr),
.addr_o(data_addr_trans)
);
// SEC_CM: BUS.INTEGRITY
tlul_adapter_host #(
.MAX_REQS(2),
.EnableDataIntgGen(~SecureIbex)
) tl_adapter_host_d_ibex (
.clk_i,
.rst_ni,
.req_i (data_req),
.instr_type_i (prim_mubi_pkg::MuBi4False),
.gnt_o (data_gnt),
.addr_i (data_addr_trans),
.we_i (data_we),
.wdata_i (data_wdata),
.wdata_intg_i (data_wdata_intg),
.be_i (data_be),
.valid_o (data_rvalid),
.rdata_o (data_rdata),
.rdata_intg_o (data_rdata_intg),
.err_o (data_err),
.intg_err_o (dbus_intg_err),
.tl_o (tl_d_ibex2fifo),
.tl_i (tl_d_fifo2ibex)
);
tlul_fifo_sync #(
.ReqPass(FifoPass),
.RspPass(FifoPass),
.ReqDepth(FifoDepth),
.RspDepth(FifoDepth)
) fifo_d (
.clk_i,
.rst_ni,
.tl_h_i (tl_d_ibex2fifo),
.tl_h_o (tl_d_fifo2ibex),
.tl_d_o (cored_tl_h_o),
.tl_d_i (cored_tl_h_i),
.spare_req_i (1'b0),
.spare_req_o (),
.spare_rsp_i (1'b0),
.spare_rsp_o ());
`ifdef RVFI
ibex_tracer ibex_tracer_i (
.clk_i,
.rst_ni,
.hart_id_i,
.rvfi_valid,
.rvfi_order,
.rvfi_insn,
.rvfi_trap,
.rvfi_halt,
.rvfi_intr,
.rvfi_mode,
.rvfi_ixl,
.rvfi_rs1_addr,
.rvfi_rs2_addr,
.rvfi_rs3_addr,
.rvfi_rs1_rdata,
.rvfi_rs2_rdata,
.rvfi_rs3_rdata,
.rvfi_rd_addr,
.rvfi_rd_wdata,
.rvfi_pc_rdata,
.rvfi_pc_wdata,
.rvfi_mem_addr,
.rvfi_mem_rmask,
.rvfi_mem_wmask,
.rvfi_mem_rdata,
.rvfi_mem_wdata
);
`endif
//////////////////////////////////
// Peripheral functions
//////////////////////////////////
logic intg_err;
tlul_pkg::tl_h2d_t tl_win_h2d;
tlul_pkg::tl_d2h_t tl_win_d2h;
rv_core_ibex_cfg_reg_top u_reg_cfg (
.clk_i,
.rst_ni,
.tl_i(cfg_tl_d_i),
.tl_o(cfg_tl_d_o),
.reg2hw,
.hw2reg,
.intg_err_o (intg_err),
.tl_win_o(tl_win_h2d),
.tl_win_i(tl_win_d2h),
.devmode_i (1'b1) // connect to real devmode signal in the future
);
///////////////////////
// Region assignments
///////////////////////
for(genvar i = 0; i < NumRegions; i++) begin : gen_ibus_region_cfgs
assign ibus_region_cfg[i].en = reg2hw.ibus_addr_en[i];
assign ibus_region_cfg[i].matching_region = reg2hw.ibus_addr_matching[i];
assign ibus_region_cfg[i].remap_addr = reg2hw.ibus_remap_addr[i];
end
for(genvar i = 0; i < NumRegions; i++) begin : gen_dbus_region_cfgs
assign dbus_region_cfg[i].en = reg2hw.dbus_addr_en[i];
assign dbus_region_cfg[i].matching_region = reg2hw.dbus_addr_matching[i];
assign dbus_region_cfg[i].remap_addr = reg2hw.dbus_remap_addr[i];
end
///////////////////////
// Error assignment
///////////////////////
assign fatal_intg_err = fatal_intg_event;
assign fatal_core_err = fatal_core_event;
assign recov_core_err = recov_core_event;
assign hw2reg.err_status.reg_intg_err.d = 1'b1;
assign hw2reg.err_status.reg_intg_err.de = intg_err;
assign hw2reg.err_status.fatal_intg_err.d = 1'b1;
assign hw2reg.err_status.fatal_intg_err.de = fatal_intg_err;
assign hw2reg.err_status.fatal_core_err.d = 1'b1;
assign hw2reg.err_status.fatal_core_err.de = fatal_core_err;
assign hw2reg.err_status.recov_core_err.d = 1'b1;
assign hw2reg.err_status.recov_core_err.de = recov_core_err;
///////////////////////
// Alert generation
///////////////////////
logic [NumAlerts-1:0] alert_test;
assign alert_test[0] = reg2hw.alert_test.fatal_sw_err.q &
reg2hw.alert_test.fatal_sw_err.qe;
assign alert_test[1] = reg2hw.alert_test.recov_sw_err.q &
reg2hw.alert_test.recov_sw_err.qe;
assign alert_test[2] = reg2hw.alert_test.fatal_hw_err.q &
reg2hw.alert_test.fatal_hw_err.qe;
assign alert_test[3] = reg2hw.alert_test.recov_hw_err.q &
reg2hw.alert_test.recov_hw_err.qe;
localparam bit [NumAlerts-1:0] AlertFatal = '{1'b0, 1'b1, 1'b0, 1'b1};
logic [NumAlerts-1:0] alert_events;
logic [NumAlerts-1:0] alert_acks;
import prim_mubi_pkg::mubi4_test_true_loose;
import prim_mubi_pkg::mubi4_t;
assign alert_events[0] = mubi4_test_true_loose(mubi4_t'(reg2hw.sw_fatal_err.q));
assign alert_events[1] = mubi4_test_true_loose(mubi4_t'(reg2hw.sw_recov_err.q));
assign alert_events[2] = intg_err | fatal_intg_err | fatal_core_err;
assign alert_events[3] = recov_core_err;
logic unused_alert_acks;
assign unused_alert_acks = |alert_acks;
// recoverable alerts are sent once and silenced until activated again.
assign hw2reg.sw_recov_err.de = alert_acks[1];
assign hw2reg.sw_recov_err.d = prim_mubi_pkg::MuBi4False;
for (genvar i = 0; i < NumAlerts; i++) begin : gen_alert_senders
prim_alert_sender #(
.AsyncOn(AlertAsyncOn[0]),
.IsFatal(AlertFatal[i])
) u_alert_sender (
.clk_i,
.rst_ni,
.alert_test_i(alert_test[i]),
.alert_req_i(alert_events[i]),
.alert_ack_o(alert_acks[i]),
.alert_state_o(),
.alert_rx_i(alert_rx_i[i]),
.alert_tx_o(alert_tx_o[i])
);
end
//////////////
// RND Data //
//////////////
logic [31:0] rnd_data_q, rnd_data_d;
logic rnd_valid_q, rnd_valid_d;
logic rnd_fips_q, rnd_fips_d;
logic edn_req;
logic [31:0] edn_data;
logic edn_ack;
logic edn_fips;
always_comb begin
rnd_valid_d = rnd_valid_q;
rnd_data_d = rnd_data_q;
rnd_fips_d = rnd_fips_q;
if (reg2hw.rnd_data.re) begin
rnd_valid_d = '0;
rnd_data_d = '0;
rnd_fips_d = '0;
end else if (edn_req && edn_ack) begin
rnd_valid_d = 1'b1;
rnd_data_d = edn_data;
rnd_fips_d = edn_fips;
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
rnd_valid_q <= '0;
rnd_data_q <= '0;
rnd_fips_q <= '0;
end else begin
rnd_valid_q <= rnd_valid_d;
rnd_data_q <= rnd_data_d;
rnd_fips_q <= rnd_fips_d;
end
end
assign edn_req = ~rnd_valid_q;
prim_edn_req #(
.OutWidth(32)
) u_edn_if (
.clk_i,
.rst_ni,
.req_chk_i(1'b1),
.req_i(edn_req),
.ack_o(edn_ack),
.data_o(edn_data),
.fips_o(edn_fips),
.err_o(),
.clk_edn_i,
.rst_edn_ni,
.edn_o,
.edn_i
);
assign hw2reg.rnd_data.d = rnd_data_q;
assign hw2reg.rnd_status.rnd_data_valid.d = rnd_valid_q;
assign hw2reg.rnd_status.rnd_data_fips.d = rnd_fips_q;
logic unused_reg2hw;
assign unused_reg2hw = |reg2hw.rnd_data.q;
// fpga build info hook-up
assign hw2reg.fpga_info.d = fpga_info_i;
/////////////////////////////////////
// The carved out space is for DV emulation purposes only
/////////////////////////////////////
import tlul_pkg::tl_h2d_t;
import tlul_pkg::tl_d2h_t;
localparam int TlH2DWidth = $bits(tl_h2d_t);
localparam int TlD2HWidth = $bits(tl_d2h_t);
logic [TlH2DWidth-1:0] tl_win_h2d_int;
logic [TlD2HWidth-1:0] tl_win_d2h_int;
tl_d2h_t tl_win_d2h_err_rsp;
prim_buf #(
.Width(TlH2DWidth)
) u_tlul_req_buf (
.in_i(tl_win_h2d),
.out_o(tl_win_h2d_int)
);
prim_buf #(
.Width(TlD2HWidth)
) u_tlul_rsp_buf (
.in_i(tl_win_d2h_err_rsp),
.out_o(tl_win_d2h_int)
);
// Interception point for connecting simulation SRAM by disconnecting the tl_d output. The
// disconnection is done only if `SYNTHESIS is NOT defined AND `RV_CORE_IBEX_SIM_SRAM is
// defined.
// This define is used only for verilator as verilator does not support forces.
`ifdef RV_CORE_IBEX_SIM_SRAM
`ifdef SYNTHESIS
// Induce a compilation error by instantiating a non-existent module.
illegal_preprocessor_branch_taken u_illegal_preprocessor_branch_taken();
`endif
`else
assign tl_win_d2h = tl_d2h_t'(tl_win_d2h_int);
`endif
tlul_err_resp u_sim_win_rsp (
.clk_i,
.rst_ni,
.tl_h_i(tl_h2d_t'(tl_win_h2d_int)),
.tl_h_o(tl_win_d2h_err_rsp)
);
// Assertions for CPU enable
`ASSERT(FpvSecCmIbexFetchEnable0_A,
fatal_core_err
|=>
lc_ctrl_pkg::lc_tx_test_false_loose(fetch_enable))
`ASSERT(FpvSecCmIbexFetchEnable1_A,
lc_ctrl_pkg::lc_tx_test_false_loose(lc_cpu_en_i)
|->
##2 lc_ctrl_pkg::lc_tx_test_false_loose(fetch_enable))
`ASSERT(FpvSecCmIbexFetchEnable2_A,
lc_ctrl_pkg::lc_tx_test_false_loose(pwrmgr_cpu_en_i)
|->
##2 lc_ctrl_pkg::lc_tx_test_false_loose(fetch_enable))
`ASSERT(FpvSecCmIbexFetchEnable3_A,
lc_ctrl_pkg::lc_tx_test_true_strict(lc_cpu_en_i) &&
lc_ctrl_pkg::lc_tx_test_true_strict(pwrmgr_cpu_en_i) ##1
lc_ctrl_pkg::lc_tx_test_true_strict(local_fetch_enable_q) &&
!fatal_core_err
|=>
lc_ctrl_pkg::lc_tx_test_true_strict(fetch_enable))
`ASSERT(FpvSecCmIbexFetchEnable3Rev_A,
##2 lc_ctrl_pkg::lc_tx_test_true_strict(fetch_enable)
|->
$past(lc_ctrl_pkg::lc_tx_test_true_strict(lc_cpu_en_i), 2) &&
$past(lc_ctrl_pkg::lc_tx_test_true_strict(pwrmgr_cpu_en_i), 2) &&
$past(!fatal_core_err))
// Alert assertions for reg_we onehot check
`ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, u_reg_cfg, alert_tx_o[2])
`ASSERT_PRIM_ONEHOT_ERROR_TRIGGER_ALERT(RvCoreRegWeOnehotCheck_A,
u_core.gen_regfile_ff.register_file_i.gen_wren_check.u_prim_onehot_check, alert_tx_o[2])
`ifdef INC_ASSERT
if (ICache && ICacheScramble) begin : gen_icache_scramble_asserts
// Sample icache scramble key for use in assertions below.
// pragma coverage off
//VCS coverage off
logic [otp_ctrl_pkg::FlashKeyWidth-1:0] icache_otp_key_q;
always_ff @(posedge clk_i, negedge rst_ni) begin
if (!rst_ni) begin
icache_otp_key_q <= '0;
end else if (icache_otp_key_i.ack) begin
icache_otp_key_q <= icache_otp_key_i.key;
end
end
//VCS coverage on
// pragma coverage on
// Ensure that when a scramble key is received, it is correctly forwarded to the core. The core
// will then internally ensure that the key is correctly applied to the icache scrambled
// memory primitives.
`ASSERT(IbexIcacheScrambleKeyForwardedToCore_A,
icache_otp_key_i.ack
|-> ##[0:10] // upper bound is not exact, but it should not take more than 10 cycles
u_core.scramble_key_valid_i && (u_core.scramble_key_i == icache_otp_key_q)
)
// Ensure that when a FENCE.I is executed, a new icache scramble key is requested.
`ASSERT(IbexIcacheScrambleKeyRequestAfterFenceI_A,
u_core.u_ibex_core.id_stage_i.instr_valid_i
&& u_core.u_ibex_core.id_stage_i.decoder_i.opcode == ibex_pkg::OPCODE_MISC_MEM
&& u_core.u_ibex_core.id_stage_i.decoder_i.instr[14:12] == 3'b001 // FENCE.I
|-> ##[0:10] // upper bound is not exact, but it should not take more than a few cycles
icache_otp_key_o.req
)
end
`define ASSERT_IBEX_CORE_ERROR_TRIGGER_ALERT(__assert_name, __alert_name, _hier, __error_name) \
if (1) begin : g_``__error_name``_assert_signals \
logic __error_name; \
assign __error_name = u_core._hier``.__error_name; \
\
logic unused_assert_connected; \
`ASSERT_INIT_NET(AssertConnected_A, unused_assert_connected === 1'b1) \
end \
`ASSERT_ERROR_TRIGGER_ALERT(__assert_name, g_``__error_name``_assert_signals, __alert_name, 0, \
30, // MAX_CYCLES_, use a large value as ibex clock is 4x faster than clk in alert_handler \
__error_name)
`ASSERT_IBEX_CORE_ERROR_TRIGGER_ALERT(IbexPcMismatchCheck_A, alert_tx_o[2],
u_ibex_core.if_stage_i, pc_mismatch_alert_o)
`ASSERT_IBEX_CORE_ERROR_TRIGGER_ALERT(IbexRfEccErrCheck_A, alert_tx_o[2], u_ibex_core,
rf_ecc_err_comb)
`ASSERT_IBEX_CORE_ERROR_TRIGGER_ALERT(IbexLoadRespIntgErrCheck_A, alert_tx_o[2], u_ibex_core,
lsu_load_resp_intg_err)
`ASSERT_IBEX_CORE_ERROR_TRIGGER_ALERT(IbexStoreRespIntgErrCheck_A, alert_tx_o[2], u_ibex_core,
lsu_store_resp_intg_err)
`ASSERT_IBEX_CORE_ERROR_TRIGGER_ALERT(IbexInstrIntgErrCheck_A, alert_tx_o[2], u_ibex_core,
instr_intg_err)
`endif // ifdef INC_ASSERT
endmodule