| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| // Testbench module for prim_lfsr, sweeps through all implementations |
| // within a certain range to check whether they are max length. |
| |
| module prim_lfsr_tb; |
| |
| import dv_utils_pkg::*; |
| `include "dv_macros.svh" |
| |
| ////////////////////////////////////////////////////// |
| // Build configurations: |
| // LFSR_TYPE; The type of LFSR used. Choices: "GAL_XOR" or "FIB_XOR" |
| // MIN_LFSR_DW: Minimum LFSR width tested. |
| // MAX_LFSR_DW: Maximum LFSR width tested. |
| ////////////////////////////////////////////////////// |
| `ifdef LFSR_TYPE |
| localparam string LfsrType = `LFSR_TYPE; |
| `else |
| localparam string LfsrType = "GAL_XOR"; |
| `endif |
| `ifdef MIN_LFSR_DW |
| localparam int unsigned MinLfsrDw = `MIN_LFSR_DW; |
| `else |
| localparam int unsigned MinLfsrDw = 4; |
| `endif |
| `ifdef MAX_LFSR_DW |
| localparam int unsigned MaxLfsrDw = `MAX_LFSR_DW; |
| `else |
| localparam int unsigned MaxLfsrDw = 32; |
| `endif |
| |
| // The default seed of the LFSR. |
| // |
| // This is fixed to 1. It is unused in simulations. The `prim_lfsr` instead, randomizes the |
| // default seed value (DefaultSeedLocal) at runtime. This is enforced with |
| // +prim_lfsr_use_default_seed=0 plusarg. |
| localparam logic SEED = 1'b1; |
| |
| // The StatePerm below is only defined for LFSRs up to 256bit wide. |
| `ASSERT_INIT(MaxStateSizeCheck_A, MaxLfsrDw < 256) |
| |
| logic [MaxLfsrDw:MinLfsrDw] lfsr_en, err, test_done; |
| logic [MaxLfsrDw:MinLfsrDw][MaxLfsrDw-1:0] state_out; |
| logic [MaxLfsrDw:MinLfsrDw][MaxLfsrDw-1:0] lfsr_periods; |
| |
| for (genvar k = MinLfsrDw; k <= MaxLfsrDw; k++) begin : gen_duts |
| // This is used to specify an identity permutation via the custom state output |
| // permutation parameter for all LFSRs up to 256bit wide. |
| localparam int Dw = $clog2(k); |
| localparam logic [255:0][Dw-1:0] StatePerm = '{ |
| Dw'(32'd255), Dw'(32'd254), Dw'(32'd253), Dw'(32'd252), |
| Dw'(32'd251), Dw'(32'd250), Dw'(32'd249), Dw'(32'd248), |
| Dw'(32'd247), Dw'(32'd246), Dw'(32'd245), Dw'(32'd244), |
| Dw'(32'd243), Dw'(32'd242), Dw'(32'd241), Dw'(32'd240), |
| Dw'(32'd239), Dw'(32'd238), Dw'(32'd237), Dw'(32'd236), |
| Dw'(32'd235), Dw'(32'd234), Dw'(32'd233), Dw'(32'd232), |
| Dw'(32'd231), Dw'(32'd230), Dw'(32'd229), Dw'(32'd228), |
| Dw'(32'd227), Dw'(32'd226), Dw'(32'd225), Dw'(32'd224), |
| Dw'(32'd223), Dw'(32'd222), Dw'(32'd221), Dw'(32'd220), |
| Dw'(32'd219), Dw'(32'd218), Dw'(32'd217), Dw'(32'd216), |
| Dw'(32'd215), Dw'(32'd214), Dw'(32'd213), Dw'(32'd212), |
| Dw'(32'd211), Dw'(32'd210), Dw'(32'd209), Dw'(32'd208), |
| Dw'(32'd207), Dw'(32'd206), Dw'(32'd205), Dw'(32'd204), |
| Dw'(32'd203), Dw'(32'd202), Dw'(32'd201), Dw'(32'd200), |
| Dw'(32'd199), Dw'(32'd198), Dw'(32'd197), Dw'(32'd196), |
| Dw'(32'd195), Dw'(32'd194), Dw'(32'd193), Dw'(32'd192), |
| Dw'(32'd191), Dw'(32'd190), Dw'(32'd189), Dw'(32'd188), |
| Dw'(32'd187), Dw'(32'd186), Dw'(32'd185), Dw'(32'd184), |
| Dw'(32'd183), Dw'(32'd182), Dw'(32'd181), Dw'(32'd180), |
| Dw'(32'd179), Dw'(32'd178), Dw'(32'd177), Dw'(32'd176), |
| Dw'(32'd175), Dw'(32'd174), Dw'(32'd173), Dw'(32'd172), |
| Dw'(32'd171), Dw'(32'd170), Dw'(32'd169), Dw'(32'd168), |
| Dw'(32'd167), Dw'(32'd166), Dw'(32'd165), Dw'(32'd164), |
| Dw'(32'd163), Dw'(32'd162), Dw'(32'd161), Dw'(32'd160), |
| Dw'(32'd159), Dw'(32'd158), Dw'(32'd157), Dw'(32'd156), |
| Dw'(32'd155), Dw'(32'd154), Dw'(32'd153), Dw'(32'd152), |
| Dw'(32'd151), Dw'(32'd150), Dw'(32'd149), Dw'(32'd148), |
| Dw'(32'd147), Dw'(32'd146), Dw'(32'd145), Dw'(32'd144), |
| Dw'(32'd143), Dw'(32'd142), Dw'(32'd141), Dw'(32'd140), |
| Dw'(32'd139), Dw'(32'd138), Dw'(32'd137), Dw'(32'd136), |
| Dw'(32'd135), Dw'(32'd134), Dw'(32'd133), Dw'(32'd132), |
| Dw'(32'd131), Dw'(32'd130), Dw'(32'd129), Dw'(32'd128), |
| Dw'(32'd127), Dw'(32'd126), Dw'(32'd125), Dw'(32'd124), |
| Dw'(32'd123), Dw'(32'd122), Dw'(32'd121), Dw'(32'd120), |
| Dw'(32'd119), Dw'(32'd118), Dw'(32'd117), Dw'(32'd116), |
| Dw'(32'd115), Dw'(32'd114), Dw'(32'd113), Dw'(32'd112), |
| Dw'(32'd111), Dw'(32'd110), Dw'(32'd109), Dw'(32'd108), |
| Dw'(32'd107), Dw'(32'd106), Dw'(32'd105), Dw'(32'd104), |
| Dw'(32'd103), Dw'(32'd102), Dw'(32'd101), Dw'(32'd100), |
| Dw'(32'd099), Dw'(32'd098), Dw'(32'd097), Dw'(32'd096), |
| Dw'(32'd095), Dw'(32'd094), Dw'(32'd093), Dw'(32'd092), |
| Dw'(32'd091), Dw'(32'd090), Dw'(32'd089), Dw'(32'd088), |
| Dw'(32'd087), Dw'(32'd086), Dw'(32'd085), Dw'(32'd084), |
| Dw'(32'd083), Dw'(32'd082), Dw'(32'd081), Dw'(32'd080), |
| Dw'(32'd079), Dw'(32'd078), Dw'(32'd077), Dw'(32'd076), |
| Dw'(32'd075), Dw'(32'd074), Dw'(32'd073), Dw'(32'd072), |
| Dw'(32'd071), Dw'(32'd070), Dw'(32'd069), Dw'(32'd068), |
| Dw'(32'd067), Dw'(32'd066), Dw'(32'd065), Dw'(32'd064), |
| Dw'(32'd063), Dw'(32'd062), Dw'(32'd061), Dw'(32'd060), |
| Dw'(32'd059), Dw'(32'd058), Dw'(32'd057), Dw'(32'd056), |
| Dw'(32'd055), Dw'(32'd054), Dw'(32'd053), Dw'(32'd052), |
| Dw'(32'd051), Dw'(32'd050), Dw'(32'd049), Dw'(32'd048), |
| Dw'(32'd047), Dw'(32'd046), Dw'(32'd045), Dw'(32'd044), |
| Dw'(32'd043), Dw'(32'd042), Dw'(32'd041), Dw'(32'd040), |
| Dw'(32'd039), Dw'(32'd038), Dw'(32'd037), Dw'(32'd036), |
| Dw'(32'd035), Dw'(32'd034), Dw'(32'd033), Dw'(32'd032), |
| Dw'(32'd031), Dw'(32'd030), Dw'(32'd029), Dw'(32'd028), |
| Dw'(32'd027), Dw'(32'd026), Dw'(32'd025), Dw'(32'd024), |
| Dw'(32'd023), Dw'(32'd022), Dw'(32'd021), Dw'(32'd020), |
| Dw'(32'd019), Dw'(32'd018), Dw'(32'd017), Dw'(32'd016), |
| Dw'(32'd015), Dw'(32'd014), Dw'(32'd013), Dw'(32'd012), |
| Dw'(32'd011), Dw'(32'd010), Dw'(32'd009), Dw'(32'd008), |
| Dw'(32'd007), Dw'(32'd006), Dw'(32'd005), Dw'(32'd004), |
| Dw'(32'd003), Dw'(32'd002), Dw'(32'd001), Dw'(32'd000) |
| }; |
| |
| ////////////////////////////////////////////////////// |
| // clock & reset |
| ////////////////////////////////////////////////////// |
| wire clk, rst_n; |
| clk_rst_if main_clk(.clk, .rst_n); |
| |
| ////////////////////////////////////////////////////// |
| // DUTs |
| ////////////////////////////////////////////////////// |
| prim_lfsr #( |
| .LfsrType ( LfsrType ), |
| .LfsrDw ( k ), |
| .EntropyDw ( 1 ), |
| .StateOutDw ( k ), |
| .DefaultSeed ( k'(SEED) ), |
| // The case where this is disabled is already tested with FPV. |
| // Hence we cover the enabled case with custom permutations |
| // in this testbench. |
| .StatePermEn ( 1'b1 ), |
| .StatePerm ( StatePerm[MaxLfsrDw-1:0] ), |
| // Enable internal max length check. |
| .MaxLenSVA ( 1'b1 ) |
| ) i_prim_lfsr ( |
| .clk_i ( clk ), |
| .rst_ni ( rst_n ), |
| .seed_en_i ( 1'b0 ), |
| .seed_i ( '0 ), |
| .lfsr_en_i ( lfsr_en[k] ), |
| .entropy_i ( 1'b0 ), |
| .state_o ( state_out[k][k-1:0] ) |
| ); |
| |
| if (k < MaxLfsrDw) begin : gen_tie_off |
| assign state_out[k][MaxLfsrDw-1:k] = '0; |
| end |
| |
| // calculate period of LFSR: |
| assign lfsr_periods[k] = MaxLfsrDw'({{(k-1){1'b1}}, 1'b0}); |
| |
| ////////////////////////////////////////////////////// |
| // stimuli application / response checking |
| ////////////////////////////////////////////////////// |
| initial begin : p_stimuli |
| bit [MaxLfsrDw-1:0] actual_default_seed; |
| |
| lfsr_en[k] = 0; |
| err[k] = 0; |
| test_done[k] = 0; |
| |
| main_clk.set_sole_clock(1); |
| main_clk.set_active(); |
| main_clk.apply_reset(); |
| main_clk.wait_clks($urandom_range(2, 20)); |
| |
| // For simulations, we modify prim_lfsr to pick a random default seed for every |
| // invocation, instead of going with the DefaultSeed parameter. |
| actual_default_seed = MaxLfsrDw'(i_prim_lfsr.DefaultSeedLocal); |
| |
| // enable this LFSR |
| lfsr_en[k] = 1; |
| |
| $display("Starting LFSR maxlen test for width %0d: running %0d cycles", k, 2 ** k - 1); |
| for (longint unsigned i = 0; i <= lfsr_periods[MaxLfsrDw] && lfsr_en[k]; i++) begin |
| main_clk.wait_clks(1); |
| // Check if we reached the initial state again. |
| if (state_out[k] == actual_default_seed && lfsr_en[k]) begin |
| lfsr_en[k] = 1'b0; |
| // We expect this to occur only after the maximum length period. |
| if (i == lfsr_periods[k]) begin |
| $display("LFSR maxlen test for width %0d passed!", k); |
| end else begin |
| $display("LFSR maxlen test for width %0d failed at period %0d!", k, i); |
| err[k] = 1'b1; |
| end |
| end |
| end |
| |
| main_clk.wait_clks(10); |
| if (lfsr_en[k]) begin |
| $error("LFSR with width %0d never got back to the initial state!", k); |
| err[k] = 1'b1; |
| end |
| main_clk.stop_clk(); |
| test_done[k] = 1; |
| end |
| end |
| |
| initial begin |
| $display("Testing LFSR of type %0s for widths {[%0d:%0d]}", LfsrType, MinLfsrDw, MaxLfsrDw); |
| `DV_WAIT(test_done === '1, , 1_000_000_000 /*1ms*/, "prim_lfsr_tb") |
| dv_test_status_pkg::dv_test_status(.passed(err === '0 && test_done === '1)); |
| $finish(); |
| end |
| |
| // TODO: perhaps wrap this in a macro? |
| initial begin |
| bit poll_for_stop; |
| int unsigned poll_for_stop_interval_ns; |
| |
| poll_for_stop = 1'b1; |
| void'($value$plusargs("poll_for_stop=%0b", poll_for_stop)); |
| poll_for_stop_interval_ns = 1000; |
| void'($value$plusargs("poll_for_stop_interval_ns=%0d", poll_for_stop_interval_ns)); |
| if (poll_for_stop) dv_utils_pkg::poll_for_stop(.interval_ns(poll_for_stop_interval_ns)); |
| end |
| |
| endmodule : prim_lfsr_tb |