尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

SV学习记录(四)​​​​​​​

SV学习记录(四)​​​​​​​ 目录4.1 分离测试平台和设计4.2 接口用接口简化连接连接接口和端口modport在总线设计中使用modportinterface trade-offs4.3 激励时序用时钟控制同步信号的时序logic vs wire in an interfaceverilog的时序问题测试平台和设计间的竞争状态程序块和时序区域end of simulation指定设计和测试之间的延迟4.4 接口的驱动和采样interface synchronization接口信号采样接口信号驱动通过时钟块驱动接口信号接口中的双向信号program中为什么不能有always block?时钟发生器4.5 连接所有模块4.6 顶层作用域4.7 program-module交互4.8 SV断言SVA立即断言定制断言行为并发断言4.9 四端口ATM路由器4.10 ref端口的方向4.11 end of simulation4.1 分离测试平台和设计由于always都是并发执行的如果测试平台也用module来写那么可能会在最开始产生时序问题解决方法是采用program block。正常来讲如果采用端口直接连接当端口越来越多连接会变得复杂sv中的解决方式叫接口例 4.3 没有接口的顶层网络 module top; logic [1:0] grant, request; bit clk, rst; always #5 clk ~clk; ​ arb_port a1 (grant, request, rst, clk); // 例 4.1 test t1 (grant, request, rst, clk); // 例 4.2 ​ endmodule4.2 接口接口可以看作是一捆智能的连线。用接口简化连接对于接口如果是两个module之间交互的信息就设置为interface内部的logic变量如果来自外部就设置为interface的input对于module传入写interface内部调用的时候用名称.来调用内部。接口信号必须使用non-blocking来驱动。注意这里的接口连接是无方向的但这是可行的因为实际上往这个信号上写的就是output读它的就是input但问题是编译器不会检测多驱动如果你也没注意可能导致仿真结果不确定。例 4.4 接口 interface arb_if(input bit clk); logic [1:0] grant, request; logic rst; endinterface ​ 例 4.5 使用接口的模块 module arb (arb_if arbif); ... always (posedge arbif.clk or posedge arbif.rst) begin if (arbif.rst) arbif.grant 2b00; else arbif.grant next_grant; ... end endmodule ​ 例 4.7 top module module top; bit clk; always #5 clk ~clk; ​ arb_if arbif(clk); // 例 4.4 arb a1 (arbif); // 例 4.5 test t1 (arbif); // 例 4.6 endmodule : top(确实比v方便多了)连接接口和端口例 4.9 连接接口到使用端口的模块 module top; bit clk; always #5 clk ~clk; ​ arb_if arbif(clk); arb_port al (.grant (arbif.grant), // .port(ifc.signal) .request (arbif.request), .rst (arbif.rst), .clk (arbif.clk)); test tl(arbif); endmodule : topmodport前面提到的interface都没有方向如果想要方向就用modport。modport就是在interface内部定义好对每个module的方向在module内部的端口处要指明使用哪个modport顶层不用修改。例 4.10 带有 modport 的接口 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 ​ 例 4.11 接口中使用 modport 的仲裁器模型 ​ module arb (arb_if.DUT arbif); ... endmodule在总线设计中使用modport以CPU为例CPU和RAM都会驱动data这个信号就通过创建多个modport来实现比如为主、从、监视器、仲裁器创建一共四个。比如下面的代码就是一个监视模块可以打印请求是否被接受例 4.13 接口使用 modport 的仲裁器模型 module monitor (arb_if.MONITOR arbif); ​ always (posedge arbif.request[0]) begin $display(%0t: request[0] asserted, $time); (posedge arbif.grant[0]); $display(%0t: grant[0] asserted, $time); end ​ always (posedge arbif.request[1]) begin $display(%0t: request[1] asserted, $time); (posedge arbif.grant[1]); $display(%0t: grant[1] asserted, $time); end ​ endmoduleinterface trade-offsadvantages:便于重用尤其是基于某种协议进行交互的时候防止连错方便检查disadvantages:如果不是基于协议不建议使用如果两个接口是包含与被包含的关系需要能够拿出并独立驱动多出的信号4.3 激励时序用时钟控制同步信号的时序结合下面的代码来说。什么是时钟块interface中clocking...endclocking就是时钟块时钟块的作用被写进时钟块中的信号对于output会默认在上升沿的后一点时间施加默认为#0对于input会默认在上升沿的前一点时间扫描默认为#1。防止了竞争问题。怎么使用它在interface中对特定方向的信号定义好然后在modport中使用时钟块最后在module中使用接口块可以使用时钟块来指定同步信号相对于时钟的时序。例 4.14 带时钟块的接口 ​ interface arb_if (input bit clk); logic [1:0] grant, request; logic rst; ​ clocking cb (posedge clk); // 声明 cb output request; input grant; endclocking ​ modport TEST (clocking cb, // 使用 cb output rst); modport DUT (input request, rst, output grant); endinterface ​ // 这是一个简单的测试平台更好的测试程序见例 4.20 module test (arb_if.TEST arbif); initial begin arbif.cb.request 0; arbif.cb;//用这种方式等待来的时钟是clocking中要求的时钟 $display(%0t: Grant%b, $time, arbif.cb.grant); end endmodulelogic vs wire in an interface对于logic类型可以非阻塞赋值对于wire类型只能阻塞赋值所以必须要先给w赋一个local_wire再通过改变local_wire改变w例 4.15 如何驱动接口中的 logic 和 wire 信号 ​ interface asynch_if() { logic l; wire w; ​ endinterface ​ module test(asynch_if ifc); logic local_wire; assign ifc.w local_wire; ​ initial begin ifc.l 0; // 直接驱动异步 logic 信号 ... local_wire 1; ... end endmodule用wire的好处是方便进行多驱动verilog的时序问题问题在传统的verilog中无法精确控制测试平台和DUT谁先执行。正常行为上升沿R1到来DUT开始计算R1之后测试平台传来新的数据R2之前测试平台取走DUT的计算结果所以测试平台应该在时序方面也独立于设计。测试平台和设计间的竞争状态通过下面的代码讲解。对于test module来讲是阻塞赋值如果先把state拉高为1马上会激活memory module中的always块但是此时其他数据还是旧值采样上可能会出问题。可以通过nonblocking实现但是也可能会有问题。例 4.16 设计和测试平台之间的竞争状态 module memory(input wire start, write, input wire [7:0] addr, inout wire[7:0] data); logic [7:0] mem[256]; always (posedge start) begin if (write) mem[addr] data; ... end ​ endmodule ​ module test(output logic start, write, output logic [7:0] addr, data); initial begin start 0; // 信号初始化 write 0; #10; // 短暂的延时 addr 8h42; // 发起第一个指令 data 8h5a; start 1; write 1; ... end ​ endmodule程序块和时序区域问题之前说过testbench和DUT的竞争问题。这个问题的主要来源是testbench的行为和DUT的行为在同一时间块中进行难以区分先后。sv中重新划分了强制的时间区域如下所示sv启动的时候严格按照顺序启动。下面是对program块的解释。program...endprogram就是program块其中包含的代码就是一般用于测试的代码它被强制在reactive区域启动。program中不能包含always也不能例化其他端口/接口只能在顶层传入这种顺序执行的软件代码适合测试。例 4.17 使用带有时钟接口的测试平台 program automatic test (arb_if.TEST arbif); ... ​ initial begin arbif.cb.request 2b01; $display(%0t: Drove req01, $time); repeat (2) arbif.cb; if (arbif.cb.grant ! 2b01) $display(%0t: a1: grant!2b01, $time); end ​ endprogram : testend of simulation在v中可能出现永无休止的情况比如时钟一直在翻转。传统simulation结束的方法有任务完成注意如果时钟一直反转是认为任务没完成的遇到finish。但是在sv中把一个program视为一个test每当跑完其中所有initial块的最后一行代码就认为任务已经结束主动结束。如果有多个program就等最后一个跑完最后一行代码结束。指定设计和测试之间的延迟前面提到clocking...endclocking块对于output和input的固定延迟这一节把这种延迟对应的行为映射到时间域time region中了时钟块默认的延时不是随便加的延时而是锁定在特定调度区域的标记。#1step确保你在时钟沿之前安全采样建立时间#0确保你在时钟沿之后安全驱动保持时间并且驱动后的新值能自动在当前周期内触发 DUT 重新计算完美模拟了真实测试仪与芯片的物理时序关系。你可以通过想象DUT out和testbench in之间插入了一个同步器来理解4.4 接口的驱动和采样interface synchronization下面展示的是一些老的verilog中的同步方法没什么实际意义例 4.18 信号同步 program automatic test(bus_if.TB bus); initial begin bus.cb; // Continue on active edge // in clocking block repeat (3) bus.cb; // Wait for 3 active edges bus.cb.grant; // Continue on any edge (posedge bus.cb.grant); // Continue on posedge (negedge bus.cb.grant); // Continue on negedge wait (bus.cb.grant 1); // Wait for expression // No delay if already true (posedge bus.cb.grant or negedge bus.rst); // Wait for several signals end endprogram接口信号采样下图展示了一个极端情况DUT接收到的输入在时钟上升沿时刻才到来也就是25ns时刻此时输出是能够顺利采集到的这是因为EST是通过时钟块采样的注意它的采样是arbif.cb.grant。接口信号驱动主要就讲sv怎么通过驱动接口传递信号Sample 4.20 Testbench using interface with clocking block ​ program automatic test (arb_if.TEST arbif); ​ initial begin arbif.cb.request 2b01; $display(%0t: Drove req01, $time); repeat (2) arbif.cb; if (arbif.cb.grant ! 2b01) $display(%0t: grant ! 2b01, $time); end ​ endprogram : test注意命名别错别忘带cb。通过时钟块驱动接口信号如果像下面这么驱动输出会丢失请求2例 4.22 驱动一个同步接口 program test(arb_if.TEST arbif); initial begin #7 arbif.cb.request 3; // 7ns #10 arbif.cb.request 2; // 17ns #8 arbif.cb.request 1; // 25ns #15 finish; end endprogram ​ module arb(arb_if.DUT arbif); initial $monitor(%0t: req%h, $time, arbif.request); endmodule导致这个问题的原因用延迟的方式叫异步地驱动同步信号(Driving clocking block signals asynchronously)它不会看上升沿只看时间。但前面说了对于每个上升沿我们有严格的time region划分异步驱动request2暂存到cb中等下一个上升沿准备传递给DUT#25恰好是下一个上升沿而一方要读之前的2一方要写新1进去由于异步驱动不像同步驱动定义了先后所以可能丢失。如果想解决可以这么写Sample 4.23 Interface signal drive ##2 arbif.cb.request 0; // Wait 2 cycles then assign##2表示等待当前时钟块的两周期。接口中的双向信号当你在program块里给一个wire信号赋值时仿真器并不是直接修改那根物理连线net的值而是先把这个值存在一个隐藏的、临时的驱动变量里再由这个临时变量去驱动那根线。这是因为program的修改在reactive区但DUT的使用在active区为了避免时间先后问题所以是这样。那么就可以利用这个特性直接对驱动变量修改。Sample 4.24 Bidirectional signals in a program and interface interface master_if (input bit clk); wire [7:0] data; // Bidirectional signal clocking cb (posedge clk); inout data; endclocking modport TEST (clocking cb); endinterface ​ program test(master_if.TEST mif); initial begin mif.cb.data z; mif.cb; $displayh(mif.cb.data); mif.cb; mif.cb.data 8h5a; mif.cb; mif.cb.data z; end endprogramprogram中为什么不能有always block?sv是更像C的一种语言always块在每个时钟周期都被触发但是test本身是有固定顺序的所以initial更符合如果你真想用可以用initial forever时钟发生器时钟发生器不要放在program里应该放在module里还是之前说过的他们两个运行的时间域不同。好的时钟应该避免在0时刻产生沿。如果想得到随机时钟可以更改skewjitterfrequency等等。不要用形式验证验证底层时序。形式验证只能验证DUT的功能正确性。下面展示了一个好的时钟发生器module clock_generator (output bit clk); initial forever #5 clk ~clk; // Generate edges after time 0 endmodule4.5 连接所有模块如果在module中用了接口必须把接口在顶层连接了才能编译不然会报错。4.6 顶层作用域什么是$unit?这是一个作用域在任何moduleprograminterface外的声明都属于这个作用域。它是编译单元作用域其中的声明对所有内部块都有效。但对于不同平台编译单元包含的文件数可能不同。有的平台可能一次性把所有文件一起编译得到一个编译单元但有的可能就只拿出几个一起编译这样会有好几个编译单元。所以定义在$unit中的理论上来讲是大家都可以用但是由于平台问题可能无法生效。所以有不可移植的问题。问了解决这个问题有了$root。什么是$root?可以理解为unix中的/就是最顶层的文件夹这样你就可以通过$root锁定信号就算他们不在一个编译单元中。Sample 4.32 Cross-module references with $root timescale 1ns/1ns parameter TIMEOUT 1_000_000; top t1(); // Explicitly instantiate top-level module module top; bit clk; test t1(.*); endmodule ​ define TOP $root.t1 program automatic test; ... initial begin // Absolute reference $display(clk%b, $root.t1.clk); $display(clk%b, TOP.clk); // Relative reference $display(clk%b, t1.clk); end endprogram4.7 program-module交互program可以控制获取module中的信息。但module不能依赖任何外部programprogram用function来获取module中的信息比较好。4.8 SV断言SVA立即断言如果你发出一个请求预计两周期后收到grant你想检查运行是否正确。传统方法是写if如果没收到就输出错误。用断言可以实现相同功能。如果收到了断言为真会继续向下执行如果断言为假就会停止仿真输出错误。Sample 4.34 Simple immediate assertion bus.cb.request 1; repeat (2) bus.cb; a1: assert (bus.cb.grant 2b01); // rest of the test ​ Sample 4.35 Error from failed immediate assertion test.sv, 7: top.t1.a1: started at 55ns failed at 55ns Offending (bus.cb.grant 2’b01)断言的执行方法和过程语句不同如果想用它来检查复杂的时序要小心使用。定制断言行为像下面这么写当断言为假的时候就可以在原有输出基础上再多输出你想输出的内容。Sample 4.36 Creating a custom error message in an immediate assertion a1: assert (bus.cb.grant 2b01) else $error(Grant not asserted);四个断言特有的用于输出信息的函数$info, $warning, $error, and $fatal并发断言它可以在整个验证过程中持续生效检测某些信号是否出问题。property...endproperty叫做属性块。它描述的是应该发生什么对于下面的例子就是”$isunknown(request) 0“这件事应该一直成立。disable iff (rst)disable是保留的关键字iff是if and only if他们两个合起来用就表示当rst1就disable下面的检查如果rst为0就启用检查。Sample 4.39 Concurrent assertion to check for X/Z interface arb_if(input bit clk); logic [1:0] grant, request; logic rst; property request_2state; (posedge clk) disable iff (rst) $isunknown(request) 0; // Make sure no Z or X found endproperty ​ assert_request_2state: assert property (request_2state); endinterfaceSVA还有很多可以介绍本书不多提及。4.9 四端口ATM路由器下面是一个例子介绍异步传输模式的ATM路由器。这里只放两张图因为这一节主要是想展示用接口多方便没什么实际的代码内容。4.10 ref端口的方向如果你用多个inout驱动一个变量那么sv会计算如果用多个ref引用了一个变量会出现竞争因为ref会对这个变量的值进行修改而它的值取决于最后一次修改4.11 end of simulation之前讲过结束的过程这里再说一遍。每个initial块结束的时候会call $exit退出当前程序所有程序都退出后会call $finish结束仿真。在initial块后我们还可以写一个final块只能用来做一些clean up的工作或者输出信息Sample 4.48 A final block program test; int errors, warnings; initial begin ... // Main program activity end final $display(Test done with %0d errors and %0d warnings, errors, warnings); endprogram
返回列表