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 |
|
||||
+---------+--------------------+--------+-----------------------------------------------+
|
||||
| 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 |
|
||||
+---------+--------------------+--------+-----------------------------------------------+
|
||||
|
@ -500,6 +502,16 @@ Other bit fields read as zero.
|
|||
+-------+------+------------------------------------------------------------------+
|
||||
| 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 |
|
||||
| | | timing features. If the core has not been configured with |
|
||||
| | | 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. |
|
||||
+-------+------+------------------------------------------------------------------+
|
||||
|
||||
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))
|
||||
------------------------
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ Ibex User Manual
|
|||
performance_counters
|
||||
exception_interrupts
|
||||
pmp
|
||||
security
|
||||
debug
|
||||
tracer
|
||||
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
|
||||
${PRJ_DIR}/ibex/shared/rtl/prim_assert.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_dec.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_counters.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_wb_stage.sv
|
||||
${PRJ_DIR}/ibex/rtl/ibex_id_stage.sv
|
||||
|
|
|
@ -9,6 +9,7 @@ filesets:
|
|||
files_rtl:
|
||||
depend:
|
||||
- lowrisc:prim:assert
|
||||
- lowrisc:prim:lfsr
|
||||
- lowrisc:ibex:ibex_pkg
|
||||
- lowrisc:ibex:ibex_icache
|
||||
files:
|
||||
|
@ -28,6 +29,7 @@ filesets:
|
|||
- rtl/ibex_prefetch_buffer.sv
|
||||
- rtl/ibex_pmp.sv
|
||||
- rtl/ibex_wb_stage.sv
|
||||
- rtl/ibex_dummy_instr.sv
|
||||
# XXX: Figure out the best way to switch these two implementations
|
||||
# dynamically on the target.
|
||||
# - rtl/ibex_register_file_latch.sv # ASIC
|
||||
|
|
|
@ -106,8 +106,10 @@ module ibex_core #(
|
|||
|
||||
localparam int unsigned PMP_NUM_CHAN = 2;
|
||||
localparam bit DataIndTiming = SecureIbex;
|
||||
localparam bit DummyInstructions = SecureIbex;
|
||||
|
||||
// IF/ID signals
|
||||
logic dummy_instr_id;
|
||||
logic instr_valid_id;
|
||||
logic instr_new_id;
|
||||
logic [31:0] instr_rdata_id; // Instruction sampled inside IF stage
|
||||
|
@ -126,6 +128,10 @@ module ibex_core #(
|
|||
logic imd_val_we_ex;
|
||||
|
||||
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_inval;
|
||||
|
||||
|
@ -366,6 +372,7 @@ module ibex_core #(
|
|||
ibex_if_stage #(
|
||||
.DmHaltAddr ( DmHaltAddr ),
|
||||
.DmExceptionAddr ( DmExceptionAddr ),
|
||||
.DummyInstructions ( DummyInstructions ),
|
||||
.ICache ( ICache ),
|
||||
.ICacheECC ( ICacheECC )
|
||||
) if_stage_i (
|
||||
|
@ -394,6 +401,7 @@ module ibex_core #(
|
|||
.instr_fetch_err_o ( instr_fetch_err ),
|
||||
.instr_fetch_err_plus2_o ( instr_fetch_err_plus2 ),
|
||||
.illegal_c_insn_id_o ( illegal_c_insn_id ),
|
||||
.dummy_instr_id_o ( dummy_instr_id ),
|
||||
.pc_if_o ( pc_if ),
|
||||
.pc_id_o ( pc_id ),
|
||||
|
||||
|
@ -403,6 +411,10 @@ module ibex_core #(
|
|||
.pc_mux_i ( pc_mux_id ),
|
||||
.exc_pc_mux_i ( exc_pc_mux_id ),
|
||||
.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_inval_i ( icache_inval ),
|
||||
|
||||
|
@ -721,13 +733,15 @@ module ibex_core #(
|
|||
);
|
||||
|
||||
ibex_register_file #(
|
||||
.RV32E(RV32E),
|
||||
.DataWidth(32)
|
||||
.RV32E (RV32E),
|
||||
.DataWidth (32),
|
||||
.DummyInstructions (DummyInstructions)
|
||||
) register_file_i (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
|
||||
.test_en_i ( test_en_i ),
|
||||
.dummy_instr_id_i ( dummy_instr_id ),
|
||||
|
||||
// Read port a
|
||||
.raddr_a_i ( rf_raddr_a ),
|
||||
|
@ -804,6 +818,7 @@ module ibex_core #(
|
|||
ibex_cs_registers #(
|
||||
.DbgTriggerEn ( DbgTriggerEn ),
|
||||
.DataIndTiming ( DataIndTiming ),
|
||||
.DummyInstructions ( DummyInstructions ),
|
||||
.ICache ( ICache ),
|
||||
.MHPMCounterNum ( MHPMCounterNum ),
|
||||
.MHPMCounterWidth ( MHPMCounterWidth ),
|
||||
|
@ -866,6 +881,10 @@ module ibex_core #(
|
|||
.pc_wb_i ( pc_wb ),
|
||||
|
||||
.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 ),
|
||||
|
||||
.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
|
||||
// So first stage becomes valid when instruction leaves ID/EX stage and remains valid until
|
||||
// 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
|
||||
// retired)
|
||||
assign rvfi_stage_valid_d[1] = instr_done_wb;
|
||||
|
@ -1030,7 +1050,7 @@ module ibex_core #(
|
|||
end else begin
|
||||
// Without writeback stage first RVFI stage is output stage so simply valid the cycle after
|
||||
// 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
|
||||
// setup register write signals
|
||||
assign rvfi_instr_new_wb = instr_new_id;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
module ibex_cs_registers #(
|
||||
parameter bit DbgTriggerEn = 0,
|
||||
parameter bit DataIndTiming = 1'b0,
|
||||
parameter bit DummyInstructions = 1'b0,
|
||||
parameter bit ICache = 1'b0,
|
||||
parameter int unsigned MHPMCounterNum = 10,
|
||||
parameter int unsigned MHPMCounterWidth = 40,
|
||||
|
@ -81,6 +82,10 @@ module ibex_cs_registers #(
|
|||
|
||||
// CPU control bits
|
||||
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,
|
||||
|
||||
// Exception save/restore
|
||||
|
@ -160,7 +165,9 @@ module ibex_cs_registers #(
|
|||
|
||||
// CPU control register fields
|
||||
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 icache_enable;
|
||||
} CpuCtrl_t;
|
||||
|
@ -420,6 +427,11 @@ module ibex_cs_registers #(
|
|||
csr_rdata_int = {cpuctrl_rdata};
|
||||
end
|
||||
|
||||
// Custom CSR for LFSR re-seeding (cannot be read)
|
||||
CSR_SECURESEED: begin
|
||||
csr_rdata_int = '0;
|
||||
end
|
||||
|
||||
default: begin
|
||||
illegal_csr = 1'b1;
|
||||
end
|
||||
|
@ -1068,6 +1080,50 @@ module ibex_cs_registers #(
|
|||
|
||||
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
|
||||
if (ICache) begin : gen_icache_enable
|
||||
logic icache_enable_d, icache_enable_q;
|
||||
|
@ -1098,8 +1154,8 @@ module ibex_cs_registers #(
|
|||
assign icache_enable_o = cpuctrl_rdata.icache_enable;
|
||||
|
||||
// tieoff for the currently unused bits of cpuctrl
|
||||
logic [31:2] unused_cpuctrl;
|
||||
assign unused_cpuctrl = {cpuctrl_wdata[31:2]};
|
||||
logic [31:6] unused_cpuctrl;
|
||||
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
|
|
@ -15,6 +15,7 @@
|
|||
module ibex_if_stage #(
|
||||
parameter int unsigned DmHaltAddr = 32'h1A110800,
|
||||
parameter int unsigned DmExceptionAddr = 32'h1A110808,
|
||||
parameter bit DummyInstructions = 1'b0,
|
||||
parameter bit ICache = 1'b0,
|
||||
parameter bit ICacheECC = 1'b0
|
||||
) (
|
||||
|
@ -48,6 +49,7 @@ module ibex_if_stage #(
|
|||
output logic instr_fetch_err_plus2_o, // bus error misaligned
|
||||
output logic illegal_c_insn_id_o, // compressed decoder thinks this
|
||||
// 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_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_cause_e exc_cause, // selects ISR address for
|
||||
// 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_inval_i,
|
||||
|
||||
|
@ -103,6 +109,14 @@ module ibex_if_stage #(
|
|||
|
||||
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_csr_mtvec;
|
||||
|
||||
|
@ -206,7 +220,7 @@ module ibex_if_stage #(
|
|||
end
|
||||
|
||||
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 if_busy_o = prefetch_busy;
|
||||
|
@ -218,7 +232,7 @@ module ibex_if_stage #(
|
|||
// to ease timing closure
|
||||
logic [31:0] instr_decompressed;
|
||||
logic illegal_c_insn;
|
||||
logic instr_is_compressed_int;
|
||||
logic instr_is_compressed;
|
||||
|
||||
ibex_compressed_decoder compressed_decoder_i (
|
||||
.clk_i ( clk_i ),
|
||||
|
@ -226,16 +240,74 @@ module ibex_if_stage #(
|
|||
.valid_i ( fetch_valid ),
|
||||
.instr_i ( fetch_rdata ),
|
||||
.instr_o ( instr_decompressed ),
|
||||
.is_compressed_o ( instr_is_compressed_int ),
|
||||
.is_compressed_o ( instr_is_compressed ),
|
||||
.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.
|
||||
// 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)
|
||||
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);
|
||||
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
|
||||
if (!rst_ni) begin
|
||||
|
@ -256,14 +328,14 @@ module ibex_if_stage #(
|
|||
|
||||
always_ff @(posedge clk_i) 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.
|
||||
instr_rdata_alu_id_o <= instr_decompressed;
|
||||
instr_fetch_err_o <= fetch_err;
|
||||
instr_rdata_alu_id_o <= instr_out;
|
||||
instr_fetch_err_o <= instr_err_out;
|
||||
instr_fetch_err_plus2_o <= fetch_err_plus2;
|
||||
instr_rdata_c_id_o <= fetch_rdata[15:0];
|
||||
instr_is_compressed_id_o <= instr_is_compressed_int;
|
||||
illegal_c_insn_id_o <= illegal_c_insn;
|
||||
instr_is_compressed_id_o <= instr_is_compressed_out;
|
||||
illegal_c_insn_id_o <= illegal_c_instr_out;
|
||||
pc_id_o <= pc_if_o;
|
||||
end
|
||||
end
|
||||
|
|
|
@ -448,7 +448,8 @@ typedef enum logic[11:0] {
|
|||
CSR_MHPMCOUNTER29H = 12'hB9D,
|
||||
CSR_MHPMCOUNTER30H = 12'hB9E,
|
||||
CSR_MHPMCOUNTER31H = 12'hB9F,
|
||||
CSR_CPUCTRL = 12'h7C0
|
||||
CSR_CPUCTRL = 12'h7C0,
|
||||
CSR_SECURESEED = 12'h7C1
|
||||
} csr_num_e;
|
||||
|
||||
// CSR pmp-related offsets
|
||||
|
|
|
@ -12,13 +12,15 @@
|
|||
*/
|
||||
module ibex_register_file #(
|
||||
parameter bit RV32E = 0,
|
||||
parameter int unsigned DataWidth = 32
|
||||
parameter int unsigned DataWidth = 32,
|
||||
parameter bit DummyInstructions = 0
|
||||
) (
|
||||
// Clock and Reset
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
|
||||
input logic test_en_i,
|
||||
input logic dummy_instr_id_i,
|
||||
|
||||
//Read port R1
|
||||
input logic [4:0] raddr_a_i,
|
||||
|
@ -60,8 +62,34 @@ module ibex_register_file #(
|
|||
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 [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 rdata_a_o = rf_reg[raddr_a_i];
|
||||
|
|
|
@ -13,13 +13,15 @@
|
|||
*/
|
||||
module ibex_register_file #(
|
||||
parameter bit RV32E = 0,
|
||||
parameter int unsigned DataWidth = 32
|
||||
parameter int unsigned DataWidth = 32,
|
||||
parameter bit DummyInstructions = 0
|
||||
) (
|
||||
// Clock and Reset
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
|
||||
input logic test_en_i,
|
||||
input logic dummy_instr_id_i,
|
||||
|
||||
//Read port R1
|
||||
input logic [ 4:0] raddr_a_i,
|
||||
|
@ -54,4 +56,12 @@ module ibex_register_file #(
|
|||
end
|
||||
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
|
||||
|
|
|
@ -13,13 +13,15 @@
|
|||
*/
|
||||
module ibex_register_file #(
|
||||
parameter bit RV32E = 0,
|
||||
parameter int unsigned DataWidth = 32
|
||||
parameter int unsigned DataWidth = 32,
|
||||
parameter bit DummyInstructions = 0
|
||||
) (
|
||||
// Clock and Reset
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
|
||||
input logic test_en_i,
|
||||
input logic dummy_instr_id_i,
|
||||
|
||||
//Read port R1
|
||||
input logic [4:0] raddr_a_i,
|
||||
|
@ -87,7 +89,7 @@ module ibex_register_file #(
|
|||
// Write address decoding
|
||||
always_comb begin : wad
|
||||
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;
|
||||
end else begin
|
||||
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.
|
||||
// The process is synchronized with the clocks mem_clocks[k], k = 1, ..., NUM_WORDS-1.
|
||||
always_latch begin : latch_wdata
|
||||
mem[0] = '0;
|
||||
for (int k = 1; k < NUM_WORDS; k++) begin : latch_wdata_word_iter
|
||||
if (mem_clocks[k]) begin
|
||||
mem[k] = wdata_a_q;
|
||||
|
@ -117,6 +118,40 @@ module ibex_register_file #(
|
|||
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
|
||||
initial begin
|
||||
$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