14.1 General This clause describes the following:
— Clocking block declarations(时钟块声明)
— Input and output skews(输入和输出偏斜)
— Clocking block signal events(时钟块信号事件)
— Cycle delays(周期延迟)
— Synchronous events(同步事件)
— Synchronous drives(同步驱动器)
14.2 Overview
模块端口连接和接口可以指定测试台通过其与被测设备(DUT)通信的信号或网络。然而,这样的规范并没有明确表示任何时序规范、同步要求或时钟规范。
clocking block构造识别时钟信号并捕获被建模的块的时序和同步要求。在关键字clocking和endclocking之间定义了一个时钟块。
clocking块集合了与特定时钟同步的信号,并使其时序明确。clocking块是基于周期的方法中的一个关键元素,它使用户能够在更高的抽象级别上编写testbenches。测试可以根据周期和事务来定义,而不是关注时间上的信号和转换。根据环境的不同,测试台可以包含一个或多个clocking块,每个clocking块都包含自己的时钟加上任意数量的信号。
clocking块将时序和同步细节从测试台的结构、功能和程序元素中分离出来。因此,用于采样和驱动时钟块信号的时序是隐含的并且相对于clocking块的时钟。这样就可以非常简洁地编写一组键操作,而无需明确使用时钟或指定时间。这些操作如下:
— Synchronous events(同步事件)
— Input sampling(输入采样)
— Synchronous drives(同步驱动器)
14.3 Clocking block declaration
clocking块的语法如下语法14-1所示。
clocking_declaration ::= // from A.6.11[ default ] clocking [ clocking_identifier ] clocking_event ;{ clocking_item }
endclocking [ : clocking_identifier ]| global clocking [ clocking_identifier ] clocking_event ; endclocking [ : clocking_identifier ]
clocking_event ::=@ identifier| @ ( event_expression )
clocking_item ::=default default_skew ;| clocking_direction list_of_clocking_decl_assign ;| { attribute_instance } assertion_item_declaration
default_skew ::=input clocking_skew| output clocking_skew| input clocking_skew output clocking_skew
clocking_direction ::=input [ clocking_skew ]| output [ clocking_skew ]| input [ clocking_skew ] output [ clocking_skew ]| inout
list_of_clocking_decl_assign ::= clocking_decl_assign { , clocking_decl_assign }
clocking_decl_assign ::= signal_identifier [ = expression ]
clocking_skew ::=edge_identifier [ delay_control ]| delay_control
edge_identifier ::= posedge | negedge | edge // from A.7.4
delay_control ::= // from A.6.5# delay_value| # ( mintypmax_expression )
语法14-1—— clocking块语法(摘录自附录A)
delay_control应为时间数值或计算为正整数值的常量表达式。
clocking_identifier指定要声明的时钟块的名称。只有默认的时钟块可以是未命名的。不能引用未命名时钟块中的声明。
signal_identifier在包含时钟块声明的范围中指定一个信号(一个网络或变量),并在时钟块中定义一个clockvar。指定的信号称为时钟信号。除非使用分层表达式(见14.5),否则时钟信号和时钟变量名称应相同。时钟信号指定限制在程序块内的变量是违法的(见6.21)。
clocking_event指定特定事件作为时钟块的时钟。用于驱动和采样给定时钟块中指定的所有其他信号的时序由其时钟事件控制。有关采样和驱动时钟信号的精确时序语义的详细信息,请参见14.13和14.16。
向clocking_direction为input类型的任何clockvar写入都是非法的。
读取clocking_direction为ouput类型的任何clockvar的值都是非法的。
clocking_direction为inout的clockvar应表现为两个clockvar,一个输入一个输出,具有相同的名称和相同的clocking_signal。读取此类输入clockvar的值应等同于读取相应的输入clockvar。
写入此类输入clockvar应等同于写入相应的输出clockvar。
clocking_skew确定要对信号进行采样或驱动的距离时钟事件的时间单位。输入偏斜隐含地为负,也就是说,它们总是指时钟之前的时间,而输出偏斜总是指时钟之后的时间(见14.4)。当时钟事件指定一个简单的边沿而不是一个数字时,可以将偏斜指定为信号的特定边沿。通过使用默认时钟项,可以为整个块指定单个偏斜。
clocking ck1 @(posedge clk);default input #1step output negedge; // legal// outputs driven on the negedge clkinput ... ;output ... ;
endclockingclocking ck2 @(clk); // no edge specified!default input #1step output negedge; // legalinput ... ;output ... ;
endclocking
分配给clockvar的表达式指定要与时钟块相关联的信号与指定的分层表达式相关联。例如,可以使用跨模块引用来代替本地端口。有关更多信息,请参阅14.5。
Example:
clocking bus @(posedge clock1);default input #10ns output #2ns;input data, ready, enable = top.mem1.enable;output negedge ack;input #1step addr;
endclocking
在前面的例子中,第一行声明了一个称为bus的时钟块,该时钟块将在信号clock1的上升沿上被时钟控制。第二行规定,默认情况下,时钟块中的所有信号应使用10ns的输入偏斜和2ns的输出偏斜。下一行将三个输入信号添加到时钟块:data, ready和enable;最后一个信号指的是分层信号top.mem1.enable。第四行将信号ack添加到时钟块并覆盖默认输出偏斜,使得ack在时钟的下降沿上被驱动。最后一行加上信号addr并覆盖默认输入偏斜,使得addr在时钟的上升沿之前一步被采样。
除非另有规定,否则默认输入偏差为1step,默认输出偏差为0。step是一个特殊的时间单位,其值在3.14.3中定义。1step输入偏斜允许输入信号在时钟事件之前的时间步中(即,在之前的延迟区域中)对其稳态值进行采样。
14.4 Input and output skews
在指定的时钟事件处对输入(或inout)信号进行采样。如果指定了输入偏斜,则在时钟事件之前以偏斜时间单位对信号进行采样。类似地,输出(或inout)信号在相应的时钟事件之后被驱动为偏斜模拟时间单位。图14-1显示了上升沿时钟的基本采样和驱动时序。
偏斜应该是一个常量表达式,并且可以指定为一个参数。如果偏斜没有指定时间单位,则使用当前时间单位。如果使用数字,则使用当前范围的时间刻度来解释偏移。
clocking dram @(clk);input #1ps address;input #5 output #6 data;
endclocking
1step的输入偏斜表示要在前一时间步结束时对信号进行采样。换言之,采样的值总是紧接在相应时钟边沿之前的信号的最后一个值。
注——当程序块外的事件控件对与时钟块相同的时钟和事件控件尝试读取时钟块成员后的语句敏感时,时钟块并不能消除潜在的竞争。竞争是在读取旧的采样值和新的采样值之间进行的。
具有明确#0偏斜的输入应在其相应时钟事件的同时进行采样,但为了避免竞争,应在观测区域对其进行采样。
同样,在Re-NBA区域,无偏斜(或显式#0偏斜)的时钟块输出应在其指定时钟事件的同时驱动。偏斜是声明性结构;因此,它们在语义上与语法相似的程序延迟语句有很大不同。特别是,显式#0偏斜不会挂起任何进程,也不会执行或采样Inactive区域中的值。
14.5 Hierarchical expressions
时钟块中的任何信号都可以与任意层次表达式相关联。如14.3中所述,通过在层次表达式后面加上等号(=)来引入层次表达式:
clocking cd1 @(posedge phi1);input #1step state = top.cpu1.state;
endclocking
但是,层次表达式并不局限于其他作用域中的简单名称或信号。它们可以用于声明其他作用域或当前作用域中的信号的切片和级联(或其组合)。
clocking mem @(clock);input instruction = { opcode, regA, regB[3:1] };
endclocking
在时钟块中,在其声明中分配给信号的任何表达式都应是在端口连接到适当方向的端口时合法的表达式。时钟input或inout声明中分配给信号的任何表达式都应是连接到模块输入端口的合法表达式。在时钟output或inout声明中分配给信号的任何表达式都应是连接到模块输出端口的合法表达式。
clocking中inout声明不是inout端口;它是两个时钟声明的简写,一个输入,一个输出,具有相同的信号。因此这种信号必须同时满足时钟input和时钟输出的要求,但是不需要满足连接到模块的输入输出端口的更严格的要求。特别地,将变量指定为时钟inout信号是可以接受的。
14.6 Signals in multiple clocking blocks
相同的信号——时钟、输入、输出或输出——可以出现在多个时钟块中。当时钟块使用相同的时钟(或时钟表达式)时,它们应共享相同的同步事件,其方式与同一时钟可以控制多个锁存器的方式相同。14.13中描述了输入语义,14.16中描述了输出语义。
14.7 Clocking block scope and lifetime
时钟块既是一个声明,也是该声明的一个实例。不需要单独的实例化步骤。相反,会为包含声明的块的每个实例创建一个副本(就像一个always过程一样)。一旦声明,时钟信号可通过时钟块名称和点(.)运算符获得:
dom.sig // signal sig in clocking dom
不能嵌套多个时钟块。它们不能在函数、任务或包内部声明,也不能在编译单元中的所有声明之外声明。时钟块只能在模块、接口、检查器或程序内部声明(见第24条)。时钟块的静态生存期和作用域在其封闭模块、接口或程序的本地。
14.8 Multiple clocking blocks example
在这个例子中,一个简单的测试程序包括两个时钟块。第24条讨论了本例中使用的程序结构。
program test( input phi1, input [15:0] data, output logic write,input phi2, inout [8:1] cmd, input enable);reg [8:1] cmd_reg;clocking cd1 @(posedge phi1);input data;output write;input state = top.cpu1.state;endclockingclocking cd2 @(posedge phi2);input #2 output #4ps cmd;input enable;endclockinginitial begin// program begins here...// user can access cd1.data , cd2.cmd , etc…endassign cmd = enable ? cmd_reg: 'x;
endprogram
测试程序可以实例化并连接到DUT(cpu和mem)。
module top;logic phi1, phi2;wire [8:1] cmd; // cannot be logic (two bidirectional drivers)logic [15:0] data;test main (phi1, data, write, phi2, cmd, enable);cpu cpu1 (phi1, data, write);mem mem1 (phi2, cmd, enable);endmodule
14.9 Interfaces and clocking blocks
时钟块封装共享公共时钟的一组信号;因此,使用SystemVerilog接口指定时钟块(见第25条)可以显著减少连接测试台所需的代码量。此外,由于测试台内时钟块中的信号方向与测试台有关,而不是与测试中的设计有关,因此modport声明(见25.5)可以适当地描述任一方向。测试台程序可以包含在程序中,其端口可以是与每个时钟块中声明的信号相对应的接口。当从测试台侧(即modport测试)观察时,接口的导线将具有与时钟块中指定的方向相同的方向,而当从DUT(即modport-DUT)观察时则相反。
例如,可以使用以下接口重写前面的示例:
interface bus_A (input clk);logic [15:0] data;logic write;modport test (input data, output write);modport dut (output data, input write);
endinterfaceinterface bus_B (input clk);logic [8:1] cmd;logic enable;modport test (input enable);modport dut (output enable);
endinterfaceprogram test( bus_A.test a, bus_B.test b );clocking cd1 @(posedge a.clk);input data = a.data;output write = a.write;inout state = top.cpu1.state;endclockingclocking cd2 @(posedge b.clk);input #2 output #4ps cmd = b.cmd;input en = b.enable;endclockinginitial begin// program begins here...// user can access cd1.data, cd1.write, cd1.state,// cd2.cmd, and cd2.enend
endprogram
测试模块可以像以前一样进行实例化和连接:
module top;logic phi1, phi2;bus_A a (phi1);bus_B b (phi2);test main (a, b);cpu cpu1 (a);mem mem1 (b);endmodule
14.10 Clocking block events
无论用于声明时钟块的实际时钟事件是什么,都可以通过使用时钟块名称直接获得时钟块的时钟事件。
For example:
clocking dram @(posedge phi1);inout data;output negedge #1 address;
endclocking
dram时钟块的时钟事件可用于等待该特定事件:
@(dram);
前面的语句相当于@(posedge phi1)。
14.11 Cycle delay: ##
##运算符可用于将执行延迟指定数量的时钟事件或时钟周期。周期延迟语句的语法如下语法14-2所示。
procedural_timing_control_statement ::= // from A.6.5procedural_timing_control statement_or_null
procedural_timing_control ::=...| cycle_delay
cycle_delay ::= // from A.6.11## integral_number| ## identifier| ## ( expression )
Syntax 14-2—Cycle delay syntax (excerpt from Annex A)
表达式可以是任何计算结果为正整数值的SystemVerilog表达式。一个周期的构成由默认的计时效应决定(见14.12)。如果没有为当前模块、接口、检查器或程序指定默认时钟,则编译器应发出错误。
Example:
##5; // wait 5 cycles (clocking events) using the default clocking
##(j + 1); // wait j+1 cycles (clocking events) using the default clocking
周期延迟计时控制应等待指定数量的时序事件。这意味着对于在与相关联的时钟事件不一致的模拟时间执行的##1语句,调用过程应延迟相关时钟周期的一小部分。
##0的周期延迟经过特殊处理。如果当前时间步长中尚未发生时钟事件,则##0周期延迟将暂停调用过程,直到时钟事件发生。当一个进程执行##0周期延迟,并且相关的时钟事件已经在当前时间步长中发生时,该进程应继续执行而不暂停。当在同步驱动器的右侧使用时,##0周期延迟将没有任何影响,就好像它不存在一样。
周期延迟时间控制在阻塞或非阻塞赋值语句中的分配内延迟中使用是不合法的。
14.12 Default clocking
一个时钟块可以指定为给定模块、接口、程序或检查器内所有周期延迟操作的默认值。默认时钟规范语句的语法如下语法14-3所示。
module_or_generate_item_declaration ::= // from A.1.4...| default clocking clocking_identifier ;...
checker_or_generate_item_declaration ::= // from A.1.8...| default clocking clocking_identifier ;...
clocking_declaration ::= // from A.6.11[ default ] clocking [ clocking_identifier ] clocking_event ;{ clocking_item }
endclocking [ : clocking_identifier ]
Syntax 14-3—Default clocking syntax (excerpt from Annex A)
clocking_identifier应为时钟块的名称。在模块、接口、程序或检查器中只能指定一个默认 clocking。在同一模块、接口、程序或检查器中指定默认 clocking不止一次将导致编译器错误。默认 clocking仅在包含默认 clocking规范语句的范围内有效。此范围包括包含声明的模块、接口、程序或检查器,以及任何嵌套的模块、界面或检查器。它不包括实例化的模块、接口或检查器。
Example 1: Declaring a clocking as the default:
program test(input logic clk, input logic [15:0] data);default clocking bus @(posedge clk);inout data;endclockinginitial begin## 5;if (bus.data == 10)## 1;else...end
endprogram
Example 2: Assigning an existing clocking to be the default:
module processor ...clocking busA @(posedge clk1); ... endclockingclocking busB @(negedge clk2); ... endclockingmodule cpu( interface y );default clocking busA ;initial begin## 5; // use busA => (posedge clk1)...endendmodule
endmodule
14.13 Input sampling
所有时钟块输入(输入或输出)都在相应的时钟事件中采样。如果输入偏斜不是显式#0,则采样的值对应于时钟事件之前时间步长偏斜时间单位的延迟区域的信号值(参见14.4中的图14-1)。如果输入偏斜是显式#0,则采样的值对应于观测区域中的信号值。在这种情况下,在观测区域处理结束时,新采样值应可用于读取。如果在处理反应区域时,模拟必须在不提前时间的情况下处理活动事件(从而多次执行观察区域),则除非在活动区域集中发生新的时钟事件,否则不应对以#0偏斜采样的时钟输入进行重新采样。
注释--当时钟事件由程序的执行触发时,在时钟块输入值的更新和读取该值而不与相应的时钟事件同步的程序之间存在潜在的竞争。当从模块内触发计时块事件时,此竞争不存在。
在处理其指定的时钟事件时,时钟块应在触发与时钟块名称相关的事件之前更新其采样值。该事件应在观测区域触发。因此,保证等待时钟块本身的进程读取更新的采样值,而与执行等待或触发进程的调度区域无关。
例如:
clocking cb @(negedge clk);input v;
endclockingalways @(cb) $display(cb.v);
always @(negedge clk) $display(cb.v);
保证前面的第一个always过程显示信号v的更新采样值。相反,第二个always 过程表现出潜在的竞争,并且可以显示旧的或新更新的采样值。当input或inout时钟变量出现在任何表达式中时,其值是信号的采样值;即,时钟块在最近的时钟事件中采样的值。当同一信号是多个时钟块的输入时,语义是直接的;每个时钟块利用其自己的时钟事件对相应的信号进行采样。
14.14 Global clocking
时钟块可以被声明为设计层次结构的全部或部分的全局时钟。全局时钟的主要目的是指定模拟中的哪个时钟事件对应于形式验证中使用的主系统时钟(见F.3.1)。当存在多个时钟并且构建单个全局时钟事件具有挑战性时,可以针对整个设计或者针对设计中的不同子系统单独地进行这样的规范。全局时钟声明的语法如下语法14-4所示。
clocking_declaration ::= // from A.6.11...| global clocking [ clocking_identifier ] clocking_event ; endclocking [ : clocking_identifier ]
Syntax 14-4—Global clocking syntax (excerpt from Annex A)
全局时钟可以在模块、接口、检查器或程序中声明。给定的模块、接口、检查器或程序最多应包含一个全局时钟声明。全局时钟不应在生成块中声明。
尽管在设计层次结构的不同部分可能会出现多个全局时钟声明,但在详细设计层次结构中的每个点上,最多只能有一个全局时钟宣布有效。$global_clock系统函数应用于显式引用有效全局时钟声明中的事件表达式。使用以下分层查找规则来确定特定引用的有效全局时钟声明,这些规则反复检查设计层次结构,以找到最接近引用点的全局时钟声明。
a) 在封闭的模块、接口、检查器或程序实例范围中查找全局时钟声明。如果找到,查找将终止,结果为该全局时钟声明的事件表达式。如果未找到,并且当前范围是顶级层次结构块(见3.11),则查找终止,并将导致错误。否则,继续执行步骤b)。
b) 在封闭实例化的父模块、接口或检查器实例范围中查找全局时钟声明。如果找到,查找将终止,结果为该全局时钟声明的事件表达式。否则,继续向上层次结构,直到找到全局时钟声明或到达顶级层次结构块。如果没有找到全局时钟声明,并且到达了顶级层次结构块,则查找将终止,并将导致错误。