[SystemVerilog] Clocking
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烧写
- 下载器无限扩展
- 配备专属程序固化软件,一键烧写,能大大减小程序固化时间!