3.1. Transaction Based Testbench
3.1.1. Basic Transactions
A transaction is an operation on a device interface. As such it could be a cpu read or a cpu write. A transaction is
often either specified or created using a procedure. A test is created by calling transactions in a given sequence with
specified values. For example,
CpuTestProc : process -- TestCtrl_UartRx1
-- Declarations left out
begin
wait until nReset = '1' ;
CpuWrite(CpuRec, UART_DIVISOR_HIGH, X"0000");
CpuWrite(CpuRec, UART_DIVISOR_LOW, X"000A");
CpuWrite(CpuRec, UART_CFG1, X"00" & "00" &
PARITY_EVEN & STOP_BITS_1 & DATA_BITS_8);
. . .
CpuRead (CpuRec, UART_TX_INT_STAT, DataO);
SyncTo(SyncIn => UartTxRdy, SyncOut => CpuRdy);
for I in 0 to 3 loop
CpuPoll(CpuRec, UART_RX_INT_STAT, RX_DATA_VALID, '1') ;
CpuReadCheck (CpuRec, UART_DATA, X"4A" + I, true);
end loop ;
. . .
A simple approach to transactions is to implement the interface behavior in the procedure call. The limitation to this
is that procedure is strictly sequential. While sequential code works well for some applications (like cpu read and
cpu write) it does not work well for applications that require detailed statemachines (such as a UART receiver).
3.1.2. Transaction Based Bus Functional Models
In the approach shown below, each independent interface behavior is implemented in a separate interface behavior
model (aka: transactor, server) and all of the transactions are grouped together in a single transaction source (aka:
test control, client). The transaction source specifies sequences of transactions. The interface behavioral model
creates the interface behavior specified in the transaction. The connection between the transaction source and
interface behavior model can be represented as an interface (CpuIf, UartTxIf, UartRxIf).
The interface is used to abstractly carry the data items needed to support the transactions. Some interface elements
will have a single driver and some will have multiple drivers. There is also a need for handshaking between the
models. To support a wide range of modeling styles, the handshaking will need to support both blocking (such as
read request and return with the read data) and non-blocking (accept the data from serial port now or it is gone). It
may also need to support closed loop handshaking (ready-ready or ready-ack – perhaps only for blocking) and openloop
handshaking (event or edge based – perhaps only for non-blocking).
The interface behavior model receives transactions from the transaction source via the interface and creates
appropriate signaling to the UUT. Implementing the interface behavior model as an entity facilitates modeling
behavior that requires multiple processes (complex statemachines). In addition there are items that need to be
modeled (such as assertions and protocol checkers) that are convenient to group in the same model.
Grouping all transactions into a single transaction source facilitates creating interactions between different models.
Most interactions can be controlled by signals local to the transaction source rather than by signals that must go
through hierarchical levels of the design. Note that the transaction source has one interface per interface behavior
model. Hence an entity port list can contain multiple interfaces (interface here being more like a composite type and
not a separate IO list).
A further benefit of this modeling methodology is that if a model in the system needs to change, such as changing
the CpuModel from an X86 type model to a 68000, only the model and the UUT need to change. Many or all of the
transactions specified in the transaction source remain unchanged.
Details of an approach that uses this methodology and implements the interface as a record with elements that are
based on std_logic is available in the second half of the paper titled, "Accelerating Verification Through Pre-Use of
System-Level Testbench Components" that is available at:
http://www.synthworks.com/papers/index.htm.