首页 > 其他分享 >SV学习(3)——接口interface、modport、时钟块clocking

SV学习(3)——接口interface、modport、时钟块clocking

时间:2022-10-04 23:36:42浏览次数:79  
标签:modport clocking clk SV 接口 logic interface data

SV学习(3)——接口interface、modport、时钟块clocking

========

链接: interface中的clocking

========

1. 接口interface

SV引入了一个重要的数据类型:interface。主要作用有两个,一是简化模块之间的连接;二是实现类和模块之间的通信。

使用接口使得连接更加简洁而不易出差,如果需要在一个接口中放入一个新的信号,就只需要在接口定义和实际使用这个接口的模块中做对应的修改,而不需要改变其他模块。接口不可以例化,但是可以使用接口的指针,找到接口的实例,然后再找接口实例中的信号。

接口使用方法:

  1. 在interface的端口列表只需要定义时钟、复位等公共信号,或者不定义任何端口信号,转而在变量列表中定义各个需要跟DUT和TB连接的logic变量。
  2. interface可以用参数化方式提高复用性(parameter)
  3. 对于有对应interface的DUT和TB,在其例化的时候,也只需要传递匹配的interface变量名即可完成interface的变量传递

由于接口即可以在硬件上(module)使用,又可以在软件上(class)使用,interface作为SV中唯一的硬件和软件环境的媒介交互,modport可以进一步限定信号传输的方向,避免端口连接的错误。

接口中使用task和function:

  1. 接口可以包含task和function,也可以在接口外部或者内部
  2. 如果task和function定义在模块中,使用层次结构名称,它们必须在接口中声明为extern或在modport中声明为export
  3. 多个模块的任务名不可以重复

下面举两个接口的例子:

  1. 使用interface接口的一位全加器

    `timescale	1ns / 1ns
    
    interface if_port (input bit clk);	// 声明接口
    	logic	a, b, cin, sum, cout;	// 声明所有的连接线
    	
    	clocking	cp @ (posedge clk);	// 声明在同一个时钟变化下,连接线的方向
    		output	a, b, cin;
    	endclocking
    	
    	clocking	cn @ (negedge clk);	// 下降沿出发
    		input	a, b ,cin, sum, cout;
    	endclocking
    	
    	modport	simulus (clocking cp);	// 声明端口的输入输出
    	modport	adder	(input a, b, cin,  output cout, sum);
    	modport	monitor	(clocking cn);
    
    endinterface
    
    module	simulus	(if_port.simulus port);	// 使用接口的激励模块
    	always @ (port.cp)	begin
    		port.cp.a	<=	$random() % 2;
    		port.cp.b	<=	$random() % 2;
    		port.cp.cin	<=	$random() % 2;
    	end
    endmodule:	simulus
    
    module	adder	(if_port.adder port);	// 一位加法器
    //	assign	{port.cout, port.sum} = {port.a + port.b + port.cin};	// 好久没写代码,犯了这样的错
    	assign	{port.cout, port.sum} = port.a + port.b + port.cin;
    endmodule:	adder
    
    module	monitor	(if_port.monitor mon);	// 检测模块,在时钟的下降沿打印结果
    	always @ (mon.cn)	begin
    		$display ("%d + %d + %d = %d %d", mon.cn.a, mon.cn.b, mon.cn.cin, mon.cn.cout, mon.cn.sum, $time);
    	end
    endmodule
    
    module	top ( );
    	timeunit		1ns;
    	timeprecision	1ns;
    	
    	bit	clk = 0;;
    	
    	if_port	port (clk);	// 实例化所有模块,并连接接口
    	simulus	sim	(port.simulus);
    	adder	add (port.adder);
    	monitor	mon (port.monitor);
    	
    	always #10 clk = ~clk;
    
    endmodule
    

    在这里插入图片描述

  2. 使用interface接口的读存储器

    // interface_example
    
    interface membus (	// 声明接口
    	input	logic	clk
    );	// port和module的port一样,用于top的
    
    	// 声明用于内部模块例化的连接
    	logic			mrdy	;
    	logic			wen		;
    	logic			ren		;
    	logic	[ 7: 0]	addr	;
    	logic	[ 7: 0]	c2m_data;
    	logic	[ 7: 0]	m2c_data;
    	wor				status	;
    // 对于确定了方向的可以在interface的port里面声明,如clk,
    // 而对于随着模块不同、方向不同的用logic先声明,然后再用modport指明具体方向
    	
    	task	reply_read (
    		input	logic	[ 7: 0]		data,
    		integer	delay
    	);
    		#delay;
    		@ (negedge clk)
    		mrdy = 1'b0;
    		m2c_data = data;	// slave回复数据
    		@ (negedge clk)
    		mrdy = 1'b1;	
    	endtask
    	
    	task	read_memory (
    		input	logic	[ 7: 0]	raddr	,
    		output	logic	[ 7: 0]	data
    	);
    		@ (posedge clk);
    		ren = 1'b0;
    		addr = raddr;		// master申请读数据
    		@ (negedge mrdy);
    		@ (posedge clk);
    		data = m2c_data;	// master得到数据
    		ren = 1'b1;
    	endtask
    	
    	// 在接口中使用modport,将信号分组并指定方向
    	modport master (
    		output	wen, ren, addr, c2m_data, status,
    		input	mrdy, m2c_data,
    		import	read_memory
    	); 
    	
    	modport	slave (
    		input	wen, ren, addr, c2m_data, status,
    		output	mrdy, m2c_data,
    		import	reply_read
    	);
    	
    endinterface
    
    /* *************** mem_core *************** */
    module mem_core (membus.slave mb);	// 用salve接口声明
    	logic	[ 7: 0]	mem [ 255: 0];
    	
    	initial	begin
    		for (int i = 0; i < 256; i++)
    		mem[i] = i;
    	end
    	
    	assign	mb.status = 1'b0;
    	always @ (negedge mb.ren)
    		mb.reply_read (mem[mb.addr], 100);
    endmodule
    
    /* *************** cpu_core *************** */
    module cpu_core (membus.master mb);	// 用master接口声明
    	assign	mb.status = 1'b0;
    	initial	begin
    		logic	[ 7: 0]	read_data;
    		mb.read_memory (8'b0001_0000, read_data);
    		$display ("Read Result", $time, read_data);
    	end
    endmodule
    
    
    /* *************** top *************** */
    module top ();
    	wor		status;	// wor:当有多个驱动源驱动wor型数据时,将产生线或结构
    	logic	clk = 1'b0;
    	
    	membus mb (clk);
    	
    //	mem_core mem (mb.slave);
    	mem_core mem (mb);
    	cpu_core cpu (mb.master);
    
    	initial	begin
    		for (int i = 0; i <= 255; i++)
    			#1 clk = ~clk;
    	end
    	
    endmodule
    

    在这里插入图片描述
    在这里插入图片描述

关于interface的使用,大概知道了怎么用,接口中端口的调用没问题,比如在顶层top中实例化mem模块和cpu模块,这俩的端口连接可以用接口实现,在interface接口中,把端口的变量名声明好,再用modport将信号分组并指定输入输出方向;
但是具体的细节不太了解,比如在接口中定义了任务,如何在各自的模块中调用任务,看别人博客说是要添加export声明,但是我却用了import解决了,不懂,但是成了


2. modport

使用modport将接口中的信号分组
实现一个简单的仲裁器接口,并在仲裁器中使用接口,

// 简单接口
interface arb_if (input bit clk);
	logic [1:0] grant, request;
	logic rst;
endinterface

// 仲裁器
module arb (arb_if arbif);
	...
	always @(posedge arbif.clk or posedge arbif.rst) begin
		if (arbif.rst == 1'b1)
			arbif.grant <= 2'b00;
		else
			arbif.grant <= next_grant;
	end
	...
endmodule

// 测试平台
module test (arb_if arbif);
	...
	initial	begin
		...
		@(posedge arbif.clk);
		arbif.request <= 2'b01;
		$display ("@%0t: Drove req = 01", $time);
		repeat (2) @(posedge arbif.clk);
		if (arbif.grant != 2'b01)
			$display ("@%0t: a1: grant != 2'b01", $time);
		$finish;
	end
	...
endmodule

// 顶层
module top; 
	bit clk;
	always #5 clk = ~clk;
	
	arb_if arbif(clk);
	arb a1(arbif);
	test t1(arbif);
endmodule

上面arb在接口中使用了点对点的无信号方向的连接方式;
下面在接口中使用modport结构能将信号分组并指定方向;

// 使用mofport的接口
interface arb_if (input bit clk);
	logic [1:0] grant, request;
	logic rst;
	
	modport TEST (output request, rst,
	              input grant, clk);
	modport DUT (input request, rst, clk,
	             output grant);
	modport MONITOR (input request, grant, rst, clk);
endinterface

// 仲裁器
module arb (arb_if.DUT arbif);
	...
endmodule

// 测试平台
module test (arb_if.TEST arbif);
	...
endmodule 

// 顶层
module top;
	bit clk;
	always #5 clk = ~clk;
	
	arb_if arbif(clk);
	arb a1(arbif);
	test t1(arbif);
endmodule

两种顶层模块是一样的,因为modpoer在模块首部指明,在模块例化时就不需要指明;

在接口中不能例化模块,但是可以例化其他接口


3. 时钟块clocking

3.1. 驱动和采用的竞争问题

在RTL设计中,在同一个时间片内,可能会有一个信号同时被读取和写入,那么读取到的数据有可能会是旧数值也有可能是新数值,用非阻塞赋值 <= 就可以解决这个问题。但是在测试程序中,不能确保采集到DUT产生的最新值;

接口块可以使用时钟块来指定同步信号相对于时钟的时序。
在这里插入图片描述

在时钟上升沿采样信号,只看vld信号(1->0)就会疑惑采样到的是1还是0,在RTL仿真时,是看不到真实的物理时序信息的,但实际采样到的是1;真实电路对应的时vld_actual信号,它的变化会较clk有一个detal-cycle的延迟,这样看在时钟上升沿采集到的是1。

我们可以人为的设置加大delay,这样看波形的时候就会显示vld_actual的,让待采样数据的沿变不和时钟的沿变在一起。

还有一种方法就是,设置采样vld信号的时间,设置在时钟上升沿之前 #t 采样,这样采出来的数据也是准确的;
同样也可以设置信号输出时间,设置在时钟上升沿之后 #t 输出,这样输出的数据也是准确的;
就类似与模拟建立保存时间,在时序上在波形上,看到肉眼可见的延迟。

3.2. clocking待补充…

标签:modport,clocking,clk,SV,接口,logic,interface,data
From: https://www.cnblogs.com/amxiang/p/16754812.html

相关文章

  • SV学习(5)——类和对象、类的成员
    SV学习(5)——类和对象、类的成员1.类和对象概述2.声明类并创建对象3.赋值和拷贝,深拷贝浅拷贝4.对象的销毁5.句柄的使用6.静态变量7.静态方法8.验证为什么......
  • SV学习(6)——类的继承、句柄的使
    SV学习(6)——类的继承、句柄的使用1.类的成员访问类型2.类的继承extends3.子类索引父类的同名函数super4.成员覆盖补充一个有趣的代码5.句柄的传递6.......
  • SV学习(7)——包的使用
    1.包的定义SV提供了一种在多个module、interface和program中共享parameter、data、type、task、function、class等的方法,即利用package(包)的方法来实现。完整的验证环境......
  • SV中program & module
    相同之处:1.和module相同,program也可以定义0个或多个输入、输出、双向端口。2.一个program块内部可以包含0个或多个initial块、generate块、specparam语句、连续赋值语句......
  • csv2ECharts,**一行命令查看数据趋势图 工具分享**
    csv2ECharts一行命令查看数据趋势图!联系:[email protected],欢迎交流提出建议只有一个文件,基于shell,实现将CSV格式数据转化为数据图。运维中尝尝需要查看某个监控指标的变化......
  • SVG
    矢量图VS位图SVG是矢量图,如jpg/png等是位图位图在放大时失真体积大,矢量图不会失真且体积小位图是一个个很小的颜色方块组成的每个小方块为1px,矢量图是XML定义的,通过各......
  • Systemverilog之SVA(一)
     前言systemverilogassertion作为systemverilog引入的重要特性,在功能仿真及形式验证中有着重要的作用。相较于Verilog传统的checker,SVA作为声明性的语言,使用简单易于管......
  • 多Csv文件合并
    问题:同一文件夹内的多个Csv文件,需要合并到一个工作表里。示例工作簿在C盘下的数据源文件夹中,每列标题分别为:标题1、标题2、标题3let源=Folder.Files("C:\数据源")......
  • Svelte创建组件小结
    ·使用由HTML元素构成的简单易用语法来定义Svelte组件。script元素包含组件逻辑,style元素包含CSS样式,剩下的就是HTML标记。·向组件传递数据的常用方式是使用props。·Sv......
  • jsvmp_wencai
    网站aHR0cDovL3d3dy5pd2VuY2FpLmNvbS91bmlmaWVkd2FwL2hvbWUvaW5kZXg=直接搜索关键词  下面是要抓取的数据  逆向位置(一个即可)hook到hexin-v的生成地方......