systemverilog中clocking的用法
Clocking块在SystemVerilog中主要用于在测试平台(testbench)中同步和采样信号,特别是在接口(interface)中定义。它有助于减少竞争条件,并使得测试平台更加可读和可维护。下面我将详细介绍clocking块的用法。
1. 基本语法
systemverilog
clocking <clocking_name> @(<event>);
default input #<delay> output #<delay>;
<direction> <signal1>;
<direction> <signal2>;
// ...
endclocking
<clocking_name>:clocking块的名称。
@(<event>):通常是一个时钟事件,如posedge clk或negedge clk。
default语句:指定默认的输入和输出延迟。如果没有指定,则默认输入在时钟事件之前1step采样,输出在时钟事件之后0时间驱动。
<direction>:可以是input、output或inout。注意,在clocking块中,inout通常不被推荐,因为时钟块主要用于同步驱动和采样。
<signal>:可以是接口中的信号,或者通过<clocking_block_name>.<signal>来访问。
2. 主要作用
同步采样和驱动:确保在时钟事件发生时采样输入信号,并在指定的延迟后驱动输出信号。
减少竞争条件:通过指定输入采样和输出驱动的延迟,可以避免设计中的竞争条件。
提供时序抽象:测试平台可以在事务级操作,而不需要关心具体的时钟延迟。
3. 示例
假设我们有一个接口,包含时钟clk、输入信号data和输出信号valid。
systemverilog
interface my_interface(input bit clk);
logic [7:0] data;
logic valid;
// 定义一个clocking块,基于clk的上升沿
clocking cb @(posedge clk);
default input #1step output #0;
input data;
input valid;
// 注意:通常clocking块中不定义输出方向,因为输出通常由驱动端控制,但也可以定义输出
// 这里我们只定义输入,用于采样
endclocking
// 也可以定义另一个clocking块用于驱动输出
clocking drv_cb @(posedge clk);
default input #1step output #2;
output data;
output valid;
endclocking
// 注意:同一个信号不能在同一个clocking块中同时定义为input和output
endinterface
在测试平台中,可以使用clocking块来同步采样和驱动:
systemverilog
module testbench;
bit clk;
my_interface intf(clk);
always #5 clk = ~clk;
initial begin
// 使用clocking块采样
@(intf.cb);
$display("data = %h, valid = %b", intf.cb.data, intf.cb.valid);
// 使用clocking块驱动
intf.drv_cb.data <= 8'hFF;
intf.drv_cb.valid <= 1;
// 等待下一个时钟周期
@(intf.cb);
// 再次采样
$display("data = %h, valid = %b", intf.cb.data, intf.cb.valid);
end
endmodule
4. 延迟说明
input #<delay>:指定在时钟事件发生前<delay>时间采样输入信号。#1step表示在时钟事件前1个时间步长采样,这可以避免竞争条件。
output #<delay>:指定在时钟事件发生后<delay>时间驱动输出信号。例如,#2表示在时钟事件后2个时间单位驱动。
5. 使用clocking块的注意事项
Clocking块通常定义在接口中,但也可以定义在模块和程序中。
在同一个clocking块中,不能将同一个信号同时定义为输入和输出。如果有需要,可以定义两个不同的clocking块,一个用于采样,一个用于驱动。
在测试平台中,通过clocking块访问信号时,使用<clocking_block_name>.<signal>的格式。
Clocking块主要用于测试平台,而不是可综合的设计代码。
6. 在程序(program)中使用clocking块
SystemVerilog引入了程序(program)构造,用于将测试平台与设计分离。在程序中,可以使用clocking块来同步测试平台的操作。
systemverilog
program test_program(my_interface intf);
// 使用接口中的clocking块
initial begin
// 等待时钟事件
@(intf.cb);
// 采样信号
$display("data = %h", intf.cb.data);
end
endprogram
7. 默认clocking
可以指定一个默认的clocking块,这样在等待时钟事件时可以直接使用@(cb)而不需要指定具体的clocking块。
systemverilog
interface my_interface(input bit clk);
clocking cb @(posedge clk);
// ...
endclocking
// 指定默认clocking
default clocking cb;
endinterface
在测试平台中,可以这样使用:
systemverilog
module testbench;
my_interface intf(clk);
// 由于在接口中指定了默认clocking,我们可以直接使用
initial begin
// 等待默认clocking的时钟事件
@(intf.cb); // 或者直接写 @(intf) 也可以,因为指定了默认clocking
end
endmodule
8. 总结
Clocking块是SystemVerilog中用于测试平台同步的重要构造,它通过定义时钟事件和延迟,使得信号采样和驱动更加可控,减少了竞争条件。在接口中定义clocking块,可以使测试平台更加模块化和可重用
SystemVerilog中的clocking块是用于同步和采样信号的重要构造,主要用于验证环境(testbench)中。它可以简化时钟域信号的时序管理,避免竞争条件。
基本语法
clocking clocking_name @(clock_event);
default input #setup_time output #hold_time;
input signal_name;
output signal_name;
inout signal_name;
endclocking
主要用途
1. 定义信号采样和驱动时序
interface bus_if(input bit clk);
logic [7:0] data;
logic valid;
logic ready;
// 定义clocking块
clocking cb @(posedge clk);
// 默认时序:输入在时钟沿前1ns采样,输出在时钟沿后2ns驱动
default input #1ns output #2ns;
// 具体信号的时序(可覆盖默认值)
input #0 data; // 在时钟沿采样
input valid; // 使用默认的#1ns
output #1 ready; // 在时钟沿后1ns驱动
endclocking
endinterface
2. 在测试平台中使用
module testbench;
bit clk;
logic [7:0] data;
logic valid;
// 实例化接口
bus_if bus(.clk(clk));
// 生成时钟
always #5 clk = ~clk;
initial begin
// 等待时钟事件
@(bus.cb);
// 采样输入信号(在时钟沿前1ns)
$display("Sampled data: %0h", bus.cb.data);
// 驱动输出信号(在时钟沿后2ns)
bus.cb.ready <= 1;
// 同步等待
repeat(3) @(bus.cb);
// 使用clocking事件作为时序控制
@(bus.cb iff bus.cb.valid);
end
endmodule
关键特性
1. 时序控制
clocking cb @(posedge clk);
// 不同时序规格
input #1step signal1; // 在前一个时间步采样
input #0 signal2; // 在时钟沿采样
input signal3; // 使用默认时序
output #2 signal4; // 时钟沿后2ns驱动
output #1step signal5; // 前一时间步驱动
endclocking
2. 跨模块使用
// 在interface中定义
interface mem_if(input bit clk);
logic [31:0] addr;
logic [31:0] data;
logic wr_en;
clocking drv_cb @(posedge clk);
default input #1 output #2;
output addr, data, wr_en;
endclocking
clocking mon_cb @(posedge clk);
default input #1;
input addr, data, wr_en;
endclocking
endinterface
// 在测试中使用不同视角
module test;
mem_if mem_intf(clk);
// 驱动端视角
initial begin
mem_intf.drv_cb.addr <= 32'h1000;
mem_intf.drv_cb.data <= 32'hABCD;
end
// 监控端视角
initial begin
forever begin
@(mem_intf.mon_cb);
$display("Monitor: addr=%h", mem_intf.mon_cb.addr);
end
end
endmodule
3. 与program块结合
program test_program(bus_if bus);
initial begin
// 自动使用clocking块的时序
bus.cb.data <= 8'hFF;
// ##延迟操作符(clocking cycle delay)
##2; // 等待2个时钟周期
// 使用clocking事件
@(bus.cb);
// 条件等待
@(bus.cb iff bus.cb.valid == 1);
// 采样
automatic logic [7:0] sampled = bus.cb.data;
end
endprogram
常用技巧
1. 避免竞争条件
clocking safe_cb @(posedge clk);
// 输入在时钟沿前采样,避免建立时间违规
default input #1step;
// 输出在时钟沿后驱动,避免保持时间违规
default output #0;
endclocking
2. 多时钟域处理
interface multi_clk_if(input bit clk1, input bit clk2);
clocking cb1 @(posedge clk1);
input data1;
output ctrl1;
endclocking
clocking cb2 @(posedge clk2);
input data2;
output ctrl2;
endclocking
endinterface
3. 在类中使用
class driver;
virtual bus_if vif;
task run();
forever begin
@(vif.cb); // 同步到时钟
// 使用clocking驱动信号
vif.cb.addr <= new_addr;
vif.cb.wr_en <= 1;
end
endtask
endclass
实际示例
// 完整的接口示例
interface axi_if(input bit clk, input bit rst_n);
// 信号声明
logic [31:0] awaddr;
logic awvalid;
logic awready;
// 驱动clocking块
clocking drv_cb @(posedge clk);
default input #1 output #2;
output awaddr, awvalid;
input awready;
endclocking
// 监控clocking块
clocking mon_cb @(posedge clk);
default input #1;
input awaddr, awvalid, awready;
endclocking
// 同步复位
modport driver(clocking drv_cb, input rst_n);
modport monitor(clocking mon_cb, input rst_n);
endinterface
// 使用示例
module tb;
bit clk, rst_n;
axi_if axi_intf(clk, rst_n);
// 时钟生成
always #5 clk = ~clk;
initial begin
rst_n = 0;
#100 rst_n = 1;
// 使用clocking块同步操作
@(axi_intf.drv_cb);
axi_intf.drv_cb.awaddr <= 32'h4000_0000;
axi_intf.drv_cb.awvalid <= 1;
// 等待握手
@(axi_intf.drv_cb iff axi_intf.drv_cb.awready);
// 清除信号
axi_intf.drv_cb.awvalid <= 0;
end
endmodule
注意事项
- clocking块只能在interface、module、program或checker中声明
- 不可综合,仅用于验证
- input表示采样,output表示驱动
- #1step是特殊的延时,表示前一个仿真时间步
- 建议在interface中定义clocking,提高重用性
clocking块是构建健壮验证环境的关键工具,它能有效管理时序并减少竞争条件。
浙公网安备 33010602011771号