ch5-Interprocess Communication

2019-10-24

SystemVerilog Interprocess Communication

​ Components in a testbench often need to communicate with each other to exchange data and check output values of the design. A few mechanisms that allow components or threads to affect the control flow of data are shown in the table below.

Events Different threads synchronize with each other via event handles in a testbench
Semaphores Different threads might need to access the same resource; they take turns by using a semaphore
Mailbox Threads/Components need to exchange data with each other; data is put in a mailbox and sent

What are Events ?

An event is a way to synchronize two or more different processes. One process waits for the event to happen while another process triggers the event. When the event is triggered, the process waiting for the event will resume execution.

1. Create an event using

1
event   eventA;    // Creates an event called "eventA"

2. Trigger an event using -> operator

1
2
->eventA;     // Any process that has access to "eventA" can trigger the event

3. Wait for event to happen

1
2
@eventA;             // Use "@" operator to wait for an event
wait (eventA.triggered); // Or use the wait statement with "eventA.triggered"

4. Pass events as arguments to functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module tb_top;
event eventA; // Declare an event handle called "eventA"

initial begin
fork
waitForTrigger (eventA); // Task waits for eventA to happen
#5 ->eventA; // Triggers eventA
join
end

// The event is passed as an argument to this task. It simply waits for the event
// to be triggered
task waitForTrigger (event eventA);
$display ("[%0t] Waiting for EventA to be triggered", $time);
wait (eventA.triggered);
$display ("[%0t] EventA has triggered", $time);
endtask
endmodule

Simulation Log

1
2
3
[20] Waiting for EventA to be triggered
[25] EventA has triggered
ncsim: *W,RNQUIE: Simulation is complete.

What’s a semaphore ?

Let’s say you wanted to rent a room in the library for a few hours. The admin desk will give you a key to use the room for the time you have requested access. After you are done with your work, you will return the key to the admin, which will then be given to someone else who wants to use the same room. This way two people will not be allowed to use the room at the same time. The key is a semaphore in this context.

A semaphore is used to control access to a resource and is known as a mutex (mutually exclusive) because only one entity can have the semaphore at a time.

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
34
35
36
37
38
39
module tb_top;
semaphore key; // Create a semaphore handle called "key"

initial begin
key = new (1); // Create only a single key; multiple keys are also possible
fork
personA (); // personA tries to get the room and puts it back after work
personB (); // personB also tries to get the room and puts it back after work
#25 personA (); // personA tries to get the room a second time
join_none
end

task getRoom (bit [1:0] id);
$display ("[%0t] Trying to get a room for id[%0d] ...", $time, id);
key.get (1);
$display ("[%0t] Room Key retrieved for id[%0d]", $time, id);
endtask

task putRoom (bit [1:0] id);
$display ("[%0t] Leaving room id[%0d] ...", $time, id);
key.put (1);
$display ("[%0t] Room Key put back id[%0d]", $time, id);
endtask

// This person tries to get the room immediately and puts
// it back 20 time units later
task personA ();
getRoom (1);
#20 putRoom (1);
endtask

// This person tries to get the room after 5 time units and puts it back after
// 10 time units
task personB ();
#5 getRoom (2);
#10 putRoom (2);
endtask
endmodule

Simulation Log

1
2
3
4
5
6
7
8
9
10
11
12
[0] Trying to get a room for id[1] ...
[0] Room Key retrieved for id[1]
[5] Trying to get a room for id[2] ...
[20] Leaving room id[1] ...
[20] Room Key put back id[1]
[20] Room Key retrieved for id[2]
[25] Trying to get a room for id[1] ...
[30] Leaving room id[2] ...
[30] Room Key put back id[2]
[30] Room Key retrieved for id[1]
[50] Leaving room id[1] ...
[50] Room Key put back id[1]

Note the following about semaphores.

  • A semaphore object key is declared and created using new () function. Argument to new () defines the number of keys.
  • You get the key by using the get () keyword which will wait until a key is available (blocking)
  • You put the key back using the put () keyword

Click here to read more about a SystemVerilog semaphore !

What’s a mailbox ?

A mailbox is like a dedicated channel established to send data between two components.

For example, a mailbox can be created and the handles be passed to a data generator and a driver. The generator can push the data object into the mailbox and the driver will be able to retrieve the packet and drive the signals onto the bus.

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// Data packet in this environment
class transaction;
rand bit [7:0] data;

function display ();
$display ("[%0t] Data = 0x%0h", $time, data);
endfunction
endclass

// Generator class - Generate a transaction object and put into mailbox
class generator;
mailbox mbx;

function new (mailbox mbx);
this.mbx = mbx;
endfunction

task genData ();
transaction trns = new ();
trns.randomize ();
trns.display ();
$display ("[%0t] [Generator] Going to put data packet into mailbox", $time);
mbx.put (trns);
$display ("[%0t] [Generator] Data put into mailbox", $time);
endtask
endclass

// Driver class - Get the transaction object from Generator
class driver;
mailbox mbx;

function new (mailbox mbx);
this.mbx = mbx;
endfunction

task drvData ();
transaction drvTrns = new ();
$display ("[%0t] [Driver] Waiting for available data", $time);
mbx.get (drvTrns);
$display ("[%0t] [Driver] Data received from Mailbox", $time);
drvTrns.display ();
endtask
endclass

// Top Level environment that will connect Gen and Drv with a mailbox
module tb_top;
mailbox mbx;
generator Gen;
driver Drv;

initial begin
mbx = new ();
Gen = new (mbx);
Drv = new (mbx);

fork
#10 Gen.genData ();
Drv.drvData ();
join_none
end
endmodule

Simulation Log

1
2
3
4
5
6
7
[0] [Driver] Waiting for available data
[10] Data = 0x9d
[10] [Generator] Put data packet into mailbox
[10] [Generator] Data put into mailbox
[10] [Driver] Data received from Mailbox
[10] Data = 0x9d
ncsim: *W,RNQUIE: Simulation is complete.

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