SystemVerilog Clocking用法详解
SystemVerilog 的 clocking 块(Clocking Block)是一种专门用于定义信号时序行为的构造,主要用于验证环境(如 UVM)中,以精确控制信号的采样和驱动时序。clocking 块通过将信号与特定时钟关联,简化了测试环境中对时序敏感信号的处理,减少了手动时序管理的复杂性。本文将详细介绍 SystemVerilog 中 clocking 块的各种用法,包括基本定义、输入输出信号、时序控制、接口中的使用、以及在验证中的应用,并提供示例代码和最佳实践。
1. Clocking 块概述
clocking 块是 SystemVerilog 中用于封装信号与时钟关系的构造,通常定义在 interface 或模块中。它的主要功能是:
- 信号采样与驱动:定义信号相对于时钟的采样和驱动时序。
- 时序抽象:屏蔽底层时序细节,简化测试用例开发。
- 验证支持:在验证环境中(如 UVM)提供标准化的时序控制接口。
- 避免竞争:通过明确的采样和驱动时间点,减少仿真中的竞争与冒险。
主要用途
- 在验证环境中精确控制信号的采样和驱动。
- 简化测试环境中对 DUT(待测设计)的时序交互。
- 与
interface结合,提供模块化的时序接口。
基本语法
clocking clocking_name @(clock_event);input input_signal;output output_signal;
endclocking
clocking_name:时钟块的名称。clock_event:触发时钟块的时钟事件(如@(posedge clk))。input_signal:输入信号,定义采样时序。output_signal:输出信号,定义驱动时序。
2. 基本 Clocking 块定义与使用
clocking 块通过指定信号的采样和驱动时序,简化了验证代码的编写。
示例:基本 Clocking 块
module top;logic clk = 0;logic [7:0] data;logic valid;// 定义 clocking 块clocking cb @(posedge clk);input data;input valid;endclocking// 测试逻辑initial beginforever #5 clk = ~clk;endinitial begin@(cb); // 等待 clocking 块的时钟边沿if (cb.valid)$display("Sampled data: %h", cb.data);end
endmodule
说明:
clocking cb定义了一个时钟块,基于clk的上升沿。input data和input valid指定信号在时钟上升沿前采样。- 测试逻辑通过
cb.data和cb.valid访问同步信号。
优点:
- 信号采样自动与时钟边沿对齐,避免手动时序控制。
- 提高了代码的可读性和可维护性。
3. 输入与输出信号的时序控制
clocking 块支持通过 input 和 output 关键字定义信号的采样和驱动时序,并可以通过延迟指定具体的时序偏移。
3.1 输入信号采样
input 信号在时钟边沿前采样,默认采样时间点为时钟边沿前的非阻塞赋值(NBA)区域。可以通过 input #delay 指定采样延迟。
3.2 输出信号驱动
output 信号在时钟边沿后驱动,默认驱动时间点为时钟边沿后的非阻塞赋值区域。可以通过 output #delay 指定驱动延迟。
示例:带时序延迟的 Clocking 块
interface simple_bus (input logic clk);logic [7:0] data;logic valid;logic ready;clocking cb @(posedge clk);input #1ns data, valid; // 采样延迟 1nsoutput #2ns ready; // 驱动延迟 2nsendclocking
endinterfacemodule receiver (simple_bus bus);always @(bus.cb) beginbus.cb.ready <= 1; // 在时钟边沿后 2ns 驱动 readyif (bus.cb.valid)$display("Received data: %h", bus.cb.data);end
endmodulemodule top;logic clk = 0;always #5 clk = ~clk;simple_bus bus_inst(.clk(clk));receiver u_receiver (.bus(bus_inst));initial beginbus_inst.data = 8'hA5;bus_inst.valid = 1;#20 $finish;end
endmodule
说明:
input #1ns表示data和valid在时钟上升沿前 1ns 采样。output #2ns表示ready在时钟上升沿后 2ns 驱动。- 模块通过
bus.cb访问同步信号。
注意:
- 延迟值必须为非负,且在仿真中有效(综合通常忽略)。
- 延迟值应根据 DUT 的时序要求设置。
4. Clocking 块与 Interface 的结合
clocking 块通常定义在 interface 中,与信号和 modport 结合,提供模块化的时序接口。
示例:接口中的 Clocking 块
interface simple_bus (input logic clk);logic [7:0] data;logic valid;logic ready;clocking cb @(posedge clk);input data, valid;output ready;endclockingmodport slave (input data, valid,output ready);
endinterfacemodule receiver (simple_bus.slave bus);always @(bus.cb) beginbus.cb.ready <= 1;if (bus.cb.valid)$display("Received data: %h", bus.cb.data);end
endmodulemodule top;logic clk = 0;always #5 clk = ~clk;simple_bus bus_inst(.clk(clk));receiver u_receiver (.bus(bus_inst));initial beginbus_inst.data = 8'hA5;bus_inst.valid = 1;#20 $finish;end
endmodule
说明:
clocking cb定义在interface中,与接口信号关联。receiver模块通过bus.cb访问同步信号。modport slave定义了信号方向,增强接口的模块化。
优点:
- 将时序控制与信号封装结合,简化验证代码。
- 支持模块化的 DUT 和测试环境连接。
5. 多时钟 Clocking 块
clocking 块支持定义多个时钟块,用于处理多时钟域的信号。
示例:多时钟 Clocking 块
interface multi_clock_bus (input logic clk1, clk2);logic [7:0] data;logic valid;clocking cb1 @(posedge clk1);input data, valid;endclockingclocking cb2 @(posedge clk2);input data, valid;endclocking
endinterfacemodule monitor (multi_clock_bus bus);always @(bus.cb1) beginif (bus.cb1.valid)$display("clk1 domain: data = %h", bus.cb1.data);endalways @(bus.cb2) beginif (bus.cb2.valid)$display("clk2 domain: data = %h", bus.cb2.data);end
endmodulemodule top;logic clk1 = 0, clk2 = 0;always #5 clk1 = ~clk1;always #7 clk2 = ~clk2;multi_clock_bus bus_inst(.clk1(clk1), .clk2(clk2));monitor u_monitor (.bus(bus_inst));initial beginbus_inst.data = 8'hA5;bus_inst.valid = 1;#50 $finish;end
endmodule
说明:
cb1和cb2分别基于clk1和clk2定义时钟块。monitor模块在不同时钟域中采样信号。- 支持多时钟域验证。
注意:
- 确保时钟信号正确连接到接口。
- 在多时钟域中,注意信号的跨时钟域处理。
6. Clocking 块在验证中的应用
clocking 块在验证环境(如 UVM)中广泛使用,用于连接 DUT 和测试环境,提供标准化的时序接口。
示例:UVM 验证中的 Clocking 块
interface simple_bus (input logic clk);logic [7:0] data;logic valid;logic ready;clocking cb @(posedge clk);input data, valid;output ready;endclocking
endinterfacemodule dut (simple_bus bus);always @(posedge bus.clk) beginif (bus.valid && bus.ready)$display("DUT received: %h", bus.data);end
endmoduleprogram testbench;import uvm_pkg::*;`include "uvm_macros.svh"logic clk = 0;always #5 clk = ~clk;simple_bus bus_if(.clk(clk));initial begin// 设置 UVM 接口uvm_config_db#(virtual simple_bus)::set(null, "*", "bus_if", bus_if);run_test();end
endprogram
说明:
simple_bus包含clocking cb,为测试环境提供同步信号访问。- UVM 测试环境通过
uvm_config_db获取虚拟接口。 dut通过接口与测试环境交互。
优点:
- 简化了 UVM 驱动器和监视器的时序控制。
- 提供标准化的信号访问接口。
7. Clocking 块的高级用法
7.1 默认时钟块
可以使用 default clocking 指定默认的时钟块,简化代码中的时序引用。
interface simple_bus (input logic clk);logic [7:0] data;logic valid;clocking cb @(posedge clk);input data, valid;endclockingdefault clocking cb; // 设置默认时钟块
endinterfacemodule monitor (simple_bus bus);always @(*) beginif (valid) // 直接引用信号,等效于 bus.cb.valid$display("Data: %h", data);end
endmodule
说明:
default clocking cb将cb设置为默认时钟块。- 信号可以直接引用(如
valid),等效于bus.cb.valid。
注意:
- 默认时钟块在复杂接口中可能降低可读性,谨慎使用。
7.2 动态时序调整
clocking 块支持在仿真中动态调整时序(通过属性),但主要用于验证。
interface simple_bus (input logic clk);logic [7:0] data;logic valid;clocking cb @(posedge clk);input #1step data, valid; // 使用 1step 采样endclocking
endinterface
说明:
#1step表示在时钟边沿前的最小时间步长采样。- 适合需要精确时序控制的验证场景。
8. 注意事项与最佳实践
-
时序定义:
- 确保采样和驱动延迟与 DUT 的时序要求一致。
- 避免过大的延迟值,以免影响仿真性能。
-
接口结合:
- 将
clocking块定义在interface中,与信号和modport结合。 - 使用
modport明确信号方向,增强模块化。
- 将
-
验证环境:
- 在 UVM 中,使用虚拟接口传递
clocking块。 - 结合
clocking块简化驱动器和监视器的开发。
- 在 UVM 中,使用虚拟接口传递
-
综合限制:
clocking块主要用于验证,不支持综合。- 确保 DUT 代码不依赖
clocking块。
-
多时钟域:
- 为每个时钟域定义独立的
clocking块。 - 注意跨时钟域信号的同步。
- 为每个时钟域定义独立的
-
代码可读性:
- 为
clocking块和信号提供清晰的命名。 - 添加注释说明采样和驱动时序。
- 为
-
调试与验证:
- 使用仿真工具验证
clocking块的时序行为。 - 检查采样和驱动时间点是否符合预期。
- 使用仿真工具验证
9. 总结
SystemVerilog 的 clocking 块是一种强大的验证工具,用于定义信号的采样和驱动时序,简化测试环境的时序管理。通过基本定义、输入输出信号、时序控制、接口结合、多时钟支持等功能,clocking 块在 UVM 等验证环境中发挥了关键作用。特别是在复杂设计中,clocking 块与 interface 的结合提供了模块化的时序接口,显著提高了验证效率和代码质量。遵循最佳实践并根据具体场景选择合适的 clocking 用法,能够有效提升验证的可靠性和可维护性。
10. 设计工具推荐
- SZ901:
SZ901 是一款基于XVC协议的FPGA网络下载器。- 最高支持53M
- 支持4路JTAG独立使用
- 支持端口合并
- 支持国产FLASH烧写
- 下载器无限扩展
- 配备专属程序固化软件,一键烧写,能大大减小程序固化时间!