mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-21 12:27:18 -04:00
1332 lines
47 KiB
Systemverilog
1332 lines
47 KiB
Systemverilog
// Copyright lowRISC contributors.
|
|
// Copyright 2018 ETH Zurich and University of Bologna, see also CREDITS.md.
|
|
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
`ifdef RISCV_FORMAL
|
|
`define RVFI
|
|
`endif
|
|
|
|
`include "prim_assert.sv"
|
|
|
|
/**
|
|
* Top level module of the ibex RISC-V core
|
|
*/
|
|
module ibex_top import ibex_pkg::*; #(
|
|
parameter bit PMPEnable = 1'b0,
|
|
parameter int unsigned PMPGranularity = 0,
|
|
parameter int unsigned PMPNumRegions = 4,
|
|
parameter int unsigned MHPMCounterNum = 0,
|
|
parameter int unsigned MHPMCounterWidth = 40,
|
|
parameter bit RV32E = 1'b0,
|
|
parameter rv32m_e RV32M = RV32MFast,
|
|
parameter rv32b_e RV32B = RV32BNone,
|
|
parameter regfile_e RegFile = RegFileFF,
|
|
parameter bit BranchTargetALU = 1'b0,
|
|
parameter bit WritebackStage = 1'b0,
|
|
parameter bit ICache = 1'b0,
|
|
parameter bit ICacheECC = 1'b0,
|
|
parameter bit BranchPredictor = 1'b0,
|
|
parameter bit DbgTriggerEn = 1'b0,
|
|
parameter int unsigned DbgHwBreakNum = 1,
|
|
parameter bit SecureIbex = 1'b0,
|
|
parameter bit ICacheScramble = 1'b0,
|
|
parameter lfsr_seed_t RndCnstLfsrSeed = RndCnstLfsrSeedDefault,
|
|
parameter lfsr_perm_t RndCnstLfsrPerm = RndCnstLfsrPermDefault,
|
|
parameter int unsigned DmHaltAddr = 32'h1A110800,
|
|
parameter int unsigned DmExceptionAddr = 32'h1A110808,
|
|
// Default seed and nonce for scrambling
|
|
parameter logic [SCRAMBLE_KEY_W-1:0] RndCnstIbexKey = RndCnstIbexKeyDefault,
|
|
parameter logic [SCRAMBLE_NONCE_W-1:0] RndCnstIbexNonce = RndCnstIbexNonceDefault
|
|
) (
|
|
// Clock and Reset
|
|
input logic clk_i,
|
|
input logic rst_ni,
|
|
|
|
input logic test_en_i, // enable all clock gates for testing
|
|
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 logic instr_req_o,
|
|
input logic instr_gnt_i,
|
|
input logic instr_rvalid_i,
|
|
output logic [31:0] instr_addr_o,
|
|
input logic [31:0] instr_rdata_i,
|
|
input logic [6:0] instr_rdata_intg_i,
|
|
input logic instr_err_i,
|
|
|
|
// Data memory interface
|
|
output logic data_req_o,
|
|
input logic data_gnt_i,
|
|
input logic data_rvalid_i,
|
|
output logic data_we_o,
|
|
output logic [3:0] data_be_o,
|
|
output logic [31:0] data_addr_o,
|
|
output logic [31:0] data_wdata_o,
|
|
output logic [6:0] data_wdata_intg_o,
|
|
input logic [31:0] data_rdata_i,
|
|
input logic [6:0] data_rdata_intg_i,
|
|
input logic data_err_i,
|
|
|
|
// Interrupt inputs
|
|
input logic irq_software_i,
|
|
input logic irq_timer_i,
|
|
input logic irq_external_i,
|
|
input logic [14:0] irq_fast_i,
|
|
input logic irq_nm_i, // non-maskeable interrupt
|
|
|
|
// Scrambling Interface
|
|
input logic scramble_key_valid_i,
|
|
input logic [SCRAMBLE_KEY_W-1:0] scramble_key_i,
|
|
input logic [SCRAMBLE_NONCE_W-1:0] scramble_nonce_i,
|
|
output logic scramble_req_o,
|
|
|
|
// Debug Interface
|
|
input logic debug_req_i,
|
|
output crash_dump_t crash_dump_o,
|
|
output logic double_fault_seen_o,
|
|
|
|
// RISC-V Formal Interface
|
|
// Does not comply with the coding standards of _i/_o suffixes, but follows
|
|
// the convention of RISC-V Formal Interface Specification.
|
|
`ifdef RVFI
|
|
output logic rvfi_valid,
|
|
output logic [63:0] rvfi_order,
|
|
output logic [31:0] rvfi_insn,
|
|
output logic rvfi_trap,
|
|
output logic rvfi_halt,
|
|
output logic rvfi_intr,
|
|
output logic [ 1:0] rvfi_mode,
|
|
output logic [ 1:0] rvfi_ixl,
|
|
output logic [ 4:0] rvfi_rs1_addr,
|
|
output logic [ 4:0] rvfi_rs2_addr,
|
|
output logic [ 4:0] rvfi_rs3_addr,
|
|
output logic [31:0] rvfi_rs1_rdata,
|
|
output logic [31:0] rvfi_rs2_rdata,
|
|
output logic [31:0] rvfi_rs3_rdata,
|
|
output logic [ 4:0] rvfi_rd_addr,
|
|
output logic [31:0] rvfi_rd_wdata,
|
|
output logic [31:0] rvfi_pc_rdata,
|
|
output logic [31:0] rvfi_pc_wdata,
|
|
output logic [31:0] rvfi_mem_addr,
|
|
output logic [ 3:0] rvfi_mem_rmask,
|
|
output logic [ 3:0] rvfi_mem_wmask,
|
|
output logic [31:0] rvfi_mem_rdata,
|
|
output logic [31:0] rvfi_mem_wdata,
|
|
output logic [31:0] rvfi_ext_mip,
|
|
output logic rvfi_ext_nmi,
|
|
output logic rvfi_ext_nmi_int,
|
|
output logic rvfi_ext_debug_req,
|
|
output logic rvfi_ext_debug_mode,
|
|
output logic rvfi_ext_rf_wr_suppress,
|
|
output logic [63:0] rvfi_ext_mcycle,
|
|
output logic [31:0] rvfi_ext_mhpmcounters [10],
|
|
output logic [31:0] rvfi_ext_mhpmcountersh [10],
|
|
output logic rvfi_ext_ic_scr_key_valid,
|
|
output logic rvfi_ext_irq_valid,
|
|
`endif
|
|
|
|
// CPU Control Signals
|
|
input ibex_mubi_t fetch_enable_i,
|
|
output logic alert_minor_o,
|
|
output logic alert_major_internal_o,
|
|
output logic alert_major_bus_o,
|
|
output logic core_sleep_o,
|
|
|
|
// DFT bypass controls
|
|
input logic scan_rst_ni
|
|
);
|
|
|
|
localparam bit Lockstep = SecureIbex;
|
|
localparam bit ResetAll = Lockstep;
|
|
localparam bit DummyInstructions = SecureIbex;
|
|
localparam bit RegFileECC = SecureIbex;
|
|
localparam bit RegFileWrenCheck = SecureIbex;
|
|
localparam bit RegFileRdataMuxCheck = SecureIbex;
|
|
localparam int unsigned RegFileDataWidth = RegFileECC ? 32 + 7 : 32;
|
|
localparam bit MemECC = SecureIbex;
|
|
localparam int unsigned MemDataWidth = MemECC ? 32 + 7 : 32;
|
|
// Icache parameters
|
|
localparam int unsigned BusSizeECC = ICacheECC ? (BUS_SIZE + 7) : BUS_SIZE;
|
|
localparam int unsigned LineSizeECC = BusSizeECC * IC_LINE_BEATS;
|
|
localparam int unsigned TagSizeECC = ICacheECC ? (IC_TAG_SIZE + 6) : IC_TAG_SIZE;
|
|
// Scrambling Parameter
|
|
localparam int unsigned NumAddrScrRounds = ICacheScramble ? 2 : 0;
|
|
|
|
// Clock signals
|
|
logic clk;
|
|
ibex_mubi_t core_busy_d, core_busy_q;
|
|
logic clock_en;
|
|
logic irq_pending;
|
|
// Core <-> Register file signals
|
|
logic dummy_instr_id;
|
|
logic dummy_instr_wb;
|
|
logic [4:0] rf_raddr_a;
|
|
logic [4:0] rf_raddr_b;
|
|
logic [4:0] rf_waddr_wb;
|
|
logic rf_we_wb;
|
|
logic [RegFileDataWidth-1:0] rf_wdata_wb_ecc;
|
|
logic [RegFileDataWidth-1:0] rf_rdata_a_ecc, rf_rdata_a_ecc_buf;
|
|
logic [RegFileDataWidth-1:0] rf_rdata_b_ecc, rf_rdata_b_ecc_buf;
|
|
|
|
// Combined data and integrity for data and instruction busses
|
|
logic [MemDataWidth-1:0] data_wdata_core;
|
|
logic [MemDataWidth-1:0] data_rdata_core;
|
|
logic [MemDataWidth-1:0] instr_rdata_core;
|
|
|
|
// Core <-> RAMs signals
|
|
logic [IC_NUM_WAYS-1:0] ic_tag_req;
|
|
logic ic_tag_write;
|
|
logic [IC_INDEX_W-1:0] ic_tag_addr;
|
|
logic [TagSizeECC-1:0] ic_tag_wdata;
|
|
logic [TagSizeECC-1:0] ic_tag_rdata [IC_NUM_WAYS];
|
|
logic [IC_NUM_WAYS-1:0] ic_data_req;
|
|
logic ic_data_write;
|
|
logic [IC_INDEX_W-1:0] ic_data_addr;
|
|
logic [LineSizeECC-1:0] ic_data_wdata;
|
|
logic [LineSizeECC-1:0] ic_data_rdata [IC_NUM_WAYS];
|
|
logic ic_scr_key_req;
|
|
// Alert signals
|
|
logic core_alert_major_internal, core_alert_major_bus, core_alert_minor;
|
|
logic lockstep_alert_major_internal, lockstep_alert_major_bus;
|
|
logic lockstep_alert_minor;
|
|
// Scramble signals
|
|
logic [SCRAMBLE_KEY_W-1:0] scramble_key_q;
|
|
logic [SCRAMBLE_NONCE_W-1:0] scramble_nonce_q;
|
|
logic scramble_key_valid_d, scramble_key_valid_q;
|
|
logic scramble_req_d, scramble_req_q;
|
|
|
|
ibex_mubi_t fetch_enable_buf;
|
|
|
|
/////////////////////
|
|
// Main clock gate //
|
|
/////////////////////
|
|
|
|
if (SecureIbex) begin : g_clock_en_secure
|
|
// For secure Ibex core_busy_q must be a specific multi-bit pattern to enable the clock.
|
|
prim_flop #(
|
|
.Width($bits(ibex_mubi_t)),
|
|
.ResetValue(IbexMuBiOff)
|
|
) u_prim_core_busy_flop (
|
|
.clk_i (clk_i),
|
|
.rst_ni(rst_ni),
|
|
.d_i (core_busy_d),
|
|
.q_o (core_busy_q)
|
|
);
|
|
assign clock_en = (core_busy_q != IbexMuBiOff) | debug_req_i | irq_pending | irq_nm_i;
|
|
end else begin : g_clock_en_non_secure
|
|
// For non secure Ibex only the bottom bit of core_busy_q is considered. Other FFs can be
|
|
// optimized away during synthesis.
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (!rst_ni) begin
|
|
core_busy_q <= IbexMuBiOff;
|
|
end else begin
|
|
core_busy_q <= core_busy_d;
|
|
end
|
|
end
|
|
assign clock_en = core_busy_q[0] | debug_req_i | irq_pending | irq_nm_i;
|
|
|
|
logic unused_core_busy;
|
|
assign unused_core_busy = ^core_busy_q[$bits(ibex_mubi_t)-1:1];
|
|
end
|
|
|
|
assign core_sleep_o = ~clock_en;
|
|
|
|
prim_clock_gating core_clock_gate_i (
|
|
.clk_i (clk_i),
|
|
.en_i (clock_en),
|
|
.test_en_i(test_en_i),
|
|
.clk_o (clk)
|
|
);
|
|
|
|
////////////////////////
|
|
// Core instantiation //
|
|
////////////////////////
|
|
|
|
// Buffer security critical signals to prevent synthesis optimisation removing them
|
|
prim_buf #(.Width($bits(ibex_mubi_t))) u_fetch_enable_buf (
|
|
.in_i (fetch_enable_i),
|
|
.out_o(fetch_enable_buf)
|
|
);
|
|
|
|
prim_buf #(.Width(RegFileDataWidth)) u_rf_rdata_a_ecc_buf (
|
|
.in_i (rf_rdata_a_ecc),
|
|
.out_o(rf_rdata_a_ecc_buf)
|
|
);
|
|
|
|
prim_buf #(.Width(RegFileDataWidth)) u_rf_rdata_b_ecc_buf (
|
|
.in_i (rf_rdata_b_ecc),
|
|
.out_o(rf_rdata_b_ecc_buf)
|
|
);
|
|
|
|
|
|
// ibex_core takes integrity and data bits together. Combine the separate integrity and data
|
|
// inputs here.
|
|
assign data_rdata_core[31:0] = data_rdata_i;
|
|
assign instr_rdata_core[31:0] = instr_rdata_i;
|
|
|
|
if (MemECC) begin : gen_mem_rdata_ecc
|
|
assign data_rdata_core[38:32] = data_rdata_intg_i;
|
|
assign instr_rdata_core[38:32] = instr_rdata_intg_i;
|
|
end else begin : gen_non_mem_rdata_ecc
|
|
logic unused_intg;
|
|
|
|
assign unused_intg = ^{instr_rdata_intg_i, data_rdata_intg_i};
|
|
end
|
|
|
|
ibex_core #(
|
|
.PMPEnable (PMPEnable),
|
|
.PMPGranularity (PMPGranularity),
|
|
.PMPNumRegions (PMPNumRegions),
|
|
.MHPMCounterNum (MHPMCounterNum),
|
|
.MHPMCounterWidth (MHPMCounterWidth),
|
|
.RV32E (RV32E),
|
|
.RV32M (RV32M),
|
|
.RV32B (RV32B),
|
|
.BranchTargetALU (BranchTargetALU),
|
|
.ICache (ICache),
|
|
.ICacheECC (ICacheECC),
|
|
.BusSizeECC (BusSizeECC),
|
|
.TagSizeECC (TagSizeECC),
|
|
.LineSizeECC (LineSizeECC),
|
|
.BranchPredictor (BranchPredictor),
|
|
.DbgTriggerEn (DbgTriggerEn),
|
|
.DbgHwBreakNum (DbgHwBreakNum),
|
|
.WritebackStage (WritebackStage),
|
|
.ResetAll (ResetAll),
|
|
.RndCnstLfsrSeed (RndCnstLfsrSeed),
|
|
.RndCnstLfsrPerm (RndCnstLfsrPerm),
|
|
.SecureIbex (SecureIbex),
|
|
.DummyInstructions(DummyInstructions),
|
|
.RegFileECC (RegFileECC),
|
|
.RegFileDataWidth (RegFileDataWidth),
|
|
.MemECC (MemECC),
|
|
.MemDataWidth (MemDataWidth),
|
|
.DmHaltAddr (DmHaltAddr),
|
|
.DmExceptionAddr (DmExceptionAddr)
|
|
) u_ibex_core (
|
|
.clk_i(clk),
|
|
.rst_ni,
|
|
|
|
.hart_id_i,
|
|
.boot_addr_i,
|
|
|
|
.instr_req_o,
|
|
.instr_gnt_i,
|
|
.instr_rvalid_i,
|
|
.instr_addr_o,
|
|
.instr_rdata_i(instr_rdata_core),
|
|
.instr_err_i,
|
|
|
|
.data_req_o,
|
|
.data_gnt_i,
|
|
.data_rvalid_i,
|
|
.data_we_o,
|
|
.data_be_o,
|
|
.data_addr_o,
|
|
.data_wdata_o(data_wdata_core),
|
|
.data_rdata_i(data_rdata_core),
|
|
.data_err_i,
|
|
|
|
.dummy_instr_id_o (dummy_instr_id),
|
|
.dummy_instr_wb_o (dummy_instr_wb),
|
|
.rf_raddr_a_o (rf_raddr_a),
|
|
.rf_raddr_b_o (rf_raddr_b),
|
|
.rf_waddr_wb_o (rf_waddr_wb),
|
|
.rf_we_wb_o (rf_we_wb),
|
|
.rf_wdata_wb_ecc_o(rf_wdata_wb_ecc),
|
|
.rf_rdata_a_ecc_i (rf_rdata_a_ecc_buf),
|
|
.rf_rdata_b_ecc_i (rf_rdata_b_ecc_buf),
|
|
|
|
.ic_tag_req_o (ic_tag_req),
|
|
.ic_tag_write_o (ic_tag_write),
|
|
.ic_tag_addr_o (ic_tag_addr),
|
|
.ic_tag_wdata_o (ic_tag_wdata),
|
|
.ic_tag_rdata_i (ic_tag_rdata),
|
|
.ic_data_req_o (ic_data_req),
|
|
.ic_data_write_o (ic_data_write),
|
|
.ic_data_addr_o (ic_data_addr),
|
|
.ic_data_wdata_o (ic_data_wdata),
|
|
.ic_data_rdata_i (ic_data_rdata),
|
|
.ic_scr_key_valid_i(scramble_key_valid_q),
|
|
.ic_scr_key_req_o (ic_scr_key_req),
|
|
|
|
.irq_software_i,
|
|
.irq_timer_i,
|
|
.irq_external_i,
|
|
.irq_fast_i,
|
|
.irq_nm_i,
|
|
.irq_pending_o(irq_pending),
|
|
|
|
.debug_req_i,
|
|
.crash_dump_o,
|
|
.double_fault_seen_o,
|
|
|
|
`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,
|
|
.rvfi_ext_mip,
|
|
.rvfi_ext_nmi,
|
|
.rvfi_ext_nmi_int,
|
|
.rvfi_ext_debug_req,
|
|
.rvfi_ext_debug_mode,
|
|
.rvfi_ext_rf_wr_suppress,
|
|
.rvfi_ext_mcycle,
|
|
.rvfi_ext_mhpmcounters,
|
|
.rvfi_ext_mhpmcountersh,
|
|
.rvfi_ext_ic_scr_key_valid,
|
|
.rvfi_ext_irq_valid,
|
|
`endif
|
|
|
|
.fetch_enable_i (fetch_enable_buf),
|
|
.alert_minor_o (core_alert_minor),
|
|
.alert_major_internal_o(core_alert_major_internal),
|
|
.alert_major_bus_o (core_alert_major_bus),
|
|
.core_busy_o (core_busy_d)
|
|
);
|
|
|
|
/////////////////////////////////
|
|
// Register file Instantiation //
|
|
/////////////////////////////////
|
|
|
|
logic rf_alert_major_internal;
|
|
if (RegFile == RegFileFF) begin : gen_regfile_ff
|
|
ibex_register_file_ff #(
|
|
.RV32E (RV32E),
|
|
.DataWidth (RegFileDataWidth),
|
|
.DummyInstructions(DummyInstructions),
|
|
// SEC_CM: DATA_REG_SW.GLITCH_DETECT
|
|
.WrenCheck (RegFileWrenCheck),
|
|
.RdataMuxCheck (RegFileRdataMuxCheck),
|
|
.WordZeroVal (RegFileDataWidth'(prim_secded_pkg::SecdedInv3932ZeroWord))
|
|
) register_file_i (
|
|
.clk_i (clk),
|
|
.rst_ni(rst_ni),
|
|
|
|
.test_en_i (test_en_i),
|
|
.dummy_instr_id_i(dummy_instr_id),
|
|
.dummy_instr_wb_i(dummy_instr_wb),
|
|
|
|
.raddr_a_i(rf_raddr_a),
|
|
.rdata_a_o(rf_rdata_a_ecc),
|
|
.raddr_b_i(rf_raddr_b),
|
|
.rdata_b_o(rf_rdata_b_ecc),
|
|
.waddr_a_i(rf_waddr_wb),
|
|
.wdata_a_i(rf_wdata_wb_ecc),
|
|
.we_a_i (rf_we_wb),
|
|
.err_o (rf_alert_major_internal)
|
|
);
|
|
end else if (RegFile == RegFileFPGA) begin : gen_regfile_fpga
|
|
ibex_register_file_fpga #(
|
|
.RV32E (RV32E),
|
|
.DataWidth (RegFileDataWidth),
|
|
.DummyInstructions(DummyInstructions),
|
|
// SEC_CM: DATA_REG_SW.GLITCH_DETECT
|
|
.WrenCheck (RegFileWrenCheck),
|
|
.RdataMuxCheck (RegFileRdataMuxCheck),
|
|
.WordZeroVal (RegFileDataWidth'(prim_secded_pkg::SecdedInv3932ZeroWord))
|
|
) register_file_i (
|
|
.clk_i (clk),
|
|
.rst_ni(rst_ni),
|
|
|
|
.test_en_i (test_en_i),
|
|
.dummy_instr_id_i(dummy_instr_id),
|
|
.dummy_instr_wb_i(dummy_instr_wb),
|
|
|
|
.raddr_a_i(rf_raddr_a),
|
|
.rdata_a_o(rf_rdata_a_ecc),
|
|
.raddr_b_i(rf_raddr_b),
|
|
.rdata_b_o(rf_rdata_b_ecc),
|
|
.waddr_a_i(rf_waddr_wb),
|
|
.wdata_a_i(rf_wdata_wb_ecc),
|
|
.we_a_i (rf_we_wb),
|
|
.err_o (rf_alert_major_internal)
|
|
);
|
|
end else if (RegFile == RegFileLatch) begin : gen_regfile_latch
|
|
ibex_register_file_latch #(
|
|
.RV32E (RV32E),
|
|
.DataWidth (RegFileDataWidth),
|
|
.DummyInstructions(DummyInstructions),
|
|
// SEC_CM: DATA_REG_SW.GLITCH_DETECT
|
|
.WrenCheck (RegFileWrenCheck),
|
|
.RdataMuxCheck (RegFileRdataMuxCheck),
|
|
.WordZeroVal (RegFileDataWidth'(prim_secded_pkg::SecdedInv3932ZeroWord))
|
|
) register_file_i (
|
|
.clk_i (clk),
|
|
.rst_ni(rst_ni),
|
|
|
|
.test_en_i (test_en_i),
|
|
.dummy_instr_id_i(dummy_instr_id),
|
|
.dummy_instr_wb_i(dummy_instr_wb),
|
|
|
|
.raddr_a_i(rf_raddr_a),
|
|
.rdata_a_o(rf_rdata_a_ecc),
|
|
.raddr_b_i(rf_raddr_b),
|
|
.rdata_b_o(rf_rdata_b_ecc),
|
|
.waddr_a_i(rf_waddr_wb),
|
|
.wdata_a_i(rf_wdata_wb_ecc),
|
|
.we_a_i (rf_we_wb),
|
|
.err_o (rf_alert_major_internal)
|
|
);
|
|
end
|
|
|
|
///////////////////////////////
|
|
// Scrambling Infrastructure //
|
|
///////////////////////////////
|
|
|
|
if (ICacheScramble) begin : gen_scramble
|
|
|
|
// SEC_CM: ICACHE.MEM.SCRAMBLE
|
|
// Scramble key valid starts with OTP returning new valid key and stays high
|
|
// until we request a new valid key.
|
|
assign scramble_key_valid_d = scramble_req_q ? scramble_key_valid_i :
|
|
ic_scr_key_req ? 1'b0 :
|
|
scramble_key_valid_q;
|
|
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (!rst_ni) begin
|
|
scramble_key_q <= RndCnstIbexKey;
|
|
scramble_nonce_q <= RndCnstIbexNonce;
|
|
end else if (scramble_key_valid_i) begin
|
|
scramble_key_q <= scramble_key_i;
|
|
scramble_nonce_q <= scramble_nonce_i;
|
|
end
|
|
end
|
|
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (!rst_ni) begin
|
|
scramble_key_valid_q <= 1'b1;
|
|
scramble_req_q <= '0;
|
|
end else begin
|
|
scramble_key_valid_q <= scramble_key_valid_d;
|
|
scramble_req_q <= scramble_req_d;
|
|
end
|
|
end
|
|
|
|
// Scramble key request starts with invalidate signal from ICache and stays high
|
|
// until we got a valid key.
|
|
assign scramble_req_d = scramble_req_q ? ~scramble_key_valid_i : ic_scr_key_req;
|
|
assign scramble_req_o = scramble_req_q;
|
|
|
|
end else begin : gen_noscramble
|
|
|
|
logic unused_scramble_inputs = scramble_key_valid_i & (|scramble_key_i) & (|RndCnstIbexKey) &
|
|
(|scramble_nonce_i) & (|RndCnstIbexNonce) & scramble_req_q &
|
|
ic_scr_key_req & scramble_key_valid_d & scramble_req_d;
|
|
|
|
assign scramble_req_d = 1'b0;
|
|
assign scramble_req_q = 1'b0;
|
|
assign scramble_req_o = 1'b0;
|
|
assign scramble_key_q = '0;
|
|
assign scramble_nonce_q = '0;
|
|
assign scramble_key_valid_q = 1'b1;
|
|
assign scramble_key_valid_d = 1'b1;
|
|
end
|
|
|
|
////////////////////////
|
|
// Rams Instantiation //
|
|
////////////////////////
|
|
|
|
if (ICache) begin : gen_rams
|
|
|
|
for (genvar way = 0; way < IC_NUM_WAYS; way++) begin : gen_rams_inner
|
|
|
|
if (ICacheScramble) begin : gen_scramble_rams
|
|
|
|
// SEC_CM: ICACHE.MEM.SCRAMBLE
|
|
// Tag RAM instantiation
|
|
prim_ram_1p_scr #(
|
|
.Width (TagSizeECC),
|
|
.Depth (IC_NUM_LINES),
|
|
.DataBitsPerMask (TagSizeECC),
|
|
.EnableParity (0),
|
|
.NumAddrScrRounds (NumAddrScrRounds)
|
|
) tag_bank (
|
|
.clk_i,
|
|
.rst_ni,
|
|
|
|
.key_valid_i (scramble_key_valid_q),
|
|
.key_i (scramble_key_q),
|
|
.nonce_i (scramble_nonce_q),
|
|
|
|
.req_i (ic_tag_req[way]),
|
|
|
|
.gnt_o (),
|
|
.write_i (ic_tag_write),
|
|
.addr_i (ic_tag_addr),
|
|
.wdata_i (ic_tag_wdata),
|
|
.wmask_i ({TagSizeECC{1'b1}}),
|
|
.intg_error_i(1'b0),
|
|
|
|
.rdata_o (ic_tag_rdata[way]),
|
|
.rvalid_o (),
|
|
.raddr_o (),
|
|
.rerror_o (),
|
|
.cfg_i (ram_cfg_i)
|
|
);
|
|
|
|
// Data RAM instantiation
|
|
prim_ram_1p_scr #(
|
|
.Width (LineSizeECC),
|
|
.Depth (IC_NUM_LINES),
|
|
.DataBitsPerMask (LineSizeECC),
|
|
.ReplicateKeyStream (1),
|
|
.EnableParity (0),
|
|
.NumAddrScrRounds (NumAddrScrRounds)
|
|
) data_bank (
|
|
.clk_i,
|
|
.rst_ni,
|
|
|
|
.key_valid_i (scramble_key_valid_q),
|
|
.key_i (scramble_key_q),
|
|
.nonce_i (scramble_nonce_q),
|
|
|
|
.req_i (ic_data_req[way]),
|
|
|
|
.gnt_o (),
|
|
.write_i (ic_data_write),
|
|
.addr_i (ic_data_addr),
|
|
.wdata_i (ic_data_wdata),
|
|
.wmask_i ({LineSizeECC{1'b1}}),
|
|
.intg_error_i(1'b0),
|
|
|
|
.rdata_o (ic_data_rdata[way]),
|
|
.rvalid_o (),
|
|
.raddr_o (),
|
|
.rerror_o (),
|
|
.cfg_i (ram_cfg_i)
|
|
);
|
|
|
|
`ifdef INC_ASSERT
|
|
// Sample scramble key whenever it is valid for use in the assertions below. This may be
|
|
// redundant with the sampling performed in the actual design, but that is okay because
|
|
// the assertions exist to check the correct functioning of the design.
|
|
logic [SCRAMBLE_KEY_W-1:0] sampled_scramble_key;
|
|
always_ff @(posedge clk_i, negedge rst_ni) begin
|
|
if (!rst_ni) begin
|
|
sampled_scramble_key <= 'x;
|
|
end else if (scramble_key_valid_i) begin
|
|
sampled_scramble_key <= scramble_key_i;
|
|
end
|
|
end
|
|
|
|
// Ensure that when a scramble key is received, it is correctly applied to the icache
|
|
// scrambled memory primitives. The upper bound in the cycle ranges below is not exact,
|
|
// but it should not take more than 10 cycles.
|
|
`ASSERT(ScrambleKeyAppliedAtTagBank_A,
|
|
scramble_key_valid_i
|
|
|-> ##[0:10]
|
|
tag_bank.key_valid_i && (tag_bank.key_i == sampled_scramble_key),
|
|
clk_i, !rst_ni
|
|
)
|
|
`ASSERT(ScrambleKeyAppliedAtDataBank_A,
|
|
scramble_key_valid_i
|
|
|-> ##[0:10]
|
|
data_bank.key_valid_i && (data_bank.key_i == sampled_scramble_key),
|
|
clk_i, !rst_ni
|
|
)
|
|
`endif
|
|
|
|
end else begin : gen_noscramble_rams
|
|
|
|
// Tag RAM instantiation
|
|
prim_ram_1p #(
|
|
.Width (TagSizeECC),
|
|
.Depth (IC_NUM_LINES),
|
|
.DataBitsPerMask (TagSizeECC)
|
|
) tag_bank (
|
|
.clk_i,
|
|
|
|
.req_i (ic_tag_req[way]),
|
|
|
|
.write_i (ic_tag_write),
|
|
.addr_i (ic_tag_addr),
|
|
.wdata_i (ic_tag_wdata),
|
|
.wmask_i ({TagSizeECC{1'b1}}),
|
|
|
|
.rdata_o (ic_tag_rdata[way]),
|
|
.cfg_i (ram_cfg_i)
|
|
);
|
|
|
|
// Data RAM instantiation
|
|
prim_ram_1p #(
|
|
.Width (LineSizeECC),
|
|
.Depth (IC_NUM_LINES),
|
|
.DataBitsPerMask (LineSizeECC)
|
|
) data_bank (
|
|
.clk_i,
|
|
|
|
.req_i (ic_data_req[way]),
|
|
|
|
.write_i (ic_data_write),
|
|
.addr_i (ic_data_addr),
|
|
.wdata_i (ic_data_wdata),
|
|
.wmask_i ({LineSizeECC{1'b1}}),
|
|
|
|
.rdata_o (ic_data_rdata[way]),
|
|
.cfg_i (ram_cfg_i)
|
|
);
|
|
|
|
end
|
|
end
|
|
|
|
end else begin : gen_norams
|
|
|
|
prim_ram_1p_pkg::ram_1p_cfg_t unused_ram_cfg;
|
|
logic unused_ram_inputs;
|
|
|
|
assign unused_ram_cfg = ram_cfg_i;
|
|
assign unused_ram_inputs = (|ic_tag_req) & ic_tag_write & (|ic_tag_addr) & (|ic_tag_wdata) &
|
|
(|ic_data_req) & ic_data_write & (|ic_data_addr) & (|ic_data_wdata) &
|
|
(|scramble_key_q) & (|scramble_nonce_q) & scramble_key_valid_q &
|
|
scramble_key_valid_d & (|scramble_nonce_q) &
|
|
(|NumAddrScrRounds);
|
|
|
|
assign ic_tag_rdata = '{default:'b0};
|
|
assign ic_data_rdata = '{default:'b0};
|
|
|
|
end
|
|
|
|
assign data_wdata_o = data_wdata_core[31:0];
|
|
|
|
if (MemECC) begin : gen_mem_wdata_ecc
|
|
prim_buf #(.Width(7)) u_prim_buf_data_wdata_intg (
|
|
.in_i (data_wdata_core[38:32]),
|
|
.out_o(data_wdata_intg_o)
|
|
);
|
|
end else begin : gen_no_mem_ecc
|
|
assign data_wdata_intg_o = '0;
|
|
end
|
|
|
|
// Redundant lockstep core implementation
|
|
if (Lockstep) begin : gen_lockstep
|
|
// SEC_CM: LOGIC.SHADOW
|
|
// Note: certain synthesis tools like DC are very smart at optimizing away redundant logic.
|
|
// Hence, we have to insert an optimization barrier at the IOs of the lockstep Ibex.
|
|
// This is achieved by manually buffering each bit using prim_buf.
|
|
// Our Xilinx and DC synthesis flows make sure that these buffers cannot be optimized away
|
|
// using keep attributes (Vivado) and size_only constraints (DC).
|
|
|
|
localparam int NumBufferBits = $bits({
|
|
hart_id_i,
|
|
boot_addr_i,
|
|
instr_req_o,
|
|
instr_gnt_i,
|
|
instr_rvalid_i,
|
|
instr_addr_o,
|
|
instr_rdata_core,
|
|
instr_err_i,
|
|
data_req_o,
|
|
data_gnt_i,
|
|
data_rvalid_i,
|
|
data_we_o,
|
|
data_be_o,
|
|
data_addr_o,
|
|
data_wdata_core,
|
|
data_rdata_core,
|
|
data_err_i,
|
|
dummy_instr_id,
|
|
dummy_instr_wb,
|
|
rf_raddr_a,
|
|
rf_raddr_b,
|
|
rf_waddr_wb,
|
|
rf_we_wb,
|
|
rf_wdata_wb_ecc,
|
|
rf_rdata_a_ecc,
|
|
rf_rdata_b_ecc,
|
|
ic_tag_req,
|
|
ic_tag_write,
|
|
ic_tag_addr,
|
|
ic_tag_wdata,
|
|
ic_data_req,
|
|
ic_data_write,
|
|
ic_data_addr,
|
|
ic_data_wdata,
|
|
scramble_key_valid_i,
|
|
ic_scr_key_req,
|
|
irq_software_i,
|
|
irq_timer_i,
|
|
irq_external_i,
|
|
irq_fast_i,
|
|
irq_nm_i,
|
|
irq_pending,
|
|
debug_req_i,
|
|
crash_dump_o,
|
|
double_fault_seen_o,
|
|
fetch_enable_i,
|
|
core_busy_d
|
|
});
|
|
|
|
logic [NumBufferBits-1:0] buf_in, buf_out;
|
|
|
|
logic [31:0] hart_id_local;
|
|
logic [31:0] boot_addr_local;
|
|
|
|
logic instr_req_local;
|
|
logic instr_gnt_local;
|
|
logic instr_rvalid_local;
|
|
logic [31:0] instr_addr_local;
|
|
logic [MemDataWidth-1:0] instr_rdata_local;
|
|
logic instr_err_local;
|
|
|
|
logic data_req_local;
|
|
logic data_gnt_local;
|
|
logic data_rvalid_local;
|
|
logic data_we_local;
|
|
logic [3:0] data_be_local;
|
|
logic [31:0] data_addr_local;
|
|
logic [MemDataWidth-1:0] data_wdata_local;
|
|
logic [MemDataWidth-1:0] data_rdata_local;
|
|
logic data_err_local;
|
|
|
|
logic dummy_instr_id_local;
|
|
logic dummy_instr_wb_local;
|
|
logic [4:0] rf_raddr_a_local;
|
|
logic [4:0] rf_raddr_b_local;
|
|
logic [4:0] rf_waddr_wb_local;
|
|
logic rf_we_wb_local;
|
|
logic [RegFileDataWidth-1:0] rf_wdata_wb_ecc_local;
|
|
logic [RegFileDataWidth-1:0] rf_rdata_a_ecc_local;
|
|
logic [RegFileDataWidth-1:0] rf_rdata_b_ecc_local;
|
|
|
|
logic [IC_NUM_WAYS-1:0] ic_tag_req_local;
|
|
logic ic_tag_write_local;
|
|
logic [IC_INDEX_W-1:0] ic_tag_addr_local;
|
|
logic [TagSizeECC-1:0] ic_tag_wdata_local;
|
|
logic [IC_NUM_WAYS-1:0] ic_data_req_local;
|
|
logic ic_data_write_local;
|
|
logic [IC_INDEX_W-1:0] ic_data_addr_local;
|
|
logic [LineSizeECC-1:0] ic_data_wdata_local;
|
|
logic scramble_key_valid_local;
|
|
logic ic_scr_key_req_local;
|
|
|
|
logic irq_software_local;
|
|
logic irq_timer_local;
|
|
logic irq_external_local;
|
|
logic [14:0] irq_fast_local;
|
|
logic irq_nm_local;
|
|
logic irq_pending_local;
|
|
|
|
logic debug_req_local;
|
|
crash_dump_t crash_dump_local;
|
|
logic double_fault_seen_local;
|
|
ibex_mubi_t fetch_enable_local;
|
|
|
|
ibex_mubi_t core_busy_local;
|
|
|
|
assign buf_in = {
|
|
hart_id_i,
|
|
boot_addr_i,
|
|
instr_req_o,
|
|
instr_gnt_i,
|
|
instr_rvalid_i,
|
|
instr_addr_o,
|
|
instr_rdata_core,
|
|
instr_err_i,
|
|
data_req_o,
|
|
data_gnt_i,
|
|
data_rvalid_i,
|
|
data_we_o,
|
|
data_be_o,
|
|
data_addr_o,
|
|
data_wdata_core,
|
|
data_rdata_core,
|
|
data_err_i,
|
|
dummy_instr_id,
|
|
dummy_instr_wb,
|
|
rf_raddr_a,
|
|
rf_raddr_b,
|
|
rf_waddr_wb,
|
|
rf_we_wb,
|
|
rf_wdata_wb_ecc,
|
|
rf_rdata_a_ecc,
|
|
rf_rdata_b_ecc,
|
|
ic_tag_req,
|
|
ic_tag_write,
|
|
ic_tag_addr,
|
|
ic_tag_wdata,
|
|
ic_data_req,
|
|
ic_data_write,
|
|
ic_data_addr,
|
|
ic_data_wdata,
|
|
scramble_key_valid_q,
|
|
ic_scr_key_req,
|
|
irq_software_i,
|
|
irq_timer_i,
|
|
irq_external_i,
|
|
irq_fast_i,
|
|
irq_nm_i,
|
|
irq_pending,
|
|
debug_req_i,
|
|
crash_dump_o,
|
|
double_fault_seen_o,
|
|
fetch_enable_i,
|
|
core_busy_d
|
|
};
|
|
|
|
assign {
|
|
hart_id_local,
|
|
boot_addr_local,
|
|
instr_req_local,
|
|
instr_gnt_local,
|
|
instr_rvalid_local,
|
|
instr_addr_local,
|
|
instr_rdata_local,
|
|
instr_err_local,
|
|
data_req_local,
|
|
data_gnt_local,
|
|
data_rvalid_local,
|
|
data_we_local,
|
|
data_be_local,
|
|
data_addr_local,
|
|
data_wdata_local,
|
|
data_rdata_local,
|
|
data_err_local,
|
|
dummy_instr_id_local,
|
|
dummy_instr_wb_local,
|
|
rf_raddr_a_local,
|
|
rf_raddr_b_local,
|
|
rf_waddr_wb_local,
|
|
rf_we_wb_local,
|
|
rf_wdata_wb_ecc_local,
|
|
rf_rdata_a_ecc_local,
|
|
rf_rdata_b_ecc_local,
|
|
ic_tag_req_local,
|
|
ic_tag_write_local,
|
|
ic_tag_addr_local,
|
|
ic_tag_wdata_local,
|
|
ic_data_req_local,
|
|
ic_data_write_local,
|
|
ic_data_addr_local,
|
|
ic_data_wdata_local,
|
|
scramble_key_valid_local,
|
|
ic_scr_key_req_local,
|
|
irq_software_local,
|
|
irq_timer_local,
|
|
irq_external_local,
|
|
irq_fast_local,
|
|
irq_nm_local,
|
|
irq_pending_local,
|
|
debug_req_local,
|
|
crash_dump_local,
|
|
double_fault_seen_local,
|
|
fetch_enable_local,
|
|
core_busy_local
|
|
} = buf_out;
|
|
|
|
// Manually buffer all input signals.
|
|
prim_buf #(.Width(NumBufferBits)) u_signals_prim_buf (
|
|
.in_i(buf_in),
|
|
.out_o(buf_out)
|
|
);
|
|
|
|
logic [TagSizeECC-1:0] ic_tag_rdata_local [IC_NUM_WAYS];
|
|
logic [LineSizeECC-1:0] ic_data_rdata_local [IC_NUM_WAYS];
|
|
for (genvar k = 0; k < IC_NUM_WAYS; k++) begin : gen_ways
|
|
prim_buf #(.Width(TagSizeECC)) u_tag_prim_buf (
|
|
.in_i(ic_tag_rdata[k]),
|
|
.out_o(ic_tag_rdata_local[k])
|
|
);
|
|
prim_buf #(.Width(LineSizeECC)) u_data_prim_buf (
|
|
.in_i(ic_data_rdata[k]),
|
|
.out_o(ic_data_rdata_local[k])
|
|
);
|
|
end
|
|
|
|
logic lockstep_alert_minor_local, lockstep_alert_major_internal_local;
|
|
logic lockstep_alert_major_bus_local;
|
|
|
|
ibex_lockstep #(
|
|
.PMPEnable (PMPEnable),
|
|
.PMPGranularity (PMPGranularity),
|
|
.PMPNumRegions (PMPNumRegions),
|
|
.MHPMCounterNum (MHPMCounterNum),
|
|
.MHPMCounterWidth (MHPMCounterWidth),
|
|
.RV32E (RV32E),
|
|
.RV32M (RV32M),
|
|
.RV32B (RV32B),
|
|
.BranchTargetALU (BranchTargetALU),
|
|
.ICache (ICache),
|
|
.ICacheECC (ICacheECC),
|
|
.BusSizeECC (BusSizeECC),
|
|
.TagSizeECC (TagSizeECC),
|
|
.LineSizeECC (LineSizeECC),
|
|
.BranchPredictor (BranchPredictor),
|
|
.DbgTriggerEn (DbgTriggerEn),
|
|
.DbgHwBreakNum (DbgHwBreakNum),
|
|
.WritebackStage (WritebackStage),
|
|
.ResetAll (ResetAll),
|
|
.RndCnstLfsrSeed (RndCnstLfsrSeed),
|
|
.RndCnstLfsrPerm (RndCnstLfsrPerm),
|
|
.SecureIbex (SecureIbex),
|
|
.DummyInstructions(DummyInstructions),
|
|
.RegFileECC (RegFileECC),
|
|
.RegFileDataWidth (RegFileDataWidth),
|
|
.MemECC (MemECC),
|
|
.DmHaltAddr (DmHaltAddr),
|
|
.DmExceptionAddr (DmExceptionAddr)
|
|
) u_ibex_lockstep (
|
|
.clk_i (clk),
|
|
.rst_ni (rst_ni),
|
|
|
|
.hart_id_i (hart_id_local),
|
|
.boot_addr_i (boot_addr_local),
|
|
|
|
.instr_req_i (instr_req_local),
|
|
.instr_gnt_i (instr_gnt_local),
|
|
.instr_rvalid_i (instr_rvalid_local),
|
|
.instr_addr_i (instr_addr_local),
|
|
.instr_rdata_i (instr_rdata_local),
|
|
.instr_err_i (instr_err_local),
|
|
|
|
.data_req_i (data_req_local),
|
|
.data_gnt_i (data_gnt_local),
|
|
.data_rvalid_i (data_rvalid_local),
|
|
.data_we_i (data_we_local),
|
|
.data_be_i (data_be_local),
|
|
.data_addr_i (data_addr_local),
|
|
.data_wdata_i (data_wdata_local),
|
|
.data_rdata_i (data_rdata_local),
|
|
.data_err_i (data_err_local),
|
|
|
|
.dummy_instr_id_i (dummy_instr_id_local),
|
|
.dummy_instr_wb_i (dummy_instr_wb_local),
|
|
.rf_raddr_a_i (rf_raddr_a_local),
|
|
.rf_raddr_b_i (rf_raddr_b_local),
|
|
.rf_waddr_wb_i (rf_waddr_wb_local),
|
|
.rf_we_wb_i (rf_we_wb_local),
|
|
.rf_wdata_wb_ecc_i (rf_wdata_wb_ecc_local),
|
|
.rf_rdata_a_ecc_i (rf_rdata_a_ecc_local),
|
|
.rf_rdata_b_ecc_i (rf_rdata_b_ecc_local),
|
|
|
|
.ic_tag_req_i (ic_tag_req_local),
|
|
.ic_tag_write_i (ic_tag_write_local),
|
|
.ic_tag_addr_i (ic_tag_addr_local),
|
|
.ic_tag_wdata_i (ic_tag_wdata_local),
|
|
.ic_tag_rdata_i (ic_tag_rdata_local),
|
|
.ic_data_req_i (ic_data_req_local),
|
|
.ic_data_write_i (ic_data_write_local),
|
|
.ic_data_addr_i (ic_data_addr_local),
|
|
.ic_data_wdata_i (ic_data_wdata_local),
|
|
.ic_data_rdata_i (ic_data_rdata_local),
|
|
.ic_scr_key_valid_i (scramble_key_valid_local),
|
|
.ic_scr_key_req_i (ic_scr_key_req_local),
|
|
|
|
.irq_software_i (irq_software_local),
|
|
.irq_timer_i (irq_timer_local),
|
|
.irq_external_i (irq_external_local),
|
|
.irq_fast_i (irq_fast_local),
|
|
.irq_nm_i (irq_nm_local),
|
|
.irq_pending_i (irq_pending_local),
|
|
|
|
.debug_req_i (debug_req_local),
|
|
.crash_dump_i (crash_dump_local),
|
|
.double_fault_seen_i (double_fault_seen_local),
|
|
|
|
.fetch_enable_i (fetch_enable_local),
|
|
.alert_minor_o (lockstep_alert_minor_local),
|
|
.alert_major_internal_o (lockstep_alert_major_internal_local),
|
|
.alert_major_bus_o (lockstep_alert_major_bus_local),
|
|
.core_busy_i (core_busy_local),
|
|
.test_en_i (test_en_i),
|
|
.scan_rst_ni (scan_rst_ni)
|
|
);
|
|
|
|
prim_buf u_prim_buf_alert_minor (
|
|
.in_i (lockstep_alert_minor_local),
|
|
.out_o(lockstep_alert_minor)
|
|
);
|
|
|
|
prim_buf u_prim_buf_alert_major_internal (
|
|
.in_i (lockstep_alert_major_internal_local),
|
|
.out_o(lockstep_alert_major_internal)
|
|
);
|
|
|
|
prim_buf u_prim_buf_alert_major_bus (
|
|
.in_i (lockstep_alert_major_bus_local),
|
|
.out_o(lockstep_alert_major_bus)
|
|
);
|
|
|
|
end else begin : gen_no_lockstep
|
|
assign lockstep_alert_major_internal = 1'b0;
|
|
assign lockstep_alert_major_bus = 1'b0;
|
|
assign lockstep_alert_minor = 1'b0;
|
|
logic unused_scan;
|
|
assign unused_scan = scan_rst_ni;
|
|
end
|
|
|
|
assign alert_major_internal_o = core_alert_major_internal |
|
|
lockstep_alert_major_internal |
|
|
rf_alert_major_internal;
|
|
assign alert_major_bus_o = core_alert_major_bus | lockstep_alert_major_bus;
|
|
assign alert_minor_o = core_alert_minor | lockstep_alert_minor;
|
|
|
|
// X checks for top-level outputs
|
|
`ASSERT_KNOWN(IbexInstrReqX, instr_req_o)
|
|
`ASSERT_KNOWN_IF(IbexInstrReqPayloadX, instr_addr_o, instr_req_o)
|
|
|
|
`ASSERT_KNOWN(IbexDataReqX, data_req_o)
|
|
`ASSERT_KNOWN_IF(IbexDataReqPayloadX,
|
|
{data_we_o, data_be_o, data_addr_o, data_wdata_o, data_wdata_intg_o}, data_req_o)
|
|
|
|
`ASSERT_KNOWN(IbexScrambleReqX, scramble_req_o)
|
|
`ASSERT_KNOWN(IbexDoubleFaultSeenX, double_fault_seen_o)
|
|
`ASSERT_KNOWN(IbexAlertMinorX, alert_minor_o)
|
|
`ASSERT_KNOWN(IbexAlertMajorInternalX, alert_major_internal_o)
|
|
`ASSERT_KNOWN(IbexAlertMajorBusX, alert_major_bus_o)
|
|
`ASSERT_KNOWN(IbexCoreSleepX, core_sleep_o)
|
|
|
|
// X check for top-level inputs
|
|
`ASSERT_KNOWN(IbexTestEnX, test_en_i)
|
|
`ASSERT_KNOWN(IbexRamCfgX, ram_cfg_i)
|
|
`ASSERT_KNOWN(IbexHartIdX, hart_id_i)
|
|
`ASSERT_KNOWN(IbexBootAddrX, boot_addr_i)
|
|
|
|
`ASSERT_KNOWN(IbexInstrGntX, instr_gnt_i)
|
|
`ASSERT_KNOWN(IbexInstrRValidX, instr_rvalid_i)
|
|
`ASSERT_KNOWN_IF(IbexInstrRPayloadX,
|
|
{instr_rdata_i, instr_rdata_intg_i, instr_err_i}, instr_rvalid_i)
|
|
|
|
`ASSERT_KNOWN(IbexDataGntX, data_gnt_i)
|
|
`ASSERT_KNOWN(IbexDataRValidX, data_rvalid_i)
|
|
`ifdef INC_ASSERT
|
|
// Ibex can have a maximum of 2 accesses outstanding on the DSide. This is because it does not
|
|
// speculative data accesses so the only requests that can be in flight must relate to a single
|
|
// ongoing load or store instruction. Due to unaligned access support a single load or store can
|
|
// generate 2 accesses.
|
|
localparam int unsigned MaxOutstandingDSideAccesses = 2;
|
|
|
|
typedef struct packed {
|
|
logic valid;
|
|
logic is_read;
|
|
} pending_access_t;
|
|
|
|
pending_access_t pending_dside_accesses_q[MaxOutstandingDSideAccesses];
|
|
pending_access_t pending_dside_accesses_d[MaxOutstandingDSideAccesses];
|
|
pending_access_t pending_dside_accesses_shifted[MaxOutstandingDSideAccesses];
|
|
|
|
for (genvar i = 0; i < MaxOutstandingDSideAccesses; i++) begin : g_dside_tracker
|
|
always_ff @(posedge clk or negedge rst_ni) begin
|
|
if (!rst_ni) begin
|
|
pending_dside_accesses_q[i] <= '0;
|
|
end else begin
|
|
pending_dside_accesses_q[i] <= pending_dside_accesses_d[i];
|
|
end
|
|
end
|
|
|
|
always_comb begin
|
|
pending_dside_accesses_shifted[i] = pending_dside_accesses_q[i];
|
|
|
|
if (data_rvalid_i) begin
|
|
if (i != MaxOutstandingDSideAccesses - 1) begin
|
|
pending_dside_accesses_shifted[i] = pending_dside_accesses_q[i + 1];
|
|
end else begin
|
|
pending_dside_accesses_shifted[i] = '0;
|
|
end
|
|
end
|
|
end
|
|
|
|
if (i == 0) begin : g_track_first_entry
|
|
always_comb begin
|
|
pending_dside_accesses_d[i] = pending_dside_accesses_shifted[i];
|
|
|
|
if (data_req_o && data_gnt_i && !pending_dside_accesses_shifted[i].valid) begin
|
|
pending_dside_accesses_d[i].valid = 1'b1;
|
|
pending_dside_accesses_d[i].is_read = ~data_we_o;
|
|
end
|
|
end
|
|
end else begin : g_track_other_entries
|
|
always_comb begin
|
|
pending_dside_accesses_d[i] = pending_dside_accesses_shifted[i];
|
|
|
|
if (data_req_o && data_gnt_i && pending_dside_accesses_shifted[i - 1].valid &&
|
|
!pending_dside_accesses_shifted[i].valid) begin
|
|
pending_dside_accesses_d[i].valid = 1'b1;
|
|
pending_dside_accesses_d[i].is_read = ~data_we_o;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
// We should never start a new data request if we've already got the maximum outstanding. We can
|
|
// start a new request in the same cycle an old one ends, in which case we'll see all pending
|
|
// accesses valid but one will be ending this cycle so the empty slot can be immediately used by
|
|
// the new request.
|
|
`ASSERT(MaxOutstandingDSideAccessesCorrect,
|
|
data_req_o |->
|
|
~pending_dside_accesses_q[MaxOutstandingDSideAccesses-1].valid | data_rvalid_i)
|
|
|
|
// Should only see a request response if we're expecting one
|
|
`ASSERT(PendingAccessTrackingCorrect, data_rvalid_i |-> pending_dside_accesses_q[0])
|
|
|
|
if (SecureIbex) begin : g_secure_ibex_mem_assert
|
|
// For SecureIbex responses to both writes and reads must specify rdata and rdata_intg (for
|
|
// writes rdata is effectively ignored by rdata_intg still checked against rdata)
|
|
`ASSERT_KNOWN_IF(IbexDataRPayloadX, {data_rdata_i, data_rdata_intg_i},
|
|
data_rvalid_i)
|
|
end else begin : g_no_secure_ibex_mem_assert
|
|
// Without SecureIbex data_rdata_i and data_rdata_intg_i are only relevant to reads. Check
|
|
// neither are X on a response to a read.
|
|
`ASSERT_KNOWN_IF(IbexDataRPayloadX, {data_rdata_i, data_rdata_intg_i},
|
|
data_rvalid_i & pending_dside_accesses_q[0].is_read)
|
|
end
|
|
|
|
// data_err_i relevant to both reads and writes. Check it isn't X on any response.
|
|
`ASSERT_KNOWN_IF(IbexDataRErrPayloadX, data_err_i, data_rvalid_i)
|
|
|
|
`ifdef RVFI
|
|
// Tracking logic and predictor for double_fault_seen_o output, relies on RVFI so only include
|
|
// it where RVFI is available.
|
|
|
|
// Returns 1'b1 if the provided instruction decodes to one that would write the sync_exc_bit of
|
|
// the CPUCTRLSTS CSR
|
|
function automatic logic insn_write_sync_exc_seen(logic [31:0] insn_bits);
|
|
return (insn_bits[6:0] == OPCODE_SYSTEM) &&
|
|
(insn_bits[14:12] inside {3'b001, 3'b010, 3'b011, 3'b101}) &&
|
|
(insn_bits[31:20] == CSR_CPUCTRLSTS);
|
|
endfunction
|
|
|
|
// Given an instruction that writes the sync_exc_bit of the CPUCTRLSTS CSR along with the value
|
|
// of the rs1 register read for that instruction and the current predicted sync_exc_bit bit
|
|
// return the new value of the sync_exc_bit after the instruction is executed.
|
|
function automatic logic new_sync_exc_bit(logic [31:0] insn_bits, logic [31:0] rs1,
|
|
logic cur_bit);
|
|
logic sync_exc_update_bit;
|
|
|
|
sync_exc_update_bit = insn_bits[14] ? 1'b0 : rs1[6];
|
|
|
|
case (insn_bits[13:12])
|
|
2'b01: return sync_exc_update_bit;
|
|
2'b10: return cur_bit | sync_exc_update_bit;
|
|
2'b11: return cur_bit & ~sync_exc_update_bit;
|
|
default: return 1'bx;
|
|
endcase
|
|
endfunction
|
|
|
|
localparam int DoubleFaultSeenLatency = 3;
|
|
logic [DoubleFaultSeenLatency-1:0] double_fault_seen_delay_buffer;
|
|
logic [DoubleFaultSeenLatency-2:0] double_fault_seen_delay_buffer_q;
|
|
logic sync_exc_seen;
|
|
logic new_sync_exc;
|
|
logic double_fault_seen_predicted;
|
|
|
|
assign new_sync_exc = rvfi_valid & rvfi_trap & ~rvfi_ext_debug_mode;
|
|
assign double_fault_seen_predicted = sync_exc_seen & new_sync_exc;
|
|
|
|
// Depending on whether the exception comes from the WB or ID/EX stage the precise timing of the
|
|
// double_fault_seen_o output vs the double fault instruction being visible on RVFI differs. At
|
|
// the earliest extreme it can be asserted the same cycle the instruction is visible on the
|
|
// RVFI. Buffer the last few cycles of double_fault_seen_o output for checking. We can
|
|
// guarantee the minimum spacing between double_fault_seen_o assertions (occurring when the
|
|
// first instruction of an exception handler continuously double faults with a single cycle
|
|
// memory access time) is sufficient that we'll only see a single bit set in the delay buffer.
|
|
assign double_fault_seen_delay_buffer = {double_fault_seen_delay_buffer_q, double_fault_seen_o};
|
|
|
|
always @(posedge clk_i or negedge rst_ni) begin
|
|
if (!rst_ni) begin
|
|
double_fault_seen_delay_buffer_q <= '0;
|
|
sync_exc_seen <= 1'b0;
|
|
end else begin
|
|
double_fault_seen_delay_buffer_q <=
|
|
double_fault_seen_delay_buffer[DoubleFaultSeenLatency-2:0];
|
|
|
|
if (new_sync_exc) begin
|
|
// Set flag when we see a new synchronous exception
|
|
sync_exc_seen <= 1'b1;
|
|
end else if (rvfi_valid && rvfi_insn == 32'h30200073) begin
|
|
// Clear flag when we see an MRET
|
|
sync_exc_seen <= 1'b0;
|
|
end else if (rvfi_valid && insn_write_sync_exc_seen(rvfi_insn)) begin
|
|
// Update predicted sync_exc_seen when the instruction modifies the relevant CPUCTRLSTS
|
|
// CSR bit.
|
|
sync_exc_seen <= new_sync_exc_bit(rvfi_insn, rvfi_rs1_rdata, sync_exc_seen);
|
|
end
|
|
end
|
|
end
|
|
|
|
// We should only have a single assertion of double_fault_seen in the delay buffer
|
|
`ASSERT(DoubleFaultSinglePulse, $onehot0(double_fault_seen_delay_buffer))
|
|
// If we predict a double_fault_seen_o we should see one in the delay buffer
|
|
`ASSERT(DoubleFaultPulseSeenOnDoubleFault,
|
|
double_fault_seen_predicted |-> |double_fault_seen_delay_buffer)
|
|
// If double_fault_seen_o is asserted we should see predict one occurring within a bounded time
|
|
`ASSERT(DoubleFaultPulseOnlyOnDoubleFault,
|
|
double_fault_seen_o |-> ##[0:DoubleFaultSeenLatency] double_fault_seen_predicted)
|
|
`endif // RVFI
|
|
`endif
|
|
|
|
`ASSERT_KNOWN(IbexIrqX, {irq_software_i, irq_timer_i, irq_external_i, irq_fast_i, irq_nm_i})
|
|
|
|
`ASSERT_KNOWN(IbexScrambleKeyValidX, scramble_key_valid_i)
|
|
`ASSERT_KNOWN_IF(IbexScramblePayloadX, {scramble_key_i, scramble_nonce_i}, scramble_key_valid_i)
|
|
|
|
`ASSERT_KNOWN(IbexDebugReqX, debug_req_i)
|
|
`ASSERT_KNOWN(IbexFetchEnableX, fetch_enable_i)
|
|
|
|
// Dummy instructions may only write to register 0, which is a special register when dummy
|
|
// instructions are enabled.
|
|
`ASSERT(WaddrAZeroForDummyInstr, dummy_instr_wb && rf_we_wb |-> rf_waddr_wb == '0)
|
|
|
|
// Ensure the crash dump is connected to the correct internal signals
|
|
`ASSERT(CrashDumpCurrentPCConn, crash_dump_o.current_pc === u_ibex_core.pc_id)
|
|
`ASSERT(CrashDumpNextPCConn, crash_dump_o.next_pc === u_ibex_core.pc_if)
|
|
`ASSERT(CrashDumpLastDataAddrConn,
|
|
crash_dump_o.last_data_addr === u_ibex_core.load_store_unit_i.addr_last_q)
|
|
`ASSERT(CrashDumpExceptionPCConn,
|
|
crash_dump_o.exception_pc === u_ibex_core.cs_registers_i.mepc_q)
|
|
`ASSERT(CrashDumpExceptionAddrConn,
|
|
crash_dump_o.exception_addr === u_ibex_core.cs_registers_i.mtval_q)
|
|
|
|
// Explicit INC_ASSERT due to instantiation of prim_secded_inv_39_32_dec below that is only used
|
|
// by assertions
|
|
`ifdef INC_ASSERT
|
|
if (MemECC) begin : g_mem_ecc_asserts
|
|
logic [1:0] data_ecc_err, instr_ecc_err;
|
|
|
|
// Check alerts from memory integrity failures
|
|
|
|
prim_secded_inv_39_32_dec u_data_intg_dec (
|
|
.data_i (data_rdata_core),
|
|
.data_o (),
|
|
.syndrome_o (),
|
|
.err_o (data_ecc_err)
|
|
);
|
|
`ASSERT(MajorAlertOnDMemIntegrityErr,
|
|
data_rvalid_i && (|data_ecc_err) |-> ##[0:5] alert_major_bus_o)
|
|
|
|
prim_secded_inv_39_32_dec u_instr_intg_dec (
|
|
.data_i (instr_rdata_core),
|
|
.data_o (),
|
|
.syndrome_o (),
|
|
.err_o (instr_ecc_err)
|
|
);
|
|
// Check alerts from memory integrity failures
|
|
`ASSERT(MajorAlertOnIMemIntegrityErr,
|
|
instr_rvalid_i && (|instr_ecc_err) |-> ##[0:5] alert_major_bus_o)
|
|
end
|
|
`endif
|
|
endmodule
|