建设网站设计,承接php网站建设,中国建筑网查询,搜狗网站收录SV 接口 前言一、接口什么时候用#xff1f;如何用#xff1f;1. 接口中的logic和wire对比2. verilog的时序问题3. 设计---测试平台 间的 竞争状态4. 仿真的结束 二. 接口的驱动和采样1. 接口同步2. 接口信号 采样 和 驱动3. 接口中的双向信号4.时钟发生器5. 为什么在program… SV 接口 前言一、接口什么时候用如何用1. 接口中的logic和wire对比2. verilog的时序问题3. 设计---测试平台 间的 竞争状态4. 仿真的结束 二. 接口的驱动和采样1. 接口同步2. 接口信号 采样 和 驱动3. 接口中的双向信号4.时钟发生器5. 为什么在program 程序中不允许使用always块6. 连接模块7. verilog 端口8. SV 端口9. SV 接口类型的端口声明10. 接口的modport链接11. 接口的总结12. 接口应用于模块与模块之间12. 接口应用于接口与端口之间13. 类和模块之间的连接14. 端口模式和时钟控制块15. 接口和模块的对比16. 接口中的logic 和 wire 对比17. 时序问题18. 程序块和时序区域(难点)19. 仿真的结束20. 指定设计和测试平台之间的时延21. 顶层作用域22. 程序-模块交互 前言
SV 第四章 SV 接口读书笔记 一、接口什么时候用如何用
作用1. 便于设计重用减少了连接错误的可能性 2. 接口包含了连接同步通信的功能。 3. 接口连接可以看出来能使代码变得简洁不易出错如果想拓展接口中的信号只需在接口中定义和使用这个信号的模块做修改不需4. 要其他操作这种特性极大降低了连接出错的几率。 什么时候用当两个块之间由两个以上的信号连接并且使用特定的协议通信的时候应当考虑使用接口信号组一次次的出现例如 网络交换机 如何用 使用接口时需要确保在你的模块和程序块program block软件建模之外声明接口变量就是说不能将接口定义在module和program内。有些编译器不支持在模块中定义接口及时某些编译器支持接口也只是所在模块的局部变量对设计的其他部分来说是不可见的。
1. 接口中的logic和wire对比
logic在测试平台的接口中使用过程赋值语句驱动一个异步信号logic信号可以直接被驱动但是logic不能用于一个信号有多个元件的驱动必须用wire类型wire变量只能被连续赋值语句驱动否则需要使用额外的代码
2. verilog的时序问题
从上一个存储单元的输入到下一个存储单元的输入的延时必须小于一个时钟周期。所以测试平台 需要在时钟沿之后驱动芯片的输入然后再下一个时钟沿之前读取输出。 如果时钟到达一些DUT的时间快于测试平台的激励但是到达另一些DUT的时钟晚于这个激励。解决办法是给系统添加一点小的延迟#0或者较大的延时#1解决时序问题。 但是如果DUT是一个含有无延时信息的RTL代码和有延时信息的门级代码混合。
3. 设计—测试平台 间的 竞争状态
如内存被start信号唤醒write addr data 信号仍然保留着原来的值。
4. 仿真的结束
如果有一个程序块完成initial块 或者 $finish 后仿真就结束了 如果存在多个程序块可以用$exit 提前中断任何一个程序块也可以使用$finish结束仿真
二. 接口的驱动和采样
1. 接口同步
测试平台需要驱动和采用设计的信号主要时通过带有时钟块的接口做到的。 异步信号通过接口时没有任何延时如 rst而时钟块的信号将得到同步。 bus.cb //在时钟块的有效时钟沿继续
repeat3 bus.cb // 等待3个有效时钟沿 bus.cb.grant // 在任何边沿继续
posedge bus.cb.grant // 上升沿继续
wait (bus.cb.grant 1) //等待表达式被执行如果已经是真不做任何延时2. 接口信号 采样 和 驱动
采样arbif.cb.grant 在时钟沿到来之前采样但是可能相对DUTtest信号采样晚两个时钟周期 驱动arbif接口名.cb时钟块名.grant 信号一定这样写不能跳跃层次 在时钟块中 应该使用同步驱动即“”来驱动信号因为 信号在赋值后并不会立即改变测试平台在Reactive区域执行设计代码在Active区域执行。
如果仅由一个模块驱动信号可以使用阻塞赋值非阻塞赋值 可减少竞争状态 测试平台 在时钟有效沿 驱动同步接口信号其值会立即传递到设计中 测试平台 在时钟有效沿之后 驱动同步接口信号其值将会在下一个有效沿传递到设计中 为避免上述可能会丢失 测试的data或者赋值 而不能驱动到进入设计中 最好用如下方式
##2 arbif.cb.request 0; // 等待两个时钟周期后赋值
或者可以写成repeat(2) bus.cb3. 接口中的双向信号
Verilog中如果想驱动一个双向信号如 过程中的双向端口需要用一个连续赋值语句来将reg连接到wire SV中如果是同步信号用连续赋值 异步信号虚接口或者跨模块引用和连续赋值语句 program中不允许使用always块
4.时钟发生器
时钟发生器 应该放在一个模块中这样其与设计结合的更加紧密创建一个时钟树随着时钟信号进入系统并在块之间传递的时候必须仔细地控制时钟地抖动。
1.1 在时钟0之后#5产生时钟沿一般所有的 时钟边沿 使用阻塞赋值生成
module clk_generater(output bit clk)initialalways #5 clk ~clk;// 在时钟0之后#5产生时钟沿
endmodule1.2 如果你确实需要在时钟0时刻产生一个时钟边沿那么可以使用 非阻塞赋值语句 设置初始
值 这就保证了时序逻辑电路比如always块都会在 时钟变化之前 执行2.0 在program块中使用forever生成时钟这是错误的
initialforever # 5 clk ~clk;
initialforever (posedge clk)out_sig ~ out_sig;
不应该把时钟发生器放在程序块里放在program里会引起信号之间的竞争out_sig 和 clk都从reactive区域进入设计。 功能验证 只关心 在正确的时钟周期内提供正确的值而不是纳秒级的延时和时钟的相对偏移。 不要使用功能验证 来验证 底层时序应该在静态时序分析工具中完成
5. 为什么在program 程序中不允许使用always块
SV中program中可以使用initial因为最后一个initial结束的时候仿真实际上也默认结束了就像执行了$finish一样 但是加入了 always块 它将会永远不结束必须调用$exit来发出结束program快的信号
6. 连接模块
module top;bit clk;always #4 clk ~clk;arb_if arbif(.*); // 连接测试和设计地接口arb a1(.*); //设计模块test t1(.*);//测试模块
endmodule: top
使用 .*隐式端口连接自动连接 模块实例的端口到具体信号 只要端口 和 信号地名字
和数据类型相同。7. verilog 端口
verilog 端口声明
module count ( inout wire[31:0] data,output reg[31:0] result,output reg co,input [31:0] a,biput tril ci);
...
endmodule//verilog里端口的名称方向位数和类型都可以在端口列表声明
//也可以在端口列表只声明端口名称在module内声明方向位宽和类型
//若没有显式声明端口类型则默认wire类型默认位宽为类型的默认位宽
//上例中a,b默认类型为wireco的位宽为1
//为避免诸多不必要的麻烦声明端口时尽量名称方向位宽类型一并声明一些限制所有端口必须显式声明方向必须重新指定端口方向才能改变后面端口类型必须重新指定端口方向和类型才能改变后面的端口位宽。
verilog端口的连接
//位置对应方式连接
module and_1( a,b,c); // 端口声明模块定义
input a,b;
output c;
assign cab;
endmodulemodule logic(in1,in2,q); // 端口声明模块定义
inpu in1,in2;
output q;
and_1 U1 (in1,in2,q); //模块例化连接 按顺序a连in1,b连in2,c连q
endmodule//端口对应方式连接
module and_1( ..) // 端口声明模块定义
..
endmodule
module logic(..); // 端口声明模块定义
..
and_1 U1(.a(in1),.b(in2),.c(q)); //模块例化连接 //按位置a连in1,b连in2,c连q//.选择底层模块的端口,后面()里是连接的顶层模块端口如果and_1模块连接很多个模块则需要其他每个模块都要在and_1模块要声明一次连接时又要声明一次。而sv接口中将所有模块端口信号包裹在interface和endinterface中集中声明然后重复使用。
endmodule //用这种方法可以不按顺序写8. SV 端口
sv端口 sv的端口在verilog端口基础上做了一些延伸扩展
指定了缺省方向为inout端口列表中下一个端口定义了类型但没有方向则方向参照前一个端口。
需要注意如果第一个端口既没有方向也没有类型那么后面的所有端口都不能有方向和类型。
可以理解为此时端口列表只声明端口名称方向和类型都在模块内声明。
9. SV 接口类型的端口声明
//显式声明一个接口类型的端口
interface chip_bus;
...
endinterfacemodule ram (chip_bus pins, input clock);
//在verilog学习后刚接触sv接口总是想写成“方向类型名称”比如 input logic[3:0] abc;
//这里chip_bus已经是一个interface类型
//且interface类型本身就可以具有inputoutput或者inout方向
//可以理解成chip_bus本身已经包括方向类型而pins就是变量名称
..
endmodule//隐式声明一个接口类型端口
interface chip_bus;
...
endinterfacemodule arm (interface pins, input clock);
//参照上例“方向类型名称”明显没有声明“方向类型”而是用interface代替名字为pins
//这里只是举例隐式声明的方法但个人来看在实际应用中interface的定义不止一个
//如果都用隐式声明个人理解会有不妥所以实际编写代码应该显式声明最稳妥
1.显式声明的 接口类型端口必须连接到同一类型接口实例
2.隐式声明的 接口类型端口可以连接到任何类型接口实例10. 接口的modport链接
编辑器会依次来检查连线方向是否发生错误。在接口中使用modport结构能够将信号分组并指定方向。 在设计中可以通过两种方法使用modport名 一种是在接口信号的程序和模块中使用modport名 另外也可以在顶层模块中使用modport名然后把接口放到程序和模块的端口表中moduleprogram中使用interface声明top层使用interface.modport声明。 正常情况下使用第一种连接方式因为modport是接口实现的细节不应该出现在顶层模块中。特殊情况如果一个模块需要多次例化每次例化需要连接到不同的modport那么此时应该使用第二种连接方式。
//在例化时选择modport
interface chip_bus(input logic clock,rest_n);logic interrupt_request, grant, ready;logic [31:0] address;wire [63:0] data;
modport master ( input interrupt_request,input address;output grant, ready,inout data,input clock, reset_n); //接口中相对于master的信号及方向
modport slave ( output interrupt_request,output address;input grant, ready,inout data,input clock, reset_n); //接口中相对于slave的信号及方向
endinterface //modport中只定义信号对于不同模块的方向而类型和位宽在接口中声明即可。
定义端口时就选择连接的modport
module primary (chip_bus pins);
..
endmodulemodule secondary (chip_bus pins);
..
endmodulemodule chip (input logic clock,restn);chip_bus bus (clock,resetn); //连接接口前一定要例化接口primary i1 (bus.master); //例化primary时选择连接bus中的modport mastersecondary i2 (bus.slave); //例化secondary时选择连接bus中的modport slave
endmodule11. 接口的总结
interface my_intf (..);
..
endinterfacemodule abc (my_intf intf);//接口类型端口名称为intf
..
endmodulemodule top (..);my_intf intf (..);//例化名称为int
f
abc U1 (.*)
..
endmodule
//此时 **接口例化名称 和 模块内端口名称** 相同连接时可用.*自动连接
//如果不同则用.索引端口再连接注意 1.如果一个模块端口列表中有接口类型的端口那么这个端口必须被连接到 接口实例或者其它接口类型端口
12. 接口应用于模块与模块之间
interface arb_if(input bit clk);logic [1:0] grant,request;logic rst;
endinterface
module arb(arb_if arbif);
//...
always(posedge arbif.clk or negedge arbif.rst)beginif(arbif.rst)arbif.grant 0;elsearb_if.grant this.grant;end
endmodulemodule test(arb_if arbif);
//...initial begin//...end
endmodulemodule top;bit clk;always #5 clk!clk;arb_if arbif(clk);arb a1(arbif);test t1(arbif);
endmodule12. 接口应用于接口与端口之间
module arb_port(input logic [1:0] request,output logic [1:0]grant,input logic clk,input logic rstn);
//...
endmodulemodule top;bit clk;always #5 clk !clk;arb_if arbif(clk); arb_port(.grant(arb_if.grant),.request(arb_if.request),.clk(arb_if.clk),.rstn(arb_if.rstn));
endmodule13. 类和模块之间的连接
interface SBus;
endinterfaceclass a;virtual SBus bus;function new(virtual SBus s)bus s;//传入一个interface给到这里的上面定义的bus中endfunction//...
endclass对比
对于第一点在没有接口的情况下模块与模块之间的连接实际上通过模块间独立的信号连接当存在接口时是通过一个公共接口分别不同模块间的端口连接在外部环境中没有各个独立的信号而是只存在一个接口第三点实际上是实现类和模块之间的连接在顶层模块中同样是实现模块和接口的连接但是在该接口需要在类中初始化最终实现模块和类的连接 总结
14. 端口模式和时钟控制块
modportmodport只能做方向的声明而不能声明变量也就是modport中所有的变量必须在interface中先声明才能在modport中规定方向
clocking不仅可以定义在interface也可以定义在module,program中另外clocking列举的的信号不是自己定义的而是由interface定义的或者其他声明clocking的模块定义的。 1利用clocking采样/利用clocking做数据驱动/接口的同步repeat (3) bus.cb表示等待三个有效时钟沿2时钟块中使用modport时同步信号需要加上接口名和时钟块名前缀。可以采用clocking块去解决采样过程中的竞争问题 竞争问题比如clk1驱动clk2信号是clk1同步信号和d1信号是d1的信号那么在clk1上升沿采集d1为1那么等到clk2上升沿再次采集d1时才会变成2.这是由于实际上clk2和d1信号均是由clk1驱动的因此均和clk1存在一个延迟也就是在clk1上升沿时采样clk2和d1信号均没有发送驱动而当在clk2上升沿采样时clk2和d1相较于clk1的延迟已过去所以此时采样实际时d11也就是为2.
接口块可以使用时钟块来指定同步信号相对于时钟的时序。时钟块中的任何信号都将同步地驱动或采样这就保证了测试平台在正确的时间点与信号交互。 一个接口可以包含多个时钟块因为每个块中都只有一个时钟表达式所以每一个对应一个时钟域。可以在时钟块中使用default语句指定一个时钟偏移。一旦定义了时钟块测试平台就可以用arbif.cb表达式等待时钟而不需要描述确切的式中信号和边沿(posedge arbif.cb)。这样即使改变了时钟块中的时钟或者边沿也不需要修改测试平台的代码。
//带时钟块的接口
interface arb_if(input bit clk);logic [1:0] grant, request;logic rst;clocking cb (posedge clk);output request;input grant;endclocking modport TEST (clocking cb, output rst);modport DUT (input request, rst, output grant);
endinterface
//测试平台
module test(arb_if.TEST arbif);initialbeginarbif_cb_request 0;arbif.cb;$display(%0t: Grant%b, $time, arbif.cb.grant);end
endmodule15. 接口和模块的对比
模块的端口如果声明为inputoutput或者inout时那么列化时可以不连接模块端口如果声明为interface那么例化时必须连接到一个接口实例或者另外一个接口端口
接口端口指的是接口中声明端口比如外部接入的时钟信号或者复位信号接口端口中一般只定义一些时钟信号或复位信号等公共信号interface bus(input logic clock) 接口无法例化模块但是接口可以例化接口接口内部可以声明所以变量或者线网注意是内部接口和模块的连接将接口与端口逐一连接在顶层top中采用模块名 实例名接口名的方式。 软件中不能例化接口 但是可以利用接口的指针找到接口的实例进而找到实例中的信号。
16. 接口中的logic 和 wire 对比
logic 用于接口中的信号可以直接被驱动 wire 只能被连续赋值语句驱动是时钟块中的信号始终是同步的接口中的wire类型不能直接驱动需在module中定义logic类型驱动信号使用assign赋值语句将信号线链接module中通过logic类型驱动
//接口中驱动logic和wire信号
interface asynch_if();logic l;wire w;
endinterfacemodule test(asynch_if ifc);logic local_wire;//接口中的wire类型不能直接驱动需在module中定义logic类型驱动信号使用assign赋值语句将信号线连接module中通过logic类型驱动assign ifc.w local_wire; initialbeginifc.l 0; //直接驱动异步logic信号local_wire 1; //借助logic驱动wire类型end
endmodule17. 时序问题
在实际的硬件设计中DUT中的存储单元在时钟的有效沿锁存输入信号这些数值由存储单元输出然后通过逻辑块到达下一个存储单元。从上一个存储单元的输入到下一个存储单元的输入延时必须小于一个时钟周期。所以测试仪需要在时钟沿之后驱动芯片的输入然后在下一个时钟之前读取输出。测试平台应该模拟测试仪这种行为应该在有效时钟边沿或边沿之后驱动待测设计然后在有效时钟沿到达之前在满足协议时序的前提下尽可能晚地采样。 如果DUT和测试程序仅由Verilog模块构成这几乎是不可能实现的。如果测试平台在时钟边沿驱动DUT就会存在竞争状态。 如果时钟到达一个DUT的时间快于测试平台的激励但是到达另一个DUT的时钟又晚于这个激励这种情况会导致DUT的外部时钟沿都是在相同的仿真时间达到DUT内部有一些输入在上个时钟周期采样但是其他的输入却在当前时钟周期采样。
解决此问题的一种方法是给系统添加延时。比如#00时延会强迫V代码的线程停止并在所有其他代码完成之后被重新调度执行。但在一个大型的设计中往往不可避免地存在多个线程都想最后执行#0带来的结果是每次运行结果不确定所以要避免使用#0以免代码不稳定并且不可移植。
另外一个解决方法是使用一个较大的延时#1。 RTL代码除了时钟沿之外没有其他时序信息所以逻辑电路在时钟沿之后一个时间单位就会稳定。 但是如果一个模块使用1ns时间精度而其他仅使用10ps的时间精度呢那么#1意味着1ns 10ps 还是其他的时间长度呢。**你需要在时钟的有效沿之后并且是在任何事件发生之前而非在一段时间之内尽快地驱动设计。**所以应当避免使用#1延时解决时序问题。
对DUT输出信号的采样存在着相同的问题希望在时钟有效沿到来之前的最后时刻捕获信号的值 你可能直到下个时钟沿会出现在100ns但是不能在100ns出现时钟边沿的时钟采样因为设计的输出值可能已经改变了应当在时钟沿到达之前的 Tsetup 时间上采样。
18. 程序块和时序区域(难点)
竞争问题的根源在于设计和测试平台的事件混合在同一个时间片time slot内 即使在纯RTL程序中也会发生同样的问题。如果存在一种可以将时间轴上分开这些时间的方法如在100ns时刻测试平台可以在时钟信号变化或者设计产生任何活动之前采样设计的输出信号。在所有的事件执行完毕后测试平台开始下一个动作。在SV中测试平台的代码在一个程序块中但是程序块中不能有任何的层次级别例如模块的实例接口或者其他程序。
SV引入了一种新的时间片的划分方式在V中大多数的时间在有效区域执行。在一个时间片首先执行Active区域在这个区域中运行设计事件包括RTL门级代码和时钟发生器。第二个区域是Oberved区域执行断言。接下来就是执行测试平台的Reactive区域。注意时间并不是单向向前流动Observed和Reactive区域的事件可以触发本时钟周期内Active区域中进一步的设计事件。最后是Postponed区域他将在时间片的最后所有设计活动都结束后的只读时间段采样信号。
19. 仿真的结束
在V中仿真在调度事件存在的时候会继续执行直到遇到$finish。
SV中增加了一种结束仿真的方法。SV把任何一个程序块都视为含有一个测试如果仅有一个程序块那么当完成所有的initial块中的最后一个语句时仿真就结束了因为编译器认为这就是测试的结尾。即使还有模块或者程序块的线程在运行仿真也会结束。所以当测试结束时无需关闭所有的monitor和driver。如果存在多个程序块仿真在最后一个程序块结束时结束这样最后一个测试完成时仿真就会结束。可以执行$exit提前中断任何一个程序块也可以使用$finish来结束仿真。
仿真并没有完全结束。模块或者程序块可以定义一个或者多个final块执行仿真器退出前的代码。
programint errors,warnings;initial begin...endfinal$display(Test done with %0d errors and %0d warnings, errors, warnings);
endprogram20. 指定设计和测试平台之间的时延
时钟块的默认时序是在#1step延时之后采样输入信号在#0延时之后驱动输出信号。 #1step延时规定了信号在前一个时间片的Postponed区域在设计有任何新的动作之前被采样这样就可以在时钟改变之前捕获输出值。 因为时钟模块的原因测试平台的输出信号是同步的所以他们直接被送入设计中在Reactive区域运行的程序块在同一个时间片再一次触发Actie区域。可以想象时钟块在设计和测试平台中插入了一个同步器来理解。不理解
21. 顶层作用域
有时需要在仿真过程中创建程序或者模块之外的对象以便参与仿真的所有对象都可以访问他们。在V中只有宏定义可以跨越模块的边界而且经常被用来创建全局变量。
SV中引入了编译单元它是仪器编译的源文件的一个组合。任何modulemacromoduleinterfaceprogrampackage或者primitive边界之外的作用域被称为编译单元作用域也称为$unit。这个作用域内的任何成员比如parameter都类似于全局变量因为它可以被所有低一级的块访问。但是它们又不同于真正的全局成员例如parameter在编译时其他源文件不可见。
有些工具比如Synopsys VCS它同时编译所有的SV代码所以$unit是全局的。但是Synopsys Design Compiler一次编译一个模块或者一组模块这时$unit可能只包含了一个或者几个文件的内容。其他供应商的EDA工具可能一次编译所有的文件或者只是一个子集结果导致 $unit是不可移植的。
实例名$root允许你从顶层作用域开始明确地引用系统中的成员名。此时$root类似于Unix文件系统中的根目录/。对于VCS这样一次编译所有文件的工具。$root和$unit是等价的。当你的代码引用另一个模块中的成员时编译器首先在本作用域内查找然后在上一层作用域内查找如此往复直到到达顶层作用域。可以通过使用$root指定绝对路径明确地引用跨模块的变量。
22. 程序-模块交互
程序块可以读写模块中的所有信号可以你调用模块中的所有例程这个例程可以改变内部信号的值称为后门SV需要在设计中写一个任务改变信号的值在程序中调用这个任务但模块看不到程序块。这是因为测试平台需要访问和控制设计但是设计却独立于测试平台中的任何东西。 测试平台可以用函数从DUT获取信息是一个好办法。