第3章 流水操作和建模
verilog的流水线操作效率高,速度快,但其存在两方面缺点:
1 步骤之间存在无法预测的潜伏期;
2 串并联的流水线操作中,数据流都只有一个前向方向,诸如我们涉及到的控制和驱动领域,就不适合。而繁杂的数据处理和算法,比较适合流水线操作,这也是为什么许多AI项目采用硬件加速器的原因之一。比如CNN的卷积运算就可以通过FPGA来实现加速。
由于流水线操作不适用于控制工程中的原因,本文将不再对流水线技术有过多的研究。
第四章 模块的沟通
目标:利用Start_Sig和Done_Sig信号实现Verilog的模块放顺序操作。
1 探索Start_Sig和Done_Sig的协调性
Start_Sig----顺序操作中的函数调用----启动信号
Done_Sig----顺序操作中的“函数返回”----反馈信号
分析代码,对应查看时序仿真结果,分析即可,暂时感觉没什么难度。
流水操作是“永远向前走”的操作方式,因为它只需要将完成的工作丢给下一个步骤而仿顺序操作,为了与模块间进行沟通,则需要配备控制信号。所以流水操作在某种程度上比放顺序操作单调并且更难控制。
4.2 同步FIFO (我最感兴趣的Part)
同步FIFO实际上就是一个移位寄存器。以深度为4的寄存器为例。
下面分析写操作。当Write_Req为高时,寄存器第一个格子开始依次读入A,B,C,D,count寄存器的值会从1递增到4.如图1所示。条件限制count<4. 所以T5时刻的E就无法写入到寄存器中。count的值仍旧为4.
图1 T4时刻,ABCD依次被存入4个寄存器的情况
接下来是读操作。假设从T6开始,Read_Req一直被拉高,T6的未来是A被读出,count的值-1变成3. 一直到T9时,由于T8的决定,寄存器的第二个格子C已经被输出,还剩下一个D。T9会重复读操作,在T9的未来,数据D会被读出,同时count的值会递减为0.效果如图2所示。
图2 T10时寄存器及count状态
同步FIFO先进先出。
如果写操作和读操作同时发生在T8时刻。假设在T8时刻做的决定时同时读写操作,则count=count+1-1=count. 在T8的未来即T9时刻,E会被读入寄存器,C会get out, as shown in Fig.3.
图3 T8时刻同时读写操作结果
总结Conclusion
同步FIFO的操作有读、写和读写同时操作。典型的FIFO控制信号有:Write_Req, Read_Req, Full_Sig和Empty_Sig,“人如其名”,其功能与命名一致。但这些控制信号,仅适合异步FIFO。
同步FIFO的行为遵守“时间点”的概念,决定的结果是产生在该时间点的未来,参考值是该时间点的过去。
对同步FIFO的调用,绝对需要控制信号。
4.3 同步FIFO的实验代码
//学写仿真文件 以同步FIFO为例
module fifo_module_simulation();
reg clk;
reg rst_n;
reg Write_Req;
reg[7:0]FIFO_Write_Data;
reg Read_Req;
wire[7:0]FIFO_Read_Data;
wire Empty_Sig;
wire Full_Sig;
wire[7:0]SQ_rS1;
wire[7:0]SQ_rS2;
wire[7:0]SQ_rS3;
wire[7:0]SQ_rS4;
wire[2:0]SQ_Count;
//模块例化
fifo_module i1
(
.clk(clk),
.rst_n(rst_n),
.Wire_Req(Write_Req),
.FIFO_Write_Data(FIFO_Write_Data),
.Read_Req(Read_Req),
.FIFO_Read_Data(FIFO_Read_Data),
.Empty_Sig(Empty_Sig),
.Full_Sig(Full_Sig),
.SQ_rS1(SQ_rS1),
.SQ_rS2(SQ_rS2),
.SQ_rS3(SQ_rS3),
.SQ_rS4(SQ_rS4),
.SQ_Count(SQ_Count)
);
initial
begin //#10代表延迟了10个时间单位,以仿真中1ps为单位则该处延迟了10ps
rst_n=0;#10;rst_n=1;
clk=0;forever #10 clk=~clk; //每10ps反转一次时钟形成一个周期为20ps的方波
end
reg[3:0]i;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin //initial parameters
i<=4'd0;
Write_Req<=1'b0;
Read_Req<=1'b0;
FIFP_Write_Data<=8'd0;
end
else
case(i)
//写一个数据,每一个步骤消耗两个时钟,这里用俩个步骤表示
0:
begin Write_Req<=1'b1;FIFO_Write_Data<=8'd5;i<=i+1'b1;end
1:
begin Write_Req<=1'b0;i<=i+1'b1;end
//读一个数据 同时拉高读写使能信号
2:
begin Write_Req<=1'b1;FIFO_Write_Data<=8'd6;
Read_Req<=1'b1;
i<=i+1'b1;
end
3:
begin Write_Req<=1'b0;Read_Req<=1'b0;i<=i+1'b1;end
//再读一个数据,4,5时写时钟还是保持低电位
4: //拉高
begin Read_Req<=1'b1;i<=i+1'b1;end
5: //拉低
begin Read_Req<=1'b0;i<=i+1'b1;end
//这里每个步骤消耗一个时钟,功能与0-4步骤相似
6: //拉高写,并写入数据100
begin Write_Req<=1'b1;Read_Req<=1'b0;
FIFO_Write_Data<=8'd100;i<=i+1'b1;
end
7: //同时拉高读写,并写入33
begin Write_Req<=1'b1;Read_Req<=1'b1;
FIFO_Write_Data<=8'd33;i<=i+1'b1;
end
8: //拉高读
begin Write_Req<=1'b0;Read_Req<=1'b1;
i<=i+1'b1;
end
9: //这里读和写信号都是高的,但是上一个时钟FIFO已经空了,因此读在此处不成立,只有写。并写入数据99
begin Write_Req<=1'b1;FIFO_Write_Data<=8'd99;i<=i+1'b1;end
10: //这里只有读信号
begin Write_Req<=1'b0;Read_Req<=1'b1;i<=i+1'b1;end
11: //拉低读信号
begin Read_Req<=1'b0;i<=i+1'b1;end
//step 12与13用来测试使用Full_Sig信号和Empty_Sig信号对同步FIFO的结果
12:
if(Full_Sig)begin Write_Req<=1'b0;i<=i+1'b1;end
else begin Write_Req<=1'b1;Read_Req<=1'b0;FIFO_Write_Data<=FIFO_Write_Data;
13: //陆续从FIFO读取数据,直到Empty_Sig拉高为止。
if(Empty_Sig)begin Read_Req<=1'b0;i<=i+1'b1;end
else begin Write_Req<=1'b0;Read_Req<=1'b1;end
14:
begin i<=4'd14;end
endcase
endmodule
实验总结:
1 FIFO的调用需要控制信号,不然会出现程序忽略Empty_Sig,或者Full_Sig的情况
2 Full_Sig和Empty_Sig不适合同步FIFO的写操作
3 QuartusII产生的是异步FIFO,但可以兼容同步FIFO。
4 对同步FIFO的调用需要 控制信号
实验十八
该实验通过Left_Sig,用来反馈出FIFO目前的空格数目,用以取代Empty_Sig和Full_Sig.
//这中方法的缺陷是必须事先直到FIFO的深度否则无法计算left的值
module fifo_module_2
(
input clk,
input rst_n, //每个.v文件都有的时钟和复位信号,在ZYNQ中要特别注意分频
input Write_Req, //使能标记信号
input[7:0]FIFO_Write_Data,
input Read_Req, //使能标记信号
output[7:0]FIFO_Read_Data,
output[2:0]Left_Sig
);
parameter DEEP=3'd4;
reg[7:0]rShift[DEEP:0];
reg[2:0]Count;
reg[7:0]Data;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
rShift[0]<=8'd0;rShift[1]<=8'd0;rShift<=8'd0;
rShift[3]<=8'd0;rShift[4]<=8'd0;
Count<=3'd0;
Data<=8'd0;
end
else if(Read_Req&&Write_Req&&Count<DEEP&&Count>0)
begin
rShift[1]<=FIFO_Write_Data;
rShift[2]<=rShift[1];
rShift[3]<=rShift[2];
rShift[4]<=rShift[3];
Data<=rShift[Count];
end
else if(Write_Req&&Count<DEEP)
begin
rShift[1]<=FIFO_Write_Data;
rShift[2]<=rShift[1];
rShift[3]<=rShift[2];
rShift[4]<=rShift[3];
Count<=Count+1'b1;
end
else if(Read_Req&&Count>0)
begin
Data<=rShift[Count];
Count<=Count-1'b1;
end
assign FIFO_Read_Data=Data;
assign Left_Sig=DEEP-Count;
endmodule
fifo_module2_2.vt
module fifo_module2_2_simulation();
reg clk;
reg rst_n;
reg Write_Req;
reg[7:0]FIFO_Write_Data;
reg Read_Req;
wire[7:0]FIFO_Read_Data;
wire[2:0]Left_Sig;
//
fifo_module2_2 U1
(
.clk(clk),
.rst_n(rst_n),
.Write_Req(Write_Req),
.FIFO_Write_Data(FIFO_Write_Data),
.Read_Req(Read_Req),
.FIFO_Read_Data(FIFO_Read_Data),
.Left_Sig(Left_Sig)
);
initial
begin
rst_n=0;#10;rst_n=1;
clk=0;forever#10 clk=~clk;
end
//
reg[4:0]i;
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
i<=5'd0;
Write_Req<=1'b0;
Read_Req<=1'b0;
FIFO_Write_Data<=8'd0;
end
else
case(i)
//
0:
begin Write_Req<=1'b1;Read_Req<==1'b0;FIFO_Write_Data<=8'd1;i<=i+1'b1;end
1:
begin Write_Req<=1'b1;Read_Req<=1'b0;FIFO_Write_Data<=8'd2;i<=i+1'b1;end
2:
begin Write_Req<=1'b1;Read_Req<=1'b0;FIFO_Write_Data<=8'd3;i<=i+1'b1;end
3:
begin Write_Req<=1'b1;Read_Req<=1'b0;FIFO_Write_Data<=8'd4;i<=i+1'b1;end
//
4:
begin Write_Req<=1'b0;Read_Req<=1'b1;i<=i+1'b1;end
5:
begin Write_Req<=1'b0;Read_Req<=1'b1;i<=i+1'b1;end
6:
begin Write_Req<=1'b0;Read_Req<=1'b1;i<=i+1'b1;end
7:
begin Write_Req<=1'b0;Read_Req<=1'b1;i<=i+1'b1;end
//
8: //利用控制信号对FIFO写入数据,直到if条件满足
if(Left_Sig<=1)begin Write_Req<=1'b0;i<=i+1'b1;end
else begin Write_Req<=1'b1;Read_Req<=1'b0;FIFO_Write_Data<=FIFO_Write_Data+1'b1;end
9: //利用控制信号对FIFO陆续读出数据,直到if条件满足
if(Left_Sig>=3)begin Read_Req<=1'b0;i<=i+1'b1;end
else begin Write_Req<=1'b0;Read_Req<=1'b1;end
//10-15的操作,主要是把同步FIFO想象为它同时被两方调用,一方写一方读
//10-13为A方向同步FIFO写入数据;12-15为B方向同步读取数据
10:
if(Left_Sig>=1)begin Write_Req<=1'b1;FIFO_Write_Data<=8'd5;i<=i+1'b1;end
else begin Write_Req<=1'b0;i<=i+1'b1;end
11:
if(Left_Sig>=1)begin Write_Req<=1'b1;FIFO_Write_Data<=8'd6;i<=i+1'b1;end
else begin Write_Req<=1'b0;i<=i+1'b1;end
12:
begin
if(Left_Sig>=1)begin Write_Req<=1'b1;FIFO_Write_Data<=8'd7;end
else Write_Req<=1'b0;
if(Left_Sig<=3)begin Read_Req<=1'b1;end
else Read_Req<=1'b0;
i<=i+1'b1;
end
13:
begin
if(Left_Sig>=1)begin Write_Req<=1'b1;FIFO_Write_Data<=8'd8;end
else Write_Req<=1'b0;
if(Left_Sig<=3)begin Read_Req<=1'b1;end
else Read_Req<=1'b0;
i<=i+1'b1;
end
14:
if(Left_Sig<=3)begin Write_Req<=1'b0;Read_Req<=1'b1;i<=i+1'b1;end
else begin Read_Req<=1'b0;i<=i+1'b1;end
15:
if(Left_Sig<=3)begin Read_Req<=1'b1;i<=i+1'b1;end
else begin Read_Req<=1'b0;i<=i+1'b1;end
16:
begin Read_Req<=1'b0;i<=5'd16;end
endcase
endmodule