systemverilog_接口
一 .概述
systemverilog在verilog的基础上,扩展了接口(interface),将多个端口封装在一起,使代码便于维护;
二.接口的内容
1.语法:
interface 接口名(input 时钟信号、复位信号 //和testbench连接的);
变量声明;
参数声明;
modport 约束各种模块信号方向;(约束首先要与接口参数列表一致)
clocking块约束信号方向的同时,确定信号采样和被驱动的时刻;
方法,过程块,断言,覆盖率统计等;(比较少用)
endinterface
注意:
接口包含体现其功能的modpor约束t块和clocking块,同时还可以包含同模块一样的变量、参数、方法、过程块;
module可以例化接口,接口可以例化接口,但是接口不能例化module;
模块端口连接,verilog是按照端口连接,systemverilog可以用接口进行连接;
systemverilog使用接口时,可以直接用成员变量连(iterface名.成员变量连),也可以用modport块连接(约束了信号方向,建议采用此种方法),可以直接通过接口连接(没有约束信号方向);
systemverilog每一种连接方法,只要模块定义时定义成对应方式即可;
2.modport约束块
(1)作用
约束信号方向。对于同一组信号,不同模块同一信号的输入输出方向是不一致的,所以使用modport约束各个模块中信号的输入、输出方向。
(2)语法
定义语法:
modport 约束块名字(
input 输入信号1,输入信号2,...,输入信号m,
output 输出信号1,输出信号2,...,输出信号n
);
声明语法:
//接口实例化
接口名 接口实例化名 (时钟信号,复位信号);
//modport块实例化
modport块名 modport块实例化名(
//信号列表
modprot块就按照modport块传
);
(3)注意
mordport块中只需要指明信号方向,不需要声明信号类型和宽度;
三.接口的使用
1.步骤
(1)定义接口
(2)在各个模块中声明接口型端口;
(3)在testbech实例化接口,按照模块中的具体方式建立连接;
2.例子
(1)例子1:通过成员变量进行连接
interface my_if(input logic clk ,rst_n);
logic flag1;
logic flag2;
logic flag3;
modport my_md1(
input clk,rst_n,flag1,
output flag2
);
modport my_md2(
input clk,rst_n,flag2,
output flag3
);
endinterface
module test_interface;
logic clk;
logic rst_n;
parameter T =20;
my_if m_if(
.clk (clk ),
.rst_n (rst_n )
);
initial begin
clk = 1'b0;
rst_n = 1'b0;
m_if.flag1 = 1'b0;
#(T);
rst_n = 1'b1;
#T;
m_if.flag1 = 1'b1;
#T;
m_if.flag1 = 1'b0;
end
always#(T/2) clk = !clk;
cnt10_1 cnt_u1(
.clk (m_if.clk),
.rst_n (m_if.rst_n),
.flag_in (m_if.flag1),
.flag_out (m_if.flag2)
);
cnt10_1 cnt_u2(
.clk (m_if.clk),
.rst_n (m_if.rst_n),
.flag_in (m_if.flag2),
.flag_out (m_if.flag3)
);
endmodule
module cnt10_1(
input clk ,
input rst_n ,
input flag_in ,
output reg flag_out
);
reg [3:0] cnt_10;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_10 <= 'd0;
else if(flag_in)
cnt_10 <= 4'd1;
else if((4'd0<cnt_10) && (cnt_10 < 10))
cnt_10 <= cnt_10 + 1'd1;
else
cnt_10 <= 'd0;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
flag_out <= 'd0;
else if(cnt_10 == 4'd10)
flag_out <= 1'b1;
else
flag_out <= 1'b0;
end
endmodule
结果:
(2)例子2:通过modport块进行参数连接
interface my_if(input logic clk ,rst_n);
logic flag1;
logic flag2;
modport my_md1(
input clk,rst_n,flag1,
output flag2
);
endinterface
module test_interface;
logic clk;
logic rst_n;
parameter T =20;
my_if m_if(
.clk (clk ),
.rst_n (rst_n )
);
initial begin
clk = 1'b0;
rst_n = 1'b0;
m_if.flag1 = 1'b0;
#(T);
rst_n = 1'b1;
#T;
m_if.flag1 = 1'b1;
#T;
m_if.flag1 = 1'b0;
end
always#(T/2) clk = !clk;
cnt10_1 cnt_u1(
.m_if (m_if.my_md1)
);
endmodule
module cnt10_1(
my_if.my_md1 m_if
);
reg [3:0] cnt_10;
always@(posedge m_if.clk or negedge m_if.rst_n)begin
if(!m_if.rst_n)
cnt_10 <= 'd0;
else if(m_if.flag1)
cnt_10 <= 4'd1;
else if((4'd0<cnt_10) && (cnt_10 < 10))
cnt_10 <= cnt_10 + 1'd1;
else
cnt_10 <= 'd0;
end
always@(posedge m_if.clk or negedge m_if.rst_n)begin
if(!m_if.rst_n)
m_if.flag2<= 'd0;
else if(cnt_10 == 4'd10)
m_if.flag2 <= 1'b1;
else
m_if.flag2 <= 1'b0;
end
endmodule
结果:
(3)例子3:通过接口进行参数连接
interface my_if(input logic clk ,rst_n);
logic flag1;
logic flag2;
modport my_md1(
input clk,rst_n,flag1,
output flag2
);
endinterface
module test_interface;
logic clk;
logic rst_n;
parameter T =20;
my_if m_if(
.clk (clk ),
.rst_n (rst_n )
);
initial begin
clk = 1'b0;
rst_n = 1'b0;
m_if.flag1 = 1'b0;
#(T);
rst_n = 1'b1;
#T;
m_if.flag1 = 1'b1;
#T;
m_if.flag1 = 1'b0;
end
always#(T/2) clk = !clk;
cnt10_1 cnt_u1(
.m_if (m_if
)
);
endmodule
module cnt10_1(
my_if m_if
);
reg [3:0] cnt_10;
always@(posedge m_if.clk or negedge m_if.rst_n)begin
if(!m_if.rst_n)
cnt_10 <= 'd0;
else if(m_if.flag1)
cnt_10 <= 4'd1;
else if((4'd0<cnt_10) && (cnt_10 < 10))
cnt_10 <= cnt_10 + 1'd1;
else
cnt_10 <= 'd0;
end
always@(posedge m_if.clk or negedge m_if.rst_n)begin
if(!m_if.rst_n)
m_if.flag2<= 'd0;
else if(cnt_10 == 4'd10)
m_if.flag2 <= 1'b1;
else
m_if.flag2 <= 1'b0;
end
endmodule
结果:
四.时钟块(clocking block)
1.采样和数据驱动
时钟触发沿到来时,触发器会采样输入信号线上的信号,经延迟后会更新输出信号线上的信号;
为了采样时输入信号更稳定,我们会设置采样时间,让触发器采样触发沿前一个微小时间段处的输入值;
为了使输出信号更符合实际硬件电路,我们设置驱动时间,让触发器在时钟触发沿到来后一个微小时间之后再驱动输出信号;
2.clocking block的作用
设置采样和驱动时间、约束信号方向;
注意1:
时钟块(clcking block)可以在interface、module和program中被定义;
3.接口的工作机理:
(1)时钟沿,采样沿,驱动沿
理论的数字电路中(触发器):为了满足建立时间,待采样的输入信号必须提前时钟触发沿保持稳定;而由于数字电路存在延迟,输出数据会相较时钟触发沿延迟一点时间,才会更新在输出。在clocking block中,为了和实际数字电路更一致,可以设置采样沿提前于时钟触发沿的时间,也可以设置驱动沿滞后于时钟触发沿的时间。采样沿提前时间在未设置时,默认为1step,是一个远小于1ps的微小时刻;驱动沿未设置时,默认为**#0**,这个**#0**不是数字0,也代表了一个远小于1ps微小时刻。
(2)#######注意#####
接口成员变量和时钟块中的信号同时存在;
对于输入信号,会将采样沿采集到的值,在时钟触发沿立刻更新到时钟块内部;
对于输出信号,会将时钟触发时刻更新的值,在驱动沿驱动给接口中的成员变量;
4.语法
定义语法
clocking 时钟块名字 @(posedge/negedge驱动时钟);
default input #采样沿提前时间 output #驱动沿滞后时间;
input 采样信号名;
output (也可以额外声明posedge)驱动信号名;
input #时间 信号名; //额外指定输入信号的采样沿提前时刻
output #时间 信号名; //额外指定输出信号的驱动沿滞后时刻
endclocking关键字
使用语法:
//触发
@时钟块的名字 后续操作;
//信号索引
时钟块名字.信号
注意:
如果没有设置采样时间,默认的采样时间是1step(很小);
如果没有设置驱动时间,默认的驱动时间是#0(很小);
5.例子
(0)注意
分析前,先看四.3.(2)的注意部分;
(1)例1:未设置采样沿提前时刻和驱动沿滞后时刻
A 代码
interface my_if(input bit clk);
bit [7:0] sig1;
bit [7:0] sig2;
clocking cb @(posedge clk);
input sig1;
output sig2;
endclocking
endinterface
module test_blocking;
bit clk;
parameter T = 20;
initial begin
forever #(T/2) clk = !clk;
end
my_if mif(clk);
initial begin
#T;
mif.sig1 = 8'h12;
mif.cb.sig2 <= 8'h34;
$display("sig1 is %h",mif.cb.sig1);
@mif.cb;
mif.sig1 = 8'h56;
mif.cb.sig2 <= 8'h78;
$display("sig1 is %h",mif.cb.sig1);
end
endmodule
B结果
说明:
上面的sig1和sig2是interface中的成员变量,下面的sig1和sig2是时钟块中的输入输出信号,采样沿提前时间和驱动沿滞后时间取默认值;
可以看出:
输入信号(sig1),将成员变量采样沿采集到的值,在时钟触发沿更新给了时钟块中的输入信号;
输出信号(sig2),将时钟块中的输出信号在时钟触发时刻更新的值,在驱动沿更新给了接口中的成员变量;
(2)例2:设置了采样沿提前时刻和驱动沿滞后时刻
A代码
interface my_if(input bit clk);
bit [7:0] sig1;
bit [7:0] sig2;
clocking cb@(posedge clk);
default input #2ns output #2.4ns;
input sig1;
output sig2;
endclocking
endinterface
module test_blocking1;
bit clk;
initial begin
forever #3 clk = !clk;
end
my_if itf(clk);
initial begin
itf.sig1 = 8'h00;
itf.cb.sig2 <= 8'h00;
#4 itf.sig1 = 8'h12;
#4 itf.sig1 = 8'h34;
#5 itf.sig1 = 8'h56;
#1 itf.cb.sig2 <= 8'h11;
#7 itf.cb.sig2 <= 8'h22;
#3 itf.cb.sig2 <= 8'h33;
end
endmodule
B结果
说明:
上面的sig1和sig2是interface中的成员变量,下面的sig1和sig2是时钟块中的输入输出信号,采样沿提前时间为2ns和驱动沿滞后时间为2.4ns;
观察的时刻是15ns的时钟触发沿;
输入信号(sig1),将成员变量在采样沿(13ns)采集到的值,在时钟触发沿更新给了时钟块中的输入信号;
输出信号(sig2),将时钟块中的输出信号在时钟触发时刻(15ns)更新的值,在驱动沿(17.4ns)更新给了接口中的成员变量;
(3)例3:特殊用法(将采样沿和驱动沿设置成时钟触发沿)
A 用法
将采样沿提前时间和驱动沿滞后时间用posedge和negedge关键字替代;
B代码
interface my_if(input bit clk);
bit [7:0] sig1;
bit [7:0] sig2;
clocking cb@(posedge clk);
default input posedge output negedge;
input sig1;
output sig2;
endclocking
endinterface
module test_blocking2;
bit clk;
always#(3) clk = !clk;
my_if itf(clk);
initial begin
itf.sig1 = 8'h00;
itf.cb.sig2 <= 8'h00;
#4 itf.sig1 = 8'h12;
#4 itf.sig1 = 8'h34;
#5 itf.sig1 = 8'h56;
#1 itf.cb.sig2 <= 8'h11;
#7 itf.cb.sig2 <= 8'h22;
#3 itf.cb.sig2 <= 8'h33;
end
endmodule
C结果