1
\$\begingroup\$

I am trying to build NN in system verilog, but I am facing a problem with neuron implementaion, as it only displays 'x'.

//Neuron Code

module DenseNeuron # (parameter noOfInputs=20, Width=8)
    (
    input clk=0,reset,
    input signed [Width-1:0]  inputX [0:noOfInputs-1],
    input signed [Width-1:0] weightX[0:noOfInputs-1],
    input signed[Width-1:0]  Biases,
    output  reg signed [2*Width-1:0] NeuronOutput
    );
    reg [2*Width-1:0] sum1,sum2,sum3,sum4,sum5;
    reg [2*Width-1:0] sumFOrActivation,mulsum;
    reg [2*Width-1:0] intermediateSum;
    reg [2*Width-1:0] mul [0:noOfInputs-1];

    /* ReLU reluInstance
        (
        .dataInput(sumFOrActivation),
        .dataOutput(NeuronOutput)
        ); */

always @(posedge clk or negedge reset) begin
    if (~reset) 
    begin
        
        sumFOrActivation={2*Width-1{1'b0}}; 
    end
    else
    begin
/*      for ( int i=0;i<noOfInputs;i++) 
            begin 
                mul[i] = inputX[i]*weightX[i];
                mulsum = mul[i];
                intermediateSum = sum;
            end
        
        
        sumFOrActivation<=sum+Biases;
        
        NeuronOutput = sumFOrActivation; */ 
        mul[0] = inputX[0]*weightX[0];
        mul[1] = inputX[1]*weightX[1];
        mul[2] = inputX[2]*weightX[2];
        mul[3] = inputX[3]*weightX[3];
        mul[4] = inputX[4]*weightX[4];
        
        mul[5] = inputX[5]*weightX[5];
        mul[6] = inputX[6]*weightX[6];
        mul[7] = inputX[7]*weightX[7];
        mul[8] = inputX[8]*weightX[8];
        mul[9] = inputX[9]*weightX[9];
        
        mul[10] = inputX[10]*weightX[10];
        mul[11] = inputX[11]*weightX[11];
        mul[12] = inputX[12]*weightX[12];
        mul[13] = inputX[13]*weightX[13];
        mul[14] = inputX[14]*weightX[14];
        
        mul[15] = inputX[15]*weightX[15];
        mul[16] = inputX[16]*weightX[16];
        mul[17] = inputX[17]*weightX[17];
        mul[18] = inputX[18]*weightX[18];
        mul[19] = inputX[19]*weightX[19];
    
        sum1 = mul[0]+mul[1]+mul[2]+mul[3]+mul[4];
        sum2= mul[5]+mul[6]+mul[7]+mul[8]+mul[9];
        sum3=mul[10]+mul[11]+mul[12]+mul[13]+mul[14];
        sum4=mul[15]+mul[16]+mul[17]+mul[18]+mul[19];
    
    sumFOrActivation=sum1+sum2+sum3+sum4+Biases;

    
    end
    NeuronOutput <= sumFOrActivation;
    
end

endmodule
///////////////////
//Neuron TB
`timescale 1ns/1ns

module DenseNeuron_TB  # (parameter noOfInputs=20, Width=8,BufferLength=960);
    reg clk=0;
    reg reset=1;
    reg signed [Width-1:0]  inputX [0:noOfInputs-1];
    reg signed [Width-1:0]  weightX[0:noOfInputs-1];
    reg signed [Width-1:0]   Biases;
    wire  signed [2*Width-1:0]  NeuronOutput;
    reg  signed [2*Width-1:0] sum=0;
    ///Bufers to read input and weights and bias values
    reg signed [Width-1:0]  BufferweightX[0:BufferLength-1];
    integer fd,temp;
    //instantiate a neuron
    
        DenseNeuron NeuronInstanse 
    (   
        .clk(clk),
        .reset(reset),
        .inputX(inputX),
        .weightX(weightX),
        .Biases(Biases),
        .NeuronOutput(NeuronOutput) 
    );
    

    // Generate the Clock
    always begin 
        #200 clk =  ~clk;
         
        
    end
    initial begin
        //read from file
        //$readmemh = ("./MEM/\weightsLayer1.txt",BufferweightX);
        //open the weight file and read the values
        
        $display("Initial begin");
        fd=$fopen("weightsLayer1.txt","r");

        for(int i=0;i<noOfInputs;i++)
        begin
            temp=$fscanf(fd, "%d\n",BufferweightX[i]);
        
            inputX[i]=-1;
            #1 $display(inputX[i],BufferweightX[i]);
            sum=sum+(BufferweightX[i]*inputX[i]);

        end
        $strobe(sum+1);
        Biases=1;
        
    $fclose(fd);
    #2 $display(NeuronOutput);
    #50 $stop();
    end
    
            

    
endmodule//NeuronTB end
////////////////////////
//OUTPUT
# Initial begin
#   -1  -8
#   -1  -1
#   -1   9
#   -1  -5
#   -1  -2
#   -1  -1
#   -1  -2
#   -1   6
#   -1  -5
#   -1 -12
#   -1   5
#   -1   0
#   -1 -14
#   -1   2
#   -1  10
#   -1  -6
#   -1  -3
#   -1  -4
#   -1   6
#   -1   9
#          17
#      x
##Content of weightLayer1.txt##
-8
-1
9
-5
-2
-1
-2
6
-5
-12
5
0
-14
2
10
-6
-3
-4
6
9
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Your testbench never sets reset to 0. You call $strobe(NeuronOutput) at time 0. It should be after the first clock edge. Use a nonblocking assignment NeuronOutput <= sumFOrActivation; for any sequential assignments to avoid race conditions in simulation. And put that outside the if/else branches. \$\endgroup\$
    – dave_59
    Commented Dec 1, 2022 at 22:05
  • \$\begingroup\$ I did what you mentioned, and it is the same. \$\endgroup\$ Commented Dec 1, 2022 at 22:14
  • 2
    \$\begingroup\$ Just an architectural remark: I know this looks a lot like it's a very nice and parallel approach. Let's hope you have as many DSP slices on FPGA (assuming the underlying design is targeting an FPGA) as you have parallel multiplications and that your toolchain automatically infers their use from you using * all over the place. Implementing a multiplier in LUTs needs a massive amount of FPGA resources, if it's done in a combinatorial way. Also, not sure why you care about partial summation of numbers in your sum1,sum2.. if you do a full 8×8 bit multiplication. \$\endgroup\$ Commented Dec 2, 2022 at 9:11

1 Answer 1

2
\$\begingroup\$

Using $display is an inefficient way to debug simulations. You need another approach: debugging using waveforms.

First, you need to dump a waveform database, such as VCD. Then you need to learn to use a waveform debugger tool, which most simulators have.

Using waveforms, I can quickly see that the weightX input to the DenseNeuron module is always unknown (x). You need to decide how you want to drive that module input.

Another issue is that the reset signal is always 1 in the testbench. Since the DenseNeuron module uses an active-low reset, this means the logic is not reset properly. You should set reset to 0 at time 0, then after a delay, set it to 1. For example:

reg reset;
initial begin
    reset = 0;
    #500 reset = 1;
end

You need to coordinate the timing of reset with the other input signals, of course.

Another problem is that you $stop the simulation before the 1st clock edge. You should increase the delay so that there are several clock cycles. For example, change:

#50 $stop();

to:

#5000 $stop();
\$\endgroup\$

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.