Systemverilog interface 学习记录
1.作用:便于验证平台TB和待测设计DUT的连接
2.验证平台和待测设计的连接方式有以下几种
一:1. .name (TB wire_name)
这个 全加器,序列检测器,状态机中verilog的编写和TB仿真时使用的方式
举一个两个模块的例子的编写代码
首先是一个arbiter
module arbiter( //模块名为arbiter
output logic [1:0] grant, //这里是,而不是;是2bit的
input login [1:0] request, //也对应为2bit
input logic reset_n,
input logic clk //都为logic类型
);
endmodule
接下来是test代码块
module test(
input logic [1:0] grant,
output logic [1:0] request,
output logic reset_n,
input logic clk //都为logic类型
);
然后是外面的top模块,将test,arbiter模块包裹起来,相当于TB。要连接的话就需要把这两个模块例化在top里面。
module top; //这里相当于TB中的声明格式
logic [1:0] grant, request; //这里直接放到一起声明了,没有方向
logic clk,reset_n;
arbiter uo_arbiter(.grant(grant), .request(request), .reset_n(reset_n), .clk(clk));
test uo_test (.grant(grant), .request(request), .reset_n(reset_n), .clk(clk));
endmodule //第一个.grant为arbiter里面的grant,括号里面的为top中定义的grant
- 下面的两种方式是SV里的
上面的top模块用这种方法为下面的模块
module top;
logic [1:0] grant, request;
logic clk,reset_n;
arbiter u0_arbiter(.*); //u0_arbiter为例化名
test u0_test(.*)'
endmodule
这种方式虽然简单但是不推荐,因为要保证名字必须具有相同的端口位宽,名字得相同。
- SV隐士连接 .name连接
`在这里插入代码片`module top;
logic [1:0] grant, request;
logic clk,reset_n;
arbiter u0_arbiter(.grant, request, .reset_n, clk);
test u0_test(.grant, request, .reset_n, clk);
endmodule
要求名字和位宽必须一致
3.上面的例子指出的verilog连接的缺点:1.需要修改端口名时就要修改所欲的模块。 interface的优点:1.是一个独立的文件 2.独立的端口类型
4.利用interface连接的代码如下
1.先声明一个interface,把端口都放进来,这里还是举上面的arbiter为例:
interface arb_if(input bit clk); //传入了参数时钟,因为下面的信号都是随时钟变化的
logic [1:0] grant, request; //信号的类型为bit类型
logic reset_n;
endinterface
需要注意的是这里只是声明,如果没有下面的实例化编译结束就会消失,不会占用内存。
接下来为top模块
module top;
bit clk;
alwyas #5 clk=~clk;
arb_if arbif(clk); //这里要例化,把内部产生的clk传递进来,
arbiter u_0arbiter(.arbif);//调用时直接把例化名传递进来就可以了
test u_0test(.arbif);
endmodule
如果要单独对其中一个信号赋值,可以arbif.request,加个.就行
4. 如何使用interface信号
interface arb_if(input bit clk);
logic [1:0] grant, request;
logic reset_n;
endinterface
module tb(**arb_if arbif**); //必须要先进行实例化,没有实例化编译的时候会消失
...
initial beign
@(posedge arbif.clk); //通过.号来引用接口中的信号,这里等待的时钟就是interface里面的时钟clk
arbif.request<=2'b01;//赋值
$display("%0t: drive req=01",$time);
repeat(2)@(posedge arbif.clk); //重复两个时钟周期
if(arbif.grant!=2'b01)
$display("%0t:a1:grant!=2'b01",$time);
$finish;
end
endmodule
- 接口与端口的连接
interface arb_if(input bit clk);
logic [1:0] grant, request;
logic reset_n;
endinterface
module top;
bit clk;
always #5 clk=~clk;
arb_if arbif(clk);//例化的时候就是把产生的时钟放进来
arbiter u0_arbiter(.grant (arbif.grant),
.request (arbif.request),
.reset_n (arbif.reset_n),
.clk (arbif.clk));
test u0_test(arbif);
endmodule
<interface_name>.<internal_signal_name>
二:modport:为内部的接口信号提供不同的视图,一个interface可以包含任意数量的modport。modport只是声明连接模块的信号的端口方向。
1.在interface中声明modport
interface arb_if(input bit clk);
logic [1:0] grant, request;
logic reset_n;
modport DUV(input request,reset_n,clk,output grant);
modport TEST(output request, reset_n,input clk,grant);
endinterface
注意1.在interface中声明modport时,只需声明方向,不需声明位宽。interface中定义的信号是没有方向的
2.调用 1)设计模块中使用interface中的modport
module arbiter(arb_if.DUV arbif_duv); //例化 模块名+例化名
...
endmodule
2)在测试案例中使用interface中的modport
module tb (arb_if.TEST arbif_test); //例化 模块名+例化名
...
endmodule
总结:interface是一组信号,独立的文件,不能包含设计的层次结构。
三:Clocking模块
SV中使用clocking模块控制同步信号,将信号同步到某一个特定的时钟。clocking模块主要使用在验证平台中,不能用作rtl设计,一个interface可以包含多个clocking模块。
1.定义
clocking dram @c(clk)//等待时钟边沿触发
input #1ps address;
input #5;
output $6 data;
endclocking
interface arbiter_if(input bit clk);
logic [1:0] grant, requsest;
logic reset_n;
clocking cb@(posedge clk);//同步到时钟上来
output request;
input grant;
endclocking
modport TEST(clocking cb, output reset_n;
modport DUT(input request, reset_n,output grant);
endinterface
2.引用 格式 <my_interface.cb.singnal_name>
arbiter_if arbif;
arbif.cb.gratn<=2'b01; //实例化的名字+clocking的名字+内部信号名
if(arbif.cb.grant!=2'b01)
@arbif.cb //等待下一个时钟周期的上沿
值得注意的是clocking和modport模块都是可用可不用的
3. 驱动输出信号是非阻塞,arbif.cb.request<=1;
采样输入信号是阻塞赋值value=arbif.cb.grant;
总结:interface中的clocking只能用于验证平台,不能用于RTL设计。好处是能够显示指明同步时钟域,保证一个同步行为,保证DUT和TB的信号在同一个时钟域。一个clocking模块中只有一个clock。clocking模块中的信号方向与TB相关。