Easy Tutorial
❮ Cpp Two Colon Usage What Is A Virtual Method ❯

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);

If T( refevent - dataevent) < setup_limit, a report of a violation will be printed.

$hold(ref_event, data_event, hold_limit);

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<&lt;2),
    .b (num<&lt;3),
    .c (1'b0),
    .so (adder1),
    .co ());

wire [7:0] adder2;
full_adder8 u2_adder8(
    .a (num<&lt;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); ```

When ref_event (reset) < data_event (clock) and T(data_event - ref_event) < recovery_limit, that is, if the reset signal

Follow on WeChat

❮ Cpp Two Colon Usage What Is A Virtual Method ❯