ch6-Clocking block

2019-11-02

SystemVerilog Clocking Blocks

​ Module ports and interfaces by default do not specify any timing requirements or synchronization schemes between signals. A clocking block defined between clocking and endcocking does exactly that. It is a collection of signals synchronous with a particular clock and helps to specify the timing requirements between the clock and the signals.

This would allow test writers to focus more on transactions rather than worry about when a signal will interact with respect to a clock. A testbench can have many clocking blocks, but only one block per clock.

Syntax

1
2
3
4
5
[default] clocking [identifier_name] @ [event_or_identifier]
default input #[delay_or_edge] output #[delay_or_edge]
[list of signals]
endclocking

The delay_value represents a skew of how many time units away from the clock event a signal is to be sampled or driven. If a default skew is not specified, then all input signals will be sampled #1step and output signlas driven 0ns after the specified event.

1
2
3
4
5
6
7
8
9
10
11
12
13
clocking ckb @ (posedge clk);
default input #1step output negedge;
input ...;
output ...;
endclocking

clocking ck1 @ (posedge clk);
default input #5ns output #2ns;
input data, valid, ready = top.ele.ready;
output negedge grant;
input #1step addr;
endclocking

Note the following:

  • A clocking block called ck1 is created which will be active on the positive edge of clk
  • By default, all input signals within the clocking block will be sampled 5ns before and all output signals within the clocking block will be driven 2ns after the positive edge of the clock clk
  • data, valid and ready are declared as inputs to the block and hence will be sampled 5ns before the posedge of clk
  • grant is an output signal to the block with its own time requirement. Here grant will be driven at the negedge of clk instead of the default posedge.

Use within an interface

Simply put, a clocking block encapsulates a bunch of signals that share a common clock. Hence declaring a clocking block inside an interface can help save the amount of code required to connect to the testbench and may help save time during development.

重要

重要:Signal directions inside a clocking block are with respect to the testbench and not the DUT.

SystemVerilog Clocking Blocks Part II

Clocking blocks allow inputs to be sampled and outputs to be driven at a specified clock event. If an input skew is mentioned for a clocking block, then all input signals within that block will be sampled at skew time units before the clock event. If an output skew is mentioned for a clocking block, then all output signals in that block will be driven skewtime units after the corresponding clock event.

What are input and output skews ?

​ A skew is specified as a constant expression or as a parameter. If only a number is used, then the skew is interpreted to follow the active timescale in the given scope.

1
2
3
4
5
6
clocking cb @(clk);
input #1ps req; // sample 1ps before clock edge
output #2 gnt; // drive 2ns after clock edge
input #1 output #3 sig; // input skew and output skew
endclocking

​ In the example given above, we have declared a clocking block of the name cb to describe when signals belonging to this block has to be sampled. Signal req is specified to have a skew of 1ps and will be sampled 1 ps before the clock edge clk. The output signal gnt has an output skew of 2 time units and hence will follow the timescale followed in the current scope. If we have a timescale of 1ns/1ps then #2 represents 2 ns and hence will be driven 2 ns after the clock edge. The last signal sig is of inout type and will be sampled 1 ns before the clock edge and driven 3 ns after the clock edge.

An input skew of 1step indicates that the signal should be sampled at the end of the previous time step, or in other words, immediately before the positive clock edge.

1
2
3
4
clocking cb @(posedge clk);
input #1step req;
endclocking

​ Inputs with explicit #0 skew will be sampled at the same time as their corresponding clocking event, but in the Observed region to avoid race conditions. Similarly, outputs with no skew or explicit #0 will be driven at the same time as the clocking event, in the Re-NBA region.

Example

​ Consider a simple design with inputs clk and req and drives an output signal gnt. To keep things simple, lets just provide grant as soon as a request is received.

1
2
3
4
5
6
7
8
module des (input req, clk, output reg gnt);
always @ (posedge clk)
if (req)
gnt <= 1;
else
gnt <= 0;
endmodule

To deal with the design port signals, let’s create a simple interface called _if.

1
2
3
4
5
6
7
8
9
10
interface _if (input bit clk);
logic gnt;
logic req;

clocking cb @(posedge clk);
input #1ns gnt;
output #5 req;
endclocking
endinterface

The next step is to drive inputs to the design so that it gives back the grant signal.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
module tb;  
bit clk;

// Create a clock and initialize input signal
always #10 clk = ~clk;
initial begin
clk <= 0;
if0.cb.req <= 0;
end

// Instantiate the interface
_if if0 (.clk (clk));

// Instantiate the design
des d0 ( .clk (clk),
.req (if0.req),
.gnt (if0.gnt));

// Drive stimulus
initial begin
for (int i = 0; i < 10; i++) begin
bit[3:0] delay = $random;
repeat (delay) @(posedge if0.clk);
if0.cb.req <= ~ if0.cb.req;
end
#20 $finish;
end
endmodule

It can be seen from simulation output window that req is driven #5ns after the clock edge.

img

Output skew

To get a clear picture of the output skew, lets tweak the interface to have three different clocking blocks each with a different output skew. Then let us drive req with each of the clocking blocks to see the difference.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface _if (input bit clk);
logic gnt;
logic req;

clocking cb_0 @(posedge clk);
output #0 req;
endclocking

clocking cb_1 @(posedge clk);
output #2 req;
endclocking

clocking cb_2 @(posedge clk);
output #5 req;
endclocking
endinterface

In our testbench, we’ll use a for loop to iterate through each stimulus and use a different clocking block for each iteration.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module tb;
// ... part of code same as before

// Drive stimulus
initial begin
for (int i = 0; i < 3; i++) begin
repeat (2) @(if0.cb_0);
case (i)
0 : if0.cb_0.req <= 1;
1 : if0.cb_1.req <= 1;
2 : if0.cb_2.req <= 1;
endcase
repeat (2) @ (if0.cb_0);
if0.req <= 0;
end
#20 $finish;
end

endmodule

img

Input skew

To understand input skew, we’ll change the DUT to simply provide a random value every #1ns just for our purpose.

1
2
3
4
module des (output reg[3:0] gnt);
always #1 gnt <= $random;
endmodule

The interface block will have different clocking block declarations like before each with a different input skew.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
interface _if (input bit clk);
logic [3:0] gnt;

clocking cb_0 @(posedge clk);
input #0 gnt;
endclocking

clocking cb_1 @(posedge clk);
input #1step gnt;
endclocking

clocking cb_2 @(posedge clk);
input #1 gnt;
endclocking

clocking cb_3 @(posedge clk);
input #2 gnt;
endclocking
endinterface

In the testbench, we’ll fork 4 different threads at time 0ns where each thread waits for the positive edge of the clock and samples the output from DUT.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
module tb;
bit clk;

always #5 clk = ~clk;
initial clk <= 0;

_if if0 (.clk (clk));
des d0 (.gnt (if0.gnt));

initial begin
fork
begin
@(if0.cb_0);
$display ("cb_0.gnt = 0x%0h", if0.cb_0.gnt);
end
begin
@(if0.cb_1);
$display ("cb_1.gnt = 0x%0h", if0.cb_1.gnt);
end
begin
@(if0.cb_2);
$display ("cb_2.gnt = 0x%0h", if0.cb_2.gnt);
end
begin
@(if0.cb_3);
$display ("cb_3.gnt = 0x%0h", if0.cb_3.gnt);
end
join
#10 $finish;
end

endmodule

The output waveform is shown below and it can be seen that the design drives a random value every #1ns.

img

It’s important to note that the testbench code which sampled through cb_1 clocking block managed to get the value 0x3 while cb_0 got 0xd. Note that these values may be different for other simulators since they can take a different randomization seed value.

Simulation Log

1
2
3
4
5
6
ncsim> run
cb_3.gnt = 0x9
cb_2.gnt = 0x3
cb_1.gnt = 0x3
cb_0.gnt = 0xd
Simulation complete via $finish(1) at time 15 NS + 0

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!