mirror of
https://github.com/lowRISC/ibex.git
synced 2025-06-28 09:17:17 -04:00
[rtl] Add dummy instruction insertion
- Adds a new module in the IF stage to inject dummy instructions into the pipeline - Control / frequency of insertion is governed by configuration CSRs - Extra CSR added to allow reseed of the internal LFSR useed for randomizing insertion - Extra logic added to the register file to make dummy instruction writebacks look like real intructions (via the zero register) Signed-off-by: Tom Roberts <tomroberts@lowrisc.org>
This commit is contained in:
parent
5c7cdfe14e
commit
d5ee96fff6
15 changed files with 1008 additions and 75 deletions
|
@ -66,7 +66,9 @@ Ibex implements all the Control and Status Registers (CSRs) listed in the follow
|
||||||
+---------+--------------------+--------+-----------------------------------------------+
|
+---------+--------------------+--------+-----------------------------------------------+
|
||||||
| 0x7B3 | ``dscratch1`` | RW | Debug Scratch Register 1 |
|
| 0x7B3 | ``dscratch1`` | RW | Debug Scratch Register 1 |
|
||||||
+---------+--------------------+--------+-----------------------------------------------+
|
+---------+--------------------+--------+-----------------------------------------------+
|
||||||
| 0x7C0 | ``cpuctrl`` | RW | CPU Control Register (Custom CSR) |
|
| 0x7C0 | ``cpuctrl`` | WARL | CPU Control Register (Custom CSR) |
|
||||||
|
+---------+--------------------+--------+-----------------------------------------------+
|
||||||
|
| 0x7C1 | ``secureseed`` | WARL | Security feature random seed (Custom CSR) |
|
||||||
+---------+--------------------+--------+-----------------------------------------------+
|
+---------+--------------------+--------+-----------------------------------------------+
|
||||||
| 0xB00 | ``mcycle`` | RW | Machine Cycle Counter |
|
| 0xB00 | ``mcycle`` | RW | Machine Cycle Counter |
|
||||||
+---------+--------------------+--------+-----------------------------------------------+
|
+---------+--------------------+--------+-----------------------------------------------+
|
||||||
|
@ -500,6 +502,16 @@ Other bit fields read as zero.
|
||||||
+-------+------+------------------------------------------------------------------+
|
+-------+------+------------------------------------------------------------------+
|
||||||
| Bit# | R/W | Description |
|
| Bit# | R/W | Description |
|
||||||
+-------+------+------------------------------------------------------------------+
|
+-------+------+------------------------------------------------------------------+
|
||||||
|
| 5:3 | WARL | **dummy_instr_mask:** Mask to control frequency of dummy |
|
||||||
|
| | | instruction insertion. If the core has not been configured with |
|
||||||
|
| | | security features (SecureIbex parameter == 0), this field will |
|
||||||
|
| | | always read as zero (see :ref:`security`). |
|
||||||
|
+-------+------+------------------------------------------------------------------+
|
||||||
|
| 2 | WARL | **dummy_instr_en:** Enable (1) or disable (0) dummy instruction |
|
||||||
|
| | | insertion features. If the core has not been configured with |
|
||||||
|
| | | security features (SecureIbex parameter == 0), this field will |
|
||||||
|
| | | always read as zero (see :ref:`security`). |
|
||||||
|
+-------+------+------------------------------------------------------------------+
|
||||||
| 1 | WARL | **data_ind_timing:** Enable (1) or disable (0) data-independent |
|
| 1 | WARL | **data_ind_timing:** Enable (1) or disable (0) data-independent |
|
||||||
| | | timing features. If the core has not been configured with |
|
| | | timing features. If the core has not been configured with |
|
||||||
| | | security features (SecureIbex parameter == 0), this field will |
|
| | | security features (SecureIbex parameter == 0), this field will |
|
||||||
|
@ -510,6 +522,20 @@ Other bit fields read as zero.
|
||||||
| | | parameter == 0), this field will always read as zero. |
|
| | | parameter == 0), this field will always read as zero. |
|
||||||
+-------+------+------------------------------------------------------------------+
|
+-------+------+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
Security Feature Seed Register (secureseed)
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
CSR Address: ``0x7C1``
|
||||||
|
|
||||||
|
Reset Value: ``0x0000_0000``
|
||||||
|
|
||||||
|
Accessible in Machine Mode only.
|
||||||
|
|
||||||
|
Custom CSR to allow re-seeding of security-related pseudo-random number generators.
|
||||||
|
A write to this register will update the seeding of pseudo-random number generators inside the design.
|
||||||
|
This allows software to improve the randomness, and therefore security, of certain features by periodically reading from a true random number generator peripheral.
|
||||||
|
Seed values are not actually stored in a register and so reads to this register will always return zero.
|
||||||
|
|
||||||
Time Registers (time(h))
|
Time Registers (time(h))
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ Ibex User Manual
|
||||||
performance_counters
|
performance_counters
|
||||||
exception_interrupts
|
exception_interrupts
|
||||||
pmp
|
pmp
|
||||||
|
security
|
||||||
debug
|
debug
|
||||||
tracer
|
tracer
|
||||||
rvfi
|
rvfi
|
||||||
|
|
46
doc/security.rst
Normal file
46
doc/security.rst
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
.. _security:
|
||||||
|
|
||||||
|
Security Features
|
||||||
|
=================
|
||||||
|
|
||||||
|
Ibex implements a set of extra features (when the SecureIbex parameter is set) to support security-critical applications.
|
||||||
|
All features are runtime configurable via bits in the **cpuctrl** custom CSR.
|
||||||
|
|
||||||
|
Data Independent Timing
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
When enabled (via the **data_ind_timing** control bit in the **cpuctrl** register), execution times and power consumption of all instructions shall be independent of input data.
|
||||||
|
This makes it more difficult for an external observer to infer secret data by observing power lines or exploiting timing side-channels.
|
||||||
|
|
||||||
|
In Ibex, most instructions already execute independent of their input operands.
|
||||||
|
When data-independent timing is enabled:
|
||||||
|
* Branches execute identically regardless of their taken/not-taken status
|
||||||
|
* Early completion of multiplication by zero/one is removed
|
||||||
|
* Early completion of divide by zero is removed
|
||||||
|
|
||||||
|
Dummy Instruction Insertion
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
When enabled (via the **dummy_instr_en** control bit in the **cpuctrl** register), dummy instructions will be inserted at random intervals into the execution pipeline.
|
||||||
|
The dummy instructions have no functional impact on processor state, but add some difficult-to-predict timing and power disruption to executed code.
|
||||||
|
This disruption makes it more difficult for an attacker to infer what is being executed, and also makes it more difficult to execute precisely timed fault injection attacks.
|
||||||
|
|
||||||
|
The frequency of injected instructions can be tuned via the **dummy_instr_mask** bits in the **cpuctrl** register.
|
||||||
|
|
||||||
|
+----------------------+----------------------------------------------------------+
|
||||||
|
| **dummy_instr_mask** | Interval |
|
||||||
|
+----------------------+----------------------------------------------------------+
|
||||||
|
| 000 | Dummy instruction every 0 - 4 real instructions |
|
||||||
|
+----------------------+----------------------------------------------------------+
|
||||||
|
| 001 | Dummy instruction every 0 - 8 real instructions |
|
||||||
|
+----------------------+----------------------------------------------------------+
|
||||||
|
| 011 | Dummy instruction every 0 - 16 real instructions |
|
||||||
|
+----------------------+----------------------------------------------------------+
|
||||||
|
| 111 | Dummy instruction every 0 - 32 real instructions |
|
||||||
|
+----------------------+----------------------------------------------------------+
|
||||||
|
|
||||||
|
Other values of **dummy_instr_mask** are legal, but will have a less predictable impact.
|
||||||
|
|
||||||
|
The interval between instruction insertion is randomized in the core using an LFSR.
|
||||||
|
Sofware can periodically re-seed this LFSR with true random numbers (if available) via the **secureseed** CSR.
|
||||||
|
This will make the insertion interval of dummy instructions much harder for an attacker to predict.
|
|
@ -15,6 +15,7 @@ ${PRJ_DIR}/ibex/shared/rtl/prim_clock_gating.sv
|
||||||
+incdir+${PRJ_DIR}/ibex/rtl
|
+incdir+${PRJ_DIR}/ibex/rtl
|
||||||
${PRJ_DIR}/ibex/shared/rtl/prim_assert.sv
|
${PRJ_DIR}/ibex/shared/rtl/prim_assert.sv
|
||||||
${PRJ_DIR}/ibex/shared/rtl/prim_generic_ram_1p.sv
|
${PRJ_DIR}/ibex/shared/rtl/prim_generic_ram_1p.sv
|
||||||
|
${PRJ_DIR}/ibex/shared/rtl/prim_lfsr.sv
|
||||||
${PRJ_DIR}/ibex/shared/rtl/prim_secded_28_22_enc.sv
|
${PRJ_DIR}/ibex/shared/rtl/prim_secded_28_22_enc.sv
|
||||||
${PRJ_DIR}/ibex/shared/rtl/prim_secded_28_22_dec.sv
|
${PRJ_DIR}/ibex/shared/rtl/prim_secded_28_22_dec.sv
|
||||||
${PRJ_DIR}/ibex/shared/rtl/prim_secded_72_64_enc.sv
|
${PRJ_DIR}/ibex/shared/rtl/prim_secded_72_64_enc.sv
|
||||||
|
@ -28,6 +29,7 @@ ${PRJ_DIR}/ibex/rtl/ibex_controller.sv
|
||||||
${PRJ_DIR}/ibex/rtl/ibex_cs_registers.sv
|
${PRJ_DIR}/ibex/rtl/ibex_cs_registers.sv
|
||||||
${PRJ_DIR}/ibex/rtl/ibex_counters.sv
|
${PRJ_DIR}/ibex/rtl/ibex_counters.sv
|
||||||
${PRJ_DIR}/ibex/rtl/ibex_decoder.sv
|
${PRJ_DIR}/ibex/rtl/ibex_decoder.sv
|
||||||
|
${PRJ_DIR}/ibex/rtl/ibex_dummy_instr.sv
|
||||||
${PRJ_DIR}/ibex/rtl/ibex_ex_block.sv
|
${PRJ_DIR}/ibex/rtl/ibex_ex_block.sv
|
||||||
${PRJ_DIR}/ibex/rtl/ibex_wb_stage.sv
|
${PRJ_DIR}/ibex/rtl/ibex_wb_stage.sv
|
||||||
${PRJ_DIR}/ibex/rtl/ibex_id_stage.sv
|
${PRJ_DIR}/ibex/rtl/ibex_id_stage.sv
|
||||||
|
|
|
@ -9,6 +9,7 @@ filesets:
|
||||||
files_rtl:
|
files_rtl:
|
||||||
depend:
|
depend:
|
||||||
- lowrisc:prim:assert
|
- lowrisc:prim:assert
|
||||||
|
- lowrisc:prim:lfsr
|
||||||
- lowrisc:ibex:ibex_pkg
|
- lowrisc:ibex:ibex_pkg
|
||||||
- lowrisc:ibex:ibex_icache
|
- lowrisc:ibex:ibex_icache
|
||||||
files:
|
files:
|
||||||
|
@ -28,6 +29,7 @@ filesets:
|
||||||
- rtl/ibex_prefetch_buffer.sv
|
- rtl/ibex_prefetch_buffer.sv
|
||||||
- rtl/ibex_pmp.sv
|
- rtl/ibex_pmp.sv
|
||||||
- rtl/ibex_wb_stage.sv
|
- rtl/ibex_wb_stage.sv
|
||||||
|
- rtl/ibex_dummy_instr.sv
|
||||||
# XXX: Figure out the best way to switch these two implementations
|
# XXX: Figure out the best way to switch these two implementations
|
||||||
# dynamically on the target.
|
# dynamically on the target.
|
||||||
# - rtl/ibex_register_file_latch.sv # ASIC
|
# - rtl/ibex_register_file_latch.sv # ASIC
|
||||||
|
|
|
@ -104,10 +104,12 @@ module ibex_core #(
|
||||||
|
|
||||||
import ibex_pkg::*;
|
import ibex_pkg::*;
|
||||||
|
|
||||||
localparam int unsigned PMP_NUM_CHAN = 2;
|
localparam int unsigned PMP_NUM_CHAN = 2;
|
||||||
localparam bit DataIndTiming = SecureIbex;
|
localparam bit DataIndTiming = SecureIbex;
|
||||||
|
localparam bit DummyInstructions = SecureIbex;
|
||||||
|
|
||||||
// IF/ID signals
|
// IF/ID signals
|
||||||
|
logic dummy_instr_id;
|
||||||
logic instr_valid_id;
|
logic instr_valid_id;
|
||||||
logic instr_new_id;
|
logic instr_new_id;
|
||||||
logic [31:0] instr_rdata_id; // Instruction sampled inside IF stage
|
logic [31:0] instr_rdata_id; // Instruction sampled inside IF stage
|
||||||
|
@ -126,6 +128,10 @@ module ibex_core #(
|
||||||
logic imd_val_we_ex;
|
logic imd_val_we_ex;
|
||||||
|
|
||||||
logic data_ind_timing;
|
logic data_ind_timing;
|
||||||
|
logic dummy_instr_en;
|
||||||
|
logic [2:0] dummy_instr_mask;
|
||||||
|
logic dummy_instr_seed_en;
|
||||||
|
logic [31:0] dummy_instr_seed;
|
||||||
logic icache_enable;
|
logic icache_enable;
|
||||||
logic icache_inval;
|
logic icache_inval;
|
||||||
|
|
||||||
|
@ -364,10 +370,11 @@ module ibex_core #(
|
||||||
//////////////
|
//////////////
|
||||||
|
|
||||||
ibex_if_stage #(
|
ibex_if_stage #(
|
||||||
.DmHaltAddr ( DmHaltAddr ),
|
.DmHaltAddr ( DmHaltAddr ),
|
||||||
.DmExceptionAddr ( DmExceptionAddr ),
|
.DmExceptionAddr ( DmExceptionAddr ),
|
||||||
.ICache ( ICache ),
|
.DummyInstructions ( DummyInstructions ),
|
||||||
.ICacheECC ( ICacheECC )
|
.ICache ( ICache ),
|
||||||
|
.ICacheECC ( ICacheECC )
|
||||||
) if_stage_i (
|
) if_stage_i (
|
||||||
.clk_i ( clk ),
|
.clk_i ( clk ),
|
||||||
.rst_ni ( rst_ni ),
|
.rst_ni ( rst_ni ),
|
||||||
|
@ -394,6 +401,7 @@ module ibex_core #(
|
||||||
.instr_fetch_err_o ( instr_fetch_err ),
|
.instr_fetch_err_o ( instr_fetch_err ),
|
||||||
.instr_fetch_err_plus2_o ( instr_fetch_err_plus2 ),
|
.instr_fetch_err_plus2_o ( instr_fetch_err_plus2 ),
|
||||||
.illegal_c_insn_id_o ( illegal_c_insn_id ),
|
.illegal_c_insn_id_o ( illegal_c_insn_id ),
|
||||||
|
.dummy_instr_id_o ( dummy_instr_id ),
|
||||||
.pc_if_o ( pc_if ),
|
.pc_if_o ( pc_if ),
|
||||||
.pc_id_o ( pc_id ),
|
.pc_id_o ( pc_id ),
|
||||||
|
|
||||||
|
@ -403,6 +411,10 @@ module ibex_core #(
|
||||||
.pc_mux_i ( pc_mux_id ),
|
.pc_mux_i ( pc_mux_id ),
|
||||||
.exc_pc_mux_i ( exc_pc_mux_id ),
|
.exc_pc_mux_i ( exc_pc_mux_id ),
|
||||||
.exc_cause ( exc_cause ),
|
.exc_cause ( exc_cause ),
|
||||||
|
.dummy_instr_en_i ( dummy_instr_en ),
|
||||||
|
.dummy_instr_mask_i ( dummy_instr_mask ),
|
||||||
|
.dummy_instr_seed_en_i ( dummy_instr_seed_en ),
|
||||||
|
.dummy_instr_seed_i ( dummy_instr_seed ),
|
||||||
.icache_enable_i ( icache_enable ),
|
.icache_enable_i ( icache_enable ),
|
||||||
.icache_inval_i ( icache_inval ),
|
.icache_inval_i ( icache_inval ),
|
||||||
|
|
||||||
|
@ -721,24 +733,26 @@ module ibex_core #(
|
||||||
);
|
);
|
||||||
|
|
||||||
ibex_register_file #(
|
ibex_register_file #(
|
||||||
.RV32E(RV32E),
|
.RV32E (RV32E),
|
||||||
.DataWidth(32)
|
.DataWidth (32),
|
||||||
|
.DummyInstructions (DummyInstructions)
|
||||||
) register_file_i (
|
) register_file_i (
|
||||||
.clk_i ( clk_i ),
|
.clk_i ( clk_i ),
|
||||||
.rst_ni ( rst_ni ),
|
.rst_ni ( rst_ni ),
|
||||||
|
|
||||||
.test_en_i ( test_en_i ),
|
.test_en_i ( test_en_i ),
|
||||||
|
.dummy_instr_id_i ( dummy_instr_id ),
|
||||||
|
|
||||||
// Read port a
|
// Read port a
|
||||||
.raddr_a_i ( rf_raddr_a ),
|
.raddr_a_i ( rf_raddr_a ),
|
||||||
.rdata_a_o ( rf_rdata_a ),
|
.rdata_a_o ( rf_rdata_a ),
|
||||||
// Read port b
|
// Read port b
|
||||||
.raddr_b_i ( rf_raddr_b ),
|
.raddr_b_i ( rf_raddr_b ),
|
||||||
.rdata_b_o ( rf_rdata_b ),
|
.rdata_b_o ( rf_rdata_b ),
|
||||||
// write port
|
// write port
|
||||||
.waddr_a_i ( rf_waddr_wb ),
|
.waddr_a_i ( rf_waddr_wb ),
|
||||||
.wdata_a_i ( rf_wdata_wb ),
|
.wdata_a_i ( rf_wdata_wb ),
|
||||||
.we_a_i ( rf_we_wb )
|
.we_a_i ( rf_we_wb )
|
||||||
);
|
);
|
||||||
|
|
||||||
// Explict INC_ASSERT block to avoid unused signal lint warnings were asserts are not included
|
// Explict INC_ASSERT block to avoid unused signal lint warnings were asserts are not included
|
||||||
|
@ -802,16 +816,17 @@ module ibex_core #(
|
||||||
assign csr_addr = csr_num_e'(csr_access ? alu_operand_b_ex[11:0] : 12'b0);
|
assign csr_addr = csr_num_e'(csr_access ? alu_operand_b_ex[11:0] : 12'b0);
|
||||||
|
|
||||||
ibex_cs_registers #(
|
ibex_cs_registers #(
|
||||||
.DbgTriggerEn ( DbgTriggerEn ),
|
.DbgTriggerEn ( DbgTriggerEn ),
|
||||||
.DataIndTiming ( DataIndTiming ),
|
.DataIndTiming ( DataIndTiming ),
|
||||||
.ICache ( ICache ),
|
.DummyInstructions ( DummyInstructions ),
|
||||||
.MHPMCounterNum ( MHPMCounterNum ),
|
.ICache ( ICache ),
|
||||||
.MHPMCounterWidth ( MHPMCounterWidth ),
|
.MHPMCounterNum ( MHPMCounterNum ),
|
||||||
.PMPEnable ( PMPEnable ),
|
.MHPMCounterWidth ( MHPMCounterWidth ),
|
||||||
.PMPGranularity ( PMPGranularity ),
|
.PMPEnable ( PMPEnable ),
|
||||||
.PMPNumRegions ( PMPNumRegions ),
|
.PMPGranularity ( PMPGranularity ),
|
||||||
.RV32E ( RV32E ),
|
.PMPNumRegions ( PMPNumRegions ),
|
||||||
.RV32M ( RV32M )
|
.RV32E ( RV32E ),
|
||||||
|
.RV32M ( RV32M )
|
||||||
) cs_registers_i (
|
) cs_registers_i (
|
||||||
.clk_i ( clk ),
|
.clk_i ( clk ),
|
||||||
.rst_ni ( rst_ni ),
|
.rst_ni ( rst_ni ),
|
||||||
|
@ -866,6 +881,10 @@ module ibex_core #(
|
||||||
.pc_wb_i ( pc_wb ),
|
.pc_wb_i ( pc_wb ),
|
||||||
|
|
||||||
.data_ind_timing_o ( data_ind_timing ),
|
.data_ind_timing_o ( data_ind_timing ),
|
||||||
|
.dummy_instr_en_o ( dummy_instr_en ),
|
||||||
|
.dummy_instr_mask_o ( dummy_instr_mask ),
|
||||||
|
.dummy_instr_seed_en_o ( dummy_instr_seed_en ),
|
||||||
|
.dummy_instr_seed_o ( dummy_instr_seed ),
|
||||||
.icache_enable_o ( icache_enable ),
|
.icache_enable_o ( icache_enable ),
|
||||||
|
|
||||||
.csr_save_if_i ( csr_save_if ),
|
.csr_save_if_i ( csr_save_if ),
|
||||||
|
@ -1010,7 +1029,8 @@ module ibex_core #(
|
||||||
// awaiting instruction retirement and RF Write data/Mem read data whilst instruction is in WB
|
// awaiting instruction retirement and RF Write data/Mem read data whilst instruction is in WB
|
||||||
// So first stage becomes valid when instruction leaves ID/EX stage and remains valid until
|
// So first stage becomes valid when instruction leaves ID/EX stage and remains valid until
|
||||||
// instruction leaves WB
|
// instruction leaves WB
|
||||||
assign rvfi_stage_valid_d[0] = instr_id_done | (rvfi_stage_valid[0] & ~instr_done_wb);
|
assign rvfi_stage_valid_d[0] = (instr_id_done & ~dummy_instr_id) |
|
||||||
|
(rvfi_stage_valid[0] & ~instr_done_wb);
|
||||||
// Second stage is output stage so simple valid cycle after instruction leaves WB (and so has
|
// Second stage is output stage so simple valid cycle after instruction leaves WB (and so has
|
||||||
// retired)
|
// retired)
|
||||||
assign rvfi_stage_valid_d[1] = instr_done_wb;
|
assign rvfi_stage_valid_d[1] = instr_done_wb;
|
||||||
|
@ -1030,7 +1050,7 @@ module ibex_core #(
|
||||||
end else begin
|
end else begin
|
||||||
// Without writeback stage first RVFI stage is output stage so simply valid the cycle after
|
// Without writeback stage first RVFI stage is output stage so simply valid the cycle after
|
||||||
// instruction leaves ID/EX (and so has retired)
|
// instruction leaves ID/EX (and so has retired)
|
||||||
assign rvfi_stage_valid_d[0] = instr_id_done;
|
assign rvfi_stage_valid_d[0] = instr_id_done & ~dummy_instr_id;
|
||||||
// Without writeback stage signal new instr_new_wb when instruction enters ID/EX to correctly
|
// Without writeback stage signal new instr_new_wb when instruction enters ID/EX to correctly
|
||||||
// setup register write signals
|
// setup register write signals
|
||||||
assign rvfi_instr_new_wb = instr_new_id;
|
assign rvfi_instr_new_wb = instr_new_id;
|
||||||
|
|
|
@ -13,16 +13,17 @@
|
||||||
`include "prim_assert.sv"
|
`include "prim_assert.sv"
|
||||||
|
|
||||||
module ibex_cs_registers #(
|
module ibex_cs_registers #(
|
||||||
parameter bit DbgTriggerEn = 0,
|
parameter bit DbgTriggerEn = 0,
|
||||||
parameter bit DataIndTiming = 1'b0,
|
parameter bit DataIndTiming = 1'b0,
|
||||||
parameter bit ICache = 1'b0,
|
parameter bit DummyInstructions = 1'b0,
|
||||||
parameter int unsigned MHPMCounterNum = 10,
|
parameter bit ICache = 1'b0,
|
||||||
parameter int unsigned MHPMCounterWidth = 40,
|
parameter int unsigned MHPMCounterNum = 10,
|
||||||
parameter bit PMPEnable = 0,
|
parameter int unsigned MHPMCounterWidth = 40,
|
||||||
parameter int unsigned PMPGranularity = 0,
|
parameter bit PMPEnable = 0,
|
||||||
parameter int unsigned PMPNumRegions = 4,
|
parameter int unsigned PMPGranularity = 0,
|
||||||
parameter bit RV32E = 0,
|
parameter int unsigned PMPNumRegions = 4,
|
||||||
parameter bit RV32M = 0
|
parameter bit RV32E = 0,
|
||||||
|
parameter bit RV32M = 0
|
||||||
) (
|
) (
|
||||||
// Clock and Reset
|
// Clock and Reset
|
||||||
input logic clk_i,
|
input logic clk_i,
|
||||||
|
@ -81,6 +82,10 @@ module ibex_cs_registers #(
|
||||||
|
|
||||||
// CPU control bits
|
// CPU control bits
|
||||||
output logic data_ind_timing_o,
|
output logic data_ind_timing_o,
|
||||||
|
output logic dummy_instr_en_o,
|
||||||
|
output logic [2:0] dummy_instr_mask_o,
|
||||||
|
output logic dummy_instr_seed_en_o,
|
||||||
|
output logic [31:0] dummy_instr_seed_o,
|
||||||
output logic icache_enable_o,
|
output logic icache_enable_o,
|
||||||
|
|
||||||
// Exception save/restore
|
// Exception save/restore
|
||||||
|
@ -160,7 +165,9 @@ module ibex_cs_registers #(
|
||||||
|
|
||||||
// CPU control register fields
|
// CPU control register fields
|
||||||
typedef struct packed {
|
typedef struct packed {
|
||||||
logic [31:2] unused_ctrl;
|
logic [31:6] unused_ctrl;
|
||||||
|
logic [2:0] dummy_instr_mask;
|
||||||
|
logic dummy_instr_en;
|
||||||
logic data_ind_timing;
|
logic data_ind_timing;
|
||||||
logic icache_enable;
|
logic icache_enable;
|
||||||
} CpuCtrl_t;
|
} CpuCtrl_t;
|
||||||
|
@ -420,6 +427,11 @@ module ibex_cs_registers #(
|
||||||
csr_rdata_int = {cpuctrl_rdata};
|
csr_rdata_int = {cpuctrl_rdata};
|
||||||
end
|
end
|
||||||
|
|
||||||
|
// Custom CSR for LFSR re-seeding (cannot be read)
|
||||||
|
CSR_SECURESEED: begin
|
||||||
|
csr_rdata_int = '0;
|
||||||
|
end
|
||||||
|
|
||||||
default: begin
|
default: begin
|
||||||
illegal_csr = 1'b1;
|
illegal_csr = 1'b1;
|
||||||
end
|
end
|
||||||
|
@ -1068,6 +1080,50 @@ module ibex_cs_registers #(
|
||||||
|
|
||||||
assign data_ind_timing_o = cpuctrl_rdata.data_ind_timing;
|
assign data_ind_timing_o = cpuctrl_rdata.data_ind_timing;
|
||||||
|
|
||||||
|
// Generate dummy instruction signals
|
||||||
|
if (DummyInstructions) begin : gen_dummy
|
||||||
|
logic dummy_instr_en_d, dummy_instr_en_q;
|
||||||
|
logic [2:0] dummy_instr_mask_d, dummy_instr_mask_q;
|
||||||
|
|
||||||
|
assign dummy_instr_en_d = (csr_we_int && (csr_addr == CSR_CPUCTRL)) ?
|
||||||
|
cpuctrl_wdata.dummy_instr_en : dummy_instr_en_q;
|
||||||
|
|
||||||
|
assign dummy_instr_mask_d = (csr_we_int && (csr_addr == CSR_CPUCTRL)) ?
|
||||||
|
cpuctrl_wdata.dummy_instr_mask : dummy_instr_mask_q;
|
||||||
|
|
||||||
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||||
|
if (!rst_ni) begin
|
||||||
|
dummy_instr_en_q <= 1'b0; // disabled on reset
|
||||||
|
dummy_instr_mask_q <= 3'b000;
|
||||||
|
end else begin
|
||||||
|
dummy_instr_en_q <= dummy_instr_en_d;
|
||||||
|
dummy_instr_mask_q <= dummy_instr_mask_d;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign cpuctrl_rdata.dummy_instr_en = dummy_instr_en_q;
|
||||||
|
assign cpuctrl_rdata.dummy_instr_mask = dummy_instr_mask_q;
|
||||||
|
// Signal a write to the seed register
|
||||||
|
assign dummy_instr_seed_en_o = csr_we_int && (csr_addr == CSR_SECURESEED);
|
||||||
|
assign dummy_instr_seed_o = csr_wdata_int;
|
||||||
|
|
||||||
|
end else begin : gen_no_dummy
|
||||||
|
// tieoff for the unused bit
|
||||||
|
logic unused_dummy_en;
|
||||||
|
logic [2:0] unused_dummy_mask;
|
||||||
|
assign unused_dummy_en = cpuctrl_wdata.dummy_instr_en;
|
||||||
|
assign unused_dummy_mask = cpuctrl_wdata.dummy_instr_mask;
|
||||||
|
|
||||||
|
// field will always read as zero if not configured
|
||||||
|
assign cpuctrl_rdata.dummy_instr_en = 1'b0;
|
||||||
|
assign cpuctrl_rdata.dummy_instr_mask = 3'b000;
|
||||||
|
assign dummy_instr_seed_en_o = 1'b0;
|
||||||
|
assign dummy_instr_seed_o = '0;
|
||||||
|
end
|
||||||
|
|
||||||
|
assign dummy_instr_en_o = cpuctrl_rdata.dummy_instr_en;
|
||||||
|
assign dummy_instr_mask_o = cpuctrl_rdata.dummy_instr_mask;
|
||||||
|
|
||||||
// Generate icache enable bit
|
// Generate icache enable bit
|
||||||
if (ICache) begin : gen_icache_enable
|
if (ICache) begin : gen_icache_enable
|
||||||
logic icache_enable_d, icache_enable_q;
|
logic icache_enable_d, icache_enable_q;
|
||||||
|
@ -1098,8 +1154,8 @@ module ibex_cs_registers #(
|
||||||
assign icache_enable_o = cpuctrl_rdata.icache_enable;
|
assign icache_enable_o = cpuctrl_rdata.icache_enable;
|
||||||
|
|
||||||
// tieoff for the currently unused bits of cpuctrl
|
// tieoff for the currently unused bits of cpuctrl
|
||||||
logic [31:2] unused_cpuctrl;
|
logic [31:6] unused_cpuctrl;
|
||||||
assign unused_cpuctrl = {cpuctrl_wdata[31:2]};
|
assign unused_cpuctrl = {cpuctrl_wdata[31:6]};
|
||||||
|
|
||||||
|
|
||||||
////////////////
|
////////////////
|
||||||
|
|
132
rtl/ibex_dummy_instr.sv
Normal file
132
rtl/ibex_dummy_instr.sv
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
// Copyright lowRISC contributors.
|
||||||
|
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy instruction module
|
||||||
|
*
|
||||||
|
* Provides pseudo-randomly inserted fake instructions for secure code obfuscation
|
||||||
|
*/
|
||||||
|
|
||||||
|
module ibex_dummy_instr (
|
||||||
|
// Clock and reset
|
||||||
|
input logic clk_i,
|
||||||
|
input logic rst_ni,
|
||||||
|
|
||||||
|
// Interface to CSRs
|
||||||
|
input logic dummy_instr_en_i,
|
||||||
|
input logic [2:0] dummy_instr_mask_i,
|
||||||
|
input logic dummy_instr_seed_en_i,
|
||||||
|
input logic [31:0] dummy_instr_seed_i,
|
||||||
|
|
||||||
|
// Interface to IF stage
|
||||||
|
input logic fetch_valid_i,
|
||||||
|
input logic id_in_ready_i,
|
||||||
|
output logic insert_dummy_instr_o,
|
||||||
|
output logic [31:0] dummy_instr_data_o
|
||||||
|
);
|
||||||
|
|
||||||
|
localparam int unsigned TIMEOUT_CNT_W = 5;
|
||||||
|
localparam int unsigned OP_W = 5;
|
||||||
|
|
||||||
|
typedef enum logic [1:0] {
|
||||||
|
DUMMY_ADD = 2'b00,
|
||||||
|
DUMMY_MUL = 2'b01,
|
||||||
|
DUMMY_DIV = 2'b10,
|
||||||
|
DUMMY_AND = 2'b11
|
||||||
|
} dummy_instr_e;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
dummy_instr_e instr_type;
|
||||||
|
logic [OP_W-1:0] op_b;
|
||||||
|
logic [OP_W-1:0] op_a;
|
||||||
|
logic [TIMEOUT_CNT_W-1:0] cnt;
|
||||||
|
} lfsr_data_t;
|
||||||
|
localparam int unsigned LFSR_OUT_W = $bits(lfsr_data_t);
|
||||||
|
|
||||||
|
lfsr_data_t lfsr_data;
|
||||||
|
logic [TIMEOUT_CNT_W-1:0] dummy_cnt_incr, dummy_cnt_threshold;
|
||||||
|
logic [TIMEOUT_CNT_W-1:0] dummy_cnt_d, dummy_cnt_q;
|
||||||
|
logic dummy_cnt_en;
|
||||||
|
logic lfsr_en;
|
||||||
|
logic [LFSR_OUT_W-1:0] lfsr_state;
|
||||||
|
logic insert_dummy_instr;
|
||||||
|
logic [6:0] dummy_set;
|
||||||
|
logic [2:0] dummy_opcode;
|
||||||
|
logic [31:0] dummy_instr;
|
||||||
|
|
||||||
|
// Shift the LFSR every time we insert an instruction
|
||||||
|
assign lfsr_en = insert_dummy_instr & id_in_ready_i;
|
||||||
|
|
||||||
|
prim_lfsr #(
|
||||||
|
.LfsrDw ( 32 ),
|
||||||
|
.StateOutDw ( LFSR_OUT_W )
|
||||||
|
) lfsr_i (
|
||||||
|
.clk_i ( clk_i ),
|
||||||
|
.rst_ni ( rst_ni ),
|
||||||
|
.seed_en_i ( dummy_instr_seed_en_i ),
|
||||||
|
.seed_i ( dummy_instr_seed_i ),
|
||||||
|
.lfsr_en_i ( lfsr_en ),
|
||||||
|
.entropy_i ( '0 ),
|
||||||
|
.state_o ( lfsr_state )
|
||||||
|
);
|
||||||
|
|
||||||
|
// Extract fields from LFSR
|
||||||
|
assign lfsr_data = lfsr_data_t'(lfsr_state);
|
||||||
|
|
||||||
|
// Set count threshold for inserting a new instruction. This is the pseudo-random value from the
|
||||||
|
// LFSR with a mask applied (based on CSR config data) to shorten the period if required.
|
||||||
|
assign dummy_cnt_threshold = lfsr_data.cnt & {dummy_instr_mask_i,{TIMEOUT_CNT_W-3{1'b1}}};
|
||||||
|
assign dummy_cnt_incr = dummy_cnt_q + {{TIMEOUT_CNT_W-1{1'b0}},1'b1};
|
||||||
|
// Clear the counter everytime a new instruction is inserted
|
||||||
|
assign dummy_cnt_d = insert_dummy_instr ? '0 : dummy_cnt_incr;
|
||||||
|
// Increment the counter for each executed instruction while dummy instuctions are
|
||||||
|
// enabled.
|
||||||
|
assign dummy_cnt_en = dummy_instr_en_i & id_in_ready_i &
|
||||||
|
(fetch_valid_i | insert_dummy_instr);
|
||||||
|
|
||||||
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||||
|
if (!rst_ni) begin
|
||||||
|
dummy_cnt_q <= '0;
|
||||||
|
end else if (dummy_cnt_en) begin
|
||||||
|
dummy_cnt_q <= dummy_cnt_d;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Insert a dummy instruction each time the counter hits the threshold
|
||||||
|
assign insert_dummy_instr = dummy_instr_en_i & (dummy_cnt_q == dummy_cnt_threshold);
|
||||||
|
|
||||||
|
// Encode instruction
|
||||||
|
always_comb begin
|
||||||
|
unique case (lfsr_data.instr_type)
|
||||||
|
DUMMY_ADD : begin
|
||||||
|
dummy_set = 7'b0000000;
|
||||||
|
dummy_opcode = 3'b000;
|
||||||
|
end
|
||||||
|
DUMMY_MUL : begin
|
||||||
|
dummy_set = 7'b0000001;
|
||||||
|
dummy_opcode = 3'b000;
|
||||||
|
end
|
||||||
|
DUMMY_DIV : begin
|
||||||
|
dummy_set = 7'b0000001;
|
||||||
|
dummy_opcode = 3'b100;
|
||||||
|
end
|
||||||
|
DUMMY_AND : begin
|
||||||
|
dummy_set = 7'b0000000;
|
||||||
|
dummy_opcode = 3'b111;
|
||||||
|
end
|
||||||
|
default : begin
|
||||||
|
dummy_set = 7'b0000000;
|
||||||
|
dummy_opcode = 3'b000;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
// SET RS2 RS1 OP RD
|
||||||
|
assign dummy_instr = {dummy_set,lfsr_data.op_b,lfsr_data.op_a,dummy_opcode,5'h00,7'h33};
|
||||||
|
|
||||||
|
// Assign outputs
|
||||||
|
assign insert_dummy_instr_o = insert_dummy_instr;
|
||||||
|
assign dummy_instr_data_o = dummy_instr;
|
||||||
|
|
||||||
|
endmodule
|
|
@ -13,10 +13,11 @@
|
||||||
`include "prim_assert.sv"
|
`include "prim_assert.sv"
|
||||||
|
|
||||||
module ibex_if_stage #(
|
module ibex_if_stage #(
|
||||||
parameter int unsigned DmHaltAddr = 32'h1A110800,
|
parameter int unsigned DmHaltAddr = 32'h1A110800,
|
||||||
parameter int unsigned DmExceptionAddr = 32'h1A110808,
|
parameter int unsigned DmExceptionAddr = 32'h1A110808,
|
||||||
parameter bit ICache = 1'b0,
|
parameter bit DummyInstructions = 1'b0,
|
||||||
parameter bit ICacheECC = 1'b0
|
parameter bit ICache = 1'b0,
|
||||||
|
parameter bit ICacheECC = 1'b0
|
||||||
) (
|
) (
|
||||||
input logic clk_i,
|
input logic clk_i,
|
||||||
input logic rst_ni,
|
input logic rst_ni,
|
||||||
|
@ -48,6 +49,7 @@ module ibex_if_stage #(
|
||||||
output logic instr_fetch_err_plus2_o, // bus error misaligned
|
output logic instr_fetch_err_plus2_o, // bus error misaligned
|
||||||
output logic illegal_c_insn_id_o, // compressed decoder thinks this
|
output logic illegal_c_insn_id_o, // compressed decoder thinks this
|
||||||
// is an invalid instr
|
// is an invalid instr
|
||||||
|
output logic dummy_instr_id_o, // Instruction is a dummy
|
||||||
output logic [31:0] pc_if_o,
|
output logic [31:0] pc_if_o,
|
||||||
output logic [31:0] pc_id_o,
|
output logic [31:0] pc_id_o,
|
||||||
|
|
||||||
|
@ -58,6 +60,10 @@ module ibex_if_stage #(
|
||||||
input ibex_pkg::exc_pc_sel_e exc_pc_mux_i, // selects ISR address
|
input ibex_pkg::exc_pc_sel_e exc_pc_mux_i, // selects ISR address
|
||||||
input ibex_pkg::exc_cause_e exc_cause, // selects ISR address for
|
input ibex_pkg::exc_cause_e exc_cause, // selects ISR address for
|
||||||
// vectorized interrupt lines
|
// vectorized interrupt lines
|
||||||
|
input logic dummy_instr_en_i,
|
||||||
|
input logic [2:0] dummy_instr_mask_i,
|
||||||
|
input logic dummy_instr_seed_en_i,
|
||||||
|
input logic [31:0] dummy_instr_seed_i,
|
||||||
input logic icache_enable_i,
|
input logic icache_enable_i,
|
||||||
input logic icache_inval_i,
|
input logic icache_inval_i,
|
||||||
|
|
||||||
|
@ -103,6 +109,14 @@ module ibex_if_stage #(
|
||||||
|
|
||||||
logic if_id_pipe_reg_we; // IF-ID pipeline reg write enable
|
logic if_id_pipe_reg_we; // IF-ID pipeline reg write enable
|
||||||
|
|
||||||
|
// Dummy instruction signals
|
||||||
|
logic fetch_valid_out;
|
||||||
|
logic stall_dummy_instr;
|
||||||
|
logic [31:0] instr_out;
|
||||||
|
logic instr_is_compressed_out;
|
||||||
|
logic illegal_c_instr_out;
|
||||||
|
logic instr_err_out;
|
||||||
|
|
||||||
logic [7:0] unused_boot_addr;
|
logic [7:0] unused_boot_addr;
|
||||||
logic [7:0] unused_csr_mtvec;
|
logic [7:0] unused_csr_mtvec;
|
||||||
|
|
||||||
|
@ -206,7 +220,7 @@ module ibex_if_stage #(
|
||||||
end
|
end
|
||||||
|
|
||||||
assign branch_req = pc_set_i;
|
assign branch_req = pc_set_i;
|
||||||
assign fetch_ready = id_in_ready_i;
|
assign fetch_ready = id_in_ready_i & ~stall_dummy_instr;
|
||||||
|
|
||||||
assign pc_if_o = fetch_addr;
|
assign pc_if_o = fetch_addr;
|
||||||
assign if_busy_o = prefetch_busy;
|
assign if_busy_o = prefetch_busy;
|
||||||
|
@ -218,24 +232,82 @@ module ibex_if_stage #(
|
||||||
// to ease timing closure
|
// to ease timing closure
|
||||||
logic [31:0] instr_decompressed;
|
logic [31:0] instr_decompressed;
|
||||||
logic illegal_c_insn;
|
logic illegal_c_insn;
|
||||||
logic instr_is_compressed_int;
|
logic instr_is_compressed;
|
||||||
|
|
||||||
ibex_compressed_decoder compressed_decoder_i (
|
ibex_compressed_decoder compressed_decoder_i (
|
||||||
.clk_i ( clk_i ),
|
.clk_i ( clk_i ),
|
||||||
.rst_ni ( rst_ni ),
|
.rst_ni ( rst_ni ),
|
||||||
.valid_i ( fetch_valid ),
|
.valid_i ( fetch_valid ),
|
||||||
.instr_i ( fetch_rdata ),
|
.instr_i ( fetch_rdata ),
|
||||||
.instr_o ( instr_decompressed ),
|
.instr_o ( instr_decompressed ),
|
||||||
.is_compressed_o ( instr_is_compressed_int ),
|
.is_compressed_o ( instr_is_compressed ),
|
||||||
.illegal_instr_o ( illegal_c_insn )
|
.illegal_instr_o ( illegal_c_insn )
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Dummy instruction insertion
|
||||||
|
if (DummyInstructions) begin : gen_dummy_instr
|
||||||
|
logic insert_dummy_instr;
|
||||||
|
logic [31:0] dummy_instr_data;
|
||||||
|
|
||||||
|
ibex_dummy_instr dummy_instr_i (
|
||||||
|
.clk_i ( clk_i ),
|
||||||
|
.rst_ni ( rst_ni ),
|
||||||
|
.dummy_instr_en_i ( dummy_instr_en_i ),
|
||||||
|
.dummy_instr_mask_i ( dummy_instr_mask_i ),
|
||||||
|
.dummy_instr_seed_en_i ( dummy_instr_seed_en_i ),
|
||||||
|
.dummy_instr_seed_i ( dummy_instr_seed_i ),
|
||||||
|
.fetch_valid_i ( fetch_valid ),
|
||||||
|
.id_in_ready_i ( id_in_ready_i ),
|
||||||
|
.insert_dummy_instr_o ( insert_dummy_instr ),
|
||||||
|
.dummy_instr_data_o ( dummy_instr_data )
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mux between actual instructions and dummy instructions
|
||||||
|
assign fetch_valid_out = insert_dummy_instr | fetch_valid;
|
||||||
|
assign instr_out = insert_dummy_instr ? dummy_instr_data : instr_decompressed;
|
||||||
|
assign instr_is_compressed_out = insert_dummy_instr ? 1'b0 : instr_is_compressed;
|
||||||
|
assign illegal_c_instr_out = insert_dummy_instr ? 1'b0 : illegal_c_insn;
|
||||||
|
assign instr_err_out = insert_dummy_instr ? 1'b0 : fetch_err;
|
||||||
|
|
||||||
|
// Stall the IF stage if we insert a dummy instruction. The dummy will execute between whatever
|
||||||
|
// is currently in the ID stage and whatever is valid from the prefetch buffer this cycle. The
|
||||||
|
// PC of the dummy instruction will match whatever is next from the prefetch buffer.
|
||||||
|
assign stall_dummy_instr = insert_dummy_instr;
|
||||||
|
|
||||||
|
// Register the dummy instruction indication into the ID stage
|
||||||
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||||
|
if (!rst_ni) begin
|
||||||
|
dummy_instr_id_o <= 1'b0;
|
||||||
|
end else if (if_id_pipe_reg_we) begin
|
||||||
|
dummy_instr_id_o <= insert_dummy_instr;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end else begin : gen_no_dummy_instr
|
||||||
|
logic unused_dummy_en;
|
||||||
|
logic [2:0] unused_dummy_mask;
|
||||||
|
logic unused_dummy_seed_en;
|
||||||
|
logic [31:0] unused_dummy_seed;
|
||||||
|
|
||||||
|
assign unused_dummy_en = dummy_instr_en_i;
|
||||||
|
assign unused_dummy_mask = dummy_instr_mask_i;
|
||||||
|
assign unused_dummy_seed_en = dummy_instr_seed_en_i;
|
||||||
|
assign unused_dummy_seed = dummy_instr_seed_i;
|
||||||
|
assign fetch_valid_out = fetch_valid;
|
||||||
|
assign instr_out = instr_decompressed;
|
||||||
|
assign instr_is_compressed_out = instr_is_compressed;
|
||||||
|
assign illegal_c_instr_out = illegal_c_insn;
|
||||||
|
assign instr_err_out = fetch_err;
|
||||||
|
assign stall_dummy_instr = 1'b0;
|
||||||
|
assign dummy_instr_id_o = 1'b0;
|
||||||
|
end
|
||||||
|
|
||||||
// The ID stage becomes valid as soon as any instruction is registered in the ID stage flops.
|
// The ID stage becomes valid as soon as any instruction is registered in the ID stage flops.
|
||||||
// Note that the current instruction is squashed by the incoming pc_set_i signal.
|
// Note that the current instruction is squashed by the incoming pc_set_i signal.
|
||||||
// Valid is held until it is explicitly cleared (due to an instruction completing or an exception)
|
// Valid is held until it is explicitly cleared (due to an instruction completing or an exception)
|
||||||
assign instr_valid_id_d = (fetch_valid & id_in_ready_i & ~pc_set_i) |
|
assign instr_valid_id_d = (fetch_valid_out & id_in_ready_i & ~pc_set_i) |
|
||||||
(instr_valid_id_q & ~instr_valid_clear_i);
|
(instr_valid_id_q & ~instr_valid_clear_i);
|
||||||
assign instr_new_id_d = fetch_valid & id_in_ready_i;
|
assign instr_new_id_d = fetch_valid_out & id_in_ready_i;
|
||||||
|
|
||||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||||
if (!rst_ni) begin
|
if (!rst_ni) begin
|
||||||
|
@ -256,14 +328,14 @@ module ibex_if_stage #(
|
||||||
|
|
||||||
always_ff @(posedge clk_i) begin
|
always_ff @(posedge clk_i) begin
|
||||||
if (if_id_pipe_reg_we) begin
|
if (if_id_pipe_reg_we) begin
|
||||||
instr_rdata_id_o <= instr_decompressed;
|
instr_rdata_id_o <= instr_out;
|
||||||
// To reduce fan-out and help timing from the instr_rdata_id flops they are replicated.
|
// To reduce fan-out and help timing from the instr_rdata_id flops they are replicated.
|
||||||
instr_rdata_alu_id_o <= instr_decompressed;
|
instr_rdata_alu_id_o <= instr_out;
|
||||||
instr_fetch_err_o <= fetch_err;
|
instr_fetch_err_o <= instr_err_out;
|
||||||
instr_fetch_err_plus2_o <= fetch_err_plus2;
|
instr_fetch_err_plus2_o <= fetch_err_plus2;
|
||||||
instr_rdata_c_id_o <= fetch_rdata[15:0];
|
instr_rdata_c_id_o <= fetch_rdata[15:0];
|
||||||
instr_is_compressed_id_o <= instr_is_compressed_int;
|
instr_is_compressed_id_o <= instr_is_compressed_out;
|
||||||
illegal_c_insn_id_o <= illegal_c_insn;
|
illegal_c_insn_id_o <= illegal_c_instr_out;
|
||||||
pc_id_o <= pc_if_o;
|
pc_id_o <= pc_if_o;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -448,7 +448,8 @@ typedef enum logic[11:0] {
|
||||||
CSR_MHPMCOUNTER29H = 12'hB9D,
|
CSR_MHPMCOUNTER29H = 12'hB9D,
|
||||||
CSR_MHPMCOUNTER30H = 12'hB9E,
|
CSR_MHPMCOUNTER30H = 12'hB9E,
|
||||||
CSR_MHPMCOUNTER31H = 12'hB9F,
|
CSR_MHPMCOUNTER31H = 12'hB9F,
|
||||||
CSR_CPUCTRL = 12'h7C0
|
CSR_CPUCTRL = 12'h7C0,
|
||||||
|
CSR_SECURESEED = 12'h7C1
|
||||||
} csr_num_e;
|
} csr_num_e;
|
||||||
|
|
||||||
// CSR pmp-related offsets
|
// CSR pmp-related offsets
|
||||||
|
|
|
@ -11,14 +11,16 @@
|
||||||
* targeting FPGA synthesis or Verilator simulation.
|
* targeting FPGA synthesis or Verilator simulation.
|
||||||
*/
|
*/
|
||||||
module ibex_register_file #(
|
module ibex_register_file #(
|
||||||
parameter bit RV32E = 0,
|
parameter bit RV32E = 0,
|
||||||
parameter int unsigned DataWidth = 32
|
parameter int unsigned DataWidth = 32,
|
||||||
|
parameter bit DummyInstructions = 0
|
||||||
) (
|
) (
|
||||||
// Clock and Reset
|
// Clock and Reset
|
||||||
input logic clk_i,
|
input logic clk_i,
|
||||||
input logic rst_ni,
|
input logic rst_ni,
|
||||||
|
|
||||||
input logic test_en_i,
|
input logic test_en_i,
|
||||||
|
input logic dummy_instr_id_i,
|
||||||
|
|
||||||
//Read port R1
|
//Read port R1
|
||||||
input logic [4:0] raddr_a_i,
|
input logic [4:0] raddr_a_i,
|
||||||
|
@ -60,8 +62,34 @@ module ibex_register_file #(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
// R0 is nil
|
// With dummy instructions enabled, R0 behaves as a real register but will always return 0 for
|
||||||
assign rf_reg[0] = '0;
|
// real instructions.
|
||||||
|
if (DummyInstructions) begin : g_dummy_r0
|
||||||
|
logic we_r0_dummy;
|
||||||
|
logic [31:0] rf_r0;
|
||||||
|
|
||||||
|
// Write enable for dummy R0 register (waddr_a_i will always be 0 for dummy instructions)
|
||||||
|
assign we_r0_dummy = we_a_i & dummy_instr_id_i;
|
||||||
|
|
||||||
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||||
|
if (!rst_ni) begin
|
||||||
|
rf_r0 <= '0;
|
||||||
|
end else if (we_r0_dummy) begin
|
||||||
|
rf_r0 <= wdata_a_i;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Output the dummy data for dummy instructions, otherwise R0 reads as zero
|
||||||
|
assign rf_reg[0] = dummy_instr_id_i ? rf_r0 : '0;
|
||||||
|
|
||||||
|
end else begin : g_normal_r0
|
||||||
|
logic unused_dummy_instr_id;
|
||||||
|
assign unused_dummy_instr_id = dummy_instr_id_i;
|
||||||
|
|
||||||
|
// R0 is nil
|
||||||
|
assign rf_reg[0] = '0;
|
||||||
|
end
|
||||||
|
|
||||||
assign rf_reg[NUM_WORDS-1:1] = rf_reg_tmp[NUM_WORDS-1:1];
|
assign rf_reg[NUM_WORDS-1:1] = rf_reg_tmp[NUM_WORDS-1:1];
|
||||||
|
|
||||||
assign rdata_a_o = rf_reg[raddr_a_i];
|
assign rdata_a_o = rf_reg[raddr_a_i];
|
||||||
|
|
|
@ -12,14 +12,16 @@
|
||||||
* FPGA architectures, it will produce RAM32M primitives. Other vendors have not yet been tested.
|
* FPGA architectures, it will produce RAM32M primitives. Other vendors have not yet been tested.
|
||||||
*/
|
*/
|
||||||
module ibex_register_file #(
|
module ibex_register_file #(
|
||||||
parameter bit RV32E = 0,
|
parameter bit RV32E = 0,
|
||||||
parameter int unsigned DataWidth = 32
|
parameter int unsigned DataWidth = 32,
|
||||||
|
parameter bit DummyInstructions = 0
|
||||||
) (
|
) (
|
||||||
// Clock and Reset
|
// Clock and Reset
|
||||||
input logic clk_i,
|
input logic clk_i,
|
||||||
input logic rst_ni,
|
input logic rst_ni,
|
||||||
|
|
||||||
input logic test_en_i,
|
input logic test_en_i,
|
||||||
|
input logic dummy_instr_id_i,
|
||||||
|
|
||||||
//Read port R1
|
//Read port R1
|
||||||
input logic [ 4:0] raddr_a_i,
|
input logic [ 4:0] raddr_a_i,
|
||||||
|
@ -54,4 +56,12 @@ module ibex_register_file #(
|
||||||
end
|
end
|
||||||
end : sync_write
|
end : sync_write
|
||||||
|
|
||||||
|
// Reset not used in this register file version
|
||||||
|
logic unused_rst_ni;
|
||||||
|
assign unused_rst_ni = rst_ni;
|
||||||
|
|
||||||
|
// Dummy instruction changes not relevant for FPGA implementation
|
||||||
|
logic unused_dummy_instr;
|
||||||
|
assign unused_dummy_instr = dummy_instr_id_i;
|
||||||
|
|
||||||
endmodule : ibex_register_file
|
endmodule : ibex_register_file
|
||||||
|
|
|
@ -12,14 +12,16 @@
|
||||||
* register file when targeting ASIC synthesis or event-based simulators.
|
* register file when targeting ASIC synthesis or event-based simulators.
|
||||||
*/
|
*/
|
||||||
module ibex_register_file #(
|
module ibex_register_file #(
|
||||||
parameter bit RV32E = 0,
|
parameter bit RV32E = 0,
|
||||||
parameter int unsigned DataWidth = 32
|
parameter int unsigned DataWidth = 32,
|
||||||
|
parameter bit DummyInstructions = 0
|
||||||
) (
|
) (
|
||||||
// Clock and Reset
|
// Clock and Reset
|
||||||
input logic clk_i,
|
input logic clk_i,
|
||||||
input logic rst_ni,
|
input logic rst_ni,
|
||||||
|
|
||||||
input logic test_en_i,
|
input logic test_en_i,
|
||||||
|
input logic dummy_instr_id_i,
|
||||||
|
|
||||||
//Read port R1
|
//Read port R1
|
||||||
input logic [4:0] raddr_a_i,
|
input logic [4:0] raddr_a_i,
|
||||||
|
@ -87,7 +89,7 @@ module ibex_register_file #(
|
||||||
// Write address decoding
|
// Write address decoding
|
||||||
always_comb begin : wad
|
always_comb begin : wad
|
||||||
for (int i = 1; i < NUM_WORDS; i++) begin : wad_word_iter
|
for (int i = 1; i < NUM_WORDS; i++) begin : wad_word_iter
|
||||||
if (we_a_i && (waddr_a_int == i)) begin
|
if (we_a_i && (waddr_a_int == 5'(i))) begin
|
||||||
waddr_onehot_a[i] = 1'b1;
|
waddr_onehot_a[i] = 1'b1;
|
||||||
end else begin
|
end else begin
|
||||||
waddr_onehot_a[i] = 1'b0;
|
waddr_onehot_a[i] = 1'b0;
|
||||||
|
@ -109,7 +111,6 @@ module ibex_register_file #(
|
||||||
// Generate the sequential process for the NUM_WORDS words of the memory.
|
// Generate the sequential process for the NUM_WORDS words of the memory.
|
||||||
// The process is synchronized with the clocks mem_clocks[k], k = 1, ..., NUM_WORDS-1.
|
// The process is synchronized with the clocks mem_clocks[k], k = 1, ..., NUM_WORDS-1.
|
||||||
always_latch begin : latch_wdata
|
always_latch begin : latch_wdata
|
||||||
mem[0] = '0;
|
|
||||||
for (int k = 1; k < NUM_WORDS; k++) begin : latch_wdata_word_iter
|
for (int k = 1; k < NUM_WORDS; k++) begin : latch_wdata_word_iter
|
||||||
if (mem_clocks[k]) begin
|
if (mem_clocks[k]) begin
|
||||||
mem[k] = wdata_a_q;
|
mem[k] = wdata_a_q;
|
||||||
|
@ -117,6 +118,40 @@ module ibex_register_file #(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
// With dummy instructions enabled, R0 behaves as a real register but will always return 0 for
|
||||||
|
// real instructions.
|
||||||
|
if (DummyInstructions) begin : g_dummy_r0
|
||||||
|
logic we_r0_dummy;
|
||||||
|
logic r0_clock;
|
||||||
|
logic [31:0] mem_r0;
|
||||||
|
|
||||||
|
// Write enable for dummy R0 register (waddr_a_i will always be 0 for dummy instructions)
|
||||||
|
assign we_r0_dummy = we_a_i & dummy_instr_id_i;
|
||||||
|
|
||||||
|
// R0 clock gate
|
||||||
|
prim_clock_gating cg_i (
|
||||||
|
.clk_i ( clk_int ),
|
||||||
|
.en_i ( we_r0_dummy ),
|
||||||
|
.test_en_i ( test_en_i ),
|
||||||
|
.clk_o ( r0_clock )
|
||||||
|
);
|
||||||
|
|
||||||
|
always_latch begin : latch_wdata
|
||||||
|
if (r0_clock) begin
|
||||||
|
mem_r0 = wdata_a_q;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Output the dummy data for dummy instructions, otherwise R0 reads as zero
|
||||||
|
assign mem[0] = dummy_instr_id_i ? mem_r0 : '0;
|
||||||
|
|
||||||
|
end else begin : g_normal_r0
|
||||||
|
logic unused_dummy_instr_id;
|
||||||
|
assign unused_dummy_instr_id = dummy_instr_id_i;
|
||||||
|
|
||||||
|
assign mem[0] = '0;
|
||||||
|
end
|
||||||
|
|
||||||
`ifdef VERILATOR
|
`ifdef VERILATOR
|
||||||
initial begin
|
initial begin
|
||||||
$display("Latch-based register file not supported for Verilator simulation");
|
$display("Latch-based register file not supported for Verilator simulation");
|
||||||
|
|
17
shared/prim_lfsr.core
Normal file
17
shared/prim_lfsr.core
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
CAPI=2:
|
||||||
|
# Copyright lowRISC contributors.
|
||||||
|
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
name: "lowrisc:prim:lfsr:0.1"
|
||||||
|
description: "LFSR primitive"
|
||||||
|
filesets:
|
||||||
|
files_rtl:
|
||||||
|
files:
|
||||||
|
- rtl/prim_lfsr.sv
|
||||||
|
file_type: systemVerilogSource
|
||||||
|
|
||||||
|
targets:
|
||||||
|
default:
|
||||||
|
filesets:
|
||||||
|
- files_rtl
|
485
shared/rtl/prim_lfsr.sv
Normal file
485
shared/rtl/prim_lfsr.sv
Normal file
|
@ -0,0 +1,485 @@
|
||||||
|
// Copyright lowRISC contributors.
|
||||||
|
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// This module implements different LFSR types:
|
||||||
|
//
|
||||||
|
// 0) Galois XOR type LFSR ([1], internal XOR gates, very fast).
|
||||||
|
// Parameterizable width from 4 to 64 bits.
|
||||||
|
// Coefficients obtained from [2].
|
||||||
|
//
|
||||||
|
// 1) Fibonacci XNOR type LFSR, parameterizable from 3 to 168 bits.
|
||||||
|
// Coefficients obtained from [3].
|
||||||
|
//
|
||||||
|
// All flavors have an additional entropy input and lockup protection, which
|
||||||
|
// reseeds the state once it has accidentally fallen into the all-zero (XOR) or
|
||||||
|
// all-one (XNOR) state. Further, an external seed can be loaded into the LFSR
|
||||||
|
// state at runtime. If that seed is all-zero (XOR case) or all-one (XNOR case),
|
||||||
|
// the state will be reseeded in the next cycle using the lockup protection mechanism.
|
||||||
|
// Note that the external seed input takes precedence over internal state updates.
|
||||||
|
//
|
||||||
|
// All polynomials up to 34 bit in length have been verified in simulation.
|
||||||
|
//
|
||||||
|
// Refs: [1] https://en.wikipedia.org/wiki/Linear-feedback_shift_register
|
||||||
|
// [2] https://users.ece.cmu.edu/~koopman/lfsr/
|
||||||
|
// [3] https://www.xilinx.com/support/documentation/application_notes/xapp052.pdf
|
||||||
|
|
||||||
|
`include "prim_assert.sv"
|
||||||
|
|
||||||
|
module prim_lfsr #(
|
||||||
|
// Lfsr Type, can be FIB_XNOR or GAL_XOR
|
||||||
|
parameter LfsrType = "GAL_XOR",
|
||||||
|
// Lfsr width
|
||||||
|
parameter int unsigned LfsrDw = 32,
|
||||||
|
// Width of the entropy input to be XOR'd into state (lfsr_q[EntropyDw-1:0])
|
||||||
|
parameter int unsigned EntropyDw = 8,
|
||||||
|
// Width of output tap (from lfsr_q[StateOutDw-1:0])
|
||||||
|
parameter int unsigned StateOutDw = 8,
|
||||||
|
// Lfsr reset state, must be nonzero!
|
||||||
|
parameter logic [LfsrDw-1:0] DefaultSeed = LfsrDw'(1),
|
||||||
|
// Custom polynomial coeffs
|
||||||
|
parameter logic [LfsrDw-1:0] CustomCoeffs = '0,
|
||||||
|
// Enable this for DV, disable this for long LFSRs in FPV
|
||||||
|
parameter bit MaxLenSVA = 1'b1,
|
||||||
|
// Can be disabled in cases where seed and entropy
|
||||||
|
// inputs are unused in order to not distort coverage
|
||||||
|
// (the SVA will be unreachable in such cases)
|
||||||
|
parameter bit LockupSVA = 1'b1,
|
||||||
|
parameter bit ExtSeedSVA = 1'b1
|
||||||
|
) (
|
||||||
|
input clk_i,
|
||||||
|
input rst_ni,
|
||||||
|
input seed_en_i, // load external seed into the state (takes precedence)
|
||||||
|
input [LfsrDw-1:0] seed_i, // external seed input
|
||||||
|
input lfsr_en_i, // enables the LFSR
|
||||||
|
input [EntropyDw-1:0] entropy_i, // additional entropy to be XOR'ed into the state
|
||||||
|
output logic [StateOutDw-1:0] state_o // (partial) LFSR state output
|
||||||
|
);
|
||||||
|
|
||||||
|
// automatically generated with get-lfsr-coeffs.py script
|
||||||
|
localparam int unsigned GAL_XOR_LUT_OFF = 4;
|
||||||
|
localparam logic [63:0] GAL_XOR_COEFFS [61] =
|
||||||
|
'{ 64'h9,
|
||||||
|
64'h12,
|
||||||
|
64'h21,
|
||||||
|
64'h41,
|
||||||
|
64'h8E,
|
||||||
|
64'h108,
|
||||||
|
64'h204,
|
||||||
|
64'h402,
|
||||||
|
64'h829,
|
||||||
|
64'h100D,
|
||||||
|
64'h2015,
|
||||||
|
64'h4001,
|
||||||
|
64'h8016,
|
||||||
|
64'h10004,
|
||||||
|
64'h20013,
|
||||||
|
64'h40013,
|
||||||
|
64'h80004,
|
||||||
|
64'h100002,
|
||||||
|
64'h200001,
|
||||||
|
64'h400010,
|
||||||
|
64'h80000D,
|
||||||
|
64'h1000004,
|
||||||
|
64'h2000023,
|
||||||
|
64'h4000013,
|
||||||
|
64'h8000004,
|
||||||
|
64'h10000002,
|
||||||
|
64'h20000029,
|
||||||
|
64'h40000004,
|
||||||
|
64'h80000057,
|
||||||
|
64'h100000029,
|
||||||
|
64'h200000073,
|
||||||
|
64'h400000002,
|
||||||
|
64'h80000003B,
|
||||||
|
64'h100000001F,
|
||||||
|
64'h2000000031,
|
||||||
|
64'h4000000008,
|
||||||
|
64'h800000001C,
|
||||||
|
64'h10000000004,
|
||||||
|
64'h2000000001F,
|
||||||
|
64'h4000000002C,
|
||||||
|
64'h80000000032,
|
||||||
|
64'h10000000000D,
|
||||||
|
64'h200000000097,
|
||||||
|
64'h400000000010,
|
||||||
|
64'h80000000005B,
|
||||||
|
64'h1000000000038,
|
||||||
|
64'h200000000000E,
|
||||||
|
64'h4000000000025,
|
||||||
|
64'h8000000000004,
|
||||||
|
64'h10000000000023,
|
||||||
|
64'h2000000000003E,
|
||||||
|
64'h40000000000023,
|
||||||
|
64'h8000000000004A,
|
||||||
|
64'h100000000000016,
|
||||||
|
64'h200000000000031,
|
||||||
|
64'h40000000000003D,
|
||||||
|
64'h800000000000001,
|
||||||
|
64'h1000000000000013,
|
||||||
|
64'h2000000000000034,
|
||||||
|
64'h4000000000000001,
|
||||||
|
64'h800000000000000D };
|
||||||
|
|
||||||
|
// automatically generated with get-lfsr-coeffs.py script
|
||||||
|
localparam int unsigned FIB_XNOR_LUT_OFF = 3;
|
||||||
|
localparam logic [167:0] FIB_XNOR_COEFFS [166] =
|
||||||
|
'{ 168'h6,
|
||||||
|
168'hC,
|
||||||
|
168'h14,
|
||||||
|
168'h30,
|
||||||
|
168'h60,
|
||||||
|
168'hB8,
|
||||||
|
168'h110,
|
||||||
|
168'h240,
|
||||||
|
168'h500,
|
||||||
|
168'h829,
|
||||||
|
168'h100D,
|
||||||
|
168'h2015,
|
||||||
|
168'h6000,
|
||||||
|
168'hD008,
|
||||||
|
168'h12000,
|
||||||
|
168'h20400,
|
||||||
|
168'h40023,
|
||||||
|
168'h90000,
|
||||||
|
168'h140000,
|
||||||
|
168'h300000,
|
||||||
|
168'h420000,
|
||||||
|
168'hE10000,
|
||||||
|
168'h1200000,
|
||||||
|
168'h2000023,
|
||||||
|
168'h4000013,
|
||||||
|
168'h9000000,
|
||||||
|
168'h14000000,
|
||||||
|
168'h20000029,
|
||||||
|
168'h48000000,
|
||||||
|
168'h80200003,
|
||||||
|
168'h100080000,
|
||||||
|
168'h204000003,
|
||||||
|
168'h500000000,
|
||||||
|
168'h801000000,
|
||||||
|
168'h100000001F,
|
||||||
|
168'h2000000031,
|
||||||
|
168'h4400000000,
|
||||||
|
168'hA000140000,
|
||||||
|
168'h12000000000,
|
||||||
|
168'h300000C0000,
|
||||||
|
168'h63000000000,
|
||||||
|
168'hC0000030000,
|
||||||
|
168'h1B0000000000,
|
||||||
|
168'h300003000000,
|
||||||
|
168'h420000000000,
|
||||||
|
168'hC00000180000,
|
||||||
|
168'h1008000000000,
|
||||||
|
168'h3000000C00000,
|
||||||
|
168'h6000C00000000,
|
||||||
|
168'h9000000000000,
|
||||||
|
168'h18003000000000,
|
||||||
|
168'h30000000030000,
|
||||||
|
168'h40000040000000,
|
||||||
|
168'hC0000600000000,
|
||||||
|
168'h102000000000000,
|
||||||
|
168'h200004000000000,
|
||||||
|
168'h600003000000000,
|
||||||
|
168'hC00000000000000,
|
||||||
|
168'h1800300000000000,
|
||||||
|
168'h3000000000000030,
|
||||||
|
168'h6000000000000000,
|
||||||
|
168'hD800000000000000,
|
||||||
|
168'h10000400000000000,
|
||||||
|
168'h30180000000000000,
|
||||||
|
168'h60300000000000000,
|
||||||
|
168'h80400000000000000,
|
||||||
|
168'h140000028000000000,
|
||||||
|
168'h300060000000000000,
|
||||||
|
168'h410000000000000000,
|
||||||
|
168'h820000000001040000,
|
||||||
|
168'h1000000800000000000,
|
||||||
|
168'h3000600000000000000,
|
||||||
|
168'h6018000000000000000,
|
||||||
|
168'hC000000018000000000,
|
||||||
|
168'h18000000600000000000,
|
||||||
|
168'h30000600000000000000,
|
||||||
|
168'h40200000000000000000,
|
||||||
|
168'hC0000000060000000000,
|
||||||
|
168'h110000000000000000000,
|
||||||
|
168'h240000000480000000000,
|
||||||
|
168'h600000000003000000000,
|
||||||
|
168'h800400000000000000000,
|
||||||
|
168'h1800000300000000000000,
|
||||||
|
168'h3003000000000000000000,
|
||||||
|
168'h4002000000000000000000,
|
||||||
|
168'hC000000000000000018000,
|
||||||
|
168'h10000000004000000000000,
|
||||||
|
168'h30000C00000000000000000,
|
||||||
|
168'h600000000000000000000C0,
|
||||||
|
168'hC00C0000000000000000000,
|
||||||
|
168'h140000000000000000000000,
|
||||||
|
168'h200001000000000000000000,
|
||||||
|
168'h400800000000000000000000,
|
||||||
|
168'hA00000000001400000000000,
|
||||||
|
168'h1040000000000000000000000,
|
||||||
|
168'h2004000000000000000000000,
|
||||||
|
168'h5000000000028000000000000,
|
||||||
|
168'h8000000004000000000000000,
|
||||||
|
168'h18600000000000000000000000,
|
||||||
|
168'h30000000000000000C00000000,
|
||||||
|
168'h40200000000000000000000000,
|
||||||
|
168'hC0300000000000000000000000,
|
||||||
|
168'h100010000000000000000000000,
|
||||||
|
168'h200040000000000000000000000,
|
||||||
|
168'h5000000000000000A0000000000,
|
||||||
|
168'h800000010000000000000000000,
|
||||||
|
168'h1860000000000000000000000000,
|
||||||
|
168'h3003000000000000000000000000,
|
||||||
|
168'h4010000000000000000000000000,
|
||||||
|
168'hA000000000140000000000000000,
|
||||||
|
168'h10080000000000000000000000000,
|
||||||
|
168'h30000000000000000000180000000,
|
||||||
|
168'h60018000000000000000000000000,
|
||||||
|
168'hC0000000000000000300000000000,
|
||||||
|
168'h140005000000000000000000000000,
|
||||||
|
168'h200000001000000000000000000000,
|
||||||
|
168'h404000000000000000000000000000,
|
||||||
|
168'h810000000000000000000000000102,
|
||||||
|
168'h1000040000000000000000000000000,
|
||||||
|
168'h3000000000000006000000000000000,
|
||||||
|
168'h5000000000000000000000000000000,
|
||||||
|
168'h8000000004000000000000000000000,
|
||||||
|
168'h18000000000000000000000000030000,
|
||||||
|
168'h30000000030000000000000000000000,
|
||||||
|
168'h60000000000000000000000000000000,
|
||||||
|
168'hA0000014000000000000000000000000,
|
||||||
|
168'h108000000000000000000000000000000,
|
||||||
|
168'h240000000000000000000000000000000,
|
||||||
|
168'h600000000000C00000000000000000000,
|
||||||
|
168'h800000040000000000000000000000000,
|
||||||
|
168'h1800000000000300000000000000000000,
|
||||||
|
168'h2000000000000010000000000000000000,
|
||||||
|
168'h4008000000000000000000000000000000,
|
||||||
|
168'hC000000000000000000000000000000600,
|
||||||
|
168'h10000080000000000000000000000000000,
|
||||||
|
168'h30600000000000000000000000000000000,
|
||||||
|
168'h4A400000000000000000000000000000000,
|
||||||
|
168'h80000004000000000000000000000000000,
|
||||||
|
168'h180000003000000000000000000000000000,
|
||||||
|
168'h200001000000000000000000000000000000,
|
||||||
|
168'h600006000000000000000000000000000000,
|
||||||
|
168'hC00000000000000006000000000000000000,
|
||||||
|
168'h1000000000000100000000000000000000000,
|
||||||
|
168'h3000000000000006000000000000000000000,
|
||||||
|
168'h6000000003000000000000000000000000000,
|
||||||
|
168'h8000001000000000000000000000000000000,
|
||||||
|
168'h1800000000000000000000000000C000000000,
|
||||||
|
168'h20000000000001000000000000000000000000,
|
||||||
|
168'h48000000000000000000000000000000000000,
|
||||||
|
168'hC0000000000000006000000000000000000000,
|
||||||
|
168'h180000000000000000000000000000000000000,
|
||||||
|
168'h280000000000000000000000000000005000000,
|
||||||
|
168'h60000000C000000000000000000000000000000,
|
||||||
|
168'hC00000000000000000000000000018000000000,
|
||||||
|
168'h1800000600000000000000000000000000000000,
|
||||||
|
168'h3000000C00000000000000000000000000000000,
|
||||||
|
168'h4000000080000000000000000000000000000000,
|
||||||
|
168'hC000300000000000000000000000000000000000,
|
||||||
|
168'h10000400000000000000000000000000000000000,
|
||||||
|
168'h30000000000000000000006000000000000000000,
|
||||||
|
168'h600000000000000C0000000000000000000000000,
|
||||||
|
168'hC0060000000000000000000000000000000000000,
|
||||||
|
168'h180000006000000000000000000000000000000000,
|
||||||
|
168'h3000000000C0000000000000000000000000000000,
|
||||||
|
168'h410000000000000000000000000000000000000000,
|
||||||
|
168'hA00140000000000000000000000000000000000000 };
|
||||||
|
|
||||||
|
logic lockup;
|
||||||
|
logic [LfsrDw-1:0] lfsr_d, lfsr_q;
|
||||||
|
logic [LfsrDw-1:0] next_lfsr_state, coeffs;
|
||||||
|
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
// Galois XOR //
|
||||||
|
////////////////
|
||||||
|
if (64'(LfsrType) == 64'("GAL_XOR")) begin : gen_gal_xor
|
||||||
|
|
||||||
|
// if custom polynomial is provided
|
||||||
|
if (CustomCoeffs > 0) begin : gen_custom
|
||||||
|
assign coeffs = CustomCoeffs[LfsrDw-1:0];
|
||||||
|
end else begin : gen_lut
|
||||||
|
assign coeffs = GAL_XOR_COEFFS[LfsrDw-GAL_XOR_LUT_OFF][LfsrDw-1:0];
|
||||||
|
// check that the most significant bit of polynomial is 1
|
||||||
|
`ASSERT_INIT(MinLfsrWidth_A, LfsrDw >= $low(GAL_XOR_COEFFS)+GAL_XOR_LUT_OFF)
|
||||||
|
`ASSERT_INIT(MaxLfsrWidth_A, LfsrDw <= $high(GAL_XOR_COEFFS)+GAL_XOR_LUT_OFF)
|
||||||
|
end
|
||||||
|
|
||||||
|
// calculate next state using internal XOR feedback and entropy input
|
||||||
|
assign next_lfsr_state = LfsrDw'(entropy_i) ^ ({LfsrDw{lfsr_q[0]}} & coeffs) ^ (lfsr_q >> 1);
|
||||||
|
|
||||||
|
// lockup condition is all-zero
|
||||||
|
assign lockup = ~(|lfsr_q);
|
||||||
|
|
||||||
|
// check that seed is not all-zero
|
||||||
|
`ASSERT_INIT(DefaultSeedNzCheck_A, |DefaultSeed)
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
// Fibonacci XNOR //
|
||||||
|
////////////////////
|
||||||
|
end else if (64'(LfsrType) == "FIB_XNOR") begin : gen_fib_xnor
|
||||||
|
|
||||||
|
// if custom polynomial is provided
|
||||||
|
if (CustomCoeffs > 0) begin : gen_custom
|
||||||
|
assign coeffs = CustomCoeffs[LfsrDw-1:0];
|
||||||
|
end else begin : gen_lut
|
||||||
|
assign coeffs = FIB_XNOR_COEFFS[LfsrDw-FIB_XNOR_LUT_OFF][LfsrDw-1:0];
|
||||||
|
// check that the most significant bit of polynomial is 1
|
||||||
|
`ASSERT_INIT(MinLfsrWidth_A, LfsrDw >= $low(FIB_XNOR_COEFFS)+FIB_XNOR_LUT_OFF)
|
||||||
|
`ASSERT_INIT(MaxLfsrWidth_A, LfsrDw <= $high(FIB_XNOR_COEFFS)+FIB_XNOR_LUT_OFF)
|
||||||
|
end
|
||||||
|
|
||||||
|
// calculate next state using external XNOR feedback and entropy input
|
||||||
|
assign next_lfsr_state = LfsrDw'(entropy_i) ^ {lfsr_q[LfsrDw-2:0], ~(^(lfsr_q & coeffs))};
|
||||||
|
|
||||||
|
// lockup condition is all-ones
|
||||||
|
assign lockup = &lfsr_q;
|
||||||
|
|
||||||
|
// check that seed is not all-ones
|
||||||
|
`ASSERT_INIT(DefaultSeedNzCheck_A, !(&DefaultSeed))
|
||||||
|
|
||||||
|
|
||||||
|
/////////////
|
||||||
|
// Unknown //
|
||||||
|
/////////////
|
||||||
|
end else begin : gen_unknown_type
|
||||||
|
`ASSERT_INIT(UnknownLfsrType_A, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////
|
||||||
|
// Shared logic //
|
||||||
|
//////////////////
|
||||||
|
|
||||||
|
assign lfsr_d = (seed_en_i) ? seed_i :
|
||||||
|
(lfsr_en_i && lockup) ? DefaultSeed :
|
||||||
|
(lfsr_en_i) ? next_lfsr_state :
|
||||||
|
lfsr_q;
|
||||||
|
|
||||||
|
assign state_o = lfsr_q[StateOutDw-1:0];
|
||||||
|
|
||||||
|
always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg
|
||||||
|
if (!rst_ni) begin
|
||||||
|
lfsr_q <= DefaultSeed;
|
||||||
|
end else begin
|
||||||
|
lfsr_q <= lfsr_d;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////
|
||||||
|
// shared assertions //
|
||||||
|
///////////////////////
|
||||||
|
|
||||||
|
`ASSERT_KNOWN(DataKnownO_A, state_o)
|
||||||
|
|
||||||
|
// the code below is not meant to be synthesized,
|
||||||
|
// but it is intended to be used in simulation and FPV
|
||||||
|
`ifndef SYNTHESIS
|
||||||
|
function automatic logic[LfsrDw-1:0] compute_next_state(logic[LfsrDw-1:0] lfsrcoeffs,
|
||||||
|
logic[EntropyDw-1:0] entropy,
|
||||||
|
logic[LfsrDw-1:0] state);
|
||||||
|
logic state0;
|
||||||
|
|
||||||
|
// Galois XOR
|
||||||
|
if (64'(LfsrType) == 64'("GAL_XOR")) begin
|
||||||
|
if (state == 0) begin
|
||||||
|
state = DefaultSeed;
|
||||||
|
end else begin
|
||||||
|
state0 = state[0];
|
||||||
|
state = state >> 1;
|
||||||
|
if (state0) state ^= lfsrcoeffs;
|
||||||
|
state ^= LfsrDw'(entropy);
|
||||||
|
end
|
||||||
|
// Fibonacci XNOR
|
||||||
|
end else if (64'(LfsrType) == "FIB_XNOR") begin
|
||||||
|
if (&state) begin
|
||||||
|
state = DefaultSeed;
|
||||||
|
end else begin
|
||||||
|
state0 = ~(^(state & lfsrcoeffs));
|
||||||
|
state = state << 1;
|
||||||
|
state[0] = state0;
|
||||||
|
state ^= LfsrDw'(entropy);
|
||||||
|
end
|
||||||
|
end else begin
|
||||||
|
$error("unknown lfsr type");
|
||||||
|
end
|
||||||
|
|
||||||
|
return state;
|
||||||
|
endfunction : compute_next_state
|
||||||
|
|
||||||
|
// check whether next state is computed correctly
|
||||||
|
`ASSERT(NextStateCheck_A, lfsr_en_i && !seed_en_i |=> lfsr_q ==
|
||||||
|
compute_next_state(coeffs, $past(entropy_i,1), $past(lfsr_q,1)))
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ASSERT_INIT(InputWidth_A, LfsrDw >= EntropyDw)
|
||||||
|
`ASSERT_INIT(OutputWidth_A, LfsrDw >= StateOutDw)
|
||||||
|
|
||||||
|
// MSB must be one in any case
|
||||||
|
`ASSERT(CoeffCheck_A, coeffs[LfsrDw-1])
|
||||||
|
|
||||||
|
// output check
|
||||||
|
`ASSERT_KNOWN(OutputKnown_A, state_o)
|
||||||
|
`ASSERT(OutputCheck_A, state_o == StateOutDw'(lfsr_q))
|
||||||
|
|
||||||
|
// if no external input changes the lfsr state, a lockup must not occur (by design)
|
||||||
|
//`ASSERT(NoLockups_A, (!entropy_i) && (!seed_en_i) |=> !lockup, clk_i, !rst_ni)
|
||||||
|
`ASSERT(NoLockups_A, lfsr_en_i && !entropy_i && !seed_en_i |=> !lockup)
|
||||||
|
|
||||||
|
// this can be disabled if unused in order to not distort coverage
|
||||||
|
if (ExtSeedSVA) begin : gen_ext_seed_sva
|
||||||
|
// check that external seed is correctly loaded into the state
|
||||||
|
`ASSERT(ExtDefaultSeedInputCheck_A, seed_en_i |=> lfsr_q == $past(seed_i))
|
||||||
|
end
|
||||||
|
|
||||||
|
// if the external seed mechanism is not used,
|
||||||
|
// there is theoretically no way we end up in a lockup condition
|
||||||
|
// in order to not distort coverage, this SVA can be disabled in such cases
|
||||||
|
if (LockupSVA) begin : gen_lockup_mechanism_sva
|
||||||
|
// check that a stuck LFSR is correctly reseeded
|
||||||
|
`ASSERT(LfsrLockupCheck_A, lfsr_en_i && lockup && !seed_en_i |=> !lockup)
|
||||||
|
end
|
||||||
|
|
||||||
|
if (MaxLenSVA) begin : gen_max_len_sva
|
||||||
|
|
||||||
|
`ifndef SYNTHESIS
|
||||||
|
// the code below is a workaround to enable long sequences to be checked.
|
||||||
|
// some simulators do not support SVA sequences longer than 2**32-1.
|
||||||
|
logic [LfsrDw-1:0] cnt_d, cnt_q;
|
||||||
|
logic perturbed_d, perturbed_q;
|
||||||
|
logic [LfsrDw-1:0] cmp_val;
|
||||||
|
|
||||||
|
assign cmp_val = {{(LfsrDw-1){1'b1}}, 1'b0}; // 2**LfsrDw-2
|
||||||
|
assign cnt_d = (lfsr_en_i && lockup) ? '0 :
|
||||||
|
(lfsr_en_i && (cnt_q == cmp_val)) ? '0 :
|
||||||
|
(lfsr_en_i) ? cnt_q + 1'b1 :
|
||||||
|
cnt_q;
|
||||||
|
|
||||||
|
assign perturbed_d = perturbed_q | (|entropy_i) | seed_en_i;
|
||||||
|
|
||||||
|
always_ff @(posedge clk_i or negedge rst_ni) begin : p_max_len
|
||||||
|
if (!rst_ni) begin
|
||||||
|
cnt_q <= '0;
|
||||||
|
perturbed_q <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
cnt_q <= cnt_d;
|
||||||
|
perturbed_q <= perturbed_d;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ASSERT(MaximalLengthCheck0_A, cnt_q == 0 |-> lfsr_q == DefaultSeed,
|
||||||
|
clk_i, !rst_ni || perturbed_q)
|
||||||
|
`ASSERT(MaximalLengthCheck1_A, cnt_q != 0 |-> lfsr_q != DefaultSeed,
|
||||||
|
clk_i, !rst_ni || perturbed_q)
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
Loading…
Add table
Add a link
Reference in a new issue