1- Traditional Port-Based connection
Example of a possible (handshake) protocol implementation between a stimulus and a driver.
- A driver is connected to a DUT (Design Under Test)
- It reads the data to send to the DUT from a stimulus block.
- As soon as the driver tells it is ready (not busy anymore), the stimulus provides a new data together with a "data valid" signal.
- The driver reacts on this valid signal and:
- raises its "busy" flag
- sends the data to the DUT based on whatever protocol (SPI, I2C, etc...)
module stimulus module driver
+-------------------------------+ +---------------------------------+
| | busy | always @(posedge valid) |
| always @(negedge busy) |<-----------| begin |
| begin | | busy <= 1; |
| data <= $random; | | send_to_dut(data); |
| valid <= 1; | data | busy <= 0; |
| end |----------->| end |
| always @(posedge busy) | valid | task send_to_dut(input [7:0] d);|
| valid <= 0; |----------->| <consume time> |
| | | endtask |
+-------------------------------+ +---------------------------------+
- Advantages:
- very natural approach for designers
- Each module can be designed independently
- Signal names don't need to match port names
- Any receiver that understands the producer (stimulus) interface can be connected
- Disadvantages:
- very-low (signal) level of connection scheme
- No easy scaling/re-use
2- Task-Based connection
The idea is to call from the stimulus tasks embedded in the driver.
module stimulus module
driver
+-----------------------------------------+
+----------------------------------+
| | | task
send_to_dut(input [7:0] d) |
| | |
begin |
| always @(posedge clk) | | busy
<= 1; |
| if (driver_instance.is_busy() == 0) | |
<consume time> |
| begin | | busy
<= 0; |
| data <= $random; | |
endtask |
| driver_instance.send_to_dut(data); |
| |
| end | |
function is_busy(); |
| | |
return busy; |
| | |
endfunction |
+-----------------------------------------+
+----------------------------------+
- Advantages:
- Higher-level. No need to dive into signal complexity.
- Disadvantages:
- Tasks are called hierarchically
- Hence the code is not re-usable (will need to be edited if the driver instance name changes, i.e. in another project)
3-
Transaction Level Modeling
(TLM)
- Instead of calling a task embedded in the driver, our stimulus now uses an instance of a "(non-blocking) put port". This "put port" provides a "put" method (task) with no actual implementation (just its declaration). It can be called from the "put port", but its implementation is somwhere else...
- Symetrically, the driver uses an instance of the complement of a "port" called an "import". It is this "import" that actually details the implementation of the "put" method.
- TLM Terminology:
- Port: allows the stimulus to call a task implemented somewhere else.
- Import: allows the driver to provide the implementation of the method (task).
+------------------------------------+ +-----------------------------------+
| class stimulus extends ... | | class driver extends ... |
| uvm_blocking_put_port #(byte) p; | | uvm_blocking_put_import #(byte) p;|
| ... |_ | ... |
| p.put(data); -------------------->|_|-->O| |
| | | task put(byted); |
| | | <consume time> |
| | | endtask |
+------------------------------------+ +-----------------------------------+
| class stimulus extends ... | | class driver extends ... |
| uvm_blocking_put_port #(byte) p; | | uvm_blocking_put_import #(byte) p;|
| ... |_ | ... |
| p.put(data); -------------------->|_|-->O| |
| | | task put(byted); |
| | | <consume time> |
| | | endtask |
+------------------------------------+ +-----------------------------------+
- Disadvantages:
- Class-based coding required
- Not straightforward (software oriented) testbench for HW engineers
- Advantages:
- High-level. No signal detail
- Task-based
- Tasks are not hierarchically fixed
- Re-usable code
- Faster simulation
Additional TLM Terminology:
- Producer: component generating the data to exchange (the transacation) = transmitter
- Consumer: component that receives the transaction = receiver
- Initiator: component which initiates the communication = master
- Target: component that responds to the initiator = slave