1
\$\begingroup\$

On recent comments based fixed modules for FPGAs for generating a pseudo-random bit sequence are presented. The first module generates a bit sequence. The third module speeds up the generation by transferring the bus to, for example, a multiplexer which is controlled by a faster device. Hence this bus is serialized into a bit sequence.

prbs.sv

`timescale 1ns / 1ps

module prbs #
(
    parameter integer PN = 7 //3, 4, 5, 6, 7, 9, 11, 15, 17, 23, 31, 32, 36, 41
)
(
    input  logic i_clk,
    input  logic i_s_rst_n,
    input  logic i_en,

    output logic o_prbs,
    output logic o_prbs_n
);
    localparam integer TAP_1 = (PN == 3) ? 2 :
                               (PN == 4) ? 3 :
                               (PN == 5) ? 4 :
                               (PN == 6) ? 5 :
                               (PN == 7) ? 6 :
                               (PN == 9) ? 8 :
                               (PN == 10) ? 9 :
                               (PN == 11) ? 10 :
                               (PN == 15) ? 14 :
                               (PN == 17) ? 16 :
                               (PN == 18) ? 17 :
                               (PN == 20) ? 19 :
                               (PN == 21) ? 20 :
                               (PN == 22) ? 21 :
                               (PN == 23) ? 22 :
                               (PN == 25) ? 24 :
                               (PN == 28) ? 27 :
                               (PN == 29) ? 28 :
                               (PN == 31) ? 30 :
                               (PN == 33) ? 32 :
                               (PN == 35) ? 34 :
                               (PN == 36) ? 35 :
                               (PN == 39) ? 38 :
                               (PN == 41) ? 40 : 0;

    localparam integer TAP_0 = (PN == 3) ? 1 :
                               (PN == 4) ? 2 :
                               (PN == 5) ? 2 :
                               (PN == 6) ? 4 :
                               (PN == 7) ? 5 :
                               (PN == 9) ? 4 :
                               (PN == 10) ? 6 :
                               (PN == 11) ? 8 :
                               (PN == 15) ? 13 :
                               (PN == 17) ? 13 :
                               (PN == 18) ? 10 :
                               (PN == 20) ? 16 :
                               (PN == 21) ? 18 :
                               (PN == 22) ? 20 :
                               (PN == 23) ? 17 :
                               (PN == 25) ? 21 :
                               (PN == 28) ? 24 :
                               (PN == 29) ? 27 :
                               (PN == 31) ? 27 :
                               (PN == 33) ? 19 :
                               (PN == 35) ? 23 :
                               (PN == 36) ? 24 :
                               (PN == 39) ? 34 :
                               (PN == 41) ? 37 : 0;

    logic [PN - 1 : 0] lfsr; 

    assign o_prbs   = lfsr[PN - 1];
    assign o_prbs_n = ~o_prbs;                      

    always_ff @ (posedge i_clk) begin
        if (i_s_rst_n == 1'h0) begin
            lfsr <= '1;
        end
        else if (i_en == 1'h1) begin
            lfsr <= {lfsr[PN - 2 : 0], lfsr[TAP_1] ^ lfsr[TAP_0]};
        end
    end
endmodule

prbs_tb.sv

`timescale 1ns / 1ps

module prbs_tb; 
    localparam integer PN             = 7; //3, 4, 5, 6, 7, 9, 11, 15, 17, 23, 31, 32, 36, 41
    localparam integer PERIOD         = 2 ** PN;

    localparam integer CLOCK_PERIOD   = 100;

    localparam integer TEST_ITERATION = 1000;
    localparam integer CHANGE_EN_VAL  = 100;

    bit clk     = '0;
    bit s_rst_n = '0;
    bit en      = '0;
    
    logic prbs  ;
    logic prbs_n;

    integer tick = 0;

    prbs #
    (
        .PN (PN)
    )
    prbs_dut
    (
        .i_clk     (clk    ),
        .i_s_rst_n (s_rst_n),
        .i_en      (en     ),

        .o_prbs    (prbs   ),
        .o_prbs_n  (prbs_n )
    );

    always begin
         #(CLOCK_PERIOD / 2) clk = !clk;
    end

    initial begin
        s_rst_n <= '0;
        @(posedge clk);

        s_rst_n <= '1;
        en      <= '1;
        @(posedge clk);

        for(int i = 0; i < TEST_ITERATION; i++) begin
            if ((i % PERIOD) == (PERIOD - 1)) begin
                en   <= ~en;
                tick = 0;
            end
            else begin
                tick++;
            end

            @(posedge clk);
        end
        
        $stop();
    end
endmodule

prbs_wide.sv

`timescale 1ns / 1ps

module prbs_wide #
(
    parameter integer PN = 7, //3, 4, 5, 6, 7, 9, 11, 15, 17, 23, 31, 32, 36, 41
    parameter integer WIDTH = 16
)
(
    input  logic                 i_clk,
    input  logic                 i_s_rst_n,
    input  logic                 i_en,

    output logic [WIDTH - 1 : 0] o_prbs,
    output logic [WIDTH - 1 : 0] o_prbs_n
);
    localparam integer TAP_1 = (PN == 3) ? 2 :
                               (PN == 4) ? 3 :
                               (PN == 5) ? 4 :
                               (PN == 6) ? 5 :
                               (PN == 7) ? 6 :
                               (PN == 9) ? 8 :
                               (PN == 10) ? 9 :
                               (PN == 11) ? 10 :
                               (PN == 15) ? 14 :
                               (PN == 17) ? 16 :
                               (PN == 18) ? 17 :
                               (PN == 20) ? 19 :
                               (PN == 21) ? 20 :
                               (PN == 22) ? 21 :
                               (PN == 23) ? 22 :
                               (PN == 25) ? 24 :
                               (PN == 28) ? 27 :
                               (PN == 29) ? 28 :
                               (PN == 31) ? 30 :
                               (PN == 33) ? 32 :
                               (PN == 35) ? 34 :
                               (PN == 36) ? 35 :
                               (PN == 39) ? 38 :
                               (PN == 41) ? 40 : 0;

    localparam integer TAP_0 = (PN == 3) ? 1 :
                               (PN == 4) ? 2 :
                               (PN == 5) ? 2 :
                               (PN == 6) ? 4 :
                               (PN == 7) ? 5 :
                               (PN == 9) ? 4 :
                               (PN == 10) ? 6 :
                               (PN == 11) ? 8 :
                               (PN == 15) ? 13 :
                               (PN == 17) ? 13 :
                               (PN == 18) ? 10 :
                               (PN == 20) ? 16 :
                               (PN == 21) ? 18 :
                               (PN == 22) ? 20 :
                               (PN == 23) ? 17 :
                               (PN == 25) ? 21 :
                               (PN == 28) ? 24 :
                               (PN == 29) ? 27 :
                               (PN == 31) ? 27 :
                               (PN == 33) ? 19 :
                               (PN == 35) ? 23 :
                               (PN == 36) ? 24 :
                               (PN == 39) ? 34 :
                               (PN == 41) ? 37 : 0;

    logic [PN - 1 : 0] lfsr;
    logic [PN - 1 : 0] r_lfsr;  
    logic [WIDTH - 1 : 0] tmp;  
    
    always_comb begin
        lfsr = r_lfsr;

        for (int i = WIDTH - 1; i >= 0; i = i - 1) begin
            lfsr   = {lfsr[PN - 2 : 0], lfsr[TAP_1] ^ lfsr[TAP_0]};
            tmp[i] = lfsr[TAP_1] ^ lfsr[TAP_0];
        end
    end                 

    always_ff @ (posedge i_clk) begin
        if (i_s_rst_n == 1'h0) begin
            r_lfsr   <= '1;

            o_prbs   <= '0;
            o_prbs_n <= '1;
        end
        else if (i_en == 1'h1) begin
            r_lfsr <= lfsr;

            o_prbs   <= tmp;
            o_prbs_n <= ~tmp;
        end
    end
endmodule

prbs_wide_tb.sv

`timescale 1ns / 1ps

module prbs_wide_tb;
    localparam integer PN             = 7;
    localparam integer PERIOD         = 2 ** PN - 1;
    localparam integer WIDTH          = 128;

    localparam integer CLOCK_PERIOD   = 100;

    localparam integer TEST_ITERATION = 1000;
    localparam integer CHANGE_EN_VAL  = 100;

    bit clk     = '0;
    bit s_rst_n = '0;
    bit en      = '0;

    logic [WIDTH - 1 : 0] prbs  ;
    logic [WIDTH - 1 : 0] prbs_n;

    prbs_wide #
    (
        .PN    (PN   ),
        .WIDTH (WIDTH)
    )
    prbs_wide_dut
    (
        .i_clk     (clk    ),
        .i_s_rst_n (s_rst_n),
        .i_en      (en     ),

        .o_prbs    (prbs   ),
        .o_prbs_n  (prbs_n )
    );

    always begin
         #(CLOCK_PERIOD / 2) clk = !clk;
    end

    initial begin
        s_rst_n <= '0;
        @(posedge clk);

        s_rst_n <= '1;
        en      <= '1;
        @(posedge clk);

        for(int i = 0; i < TEST_ITERATION; i++) begin        
            if ((i % PERIOD) == PERIOD - 1) begin
                en <= ~en;
            end
            @(posedge clk);
        end
        
        $stop();
    end
endmodule
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

I don't see too much room for improvement.

Since you have some code which is common to 2 modules (the localparam TAP_0 and TAP_1 declarations), you could move them to a separate file and include them into the modules:

`include "common.v"

You could consider replacing the multiple conditional operators used to define TAP_0 and TAP_1 with case statements. To do so, you would also need to place the code inside a function. In general, functions are synthesizable.

localparam integer TAP_0 = tap0(PN);

function integer tap0 (integer pn);
    case (pn)
         3      : return  1;
         4, 5   : return  2;
         6, 9   : return  4;
         7      : return  5;
        10      : return  6;
        11      : return  8;
        15, 17  : return 13;
        18      : return 10;
        20      : return 16;
        21      : return 18;
        22      : return 20;
        23      : return 17;
        25      : return 21;
        28, 36  : return 24;
        29, 31  : return 27;
        33      : return 19;
        35      : return 23;
        39      : return 34;
        41      : return 37;
        default : return  0;
    endcase
endfunction

It is often beneficial to encapsulate complex code like this inside a function; it may make the code easier to understand.

A benefit of using case is that you can group inputs together which return the same value; this is done using a comma to separate the case items, such as 4, 5. This results in fewer lines of code.

This is all a matter of preference and, of course, tool support.

For TAP_1:

localparam integer TAP_1 = tap1(PN);

function integer tap1 (integer pn);
    case (pn)
          3,
          4,
          5,
          6,
          7,
          9,
         10,
         11,
         15,
         17,
         18,
         20,
         21,
         22,
         23,
         25,
         28,
         29,
         31,
         33,
         35,
         36,
         39,
         41     : return pn-1;
        default : return 0;
    endcase
endfunction

Here, all explicit case items return the simple expression pn-1. You can add as many of the case items on the same line as you want instead of having them one per line.

I attempted to use a case inside construct, but the 2 simulators I tried it on generated compile errors due to an unsupported construct. Maybe it will be supported in a future release. Refer to IEEE Std 1800-2017, section 12.5.4 Set membership case statement.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.