Skip to content

UVM

Transaction Conversion

How a driver converts a transaction into pin-level signaling — mapping each field to its signals in the protocol's order, expanding one transaction into many cycles, and sampling read data back into the response.

Drivers · Module 12 · Page 12.2

The Engineering Problem

The previous chapter established the driver's one real job: convert a transaction into pin-level activity (Module 12.1). This chapter is that conversion in depth — because it's where the driver's entire protocol knowledge lives, and where the subtle bugs are. A transaction is logical ("a read of address 0x40, length 4"); the pins are physical (which signal carries the address, when the strobe asserts, how the data beats are clocked, how the handshake completes). The conversion is the mapping between them — and it's two-directional: a write drives signals out, but a read also samples the returned data back in. Forget half of that, and the testbench drives correctly but loses the read result.

Transaction conversion is the driver's mapping of a transaction's logical fields to the protocol's pin-level signaling — a procedural routine that drives each field onto its signal(s) in the protocol's order and handshake (addr → address bus, is_read → control, data[] → data beats), expanding one transaction into the multiple pin cycles the protocol requires (Module 8.1's one-to-many). For reads, it also samples the DUT's returned data back into the transaction's response field. The conversion is the single place the protocol's encoding lives — so it's protocol-specific, while everything else stays protocol-agnostic. This chapter is the conversion: the field-to-signal mapping, the procedural structure, the one-to-many expansion, and the read sampling that's easy to forget.

How does a driver convert a transaction into pin-level signaling — mapping each field to its signals in the protocol's order, expanding one transaction into many cycles, and (for reads) sampling the returned data back into the transaction's response?

Motivation — why conversion is the driver's core

Conversion is the driver's essence because it's where the logical-to-physical translation happens, and each of its properties follows from that:

  • The transaction is logical and the pins are physical, so a mapping is required. A transaction says what ("read 0x40"); the pins need how (drive the address bus, assert the read strobe, clock the data). Bridging requires a mapping from each logical field to its physical signal(s) — there's no automatic correspondence.
  • The protocol defines the signaling, so the conversion is the protocol encoding. How a logical operation appears on the wires is the protocol — the signal order, the handshake, the timing. The conversion encodes the transaction into that protocol, so the conversion is where the protocol lives in the testbench.
  • One operation spans many cycles, so the conversion is procedural and time-consuming. A burst read is one transaction but many clock cycles of signaling (Module 8.1). So the conversion is a task that unfolds over time — driving across clock edges, looping over beats, waiting for handshakes — not a single assignment.
  • Reads return data, so the conversion is two-directional. A write only drives out; a read drives the request out and samples the response in — capturing the DUT's returned data into the transaction's response field. So conversion isn't just "drive"; for reads it's "drive and sample."
  • The protocol is isolated here, so everything else stays protocol-agnostic. Because the conversion is the only place the protocol's signaling lives, the transaction, the sequence, the scoreboard are all protocol-independent — a different protocol means a different conversion, and nothing else changes (the reuse argument).

The motivation, in one line: conversion is the driver's core because it's the logical-to-physical translation — a procedural, protocol-encoding, two-directional mapping (drive out, sample in) that expands one transaction into the protocol's many cycles — and isolating it in the driver is what keeps the rest of the testbench protocol-agnostic.

Mental Model

Hold transaction conversion as performing a transaction as the protocol's musical score:

Conversion is performing a transaction as the protocol's score: each field is a note, the protocol is the score's rules — which instrument plays it, when, with what rests and cues — and the performance expands one written phrase into the full passage the score demands, including listening for the response. The transaction is the written phrase (the meaning — "read 0x40"); the protocol is the score's notation (how that phrase is played — the address goes on this staff, the strobe is this cue, the beats are these notes, the handshake is waiting for the downbeat). The conversion is the performance: it takes each field (note) and plays it on the right instrument at the right moment (drives each field onto its signal in the protocol's order), and it expands the single phrase into the full passage — a burst read isn't one note but a measure of address, beats, and handshakes. And for a read, the performance is a duet: the driver plays its part (drives the request) and listens for the response (samples the returned data), capturing what comes back. A performer who played the notes but didn't listen for the response would miss the answer — exactly the read that drives but doesn't sample.

So conversion is playing the transaction by the protocol's rules: map each field to its signal in the right order, expand the one operation into the full multi-cycle passage, and — for reads — listen back, sampling the response. The protocol is the score; the conversion is the faithful performance, including the listening.

Visual Explanation — the field-to-signal mapping

The defining picture is the mapping: each of the transaction's logical fields drives one or more of the protocol's signals — the correspondence the conversion encodes.

Transaction fields map to protocol signals; read data maps back into the response field→ address bus→ read/write control→ data beats (out)← sampled readdatatr.addrlogical fieldtr.is_readlogical fieldtr.data[]logical fieldaddr busprotocol signalwr/rd controlprotocol signaldata beatsprotocol signalDUT read datasampled from pinstr.rdata (response)filled by conversion12
Figure 1 — the field-to-signal mapping the conversion encodes. Each transaction field drives specific protocol signals: addr → the address bus; is_read → the read/write control; data[] → the data beats; len → the number of beats; and the response field is filled FROM the sampled read data. The conversion is this correspondence made procedural — driving each field onto its signal(s) in the protocol's order, and sampling the returned data back into the response. The mapping is protocol-specific; it's the encoding the driver owns.

The figure shows the correspondence that is the conversion. On the out direction (top), each logical field drives protocol signals: tr.addr → the address bus, tr.is_read → the read/write control, tr.data[] → the data beats (for a write). The len field (not shown as a node but implied) determines how many beats — so it controls the expansion (Module 8.1). On the in direction (bottom), for a read, the DUT's returned data (sampled from the pins) flows back into tr.rdata (the response field) — the conversion fills it from what it sampled. So the conversion is bidirectional: fields → signals (drive) and sampled data → response field (capture). The crucial reading is that this mapping is protocol-specific and owned by the driver: which field goes to which signal, in what order, is the protocol's encoding, and it lives here and nowhere else. Everything to the left (the transaction's fields) is protocol-agnostic — the sequence and scoreboard see logical fields, not signals. So the driver's conversion is the translation table between the two worlds, made procedural (driven over clock cycles in the protocol's order). The diagram is the conversion's content in one picture: a field-to-signal mapping in the drive direction, and a sampled-data-to-response mapping in the read direction — the complete, two-way correspondence the driver encodes.

RTL / Simulation Perspective — the conversion routine, write and read

The conversion is a procedural routine — typically branching on the operation, driving the request, and (for reads) sampling the response. The code shows both directions.

the conversion routine: drive a write, drive-and-sample a read
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
task my_driver::drive_transaction(bus_item tr);
  @(posedge vif.clk);
  vif.addr  <= tr.addr;                 // map addr → address bus
  vif.wr    <= !tr.is_read;             // map is_read → control
  vif.valid <= 1;
 
  if (!tr.is_read) begin
    // ── WRITE: drive the data beats OUT (one per beat — the expansion) ──
    foreach (tr.data[i]) begin
      vif.wdata <= tr.data[i];          // map data[i] → data beat
      @(posedge vif.clk iff vif.ready); // handshake: wait for ready each beat
    end
  end else begin
    // ── READ: drive the request, then SAMPLE the returned data back IN ──
    @(posedge vif.clk iff vif.ready);   // wait for the data phase (protocol's read latency)
    foreach (tr.data[i]) begin
      tr.rdata[i] = vif.rdata;          // ← SAMPLE the DUT's read data INTO the response field
      @(posedge vif.clk);
    end
  end
  vif.valid <= 0;                        // de-assert; drive idle between transactions
endtask
// ✗ a READ that drives the request but never samples vif.rdata → loses the read result (DebugLab)

The routine is the conversion made concrete, and it shows the two directions. The common part maps the request fields: tr.addrvif.addr, tr.is_readvif.wr, and asserts valid — driving each logical field onto its protocol signal, on a clock edge (the protocol's timing, drilled in Module 12.4). Then the routine branches on the operation. For a write, it drives the data beats out: foreach (tr.data[i]) drives tr.data[i]vif.wdata, one per beat, waiting for ready each beat (the handshake) — this foreach is the one-to-many expansion (one transaction, len beats, Module 8.1). For a read, it's two-directional: it drives the request (already done) and then samples the responseforeach captures vif.rdata into tr.rdata[i] (the response field), one beat per cycle, at the protocol's read latency. The tr.rdata[i] = vif.rdata line is the critical read step — sampling the DUT's data back into the transaction — and the final comment flags the bug: a read that drives the request but never samples vif.rdata loses the read result (the DebugLab). After driving, the routine de-asserts and drives idle between transactions. The shape to carry: the conversion maps request fields out (always), expands data beats (the foreach), and — for readssamples the response back in (tr.rdata = vif.rdata); the sampled data is then returned (via item_done(rsp), Module 11.3) to the sequence and scoreboard.

Verification Perspective — the conversion is the protocol encoding

The conversion's role is to be the single, isolated place the protocol's encoding lives — which is what keeps the rest of the testbench protocol-agnostic. Seeing this isolation is seeing why conversion matters beyond the driver.

The conversion isolates the protocol encoding; the rest of the testbench is protocol-agnosticlogicaltransactionprotocol signaling (encoded)protocolsignaling…owns the encodingTransaction · Sequence ·Scoreboardprotocol-AGNOSTIC (logical fields)Driver conversionthe ONLY protocol-specific placeDUT pinsprotocol signalingProtocol encodingfield→signal, handshake, timing12
Figure 2 — the conversion is the single place the protocol encoding lives. The transaction, sequence, and scoreboard all work in protocol-agnostic logical terms (fields, operations). Only the driver's conversion knows the protocol's signaling — which field is which signal, the handshake, the timing. So a different protocol means a different conversion and nothing else changes: the same transactions, sequences, and scoreboard reuse across protocols. The conversion isolates the protocol, which is what makes the rest reusable.

The conversion's isolation of the protocol is the verification-architecture payoff. The transaction, sequence, and scoreboard all work in protocol-agnostic, logical terms — they deal in fields ("addr 0x40, read") and operations, never in signals or timing. The driver's conversion is the only place that knows the protocol's signalingwhich field is which signal, the handshake, the timing (the protocol encoding). So the conversion is a firewall: the protocol is contained inside it, and everything outside is protocol-independent. The consequence is reuse across protocols: change the protocol (a different bus, a different framing) and you rewrite only the conversion — the same transactions, the same sequences, the same scoreboard all carry over, because none of them ever touched the protocol. This is the deeper reason the conversion is the driver's core (Module 12.1's "the driver is the translation point"): it's not just that someone must drive the pins; it's that isolating the protocol in one routine is what makes the expensive, reusable parts (stimulus, checking) protocol-agnostic and therefore portable. The verification skill is to keep the protocol entirely inside the conversion: no signal knowledge should leak into the sequence (it deals in fields) or the scoreboard (it compares fields). When the protocol is cleanly isolated in the conversion, the testbench reuses; when it leaks, reuse breaks (the same lesson as layered sequences, Module 10.2, applied to the pin boundary). The conversion is the protocol's container, and its cleanliness is what the rest of the testbench's portability rests on.

Runtime / Execution Flow — the procedural conversion, including sampling

At run time, the conversion unfolds procedurally over clock cycles: drive the request, run the handshake, expand the beats, and — for reads — sample the response. The flow shows the steps, including the read sampling that completes the round trip.

The procedural conversion: drive the request, then drive write beats out or sample read beats in, then completedrive request → (write: drive beats out) / (read: sample beats in) → completedrive request → (write: drive beats out) / (read: sample beats in) → complete1Drive the request fieldsmap addr → address bus, is_read → control, assert valid — on aclock edge.2aWrite: drive beats OUTloop driving each data[i] → data signal, waiting for the handshake(ready) each beat.2bRead: sample beats INwait the read latency, then loop sampling vif.rdata → tr.rdata[i](the response field).3Completede-assert valid, drive idle; the response (for reads) is returnedvia item_done(rsp).
Figure 3 — the procedural conversion flow. Drive the request fields onto their signals (addr, control). For a write, loop driving each data beat OUT with the handshake. For a read, wait the protocol's latency, then loop SAMPLING each returned data beat INTO the response field. Complete (de-assert, idle). The write direction drives out; the read direction drives the request and samples in. The sampling step is what captures the read result — skip it and the read is lost.

The procedural flow shows the conversion's time-unfolding structure and its two directions. Drive the request (step 1): map the request fieldsaddr → address bus, is_read → control — and assert valid, on a clock edge (synchronous driving). This is common to both operations. Then branch. For a write (step 2a): loop driving each data beat outdata[i] → the data signal — waiting for the handshake (ready) each beat, so the driver paces to the DUT (back-pressure, Module 8.3); the loop is the one-to-many expansion. For a read (step 2b): wait the protocol's read latency, then loop sampling each returned data beat invif.rdatatr.rdata[i] (the response field) — capturing the DUT's data into the transaction. Complete (step 3): de-assert valid and drive idle; for a read, the captured response is returned to the sequence (via item_done(rsp), Module 11.3) and on to the scoreboard. The runtime insight is the asymmetry: a write is drive-only (data goes out), but a read is drive-and-sample (the request goes out, the data comes back in) — so the read flow has an extra, essential step (sampling) that the write doesn't, and it's the step most easily forgotten. The conversion's correctness depends on both directions being right: driving the request signals in the protocol's order and (for reads) sampling the response at the protocol's latency. Miss the drive and the DUT gets wrong signaling; miss the sample and the read result is lost (the DebugLab) — both are conversion failures, and the read-sampling one is the subtle one because the driving looks correct.

Waveform Perspective — a write and a read converted to pins

The conversion's two directions are clearest on a timeline: a write drives data out, while a read drives the request and samples data in — the same routine, two directions.

Conversion on the pins: a write drives data out, a read samples data in

12 cycles
Conversion on the pins: a write drives data out, a read samples data inWRITE: addr + wr driven; wdata beats W0,W1 driven OUT (transaction → DUT)WRITE: addr + wr drive…READ: addr driven, wr low (request out)READ: addr driven, wr …DUT returns data on rdata (R0,R1)DUT returns data on rd…driver SAMPLES rdata INTO the transaction's response field (read result captured)driver SAMPLES rdata I…clkwraddr404040--8C8C8C----------wdataW0W1--------------------rdata----------R0R1----------t0t1t2t3t4t5t6t7t8t9t10t11
Figure 4 — the two directions of conversion. For the WRITE, the driver maps the transaction onto the pins: addr driven, wr high, and wdata beats driven OUT (W0, W1) — the data flows from the transaction to the DUT. For the READ, the driver drives the request (addr, wr low), then the DUT returns data on rdata, which the driver SAMPLES IN (the sample markers) into the transaction's response field. Write = drive out; read = drive request + sample in. The conversion owns both directions.

The waveform shows the conversion's two directions side by side. For the WRITE (left), the driver maps the transaction onto the pins: addr is driven to 0x40, wr is high, and the wdata beats W0, W1 are driven out — the data flows from the transaction to the DUT, the out direction. For the READ (right), the driver first drives the request (addr to 0x8C, wr low) — the request out — and then the DUT returns data on rdata (R0, R1), which the driver samples in: the sample markers show the driver capturing rdata into the transaction's response field, the in direction. So the same conversion routine produces two different pin behaviors depending on the operation: a write drives data out, a read drives a request and samples data in. The crucial visual is the read's rdata being sampledthat is the step that captures the read result; without it, the rdata signal would carry correct data on the pins but never reach the transaction, so the scoreboard would see nothing (the DebugLab). The picture to carry is the asymmetry made visible: writes are one-directional (transaction → pins), reads are two-directional (request → pins, data → transaction), and the conversion owns both — driving the request fields onto their signals and, for reads, sampling the response back, at the protocol's timing. The waveform is the conversion's behavior, and reading it — which signals carry which fields, when data is driven vs sampled — is how you verify a conversion is correct in both directions.

DebugLab — the read that drove correctly but lost its data

A read where the DUT returned correct data but the scoreboard saw garbage — the driver never sampled it

Symptom

A read transaction drove the DUT correctly — on the waveform, the address went out, the read strobe was right, and the DUT returned the correct data on the read-data bus. But the scoreboard saw the wrong read data — stale or default values, not what the DUT actually returned. Writes checked perfectly; only reads came back wrong. The pins showed correct DUT behavior, yet the transaction's read data (what the scoreboard checked) was garbage.

Root cause

The driver's read conversion drove the request but never sampled the returned data into the transaction's response field — so tr.rdata stayed at its default, never reflecting what the DUT returned:

why the read data never reached the transaction
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
read conversion (BROKEN):
  vif.addr <= tr.addr;  vif.wr <= 0;  vif.valid <= 1;   // drive the request (correct)
  @(posedge vif.clk iff vif.ready);
  // ✗ MISSING: tr.rdata = vif.rdata;   ← never SAMPLES the DUT's returned data
  item_done(rsp);                                        // returns a response with DEFAULT rdata
 
on the pins:  DUT returns correct data on vif.rdata  ✓
in the txn:   tr.rdata never assigned → stays DEFAULT  ✗
→ scoreboard checks tr.rdata (default/stale), NOT the real DUT data → mismatch
 
fix — SAMPLE the returned data into the response field (at the protocol's read latency):
  @(posedge vif.clk iff vif.ready);
  tr.rdata = vif.rdata;        // ✓ capture the DUT's read data INTO the transaction
  // (multi-beat: foreach (tr.data[i]) begin tr.rdata[i] = vif.rdata; @(posedge vif.clk); end)
  rsp.set_id_info(tr);  item_done(rsp);   // return the response carrying the sampled data

This is the unsampled-read bug, the conversion's most common — and most deceptive — failure, because the driving looks completely correct. Conversion is two-directional: a write drives data out, but a read must drive the request out and sample the returned data in. This read conversion did the first half perfectly — it drove the address, the read strobe, the handshake, and the DUT returned correct data on the pins — but it never executed the sampling step (tr.rdata = vif.rdata), so the DUT's data stayed on the pins and never entered the transaction. The transaction's rdata therefore kept its default value, and when the scoreboard checked tr.rdata, it saw garbage, not the real read result — a mismatch, even though the DUT was correct. The tell is exactly this: writes pass (drive-only — no sampling needed) but reads fail, and the pins show correct DUT data that the transaction doesn't reflect — which points at the sampling, not the driving or the DUT. The fix is to sample the returned data into the response field at the protocol's read latencytr.rdata = vif.rdata (or a foreach for multi-beat) — and return the response carrying it (set_id_info + item_done(rsp), Module 11.3). The general lesson, and the chapter's thesis: converting a read is two-directionaldrive the request and sample the response into the transaction — so a read conversion that only drives (forgetting the sample, or sampling at the wrong cycle) loses the read result, producing a scoreboard mismatch despite correct pin-level behavior. The sampling step is as essential as the driving, and it's the one most easily missed because the driving is what's visible on the waveform.

Diagnosis

The tell is reads failing while writes pass, with correct DUT data on the pins. Diagnose unsampled-read bugs:

  1. Confirm writes pass but reads fail. Writes are drive-only; if they pass but reads fail, the read-specific step — sampling — is the suspect, not the common driving.
  2. Check the pins vs the transaction. If the DUT returns correct data on the read-data bus but the transaction's rdata is default/stale, the data was never sampled into the transaction.
  3. Find the sampling line in the read conversion. Confirm the conversion assigns vif.rdata into the transaction's response field; its absence is the bug.
  4. Check the sampling timing. Even if present, sampling at the wrong cycle (before the data is valid, or after it changes) captures garbage — verify it matches the protocol's read latency.
Prevention

Make read conversion two-directional and correctly timed:

  1. Sample the returned data into the response field. Every read conversion must capture vif.rdata into the transaction's response field — driving the request alone is half the job.
  2. Sample at the protocol's read latency. Capture the data at the cycle it's valid (per the protocol's timing); sampling early or late captures garbage (Module 12.4).
  3. Return the response carrying the data. Use set_id_info + item_done(rsp) so the sampled data reaches the sequence and scoreboard (Module 11.3).
  4. Test reads against known data. Drive a read of a known value and confirm the transaction's rdata matches — a quick check that the sampling works, since the driving can look correct while the sampling is broken.

The one-sentence lesson: converting a read is two-directional — drive the request and sample the DUT's returned data into the transaction's response field — so a read conversion that only drives (forgetting the sample, or mistiming it) loses the read result, producing a scoreboard mismatch despite correct pin-level behavior; sample vif.rdata into the response at the protocol's read latency and return it.

Common Mistakes

  • Forgetting to sample read data into the response. The read drives correctly but the transaction's rdata stays default; sample vif.rdata into the response field at the protocol's latency.
  • Sampling read data at the wrong cycle. Capturing before the data is valid (or after it changes) gets garbage; sample at the protocol's read latency (Module 12.4).
  • Mis-mapping a field to the wrong signal. Driving addr onto the data bus (or similar) sends wrong signaling; map each field to its correct protocol signal.
  • Wrong beat count in the expansion. Driving len-1 or len+1 beats mis-sizes a burst; loop exactly over the transaction's beats.
  • Letting protocol knowledge leak out of the conversion. Signal/timing logic in the sequence or scoreboard breaks protocol-agnosticism; keep the protocol entirely in the conversion.
  • Ignoring the handshake. Driving beats without waiting for ready races the DUT; honor the protocol's handshake each beat.

Senior Design Review Notes

Interview Insights

Transaction conversion is the driver's mapping of a transaction's logical fields to the protocol's pin-level signaling — the translation from the logical what to the physical how. It's a procedural routine, a task that unfolds over clock cycles, because one transaction expands into many cycles of signaling. It involves mapping each field to its protocol signal: the address field drives the address bus, the read/write field drives the control, the data array drives the data beats, and the length determines how many beats. The conversion drives each field onto its signal in the protocol's order, honoring the handshake and timing — waiting for ready, following the clocking. A key point is that conversion is two-directional. For a write, it only drives data out: the transaction's data goes onto the pins. For a read, it drives the request out and then samples the returned data back in, capturing the DUT's read data into the transaction's response field. So a read conversion isn't just driving — it's drive the request and sample the response, and the sampled data is then returned via item_done with a response. The conversion is also the single place the protocol's encoding lives, which is what keeps the rest of the testbench protocol-agnostic: the transaction, sequence, and scoreboard all work in logical fields, and only the conversion knows the signaling. So a different protocol means a different conversion and nothing else changes. The most common conversion bug is a read that drives the request correctly but forgets to sample the returned data, so the transaction's read data stays default and the scoreboard sees garbage even though the DUT returned correct data on the pins.

Because a read is two-directional while a write is one-directional. For a write, the data flows from the transaction to the DUT: the conversion drives the data beats out onto the pins, and that's it — it's drive-only. For a read, the data flows the other way: the conversion drives the request out — the address and the read control — but then the DUT returns the data, and the conversion must sample that returned data back in, capturing it from the pins into the transaction's response field. So a read conversion has an extra, essential step that a write doesn't: after driving the request, it waits the protocol's read latency and samples the read-data signal into the transaction. This asymmetry is exactly where the common read bug comes from: the driving half of a read looks identical in spirit to a write — drive the address and control — so it's easy to write the request-driving and forget the sampling, leaving the transaction's read data at its default. The symptom is that writes pass but reads fail, and the pins show correct DUT data that the transaction doesn't reflect, because the data was on the pins but never sampled into the transaction. The fix is to sample the returned data into the response field at the right cycle and return it. So the practical difference is that a write conversion is complete when it has driven the data out, but a read conversion is only complete when it has driven the request and sampled the response in — drive plus sample versus drive only. Forgetting that a read is two-directional, and only driving it, loses the read result despite correct pin behavior, which is why reads need extra care in conversion.

The driver almost certainly drove the read request correctly but never sampled the returned data into the transaction, so the transaction's read-data field stayed at its default and the scoreboard checked that default instead of the real DUT data. Conversion is two-directional, and a read needs both halves: drive the request out, then sample the DUT's returned data in. If the conversion does the first half — drives the address and read control, runs the handshake — but omits the sampling step that assigns the read-data signal into the transaction's response field, then on the pins the DUT returns correct data, but in the transaction the read-data field is never updated, so it carries garbage or a default. When the scoreboard compares the transaction's read data against expected, it mismatches, even though the DUT was correct. The telltale signs are that writes pass while reads fail — because writes are drive-only and need no sampling — and that the waveform shows correct data on the read-data bus that the transaction doesn't reflect. That combination points at the sampling, not at the driving or the DUT. To diagnose, you check the read conversion for a line that assigns the read-data signal into the transaction's response field, and if it's missing, that's the bug; if it's present, you check that it samples at the right cycle, because sampling before the data is valid or after it changes captures garbage. The fix is to sample the returned data into the response field at the protocol's read latency and return the response so it reaches the scoreboard. So the answer is a missing or mistimed read-data sample in the conversion.

Exercises

  1. Map the fields. For a bus transaction with addr, is_read, data[], and len, state which protocol signal(s) each field drives, and which field the read data is sampled into.
  2. Fix the read. Given a read conversion that drives the request but the scoreboard sees default data, identify the missing step and write it.
  3. Expand the burst. Explain how a length-4 burst transaction becomes multiple pin cycles, and where the loop is in the conversion.
  4. Defend the isolation. Explain why keeping the protocol only in the conversion lets the same sequences and scoreboard reuse across protocols.

Summary

  • Transaction conversion is the driver's mapping of a transaction's logical fields to the protocol's pin-level signaling — a procedural routine that drives each field onto its signal(s) in the protocol's order and handshake, expanding one transaction into the many pin cycles the protocol requires.
  • Each field maps to signals: addr → address bus, is_read → control, data[] → data beats, len → beat count — and the conversion is the single place this protocol encoding lives.
  • Conversion is two-directional: a write drives data out, but a read drives the request and samples the DUT's returned data back into the transaction's response field (returned via item_done(rsp)) — the sampling step is essential and easily forgotten.
  • Isolating the protocol in the conversion keeps the transaction, sequence, and scoreboard protocol-agnostic — so a different protocol means a different conversion and nothing else changes, which is the reuse payoff.
  • The durable rule of thumb: map each field to its signal in the protocol's order, expand the beats exactly, keep the protocol entirely inside the conversion — and for reads, sample the returned data into the response field at the protocol's read latency, because a read conversion that only drives (forgetting the sample) loses its result, causing a scoreboard mismatch despite correct pin-level behavior.

Next — Interface Driving: the conversion maps fields to signals; the next chapter drills into driving those signals — the virtual interface, synchronous driving with clocking blocks, avoiding races, and the signal-level mechanics of putting values on the pins correctly.