Verilog时序篇第3-4章

第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				

实验总结:

FIFO的调用需要控制信号,不然会出现程序忽略Empty_Sig,或者Full_Sig的情况

Full_Sig和Empty_Sig不适合同步FIFO的写操作

QuartusII产生的是异步FIFO,但可以兼容同步FIFO。

对同步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

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
时序”最容易联想到就是“时序图”,亦即模块的输出。换句话说“时序”是模块执 行过程的显性记录。一般在仿真上,模块的时序图都是在理想状态下(没有任何物理上 的问题)产生的。时序图里边包含了模块最清洁的执行记录。这些信息对于“细化”模 块来说有很大的帮助。然而影响着这些时序就是Verilog HDL 语言本身。 很多时候,虽然低级建模(建模技巧)已经可以帮助我们完成许多模块设计上的要求, 但是低级建模始终是属于“建模”的层次,亦即“塑造”模块一个大概的形状,而且是 粗糙的东西而已。这粗糙的东西,效果能不能发完全发挥? 我们需要经过“细化”它才 知道结果。 要完成“细化”的过程一点也不可以马虎。早期的建模必须留下可以“细化”的种子。 此外我们还要往模块更深入去了解它,去分析它,如果有模块有问题就调试它。这全部 的工作要求,更进一步挑战我们对Verilog HDL 语言的认识和掌握的程度。有关这一点, 再也不是:了解Verilog HDL 语言相关的语法如何使用?如何使用Verilog HDL 语言建 立一个模块?等这些Verilog HDL 语言“外观的单纯工作”,而是“深入分析”模块执 行时的“内部细节”。关于模块一切的一切过程,我们只能在“时序图”上了解而已。 这就是这本笔记命名的初衷。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小菜鸡变形记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值