An AHB agent: the pipelined driver and monitor

Drive two transfers at once, one in each phase, rebuild every transfer from the edge its address was taken and the edge its data phase ended, and keep both right through wait states and an ERROR.

Lesson
P4.11
Time
40 min
Runs on
Verilator with UVM
Check
5 questions

An AHB monitor logged a transfer at every edge where HTRANS showed NONSEQ, and for months it matched the driver beat for beat. Then a slower memory inserted two wait states on a read, and the write waiting behind it was logged three times, each copy with the wrong data. The fix was one rule: an address phase counts only at an edge where HREADY is high, and the transfer ends at the next such edge.

What the monitor sees, edge by edgeTwo writes, a read that the memory stretches by two wait states, and the first beat of a burst. The monitor publishes a transfer at the edge where its data phase ends.
address phaseHWDATAtransfer endsDriveraph and dphahb_memtwo waits on the readMonitorpend: one transfer

acc is 1 at an edge where an address phase is taken (a transfer on the wires and HREADY high); pub at an edge where the monitor publishes.

0123456clkhtransNONSEQSEQhaddr100104100200204hwriteacchwdata011220a0hreadyhrdata011pub
  1. Cycle 0

    The write to 0x100 is taken at 35 ns. The monitor remembers its address and control in pend; nothing is published yet.

  2. Cycle 1

    At 45 ns HWDATA carries 11, the data of 0x100, while the address phase of the write to 0x104 is taken. The monitor publishes write 0x100 = 11 and remembers 0x104.

  3. Cycle 2

    The read of 0x100 is taken and the write to 0x104 is published with 22. Two transfers on the wires, one in each phase, every cycle.

  4. Cycle 3

    The memory holds HREADY low. The write to 0x200 waits in the address phase; it is not a transfer yet, and the monitor counts nothing.

  5. Cycle 5

    HREADY rises at 85 ns: the read ends with 11 and the write to 0x200 is taken at the same edge. One edge, one transfer finished, one started.

Measured on the lesson's bench, 35 ns to 95 ns, with its waveform line switched on. The rows acc and pub mark the edges where an address phase is taken and where the monitor prints.

Two items on the bus at once

An AHB manager that waits for one transfer to finish before it starts the next wastes half the bus. The driver on this page keeps two items. aph is the item whose address phase is on the wires, dph the one whose data phase is running. At every edge where HREADY is high three things happen at once. The data phase of dph ends, so the driver reads HRESP and, for a read, HRDATA, and hands the item back with put(). aph moves into the data phase. The next item from the queue takes the address phase.

The agent: one item through two phasesA write item from the sequence to the scoreboard. The response goes back to the sequence when the data phase ends.
The agent: one item through two phasesA write item from the sequence to the scoreboard. The response goes back to the sequence when the data phase ends. sequence Sequence finish_item returnsat get() driver take() get(); push to thequeue driver aph the address phase driver dph the data phase; put()at its end design ahb_mem the DUT monitor Monitor pend: address untildata checker Scoreboard the memory it expects itemqueueHTRANS, HADDRHREADY highHWDATAput()the wirestransfer
  1. get()

    take() gets the item and queues it. finish_item in the sequence returns at once, so the next item follows straight away.

    Follow the calls, values and waits
    1. data write 0x104: Sequence → take()
  2. The address phase

    At an edge where HREADY is high the item leaves the queue and becomes aph. Its HTRANS and HADDR are on the wires for the next cycle, while the item before it is in its data phase.

    Follow the calls, values and waits
    1. data write 0x104: take() → aph
    2. call write 0x104: aph → ahb_mem
  3. The data phase

    At the next edge with HREADY high the memory takes the address phase and the item becomes dph. The driver puts its data on HWDATA and keeps it there through every wait state.

    Follow the calls, values and waits
    1. data write 0x104: aph → dph
    2. data write 0x104: dph → ahb_mem
  4. Two witnesses

    When the data phase ends the driver hands the item back with put(), and the monitor publishes the same write from the wires. The scoreboard updates the memory it expects.

    Follow the calls, values and waits
    1. return OKAY: dph → Sequence
    2. data write 0x104 = 22: ahb_mem → Monitor
    3. data write 0x104 = 22: Monitor → Scoreboard
Read the diagram as text
  • Sequence: finish_item returns at get()
  • take(): get(); push to the queue
  • aph: the address phase
  • dph: the data phase; put() at its end
  • ahb_mem: the DUT
  • Monitor: pend: address until data
  • Scoreboard: the memory it expects
  • Sequence → take(): item
  • take() → aph: queue
  • aph → ahb_mem: HTRANS, HADDR
  • aph → dph: HREADY high
  • dph → ahb_mem: HWDATA
  • dph → Sequence: put()
  • ahb_mem → Monitor: the wires
  • Monitor → Scoreboard: transfer

The driver pipelines because get() returns the sequence at once; an item belongs to the address phase for one HREADY and to the data phase until the next.

The driver changes its wires with nonblocking assignments at the edge, after the memory has sampled them, which is the drive-after-the-edge rule of every bench in this track. While HREADY is low nothing advances. HWDATA stays on the wires, and the waiting address phase stays put, the rule P4.4 and P4.10 checked.

The monitor joins the phases

A transfer on AHB exists in two places a cycle or more apart. The address and the control are on the wires when the address phase is taken, and the data and the response when the data phase ends. The monitor keeps one transfer in pend between those two edges. At an edge where HREADY is high it first completes pend. It reads HWDATA for a write or HRDATA for a read, and HRESP for both, and publishes. Then, if HTRANS shows NONSEQ or SEQ, it starts the next pend from the address phase taken at that same edge. A transfer that waits on the wires while HREADY is low is not taken, so the monitor never sees it twice.

nsOn the wiresHREADYThe monitor
35NONSEQ write 0x1001pend = write 0x100
45NONSEQ write 0x104, HWDATA 111publishes write 0x100 = 11; pend = write 0x104
55NONSEQ read 0x100, HWDATA 221publishes write 0x104 = 22; pend = read 0x100
65NONSEQ write 0x200 waiting0nothing
75NONSEQ write 0x200 waiting0nothing
85NONSEQ write 0x200, HRDATA 111publishes read 0x100 = 11; pend = write 0x200
95SEQ write 0x204, HWDATA a01publishes write 0x200 = a0; pend = write 0x204

Measured from the example. The write to 0x200 is on the wires at three edges and becomes one transfer.

Bursts need no special case. Every beat is a transfer with its own address phase, NONSEQ for the first and SEQ for the rest, so each INCR4 comes out as four lines. A monitor that wants the burst as one item groups the beats from NONSEQ to the last SEQ.

An ERROR, a withdrawn read and the scoreboard

The write to 0x8000 lies above the memory's err_above. Its data phase gets the two-cycle ERROR. At 175 ns the first cycle, HRESP high with HREADY low, warns the driver. The read of 0x104 waiting in the address phase will be taken at the next edge unless it acts. The driver drives IDLE and pushes the read back to the front of its queue, and the log says withdrew. At 185 ns the second cycle ends the write with ERROR and the monitor publishes it that way. The read goes out again and completes at 205 ns with 22, so order and data are kept (IHI 0033C 5.1).

The scoreboard is a uvm_subscriber with an associative array for the memory it expects. An OKAY write updates it, an OKAY read is compared with it, and a transfer with ERROR is counted apart. This memory writes nothing on an error, so the scoreboard does not model one. A DUT that might write before it signals ERROR needs a scoreboard that allows both outcomes.

Asked in interviews: AHB agents
  1. How does an AHB driver pipeline?

    It holds two items: one in the address phase and one in the data phase. At every edge where HREADY is high the data phase ends, the address-phase item moves into the data phase, and the next item takes the address phase. While HREADY is low it changes nothing.

  2. When does the monitor publish a write, and where does it get the data?

    At the edge where the write's data phase ends, HREADY high, the edge after its address phase was taken or later with wait states. The data is HWDATA at that edge, because write data belongs to the data phase.

  3. What breaks a monitor that logs at the address phase?

    Wait states: a transfer waiting on the wires shows NONSEQ at several edges and is logged several times, and the data it records belongs to another transfer. An address phase counts only at an edge with HREADY high.

  4. What does the driver do in the first cycle of an ERROR?

    If a transfer waits in the address phase it may cancel it by driving IDLE in the second cycle; this driver does, and issues the transfer again afterwards. It may also continue; the specification allows both.

  5. Why use get() and put() in the driver?

    Because two items are in flight at once. get() returns the sequence immediately, and put() returns each response when its data phase ends.

Try it

Read the log by time. Thirteen items, thirteen transfers, one line each, ten ns apart except where the read of 0x100 waits two cycles (55 to 85 ns) and where the ERROR takes two cycles and the withdrawn read goes out again (165 to 205 ns). The scoreboard checks six reads against six writes and counts the ERROR apart.

Then break it. In the monitor, drop the vif.hready test and publish at every edge where HTRANS shows a transfer: the write to 0x200 appears three times. Or delete the else if branch of the driver that withdraws the waiting item: the read of 0x104 is then taken during the ERROR, which the specification allows, and its response arrives one edge earlier.

RUNVerilator + UVM 2020.3.1 · simulator service
Edit the code, then run it
Expected among the UVM messages: 45 ns monitor write NONSEQ 0x100 data=11 OKAY 55 ns monitor write NONSEQ 0x104 data=22 OKAY 85 ns monitor read NONSEQ 0x100 data=11 OKAY 95 ns monitor write NONSEQ 0x200 data=a0 OKAY 105 ns monitor write SEQ 0x204 data=a1 OKAY 115 ns monitor write SEQ 0x208 data=a2 OKAY 125 ns monitor write SEQ 0x20c data=a3 OKAY 135 ns monitor read NONSEQ 0x200 data=a0 OKAY 145 ns monitor read SEQ 0x204 data=a1 OKAY 155 ns monitor read SEQ 0x208 data=a2 OKAY 165 ns monitor read SEQ 0x20c data=a3 OKAY 175 ns driver withdrew read 0x104: the transfer before it got ERROR 185 ns monitor write NONSEQ 0x8000 data=dead ERROR 205 ns monitor read NONSEQ 0x104 data=22 OKAY 205 ns sequence 13 responses, 1 ERROR 235 ns scoreboard 6 writes, 6 reads, 1 errors, 0 mismatches UVM_ERROR : 0
Read included file: ahb_pkg.sv
`timescale 1ns/1ns
// ahb_pkg.sv: the encodings and the arithmetic of a burst (read-only).
// The memory, the driver, the checker and the lesson text all use these functions, so they never
// disagree about the address of a beat or the lanes it uses.
package ahb_pkg;
  localparam logic [1:0] IDLE = 2'b00, BUSY = 2'b01, NONSEQ = 2'b10, SEQ = 2'b11;
  localparam logic [2:0] SINGLE = 3'b000, INCR = 3'b001, WRAP4 = 3'b010, INCR4 = 3'b011,
                         WRAP8 = 3'b100, INCR8 = 3'b101, WRAP16 = 3'b110, INCR16 = 3'b111;
  localparam logic OKAY = 1'b0, ERROR = 1'b1;

  function automatic string trans_name(input logic [1:0] t);
    case (t) IDLE: return "IDLE"; BUSY: return "BUSY"; NONSEQ: return "NONSEQ"; default: return "SEQ"; endcase
  endfunction
  function automatic string burst_name(input logic [2:0] b);
    case (b)
      SINGLE: return "SINGLE"; INCR: return "INCR"; WRAP4: return "WRAP4"; INCR4: return "INCR4";
      WRAP8: return "WRAP8"; INCR8: return "INCR8"; WRAP16: return "WRAP16"; default: return "INCR16";
    endcase
  endfunction
  function automatic string resp_name(input logic r);
    return r ? "ERROR" : "OKAY";
  endfunction
  // s when c is 1, else the empty string (a string-typed result prints the same on every simulator).
  function automatic string opt(input bit c, input string s);
    if (c) return s;
    return "";
  endfunction

  // Beats in a burst: 1 for SINGLE, 4, 8 or 16 for the fixed bursts, 0 for INCR (undefined length).
  function automatic int beats_of(input logic [2:0] b);
    case (b) SINGLE: return 1; INCR: return 0; WRAP4, INCR4: return 4; WRAP8, INCR8: return 8; default: return 16; endcase
  endfunction
  function automatic bit is_wrap(input logic [2:0] b);
    return b == WRAP4 || b == WRAP8 || b == WRAP16;
  endfunction

  // The address of the beat after the one at a: the size in bytes further on; a wrapping burst
  // returns to the start of its window (beats x size bytes, aligned to that total) at the window's end.
  function automatic int unsigned next_addr(input int unsigned a, input logic [2:0] size, input logic [2:0] b);
    int unsigned nb = 1 << size;
    int unsigned window = nb * beats_of(b);
    int unsigned base;
    if (!is_wrap(b)) return a + nb;
    base = (a / window) * window;
    if (a + nb >= base + window) return base;
    return a + nb;
  endfunction

  // The address of beat n of a burst that starts at start.
  function automatic int unsigned beat_addr(input int unsigned start, input logic [2:0] size, input logic [2:0] b, input int n);
    int unsigned a = start;
    for (int k = 0; k < n; k++) a = next_addr(a, size, b);
    return a;
  endfunction

  // The lanes a transfer at address a uses on a bus of bus_bytes bytes, packed as {upper, lower}.
  // Every AHB transfer is aligned to its size, so it uses size bytes from its own lane upwards.
  function automatic logic [15:0] beat_lanes(input int unsigned a, input logic [2:0] size, input int bus_bytes);
    int lower = a % bus_bytes;
    int upper = lower + (1 << size) - 1;
    if (upper > bus_bytes - 1) upper = bus_bytes - 1;
    return {upper[7:0], lower[7:0]};
  endfunction

  // The strobe of a transfer: a 1 for every lane it uses (bit l is lane l).
  function automatic logic [63:0] beat_strb(input int unsigned a, input logic [2:0] size, input int bus_bytes);
    logic [15:0] lanes = beat_lanes(a, size, bus_bytes);
    logic [63:0] s = 0;
    for (int l = lanes[7:0]; l <= lanes[15:8]; l++) s[l] = 1'b1;
    return s;
  endfunction

  function automatic bit aligned(input int unsigned a, input logic [2:0] size);
    return (a % (1 << size)) == 0;
  endfunction

  // True when an incrementing burst of n beats (n is used only for INCR) crosses a 1 KB boundary.
  function automatic bit crosses_1k(input int unsigned start, input logic [2:0] size, input logic [2:0] b, input int n);
    int beats = beats_of(b) ? beats_of(b) : n;
    if (is_wrap(b) || beats < 2) return 0;
    return (start / 1024) != ((start + beats * (1 << size) - 1) / 1024);
  endfunction
endpackage
Read included file: ahb_mem.sv
`timescale 1ns/1ns
// ahb_mem.sv: an AHB subordinate with a byte-addressed memory behind it (read-only in this lesson).
// It registers an address phase at an edge where HSEL is 1, HTRANS is NONSEQ or SEQ and HREADY is 1,
// then runs the data phase: wait states with HREADYOUT low, a write takes HWDATA (and HWSTRB) at the
// edge that ends the data phase, a read drives HRDATA for the cycle that ends it. IDLE and BUSY get a
// zero-wait OKAY and change nothing. The switches below are plain variables: the stimulus sets them by
// name (mem.waits = 2;).
//   waits          wait states inserted in every data phase
//   waits_at[k]    wait states for the k-th transfer this memory takes (from 0; -1 = use waits)
//   err_above      transfers at or above this address get the two-cycle ERROR (and write nothing)
//   excl_on        AHB5 exclusive access: one monitor per HMASTER, HEXOKAY on success
//   trace          print every transfer the memory takes and every byte it writes
// Broken twins, each off by default:
//   ignore_hready    registers an address phase while HREADY is low (another subordinate is stretching),
//                    so it acts on a transfer the manager may still change or cancel
//   wdata_in_addr    takes HWDATA in the address phase, so a write stores the previous transfer's data
//   act_on_idle      treats IDLE and BUSY as transfers (waits, reads, writes HWDATA); this twin is an
//                    AHB-Lite design without an HWSTRB input, so it writes the lanes HSIZE selects
//   one_cycle_error  answers ERROR in one cycle (HRESP high with HREADYOUT high)
//   ignore_size      writes every lane of the bus word, whatever HSIZE and HWSTRB say
module ahb_mem #(parameter int DATA_W = 32, parameter int DEPTH = 65536) (
  input  logic              hclk,
  input  logic              hresetn,
  input  logic              hsel,
  input  logic [31:0]       haddr,
  input  logic [1:0]        htrans,
  input  logic              hwrite,
  input  logic [2:0]        hsize,
  input  logic [2:0]        hburst,
  input  logic [3:0]        hprot,
  input  logic              hmastlock,
  input  logic [DATA_W-1:0] hwdata,
  input  logic [DATA_W/8-1:0] hwstrb,
  input  logic              hexcl,
  input  logic [3:0]        hmaster,
  input  logic              hready,
  output logic              hreadyout,
  output logic              hresp,
  output logic [DATA_W-1:0] hrdata,
  output logic              hexokay
);
  import ahb_pkg::*;
  localparam int BUS = DATA_W / 8;

  // ---- switches ----
  int waits = 0;
  int waits_at [0:63];
  int unsigned err_above = 32'hffff_ffff;
  bit excl_on = 0, trace = 0;
  bit ignore_hready = 0, wdata_in_addr = 0, act_on_idle = 0, one_cycle_error = 0, ignore_size = 0;

  // ---- the memory ----
  logic [7:0] mem [0:DEPTH-1];
  int cycle = -1;
  int taken = 0;                              // transfers taken so far

  // ---- the data phase in progress ----
  bit              d_valid = 0, d_write = 0, d_err = 0, d_exok = 0, d_xfail = 0;
  int unsigned     d_addr = 0;
  logic [2:0]      d_size = 0;
  logic [1:0]      d_trans = 0;
  logic [DATA_W-1:0] d_wd = 0;                // HWDATA seen in the address phase (the wdata_in_addr twin)
  int              d_cnt = 0;                 // wait states still to insert
  bit              e_two = 0;                 // the ERROR response is in its second cycle

  // ---- exclusive monitors, one per HMASTER ----
  bit          mon_use [0:15];
  int unsigned mon_addr [0:15];
  int          mon_bytes [0:15];

  initial begin
    for (int i = 0; i < DEPTH; i++) mem[i] = 8'h00;
    for (int k = 0; k < 64; k++) waits_at[k] = -1;
    for (int m = 0; m < 16; m++) mon_use[m] = 0;
    hreadyout = 1; hresp = 0; hrdata = 0; hexokay = 0;
  end

  function automatic logic [DATA_W-1:0] read_word(input int unsigned a);
    logic [DATA_W-1:0] w = 0;
    int unsigned word = (a / BUS) * BUS;
    for (int l = 0; l < BUS; l++) w[8*l +: 8] = mem[(word + l) % DEPTH];
    return w;
  endfunction

  // Write the lanes of one transfer: the lanes its address and size select, where the strobe is 1.
  task automatic write_bytes(input int unsigned a, input logic [2:0] size, input logic [DATA_W-1:0] data, input logic [BUS-1:0] strb);
    logic [15:0] lanes = beat_lanes(a, size, BUS);
    int unsigned word = (a / BUS) * BUS;
    int lo = ignore_size ? 0 : lanes[7:0];
    int hi = ignore_size ? BUS - 1 : lanes[15:8];
    for (int l = lo; l <= hi; l++)
      if (ignore_size || act_on_idle || strb[l]) begin
        mem[(word + l) % DEPTH] = data[8*l +: 8];
        if (trace) $display("mem e%0d:   byte 0x%0h <= %02h (lane %0d)", cycle, word + l, data[8*l +: 8], l);
      end
    // any write ends the exclusive monitors that cover one of its bytes
    for (int m = 0; m < 16; m++)
      if (mon_use[m] && a < mon_addr[m] + mon_bytes[m] && mon_addr[m] < a + (1 << size)) mon_use[m] = 0;
  endtask

  always @(posedge hclk) begin
    cycle = cycle + 1;
    if (!hresetn) begin
      d_valid = 0; e_two = 0;
      hreadyout <= 1; hresp <= 0; hexokay <= 0;
    end else begin
      // 1. the data phase that ends or waits at this edge
      if (d_valid) begin
        if (hreadyout && hready) begin
          if (d_write && !d_err && !d_xfail)
            write_bytes(d_addr, d_size, wdata_in_addr ? d_wd : hwdata, hwstrb);
          d_valid = 0;
        end else if (!hreadyout) begin
          if (d_cnt > 0) d_cnt = d_cnt - 1;
          else if (d_err) e_two = 1;
        end
      end
      // 2. the address phase on the wires at this edge
      if (hsel && (htrans[1] || act_on_idle) && (hready || (ignore_hready && !d_valid))) begin
        d_valid = 1; d_addr = haddr; d_write = hwrite; d_size = hsize; d_trans = htrans; d_wd = hwdata;
        d_cnt = (taken < 64 && waits_at[taken] >= 0) ? waits_at[taken] : waits;
        taken = taken + 1;
        d_err = (haddr >= err_above); e_two = 0;
        d_exok = 0; d_xfail = 0;
        if (excl_on && hexcl && htrans[1] && !d_err) begin
          if (!hwrite) begin
            mon_use[hmaster] = 1; mon_addr[hmaster] = haddr; mon_bytes[hmaster] = 1 << hsize; d_exok = 1;
          end else if (mon_use[hmaster] && mon_addr[hmaster] == haddr && mon_bytes[hmaster] == (1 << hsize)) begin
            d_exok = 1; mon_use[hmaster] = 0;
          end else d_xfail = 1;
        end
        if (trace) $display("mem e%0d: %0s %0s 0x%0h size %0d%0s%0s%0s", cycle, trans_name(htrans), hwrite ? "write" : "read", haddr, hsize,
                            opt(!hready, " (taken while HREADY is low)"), opt(d_err, " -> ERROR"),
                            opt(d_xfail, " -> exclusive failed"));
      end
      // 3. the response for the cycle that starts now
      if (d_valid && d_cnt > 0) begin
        hreadyout <= 0; hresp <= 0; hexokay <= 0;
      end else if (d_valid && d_err) begin
        hreadyout <= one_cycle_error ? 1'b1 : e_two; hresp <= 1; hexokay <= 0;
      end else begin
        hreadyout <= 1; hresp <= 0; hexokay <= d_valid && d_exok;
      end
      if (d_valid && !d_write) hrdata <= d_err ? '0 : read_word(d_addr);
    end
  end
endmodule

Check yourself

Predict the outputA plain program applies two monitor rules to six sampled edges: the right one (an address phase counts at an edge with HREADY high and is published at the next such edge) and the broken one (every edge with NONSEQ is a transfer). Addresses are shortened to 1, 2, 3. What line is printed?
module tb;
  // Six edges as a monitor samples them, oldest first: HTRANS (2 = NONSEQ, 0 = IDLE), HADDR, HREADY.
  localparam logic [11:0] TRANS = {2'd2, 2'd2, 2'd2, 2'd2, 2'd2, 2'd0};
  localparam logic [23:0] ADDR  = {4'h1, 4'h2, 4'h3, 4'h3, 4'h3, 4'h3};   // 0x100, 0x200, 0x300 shortened to 1, 2, 3
  localparam logic [5:0]  READY = {1'b1, 1'b1, 1'b0, 1'b0, 1'b1, 1'b1};
  string right = "", broken = "";
  bit    pend = 0;
  logic [3:0] pa;
  initial begin
    for (int k = 0; k < 6; k++) begin
      // the broken monitor: every edge with a NONSEQ on the wires is a transfer
      if (TRANS[2 * (5 - k) +: 2] == 2) broken = {broken, $sformatf(" %0h", ADDR[4 * (5 - k) +: 4])};
      // the right monitor: an address phase counts when HREADY is high; it is published at the next such edge
      if (READY[5 - k]) begin
        if (pend) right = {right, $sformatf(" %0h", pa)};
        pend = (TRANS[2 * (5 - k) +: 2] == 2); pa = ADDR[4 * (5 - k) +: 4];
      end
    end
    $display("right:%0s broken:%0s", right, broken);
  end
endmodule
ConceptA monitor records HRDATA at the edge where a read's address phase is taken. What does it record?
ConceptIn the first cycle of an ERROR, this driver pushes the item from the address phase back to the front of its queue. Why the front?
ConceptThe four beats of an INCR4 read come out of the monitor as four lines. What would it take to publish the burst as one item?
Read the waveformThe write to 0x8000 gets ERROR; the read of 0x104 is next. At which cycle does the monitor publish the read, and with what data?
An ERROR and the read behind it
01234htransNONSEQIDLENONSEQIDLEhaddr8000104hwritehreadyhresphrdataa322

Measured from the example, 165 ns to 205 ns; one value per cycle, sampled at the edge that ends the cycle.

Remember

  • An AHB driver holds two items: one in the address phase and one in the data phase; both move at every edge where HREADY is high.
  • An address phase is taken only at an edge with HREADY high; the transfer ends at the next such edge, where its data and response are on the wires.
  • The monitor joins the two edges in pend: a transfer that waits on the wires is counted once, and its data comes from the data phase.
  • In the first cycle of an ERROR the driver may cancel the waiting transfer by driving IDLE; this one withdraws it and issues it again, in order.
  • The scoreboard keeps the memory it expects and must model what the DUT does on an ERROR; this memory writes nothing.