Search code examples
verilogsystem-veriloghdl

Systemverilog recursion update value for next stage


I am trying to create a recursive logic in Systemverilog but I seem to be missing the right logic to carry the output of one iteration to the next.

Here is an example of the problem:

parameter WIDTH=4;
module test_ckt #(parameter WIDTH = 4)(CK, K, Z);

input CK;
input [WIDTH-1:0] K;
output reg Z;

wire [WIDTH/2-1:0] tt;
wire [WIDTH-1:0] tempin;
assign tempin  = K;
genvar i,j;
generate
    for (j=$clog2(WIDTH); j>0; j=j-1)
    begin: outer
        wire [(2**(j-1))-1:0] tt;
        for (i=(2**j)-1; i>0; i=i-2)
        begin
            glitchy_ckt #(.WIDTH(1)) gckt (tempin[i:i], tempin[(i-1):i-1], tt[((i+1)/2)-1]);
        end
        // How do I save the value for the next iteration?
        wire [(2**(j-1))-1:0] tempin;
        assign outer[j].tempin = outer[j].tt;
    end
endgenerate

always @(posedge CK)
begin
    // How do I use the final output here?
    Z <= tt[0];
end

endmodule

module glitchy_ckt #(parameter WIDTH = 1)(A1, B1, Z1);
input [WIDTH-1:0] A1,B1;
output Z1;
assign Z1 = ~A1[0] ^ B1[0];
endmodule

Expected topology:

                S1              S2
K3--<inv>--|==
           |XOR]---<inv>----|
K2---------|==              |
                            |==
   <--gckt--->              |XOR]
                            |==
K1--<inv>--|==              |
           |XOR]------------|
K0---------|==    <-----gckt---->

Example input and expected outputs:

Expected output:

A - 1010
    ----
 S1  0 0 <- j=2 and i=3,1.
 S2    1 <- j=1 and i=1.

Actual output:

A - 1010
    ----
 S1  0 0 <- j=2 and i=3,1.
 S2    0 <- j=1 and i=1. Here, because tempin is not updated, inputs are same as (j=2 & i=1).

Test-bench:

`timescale 1 ps / 1 ps
`include "test_ckt.v"

module mytb;

reg CK;
reg [WIDTH-1:0] A;
wire Z;

test_ckt #(.WIDTH(WIDTH)) dut(.CK(CK), .K(A), .Z(Z));

always #200 CK = ~CK;
integer i;
initial begin
    $display($time, "Starting simulation");
    #0 CK = 0;
    A = 4'b1010;
    #500 $finish;
end

initial begin
    //dump waveform
    $dumpfile("test_ckt.vcd");
    $dumpvars(0,dut);
end

endmodule

How do I make sure that tempin and tt get updated as I go from one stage to the next.


Solution

  • Your code does not have any recursion in it. You were trying to solve it using loops, but generate blocks are very limited constructs and, for example, you cannot access parameters defined in other generate iterations (but you can access variables or module instances).

    So, the idea is to use a real recursive instantiation of the module. In the following implementation the module rec is the one which is instantiated recursively. It actually builds the hierarchy from your example (I hope correctly). Since you tagged it as system verilog, I used the system verilog syntax.

    module rec#(WIDTH=1) (input logic [WIDTH-1:0]source, output logic result);
       
       if (WIDTH <= 2) begin
         always_comb
           result = source; // << generating the result and exiting recursion.
       end
       else begin:blk
          localparam REC_WDT = WIDTH / 2;
          logic [REC_WDT-1:0] newSource; 
       
          always_comb // << calculation of your expression
            for (int i = 0; i < REC_WDT; i++)
              newSource[i] = source[i*2] ^ ~source[(i*2)+1]; 
       
          rec #(REC_WDT) rec(newSource, result); // << recursive instantiation with WIDTH/2
       end // else: !if(WIDTH <= 2)
    
       initial $display("%m: W=%0d", WIDTH);  // just my testing leftover
    endmodule
    

    The module is instantiated first time from the test_ckt:

    module test_ckt #(parameter WIDTH = 4)(input logic CK, input logic [WIDTH-1:0] K, output logic Z);
       logic                 result;
       rec#(WIDTH) rec(K, result); // instantiate first time )(top)
       always_ff @(posedge CK)
         Z <= result;  // assign the results  
    endmodule // test_ckt
    

    And your testbench, a bit changed:

    module mytb;
    
       reg CK;
       reg [WIDTH-1:0] A;
       wire        Z;
    
       test_ckt #(.WIDTH(WIDTH)) dut(.CK(CK), .K(A), .Z(Z));
    
       always #200 CK = ~CK;
       integer     i;
       initial begin
          $display($time, "Starting simulation");
          CK = 0;
          A = 4'b1010;
          #500 
          A = 4'b1000;
          #500 $finish;
       end
    
       initial begin
          $monitor("Z=%b", Z);     
       end
    endmodule // mytb
    

    Use of $display/$monitor is more convenient than dumping traces for such small examples.

    I did not do much testing of what I created, so there could be issues, but you can get basic ideas from it in any case. I assume it should work with any WIDTH which is power of 2.