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