Verilog:在始终块中等待模块逻辑评估



我想在一个始终块中使用另一个模块的输出。目前,使此代码工作的唯一方法是在pi_in分配之后添加#1,以便有足够的时间让pi完成。

模块pLayer.v:的相关部分

Pi pi(pi_in,pi_out);
always @(*)
begin
for(i=0; i<constants.nSBox; i++) begin
for(j=0; j<8; j++) begin
x               = (state_value[(constants.nSBox-1)-i]>>j) & 1'b1;
pi_in           = 8*i+j;#1; /* wait for pi to finish */
PermutedBitNo   = pi_out;
y               = PermutedBitNo>>3;
tmp[(constants.nSBox-1)-y] ^= x<<(PermutedBitNo-8*y);
end
end
state_out = tmp;
end

Modllue Pi.v

`include "constants.v" 
module Pi(in, out);
input  [31:0]   in;
output [31:0]   out;
reg [31:0] out;
always @* begin
if (in != constants.nBits-1) begin
out = (in*constants.nBits/4)%(constants.nBits-1);
end else begin
out = constants.nBits-1;
end
end
endmodule

在最终实现中不应使用延迟,那么是否有其他方法不使用#1

本质上,我希望只有在pi模块以pi_In(=8*i+j)作为输入完成其工作后,才评估PermutedBitNo=pi_out。我怎样才能挡住这条线直到Pi完成?

我必须用时钟吗?如果是这样的话,请给我一个提示。

更新:

根据Krouitch的建议,我修改了我的模块。这是更新版本:

来自pLayer.v:

Pi pi(.clk (clk),
.rst (rst),
.in  (pi_in),
.out (pi_out));
counter c_i (clk, rst, stp_i, lmt_i, i);
counter c_j (clk, rst, stp_j, lmt_j, j);
always @(posedge clk)
begin
if (rst) begin
state_out = 0;
end else begin
if (c_j.count == lmt_j) begin
stp_i = 1;
end else begin
stp_i = 0;
end
// here, the logic starts
x               = (state_value[(constants.nSBox-1)-i]>>j) & 1'b1;
pi_in           = 8*i+j;
PermutedBitNo   = pi_out;
y               = PermutedBitNo>>3;
tmp[(constants.nSBox-1)-y] ^= x<<(PermutedBitNo-8*y);
// at end
if (i == lmt_i-1)
if (j == lmt_j) begin
state_out = tmp;
end
end
end
endmodule
module counter(
input  wire clk,
input  wire rst,
input  wire stp,
input  wire [32:0] lmt,
output reg  [32:0] count
);
always@(posedge clk or posedge rst)
if(rst)
count <= 0;
else if (count >= lmt)
count <= 0;
else if (stp)
count <= count + 1;
endmodule

来自Pi.v:

always @* begin
if (rst == 1'b1) begin
out_comb = 0;
end
if (in != constants.nBits-1) begin
out_comb = (in*constants.nBits/4)%(constants.nBits-1);
end else begin
out_comb = constants.nBits-1;
end
end
always@(posedge clk) begin
if (rst)
out <= 0;
else
out <= out_comb;
end

这是一个不错的软件。。。

事实上,这种语言描述硬件是没有帮助的。

在verilog中,您所写的内容将在零时间内进行模拟。这意味着你在ij上的循环也将在零时间内完成。这就是为什么当你用#1强制循环等待1个时间单位时,你会看到一些东西。

所以是的,您必须使用时钟

为了使您的系统工作,您必须实现ij的计数器,正如我所看到的。

带复位的计数器同步计数器可以这样写:

`define SIZE 10
module counter(
input  wire clk,
input  wire rst_n,
output reg [`SIZE-1:0] count
);
always@(posedge clk or negedge rst_n)
if(~rst_n)
count <= `SIZE'd0;
else
count <= count + `SIZE'd1;
endmodule

指定仅在处理pi_in时对pi_out进行采样。在数字设计中,这意味着您希望在发送pi_in和读取pi_out之间等待一个时钟周期。

在我看来,最好的解决方案是使pi模块按顺序排列,然后将pi_out视为寄存器。

要做到这一点,我会做以下事情:

module Pi(in, out);
input           clk;
input  [31:0]   in;
output [31:0]   out;
reg [31:0]  out;
wire        clk;
wire [31:0] out_comb;
always @* begin
if (in != constants.nBits-1) begin
out_comb = (in*constants.nBits/4)%(constants.nBits-1);
end else begin
out_comb = constants.nBits-1;
end
end
always@(posedge clk)
out <= out_comb;
endmodule

如果您快速使用ij以及最后一个pi模块的计数器,则会发生以下情况:

  1. 在新的时钟周期,ij将发生变化-->pi_in将同时发生相应变化(在模拟中)
  2. 在下一个时钟周期,out_comb将存储在out中,然后您将在pi_in之后一个时钟循环获得pi_out的新值

编辑

首先,在编写(同步)进程时,我建议您逐个进程只处理1个寄存器。它将使您的代码更清晰,更易于理解/调试。

另一个技巧是将组合电路与顺序电路分开。它还将使您的代码更加清晰易懂。

如果我以我之前写的计数器为例,它看起来像:

`define SIZE 10
module counter(
input  wire clk,
input  wire rst_n,
output reg [`SIZE-1:0] count
);
//Two way to do the combinatorial function
//First one
wire [`SIZE-1:0] count_next;
assign count_next = count + `SIZE'd1; 
//Second one  
reg [`SIZE-1:0] count_next;
always@*
count_next = count + `SIZE'1d1;

always@(posedge clk or negedge rst_n)
if(~rst_n)
count <= `SIZE'd0;
else
count <= count_next;

endmodule

在这里,我明白了为什么您比预期多了一个周期,这是因为您在同步过程中放置了控制pi模块的组合电路。这意味着将发生以下情况:

  1. 将评估第一个clk正边缘ij
  2. 下一个周期,对pi_in进行评估
  3. 下一个周期,捕获pi_out

所以它需要2个周期是有道理的。

为了纠正这种情况,您应该去掉同步进程中的"逻辑"部分。正如你在评论中所说的那样,这是合乎逻辑的,所以它不应该在同步过程中。

希望它能帮助

最新更新