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.
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
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[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 getScoreboard 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.
[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 actiontask 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
endtaskpeek 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
Document ownership transfer point where get is required.
Avoid mutating transactions returned by peek unless cloning first.
Limit repeated peeks in tight loops to prevent starvation of consumers that must get.
Instrument queue depth when using prolonged lookahead windows.
[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 validKey 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.