Nonblocking assignment 在 Vivado 仿真中立即分配
Nonblocking assignment assigns immediately in Vivado simulation
在实现 cordic 算法时,我的非阻塞赋值是立即赋值变量,而不是在一个时钟周期之后赋值。我不知道为什么。我的代码或 Vivado 设计套件有问题吗?
在我的代码中,我需要用 z[0] <= angle
分配 z[0]
。但是,在我的 Vivado 模拟器中,z[0]
只是与 angle
.
同时变化
环境:Windows10,Vivado 2020.2 的默认模拟器
`timescale 1ns/10ps
module cordic_tb #(
)(
);
localparam real PI = 3.1415926535;
logic clk,rst_n,en;
logic signed [31:0] step=integer'((3/180.0)*(2.0**31.0));
logic signed [31:0] angle;
logic signed [31:0] cosine,sine;
initial begin
clk=0;
en=1;
forever #50 clk=~clk;
end
initial begin
rst_n=0;
#100 rst_n=1;
end
always_ff @( posedge clk ) begin
if(!rst_n) angle='0;
else if(en) angle=angle+step;
end
cordic #()
theCordicInst(clk,rst_n,en,angle,cosine,sine);
//test 45°
/*
initial begin
clk=0;
rst_n=1;
en=1;
repeat(62)
begin
#5 clk = ~clk;
if(cosine || sine)
begin
$display((real'(cosine))*(2.0**(-31.0)));
$display((real'(sine))*(2.0**(-31.0)));
$display($sin((45.0/180.0)*3.1415926535));
end
end
end
Cordic #()
theCordicInst(clk,rst_n,en,32'h20000000,cosine,sine);
*/
endmodule
module cordic #(
parameter integer DW = 32,
parameter real K = 0.6
)(
input wire clk,rst_n,en,
input wire signed [DW-1:0] angle,
output logic signed [DW-1:0] cosine,sine
);
wire signed [DW-1:0] atan_table[0:29];
logic signed [DW-1:0] x_start,y_start;
logic signed [DW-1:0] x[0:30],y[0:30],z[0:30];
wire [1:0] domain;
assign atan_table[00]=32'b00100000000000000000000000000000;
assign atan_table[01]=32'b00010010111001000000010100011101;
assign atan_table[02]=32'b00001001111110110011100001011011;
assign atan_table[03]=32'b00000101000100010001000111010100;
assign atan_table[04]=32'b00000010100010110000110101000011;
assign atan_table[05]=32'b00000001010001011101011111100001;
assign atan_table[06]=32'b00000000101000101111011000011110;
assign atan_table[07]=32'b00000000010100010111110001010101;
assign atan_table[08]=32'b00000000001010001011111001010011;
assign atan_table[09]=32'b00000000000101000101111100101110;
assign atan_table[10]=32'b00000000000010100010111110011000;
assign atan_table[11]=32'b00000000000001010001011111001100;
assign atan_table[12]=32'b00000000000000101000101111100110;
assign atan_table[13]=32'b00000000000000010100010111110011;
assign atan_table[14]=32'b00000000000000001010001011111001;
assign atan_table[15]=32'b00000000000000000101000101111100;
assign atan_table[16]=32'b00000000000000000010100010111110;
assign atan_table[17]=32'b00000000000000000001010001011111;
assign atan_table[18]=32'b00000000000000000000101000101111;
assign atan_table[19]=32'b00000000000000000000010100010111;
assign atan_table[20]=32'b00000000000000000000001010001011;
assign atan_table[21]=32'b00000000000000000000000101000101;
assign atan_table[22]=32'b00000000000000000000000010100010;
assign atan_table[23]=32'b00000000000000000000000001010001;
assign atan_table[24]=32'b00000000000000000000000000101000;
assign atan_table[25]=32'b00000000000000000000000000010100;
assign atan_table[26]=32'b00000000000000000000000000001010;
assign atan_table[27]=32'b00000000000000000000000000000101;
assign atan_table[28]=32'b00000000000000000000000000000010;
assign atan_table[29]=32'b00000000000000000000000000000001;
assign x_start= integer'(K*(2.0**(DW-1.0)));
assign y_start='0;
assign domain=angle[31:30];
// convert angle to -pi/2 ~ pi/2, xy start point changes too
always_ff @( posedge clk ) begin
if(~rst_n)
begin
x[0] <= '0;
y[0] <= '0;
z[0] <= '0;
end
else if(en)
begin
case(domain)
2'b00,
2'b11:
begin
x[0] <= x_start;
y[0] <= y_start;
z[0] <= angle;
end
2'b01:
begin
x[0] <= -y_start;
y[0] <= x_start;
z[0] <= {2'b00,angle[29:0]};
end
2'b10:
begin
x[0] <= y_start;
y[0] <= -x_start;
z[0] <= {2'b11,angle[29:0]};
end
endcase
end
end
generate
for(genvar i=0;i<30;i=i+1)
begin:xyz
wire s=z[i][31];
wire signed [DW-1:0] x_shr=x[i]>>>i;
wire signed [DW-1:0] y_shr=y[i]>>>i;
always_ff @( posedge clk ) begin
if(~rst_n)
begin
x[i+1] <= '0;
y[i+1] <= '0;
z[i+1] <= '0;
end
else if(en)
begin
x[i+1] <= s ? x[i]+y_shr : x[i]-y_shr;
y[i+1] <= s ? y[i]-x_shr : y[i]+x_shr;
z[i+1] <= s ? z[i]+atan_table[i] : z[i]-atan_table[i];
end
end
end
endgenerate
assign cosine=x[30];
assign sine =y[30];
endmodule
问题出在测试平台代码中。在测试台中也使用非阻塞分配。变化:
always_ff @( posedge clk ) begin
if(!rst_n) angle='0;
else if(en) angle=angle+step;
end
至:
always_ff @( posedge clk ) begin
if (!rst_n) angle <= '0;
else if (en) angle <= angle+step;
end
现在,z[0]
在angle
之后改变了一个时钟:
verilog 中的非阻塞 赋值 simulation 始终在current 时钟周期中分配数据.唯一的区别是它在所有阻塞分配完成后分配数据。
在你的例子中,你有两个陈述:
always_ff @( posedge clk ) begin
angle=angle+step;
end
和
always_ff @( posedge clk ) begin
z[0] <= angle;
在 'posedge clk' 你的案例中发生了两件事:
- 阻止分配立即更新角度
- 非阻塞分配计划更新到 z[0] 并使用上面更新的 angle 的值。
- 将值赋给 z[0].
verilog保证'blocking'先发生。因此,您的 z[0] 将遵循 angle.
如果您将第一个分配更改为非阻塞
always_ff @( posedge clk ) begin
angle<=angle+step;
end
将发生以下情况:
- 非阻塞分配计划在所有阻塞分配后更新为 angle。它的值不会立即改变。
- 非阻塞分配计划更新为 z[0] 但它将使用 old 值 angle 尚未更新。
- 更新 angle 和 z[0] 的值,使用它们在 old 中评估的值阻塞分配区域。
现在,z[0] 包含 angle 的旧值,而 angle 是更新为新值。这模拟了链式触发器的行为,这就是在所有状态设备中使用 nbas 的原因。
在实现 cordic 算法时,我的非阻塞赋值是立即赋值变量,而不是在一个时钟周期之后赋值。我不知道为什么。我的代码或 Vivado 设计套件有问题吗?
在我的代码中,我需要用 z[0] <= angle
分配 z[0]
。但是,在我的 Vivado 模拟器中,z[0]
只是与 angle
.
环境:Windows10,Vivado 2020.2 的默认模拟器
`timescale 1ns/10ps
module cordic_tb #(
)(
);
localparam real PI = 3.1415926535;
logic clk,rst_n,en;
logic signed [31:0] step=integer'((3/180.0)*(2.0**31.0));
logic signed [31:0] angle;
logic signed [31:0] cosine,sine;
initial begin
clk=0;
en=1;
forever #50 clk=~clk;
end
initial begin
rst_n=0;
#100 rst_n=1;
end
always_ff @( posedge clk ) begin
if(!rst_n) angle='0;
else if(en) angle=angle+step;
end
cordic #()
theCordicInst(clk,rst_n,en,angle,cosine,sine);
//test 45°
/*
initial begin
clk=0;
rst_n=1;
en=1;
repeat(62)
begin
#5 clk = ~clk;
if(cosine || sine)
begin
$display((real'(cosine))*(2.0**(-31.0)));
$display((real'(sine))*(2.0**(-31.0)));
$display($sin((45.0/180.0)*3.1415926535));
end
end
end
Cordic #()
theCordicInst(clk,rst_n,en,32'h20000000,cosine,sine);
*/
endmodule
module cordic #(
parameter integer DW = 32,
parameter real K = 0.6
)(
input wire clk,rst_n,en,
input wire signed [DW-1:0] angle,
output logic signed [DW-1:0] cosine,sine
);
wire signed [DW-1:0] atan_table[0:29];
logic signed [DW-1:0] x_start,y_start;
logic signed [DW-1:0] x[0:30],y[0:30],z[0:30];
wire [1:0] domain;
assign atan_table[00]=32'b00100000000000000000000000000000;
assign atan_table[01]=32'b00010010111001000000010100011101;
assign atan_table[02]=32'b00001001111110110011100001011011;
assign atan_table[03]=32'b00000101000100010001000111010100;
assign atan_table[04]=32'b00000010100010110000110101000011;
assign atan_table[05]=32'b00000001010001011101011111100001;
assign atan_table[06]=32'b00000000101000101111011000011110;
assign atan_table[07]=32'b00000000010100010111110001010101;
assign atan_table[08]=32'b00000000001010001011111001010011;
assign atan_table[09]=32'b00000000000101000101111100101110;
assign atan_table[10]=32'b00000000000010100010111110011000;
assign atan_table[11]=32'b00000000000001010001011111001100;
assign atan_table[12]=32'b00000000000000101000101111100110;
assign atan_table[13]=32'b00000000000000010100010111110011;
assign atan_table[14]=32'b00000000000000001010001011111001;
assign atan_table[15]=32'b00000000000000000101000101111100;
assign atan_table[16]=32'b00000000000000000010100010111110;
assign atan_table[17]=32'b00000000000000000001010001011111;
assign atan_table[18]=32'b00000000000000000000101000101111;
assign atan_table[19]=32'b00000000000000000000010100010111;
assign atan_table[20]=32'b00000000000000000000001010001011;
assign atan_table[21]=32'b00000000000000000000000101000101;
assign atan_table[22]=32'b00000000000000000000000010100010;
assign atan_table[23]=32'b00000000000000000000000001010001;
assign atan_table[24]=32'b00000000000000000000000000101000;
assign atan_table[25]=32'b00000000000000000000000000010100;
assign atan_table[26]=32'b00000000000000000000000000001010;
assign atan_table[27]=32'b00000000000000000000000000000101;
assign atan_table[28]=32'b00000000000000000000000000000010;
assign atan_table[29]=32'b00000000000000000000000000000001;
assign x_start= integer'(K*(2.0**(DW-1.0)));
assign y_start='0;
assign domain=angle[31:30];
// convert angle to -pi/2 ~ pi/2, xy start point changes too
always_ff @( posedge clk ) begin
if(~rst_n)
begin
x[0] <= '0;
y[0] <= '0;
z[0] <= '0;
end
else if(en)
begin
case(domain)
2'b00,
2'b11:
begin
x[0] <= x_start;
y[0] <= y_start;
z[0] <= angle;
end
2'b01:
begin
x[0] <= -y_start;
y[0] <= x_start;
z[0] <= {2'b00,angle[29:0]};
end
2'b10:
begin
x[0] <= y_start;
y[0] <= -x_start;
z[0] <= {2'b11,angle[29:0]};
end
endcase
end
end
generate
for(genvar i=0;i<30;i=i+1)
begin:xyz
wire s=z[i][31];
wire signed [DW-1:0] x_shr=x[i]>>>i;
wire signed [DW-1:0] y_shr=y[i]>>>i;
always_ff @( posedge clk ) begin
if(~rst_n)
begin
x[i+1] <= '0;
y[i+1] <= '0;
z[i+1] <= '0;
end
else if(en)
begin
x[i+1] <= s ? x[i]+y_shr : x[i]-y_shr;
y[i+1] <= s ? y[i]-x_shr : y[i]+x_shr;
z[i+1] <= s ? z[i]+atan_table[i] : z[i]-atan_table[i];
end
end
end
endgenerate
assign cosine=x[30];
assign sine =y[30];
endmodule
问题出在测试平台代码中。在测试台中也使用非阻塞分配。变化:
always_ff @( posedge clk ) begin
if(!rst_n) angle='0;
else if(en) angle=angle+step;
end
至:
always_ff @( posedge clk ) begin
if (!rst_n) angle <= '0;
else if (en) angle <= angle+step;
end
现在,z[0]
在angle
之后改变了一个时钟:
verilog 中的非阻塞 赋值 simulation 始终在current 时钟周期中分配数据.唯一的区别是它在所有阻塞分配完成后分配数据。
在你的例子中,你有两个陈述:
always_ff @( posedge clk ) begin
angle=angle+step;
end
和
always_ff @( posedge clk ) begin
z[0] <= angle;
在 'posedge clk' 你的案例中发生了两件事:
- 阻止分配立即更新角度
- 非阻塞分配计划更新到 z[0] 并使用上面更新的 angle 的值。
- 将值赋给 z[0].
verilog保证'blocking'先发生。因此,您的 z[0] 将遵循 angle.
如果您将第一个分配更改为非阻塞
always_ff @( posedge clk ) begin
angle<=angle+step;
end
将发生以下情况:
- 非阻塞分配计划在所有阻塞分配后更新为 angle。它的值不会立即改变。
- 非阻塞分配计划更新为 z[0] 但它将使用 old 值 angle 尚未更新。
- 更新 angle 和 z[0] 的值,使用它们在 old 中评估的值阻塞分配区域。
现在,z[0] 包含 angle 的旧值,而 angle 是更新为新值。这模拟了链式触发器的行为,这就是在所有状态设备中使用 nbas 的原因。