[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:
Tom Roberts 2020-04-16 13:07:26 +01:00 committed by Tom Roberts
parent 5c7cdfe14e
commit d5ee96fff6
15 changed files with 1008 additions and 75 deletions

View file

@ -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))
------------------------

View file

@ -19,6 +19,7 @@ Ibex User Manual
performance_counters
exception_interrupts
pmp
security
debug
tracer
rvfi

46
doc/security.rst Normal file
View 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.

View file

@ -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

View file

@ -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

View file

@ -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 ),
@ -722,12 +734,14 @@ module ibex_core #(
ibex_register_file #(
.RV32E (RV32E),
.DataWidth(32)
.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;

View file

@ -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
View 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

View file

@ -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

View file

@ -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

View file

@ -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];

View 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,
@ -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

View 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
View 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
View 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