SystemVerilog系列实验2

SYNOPSYS—SystemVerilog入门实验2


前言

该实验的目的为:从DUT的输入端口(端口3)发送1包数据,并由DUT的输出端口(端口7)进行输出。


一、实验思路

  • 如何产生这个数据包?
  • 发送的数据包格式与内容是什么?

二、实验步骤

针对上述描述的两条思路,首先要思考的是如何去产生数据包。有些时候,数据在传输的过程中,不一定时时刻刻都会使用到,为了更加易于我们对数据的掌控,在FPGA中我们会使用Verilog来设计FIFO、寄存器等来暂时存储数据。而在SV中,充当这部分角色的则是队列、数组等。

1.声明与调用gen()

在这里,我们使用 队列 来暂时存放数据包,由于实验中规定数据包从端口3进入至DUT,从端口7输出,因此需将端口3作为源地址(Source Address),端口7作为目的地址(Destination Address),在实验1 test.sv的基础上继续敲入代码如下:

program automatic test(router_io.TB router_io);
//---one--
	bit 	[3:0] sa				;//源地址
	bit 	[3:0] da				;//目的地址
	logic	[7:0] payload[$]		;//数据包(队列)		
	initial begin
	//----创建一个vcd+dump文件来产生可见波形
		// $vcdpluson;
		reset();
//---two---
		gen();
		send();
	end
	task reset();
			router_io.reset_n 		= 1'b0	;
			router_io.cb.frame_n 	<= '1	;
			router_io.cb.valid_n 	<= '1	;
		#2 	router_io.cb.reset_n 	<= 1'b1	;
		repeat(15) @(router_io.cb);
		//change
			router_io.cb.frame_n 	<= 1'b0	;		
	endtask: reset
//---three---	
	task gen();	

	endtask: gen
endprogram: test

代码分析: one 声明三个全局变量 two 声明须在initial块中(send已提前声明,在后续会用到) three 调用采用task endtask的格式


	task gen();
//---one---
			 sa = 3;
			 da = 7;
//---four---
			payload.delete();
//---two---
			repeat($urandom_range(2,4))
//---three---
				payload.push_back($urandom);	
	endtask: gen

代码分析: 在task gen()的任务体中,根据实验要求 one 设置源地址为3,目的地址为7, two 随机将2~4(包括2与4)范围内的数 three 依次从队列的末尾填充至队列中,four 需要注意的是在进行队列操作前,须要将队列进行清空操作,防止原有队列中的数据影响后续实验结果。


2.声明与调用send()

紧接着,我们要思考发送的数据包格式是什么?数据包中的数据包含哪些内容?数据包与其他相关的输入信号的时序关系是什么?这些问题的答案,在开展验证前设计师会提供给我们,如果没有按照DUT规定的时序关系(即验证环境的输出)去发送激励,DUT将无法正常对数据进行接收。

1) 读懂时序图

在这里插入图片描述

上图中 din[sa] 为DUT的数据信号(输入):首先发4位地址(先低位后高位),之后等待5个时钟周期(上图中pad所示),最后发n位数据(先低位后高位);valid_n[sa] 为DUT的数据有效信号(输入/低有效),在数据信号发送最低位地址时,该信号既可为0又可为1,在数据开始等待5个时钟周期时,该信号置为1,在数据信号开始发送最低位数据时,该信号由高(1)变低(0),当数据全部发送完毕时,该信号又由低(0)变高(1);frame_n[sa] 为DUT的帧标志信号(输入/低有效),在数据信号开始发送最低位地址时,该信号由高(1)变低(0),并一直持续至数据的次高位时,该信号由低(0)变高(1)。


2) 用“代码”描述“时序图”

	task send();
//---one---
			send_addrs();		
//---two---
			send_pad();		
//---three---
			send_payload();
	endtask: send

代码分析: 我们调用send()进一步封装了三个子任务:send_addr()、send_pad()、send_payload(),它们分别对应时序图中的 one addresstwo padthree data


	task send_addrs();
//---one---
			router_io.cb.frame_n[sa] <= 1'b0;
//---two---
			for(int i=0; i<4; i++)begin
				router_io.cb.din[sa] <= da[i];
//---three---
				@(router_io.cb);
			end
	endtask: send_addrs

代码分析: 针对 task send_addrs() 而言,我们应在第一个红色箭头处将各个信号的变化用代码描述出来。 one 此刻 frame_n[sa] 信号由高电平(1)跳转为低电平(0),其中高电平即为初始状态,在实验1中的task reset()中已描述two 由于 valid_n[sa] 既可为1又可为0,故延续初始状态即可,此处不予重复描述;由于数据中的地址A0-A4首先按照先发低位后发高位的顺序依次发送至DUT的输入信号din[sa],因此用for()begin…end循环体来描述这四位的发送顺序,其中 three 需注意每位地址均是在每个时钟周期的上升沿进行发送。


	task send_pad();
	
//---one---
			router_io.cb.frame_n[sa] <= 1'b0;			
//---two---
			router_io.cb.valid_n[sa] <= 1'b1;
//---three---
			router_io.cb.din[sa] <= 1'b1;
//---four---
			repeat(5) @(router_io.cb);
	endtask: send_pad

代码分析: 针对 task send_pad() 而言,我们应在第二个红色箭头处将各个信号的变化用代码描述出来。 one 此时 frame_n[sa] 依旧延续低电平的状态;two valid_n[sa] 此时真正意义上变为高电平; three din[sa] 在发送完四位地址后,电平变为高电平;在第三个红色箭头的时刻到来时,第二个红色箭头距离该箭头的时间间隔为5个时钟周期,因此使用repeat(5)语句将时钟重复5个周期。


	task send_payload();
//---one---
			foreach(payload[index])begin
				for(int i=0; i<8; i++)begin
					router_io.cb.din[sa]  		<= payload[index][i];		
//---two---
					router_io.cb.valid_n[sa] 		<= 1'b0;		
//---three---
					router_io.cb.frame_n[sa] 	<= (index == (payload.size()-1))&&(i == 7);
					@(router_io.cb);
				end
			end
//---four---
			router_io.cb.valid_n[sa] <= 1'b1;
	endtask: send_payload

代码分析: 针对 task send_payload() 而言,我们应在第三个红色箭头处将各个信号的变化用代码描述出来。one din[sa] 此时应将数据按照先发低位后发高位的顺序发送,并且每8位拼成1字节,payload[index]中的index代表字节索引值,范围为0 ~ N-1,(例如payload[0][3]则代表队列中第1个字节的第4位数据),因此payload[index][i]能将所有字节的中的所有位全部覆盖,需要注意的是,index在队列中不需要声明,默认从0开始累加; two 此时valid_n[sa] 由高电平变为低电平; three 此时frame_n[sa] 应在准备发送最后1个字节的最后1位时,将低电平变为高电平(1):可以这样理解------如果字节索引值等于最后一个字节的数值且最后1字节里的位数等于最后一位时,将frame_n[sa] 变为高电平。(提示:数据包中有5个字节的数据(payload.size() = 5),index的范围为0~4,则payload.size() -1 = 5-1 = 4)


三、知识点:随机变量赋值

在task gen()的任务体中,我们将sa设置为3,da设置为7。若想将sa与da设置为随机变量,则不妨使用如下语句:

			sa = $random;
			da = $random;
//---或者---			
		    sa = $urandom;
		    da = $urandom;

除此之外,若想将sa与da在随机时进行约束,例如,将sa设置为在5 ~ 10(包括5与10)之间的随机数,da设置为10 ~ 15(包括10与15)之间的随机数,则可以编写如下代码:

			sa = $urandom_range(5,10);
			da = $urandom_range(10,15);

四、总结

在这里插入图片描述
该部分实验主要将红色框内的大致结构(数据的产生与发送)搭建了起来,后续的实验会继续在此基础上进完善以及剩余环境的构造,感谢大家支持。

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

进击的隼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值