2
\$\begingroup\$

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) ? 7 :
                               (PN == 11) ? 10 :
                               (PN == 15) ? 14 :
                               (PN == 17) ? 16 :
                               (PN == 23) ? 22 :
                               (PN == 31) ? 30 :
                               (PN == 32) ? 31 :
                               (PN == 36) ? 35 :
                               (PN == 41) ? 40 : 0;

    localparam integer TAP_0 = (PN == 3) ? 0 :
                               (PN == 4) ? 2 :
                               (PN == 5) ? 2 :
                               (PN == 6) ? 4 :
                               (PN == 7) ? 0 :
                               (PN == 9) ? 4 :
                               (PN == 11) ? 8 :
                               (PN == 15) ? 0 :
                               (PN == 17) ? 2 :
                               (PN == 23) ? 17 :
                               (PN == 31) ? 27 :
                               (PN == 32) ? 21 :
                               (PN == 36) ? 24 :
                               (PN == 41) ? 37 : 0;

    logic [PN - 1 : 0] lfsr; 

    always_comb begin
        o_prbs   = lfsr[PN - 1];
        o_prbs_n = ~lfsr[PN - 1];
    end                       

    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;

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

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

    initial begin
        forever begin
            #( CLOCK_PERIOD / 2 ) clk = !clk;
        end 
    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

        $finish;
    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) ? 7 :
                                   (PN == 11) ? 10 :
                                   (PN == 15) ? 14 :
                                   (PN == 17) ? 16 :
                                   (PN == 23) ? 22 :
                                   (PN == 31) ? 30 :
                                   (PN == 32) ? 31 :
                                   (PN == 36) ? 35 :
                                   (PN == 41) ? 40 : 0;
    
        localparam integer TAP_0 = (PN == 3) ? 0 :
                                   (PN == 4) ? 2 :
                                   (PN == 5) ? 2 :
                                   (PN == 6) ? 4 :
                                   (PN == 7) ? 0 :
                                   (PN == 9) ? 4 :
                                   (PN == 11) ? 8 :
                                   (PN == 15) ? 0 :
                                   (PN == 17) ? 2 :
                                   (PN == 23) ? 17 :
                                   (PN == 31) ? 27 :
                                   (PN == 32) ? 21 :
                                   (PN == 36) ? 24 :
                                   (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;

    logic                 clk     = '0;
    logic                 s_rst_n = '0;
    logic                 en      = '0;

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

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

    initial begin
        forever begin
            #( CLOCK_PERIOD / 2 ) clk = !clk;
        end 
    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

        $finish;
    end
endmodule
\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

I get compile errors on 2 different simulators (Cadence and Synopsys) from these 2 lines in the testbench:

logic prbs    = '0;
logic prbs_n  = '0;

Since you connect those signals to the module instance output ports, you should not set them to any value in the declaration lines. Your code has multiple drivers of the signals:

  1. The module instance output port.
  2. The continuous driver of your logic declaration.

Just declare the signals without an assignment:

logic prbs  ;
logic prbs_n;

Aside from that, I do not see any other functional problems with your code. The layout follows good coding practices, and you make good use of parameters.

In the testbench, you could take advantage of 2-state signal types. This could lead to better simulation performance, and it also better conveys the intent of your code. Since there is no need to drive x or z for your instance inputs, you can use bit instead of logic:

bit clk;
bit s_rst_n;
bit en;

Since all 2-state types default to 0, there is no need to explicitly initialize them to 0 in the declaration or elsewhere. This is purely a matter of convenience and preference. The situation is similar for your testbench counter:

int tick;

You could simplify your clock using a single line of code:

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

In your design, you could simplify the combinational logic as follows. Change:

always_comb begin
    o_prbs   = lfsr[PN - 1];
    o_prbs_n = ~lfsr[PN - 1];
end                       

to the equivalent:

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

This avoids repeating the lfsr expression.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks for your comments, I really appreciate it. Based on the comments , the code has been fixed. I commit the code to a new question. Once again thank you! \$\endgroup\$
    – Drakonof
    Commented Dec 6, 2021 at 7:34
  • 1
    \$\begingroup\$ @Drakonof: You're welcome. \$\endgroup\$
    – toolic
    Commented Dec 6, 2021 at 17:03

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.