与给定的测试平台相比,Verilog Perceptron 流水线模块输出落后一个时钟

问题描述 投票:0回答:1

我为我需要完成的实验室设计了一个单层感知器。它按预期完美运行,与给我们的测试平台相比,我收到了预期的输出。唯一的问题是,与测试平台相比,输出晚了一个时钟,这导致出现错误日志消息。我已经尝试了很多方法来查找出错的地方,因此非常感谢您的帮助。下面将给出 Verilog 和测试平台代码。

`timescale 1ns / 1ps

module perceptron(
    input rst_n,
    input clk,
    input signed [7:0] x1,
    input signed [7:0] x2,
    input valid_in,
    output reg y,
    output reg y_valid
    );
    
   reg signed [7:0] w1 = 8'sb00000010; //weight 1
   reg signed [7:0] w2 = 8'sb11111110; //weight 2
   reg signed [7:0] b = 8'sb11111101; //bias
   
   //stage 1: p1 = w1*x1 and p2 = w2*x2.
   //stage 2: s = p1 + p2 + b
   
   reg valid_pipe;
   reg signed [15:0] p1;
   reg signed [15:0] p2;
   reg signed [15:0] s;
   
   
   always @(posedge clk) begin 
        if (!rst_n) begin
            y <= 1'b0;
//            y_valid <= 1'b0;
//            w1 <= 8'b0;
//            w2 <= 8'b0;
//            b <= 8'b0;
            valid_pipe <= 1'b0;
//            p1 <= 16'b0;
//            p2 <= 16'b0;
//            s <= 16'b0;
                        
        end
        
        else begin
            //->first pipe cycle
            valid_pipe <= valid_in;
            p1 <= w1 * x1;
            p2 <= w2 * x2;
            //valid_pipe <= valid_in;
            
            //-->second pipe cycle
            //y_valid <= valid_pipe;
            s <= p1 + p2 + b;
            y <= (s >= 0) ? 1'b1 : 1'b0;
            y_valid <= valid_pipe;

            
            end 
          
        end
   
endmodule

测试台:

`timescale 1ns / 1ps

module tb_perceptron(
    );
    
    
reg clk;
reg rst_n;
    wire signed [7:0] x1;
    wire signed [7:0] x2;
    wire y;
    wire tb_y;
    reg valid_in; 
    wire y_valid;
    
    reg [3:0] addr_in, addr_y;
    
    perceptron dut (.*);   

    rom #( .addr_width (4), .data_width (8), .init_file("x1.dat") )
 x1_mem(                                                                   
.addr(addr_in),                                         
.data (x1)                                         
);    

    rom #( .addr_width (4), .data_width (8), .init_file("x2.dat") )
 x2_mem(                                                                   
.addr(addr_in),                                         
.data (x2)                                         
);    

    rom #( .addr_width (4), .data_width (1), .init_file("y.dat") )
 y_mem(                                                                   
.addr(addr_y),                                         
.data (tb_y)                                         
);    
    
    always #5 clk = ~clk;
    
    //integer file_handle;
    
    
    
    initial
        begin
        //file_handle = $fopen("output.txt", "w");
            //i edit here for delay for check. 
            //#5;
            clk = 0;
            rst_n = 1'h0;
            valid_in = 0;
            
            #73 rst_n = 1'h1;
            #17;
            addr_in = 4'h0;
            #20;
            valid_in = 1;
           
            for (integer i = 0; i < 16; i = i + 1)
            begin
                #10;
                //assert (y == tb_y);
                addr_in = addr_in + 1;
            end 
            valid_in = 0;
            #50;
            //$fclose(file_handle);                  
        end
        
        always_ff @ (posedge clk)
            begin
                if (!rst_n)
                    begin
                        addr_y <= #0.1 4'h0;
                    end
                else if (y_valid)
                    begin
                     //$fwrite(file_handle, "%b\n", y);
                     assert (y == tb_y)
                     else $error("y not equal to tb_y");
                        addr_y <= #0.1 addr_y + 1;
                    end
            end
    
    
endmodule

ROM文件:

`timescale 1ns/1ps

module rom #(addr_width = 4, data_width = 4, string init_file = "dummy.dat" )
(
input [addr_width-1:0]addr,
output [data_width-1:0]data
);

reg [data_width-1:0] mem [ (1<<addr_width)-1:0];

initial
    begin
        $readmemb (init_file, mem);
        /*for (integer i = 0; i < 16; i = i + 1)
        begin 
            mem[i] = 4'hf - i;
        end*/
    end
    
assign data = mem[addr];


endmodule
x1 dat: 
00000101
11111100
00000000
00001100
11111100
00000100
00000100
00000100
11111100
11111100
00100000
00000100
11111100
11111100
00000000
00000100


x2 dat:
11111010
00000100
00000000
11111100
00000100
11111100
11111100
00000100
11111100
00000100
11111100
11111100
00000000
11110000
00000100
11111100


y dat: 
1
0
0
1
0
1
1
0
0
0
1
1
0
1
0
1

我尝试过在不同阶段移动 valid_in 并断言 y_valid 。添加手动延迟并没有多大帮助。我可能在流水线/顺序逻辑中有错误,所以这也可能是原因。我正在使用 AMD vivado。

SIM 截图:

verilog system-verilog fpga vivado
1个回答
0
投票

模块的 else 块中的

y
y_valid
的管道阶段不同。

else begin

    valid_pipe <= valid_in;
    p1 <= w1 * x1;
    p2 <= w2 * x2;

    s <= p1 + p2 + b;
    y <= (s >= 0) ? 1'b1 : 1'b0;
    y_valid <= valid_pipe;
end 

在上面的简化片段中,您可以看到

y
需要3个周期(x1-->p1-->s-->y)才能输出,而
y_valid
只需要2个周期(valid_in-->valid_pipe) -->y_valid)。

在测试台中,您使用

y_valid
来捕获
y
y_tb

解决方案是删除一个管道形式

y
路径,或者第二种解决方案是在
y_valid
路径中添加一个管道阶段。

else begin

    valid_pipe <= valid_in;
    p1 <= w1 * x1;
    p2 <= w2 * x2;

    s <= p1 + p2 + b;
    y <= (s >= 0) ? 1'b1 : 1'b0;
    y_valid_reg <= valid_pipe; //Additional Pipeline stage on valid
    y_valid <= y_valid_reg ;
end 

也在顶部添加

reg y_valid_reg;

© www.soinside.com 2019 - 2024. All rights reserved.