3.4 Verilog Timing Checks
Category Advanced Verilog Tutorial
Keywords: setup hold recovery removal width period
Specifying path delays aims to make the timing of simulations more closely resemble the timing of actual digital circuits. Utilizing timing constraints to perform timing simulations on digital designs, checking for any violations of timing constraints, and making necessary modifications is an indispensable process in digital design.
Verilog provides several system tasks for timing checks. These system tasks can only be called within a specify block. The following introduces six commonly used system tasks for timing checks: $setup, $hold, $recovery, $removal, $width, and $period.
$setup, $hold
The system task $setup is used to check the setup time constraints of components in the design, and $hold is used to check the hold time constraints. The usage format is as follows:
$setup(data_event, ref_event, setup_limit);
data_event: The signal being checked to determine if it violates constraints
ref_event: The reference signal used for checking, usually the edge of a clock signal
setup_limit: The set minimum setup time
If T( refevent - dataevent) < setup_limit, a report of a violation will be printed.
$hold(ref_event, data_event, hold_limit);
data_event: The signal being checked to determine if it violates constraints
ref_event: The reference signal used for checking, usually the edge of a clock signal
hold_limit: The set minimum hold time
If T( dataevent - refevent ) < hold_limit, a report of a violation will be printed.
Note: The input port positions for $setup and $hold are different.
Verilog provides a system task to check both setup and hold times simultaneously:
$setuphold (ref_event, data_event, setup_limit, hold_limit);
The following illustrates the use of $setup and $hold by completing an operation where a number is multiplied by 15.
In Verilog, multiplying a variable by a constant is generally done using the method of shifting and adding, for example, the operation of multiplying the variable num by 15 can be represented as:
num x 15 = (num << 3) + (num << 2) + (num << 1) + num
This operation requires 3 adders. The following models the adder and specifies path delays.
The description of the full adder function can refer to Section 3.1 of the "Verilog Tutorial".
Example
//Single bit full adder, specifying path delays
module full_adder1(
input Ai, Bi, Ci,
output So, Co);
assign So = Ai ^ Bi ^ Ci ;
assign Co = (Ai & Bi) | (Ci & (Ai | Bi));
specify
(Ai, Bi, Ci *> So) = 1.1 ;
(Ai, Bi *> Co) = 1.3 ;
(Ci => Co) = 1.2 ;
endspecify
endmodule
//8-bit wide adder instantiation
module full_adder8(
input [7:0] a , //adder1
input [7:0] b , //adder2
input c , //input carry bit
output [7:0] so , //adding result
output co //output carry bit
);
wire [7:0] co_temp ;
full_adder1 u_adder0(
.Ai (a[0]),
.Bi (b[0]),
.Ci (c==1'b1 ? 1'b1 : 1'b0),
.So (so[0]),
.Co (co_temp[0]));
genvar i ;
generate
for(i=1; i<=7; i=i+1) begin: adder_gen
full_adder1 u_adder(
.Ai (a[i]),
.Bi (b[i]),
.Ci (co_temp[i-1]),
.So (so[i]),
.Co (co_temp[i]));
end
endgenerate
assign co = co_temp
Alternatively, adjust the logic to complete 3 addition operations within a single cycle, and change it to be spread over two cycles, with an additional register added in between for buffering to reduce timing pressure. At the same time, the period of change for the variable num should also be doubled.
The testbench is modified as follows:
## Example
timescale 1ns/1ns
define LOGIC_BUF
module test;
parameter CYCLE_10NS = 10ns;
reg clk;
initial begin
clk = 0;
# 111;
forever begin
#(CYCLE_10NS/2) clk = ~clk;
end
end
reg slow_flag = 0;
always @(posedge clk) begin
`ifdef LOGIC_BUF
slow_flag <= ~slow_flag;
`else
slow_flag <= 1'b1;
`endif
end
reg [7:0] num = 0;
always @(posedge clk) begin
if(slow_flag)
num[3:0] <= num[3:0] + 1;
end
wire [7:0] adder1;
full_adder8 u1_adder8(
.a (num<<2),
.b (num<<3),
.c (1'b0),
.so (adder1),
.co ());
wire [7:0] adder2;
full_adder8 u2_adder8(
.a (num<<1),
.b (num),
.c (1'b0),
.so (adder2),
.co ());
//====== for better time=========
//adding buffer
wire [7:0] adder1_r, adder2_r;
D8 adder1_buf(
.d (adder1),
.clk (clk),
.q (adder1_r));
D8 adder2_buf(
.d (adder2),
.clk (clk),
.q (adder2_r));
`ifdef LOGIC_BUF
wire [7:0] adder1_t = adder1_r;
wire [7:0] adder2_t = adder2_r;
`else
wire [7:0] adder1_t = adder1;
wire [7:0] adder2_t = adder2;
`endif
wire [7:0] adder3;
full_adder8 u3_adder8(
.a (adder1_t),
.b (adder2_t),
.c (1'b0),
.so (adder3),
.co ());
wire [7:0] res_mul15;
D8 data_store(
.d (adder3),
.clk (clk),
.q (res_mul15));
initial begin
forever begin
#100;
if ($time >= 1000) $finish;
end
end
endmodule // test
At this point, there are no more violations in the simulation report, and the simulation screenshot is as follows.
As can be seen from the figure, the signal can arrive early and remain unchanged for up to 8.6 ns, fully meeting the setup time requirements of the timing.
The fundamental principle of this method is to spread the timing of multiple signal changes over multiple cycles to meet the requirements of timing constraints. In addition, pipeline design, parallel design, and so on can all optimize timing.
### $recovery, $removal
The concepts of setup time and hold time both appear in the design of synchronous circuits.
For asynchronous reset flip-flops, the asynchronous reset signal also needs to meet the recovery time and removal time to effectively reset and release the reset to prevent metastability.
When releasing the reset, the reset signal needs to recover to the non-reset state a period of time before the clock edge arrives, which is the recovery time. Similar to the setup time under synchronous clocks.
When resetting, the reset signal still needs to remain unchanged for a period of time after the clock edge arrives, which is the removal time. Similar to the hold time under synchronous clocks.
The schematic diagram of recovery and removal time is as follows.
The system tasks $recovery and $removal are used to check the recovery and removal times, respectively, as follows:
$recovery (ref_event, data_event, recovery_limit); ```
ref_event: The reference signal for checking, generally the edge of the reset or zeroing signal;
data_event: The signal to be checked, generally the edge of the clock signal.
recovery_limit: The set minimum recovery time.
When ref_event (reset) < data_event (clock) and T(data_event - ref_event) < recovery_limit, that is, if the reset signal