Part 4 · TLM & Analysis · Intermediate

peek vs get: Observe Without Consume

Behavioral differences between peek and get, ordering implications, and practical patterns for scoreboards and lookahead logic.

Core semantic difference

Both peek and get retrieve transaction content, but only get consumes the item from the source. peek returns a view of the next item while leaving queue state unchanged.

This distinction is critical in layered checkers: one stage might inspect headers for routing decisions, while a later stage performs final consume for ownership transfer.

diagram
Legend: [TLM] [UVM]

queue front model

initial queue: [A][B][C]

peek(x) -> x=A
queue after peek: [A][B][C]

get(x)  -> x=A
queue after get : [B][C]
  • peek is non-destructive observation.

  • get is destructive consume with ownership transfer intent.

  • Repeated peeks without get return same front element.


Implementation sketch with blocking peek/get

systemverilog
class peek_get_source extends uvm_component;
  `uvm_component_utils(peek_get_source)
  uvm_blocking_peek_imp #(pkt, peek_get_source) peek_imp;
  uvm_blocking_get_imp  #(pkt, peek_get_source) get_imp;
  pkt q[$];

  function new(string name, uvm_component parent);
    super.new(name, parent);
    peek_imp = new("peek_imp", this);
    get_imp  = new("get_imp", this);
  endfunction

  task run_phase(uvm_phase phase);
    pkt p;
    repeat (3) begin
      p = pkt::type_id::create("p");
      assert(p.randomize());
      q.push_back(p);
    end
  endtask

  task peek(output pkt p);
    wait (q.size() > 0);
    p = q[0]; // does not remove
  endtask

  task get(output pkt p);
    wait (q.size() > 0);
    p = q.pop_front(); // consumes
  endtask
endclass

class lookahead_consumer extends uvm_component;
  `uvm_component_utils(lookahead_consumer)
  uvm_blocking_peek_port #(pkt) peek_port;
  uvm_blocking_get_port  #(pkt) get_port;

  function new(string name, uvm_component parent);
    super.new(name, parent);
    peek_port = new("peek_port", this);
    get_port  = new("get_port", this);
  endfunction

  task run_phase(uvm_phase phase);
    pkt p0, p1;
    peek_port.peek(p0);
    `uvm_info("LOOKAHEAD", $sformatf("header preview=0x%08h", p0.data), UVM_MEDIUM)

    get_port.get(p1);
    `uvm_info("LOOKAHEAD", $sformatf("consumed=0x%08h", p1.data), UVM_MEDIUM)
  endtask
endclass
diagram
[UVM] connection note

A single source can implement both peek and get interfaces.
Consumers may connect to one or both depending on role:
  - classifier/arbiter uses peek heavily
  - executor/checker eventually calls get

Scoreboard and predictor usage patterns

In checking flows, peek is useful for early comparisons or correlation windows, while get defines when an expected/actual pair is committed and removed from pending sets.

diagram
[TLM] common checker pattern

expected_fifo.peek(exp_head)
actual_fifo.peek(act_head)
if compare_headers(exp_head, act_head):
  expected_fifo.get(exp_final)
  actual_fifo.get(act_final)
  compare_payload(exp_final, act_final)
else
  wait for more data or mismatch policy action
systemverilog
task compare_loop();
  pkt exp_h, act_h, exp_f, act_f;
  forever begin
    exp_port.peek(exp_h);
    act_port.peek(act_h);

    if (exp_h.data[31:24] == act_h.data[31:24]) begin
      exp_port.get(exp_f);
      act_port.get(act_f);
      do_compare(exp_f, act_f);
    end
    else begin
      #1ns; // allow additional arrivals before declaring mismatch
    end
  end
endtask
  • peek enables safe lookahead without disturbing ordering.

  • get marks commitment and consumes queue state.

  • Use explicit consume points to avoid duplicate or skipped compares.


Correctness guardrails

  1. Document ownership transfer point where get is required.

  2. Avoid mutating transactions returned by peek unless cloning first.

  3. Limit repeated peeks in tight loops to prevent starvation of consumers that must get.

  4. Instrument queue depth when using prolonged lookahead windows.

diagram
[UVM] anti-patterns

1) perform compare on peeked object, then forget to call get -> infinite recompare
2) mutate peeked object fields -> downstream consumer sees modified data
3) call get in one thread and assume another thread's previous peek remains valid

Key takeaways

  • peek is observation; get is consumption.

  • Two-phase compare patterns rely on peek-then-get discipline.

  • Clear consume ownership prevents duplicate processing bugs.

Common pitfalls

  • Using peek-only loops that never advance queue state.

  • Mutating shared objects observed via peek.

  • Assuming peek snapshots stay valid after concurrent gets.