Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • Reading Paths
  • Concept Map
  • How to Read the Diagrams
  • Part 0: What Is a Protocol?10
  • Part 1: Timing and Clocking11
  • Part 2: Encoding and Framing12
  • Part 3: Handshakes13
  • Part 4: Flow Control17
  • Part 5: Transactions19
  • Part 6: Concurrency and Performance17
    • Latency, Throughput and Bandwidth
    • Little's Law
    • Outstanding Transactions
    • Tags and IDs
    • In-Order or Out-of-Order
    • Reorder Buffers
    • Interleaving
    • Split Completions
    • Protocol Efficiency
    • Peak and Sustained Bandwidth
    • Queueing Intuition
    • Tail Latency
    • Buffers That Hide Latency
    • Virtual Output Queues
    • Performance Modelling
    • Protocols Compared: Concurrency and Performance
    • Drills: Concurrency and Performance
  • Part 7: Ordering13
  • Part 8: Sharing and Arbitration12
  • Part 9: Deadlock and Liveness13
  • Part 10: Reliability and Recovery20
  • Part 11: Bring-Up and Link Management10
  • Part 12: Power8
  • Part 13: Shared State and Coherence9
  • Part 14: Security and Isolation7
  • Part 15: Specifying and Verifying11
  • Part 16: Protocols Dissected17
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 6: Concurrency and Performance
  1. Topics
  2. First Principles of Hardware Protocols
  3. Part 6: Concurrency and Performance
  4. Tags and IDs

Part 6 · Concurrency and Performance · Working (W)

Tags and IDs

With many requests outstanding, each response must say which request it answers. How tags and IDs work, how many you need (Little's law again), and the free-list allocator that hands them out.

On this page

    Note

    Level: Working (W) · Answers: How many requests can be in flight?

    Before this: Outstanding Transactions

    A requester has four reads outstanding. A response arrives carrying a word of data. Which of the four reads is it for? If responses always came back in order the answer would be "the oldest", but the four reads went to different targets, and the fastest one answered first. Without a label on the response, the requester cannot tell.

    How do responses find their requests, and how many labels does a requester need?

    The obvious answer, and why it breaks

    The obvious answer is to rely on order: responses come back in the order requests went out. That works with one target and a single path. With several targets of different speeds, or a fabric with several paths, forcing responses into request order means holding fast responses until slow ones arrive, so the slowest target sets everyone's latency. The alternative is to label each request and let the response carry the label back.

    The idea: a tag per outstanding request

    • Each request carries a tag. The response echoes it.

    • The requester keeps a table indexed by tag: what was asked, and where the answer should go.

    • A tag is in use from request until its last response; it must not be given to another request while in use, or two outstanding requests would be indistinguishable.

    • A free list holds the tags not in use. A new request takes one; a final response returns it.

    A request takes a free tag and records itself in the table; its completion looks up the tag and returns it to the free list.

    Three labels that look alike

    Specifications use "tag", "ID" and "transaction ID" loosely, but there are three different kinds of label, and they match responses in different ways. Before writing a requester or a scoreboard, find out which one you have.

    Label

    Names

    Outstanding at once with the same value

    How a response is matched

    Examples

    Unique transaction tag

    One transaction

    At most one

    Directly: the tag selects exactly one request

    DCLink step 5, PCIe Tag (with Requester ID), CHI TxnID

    Ordering ID

    A stream of transactions that must stay in order

    Many

    To the oldest outstanding request with that ID

    AXI AWID/ARID

    Requester identity

    Who sent it

    Many

    Routes the response back to the right requester; says nothing about which of its requests

    PCIe Requester ID, CHI SrcID, AXI master port

    A unique tag allows full reordering between every pair of requests, and needs one tag per outstanding request. An ordering ID lets a requester keep many requests in flight on a few IDs, at the price of ordering among those that share one. Real protocols often combine them: a PCIe completion is matched by Requester ID plus Tag, and an AXI interconnect widens IDs with the port number so responses can be routed back. The rest of this lesson, and DCLink, use unique tags; lesson 15.5 shows how a scoreboard differs for each.

    How many tags?

    Tags cap the number of outstanding requests, so by Little's law:

    tags≥request rate×worst-case round trip,tag bits=⌈log2​(tags)⌉
    Diagram
    Illustrative: one read per cycle at 1 GHz, worst-case round trip 300 ns
    Tags needed:  1e9 per second x 300e-9 s  = 300  ->  9 tag bits (512 tags)
    With 6 bits (64 tags):  at most 64 / 300 ns = 213 million reads per second (21% of the rate)

    Size for the worst round trip you need full throughput at, not the typical one: tags are held for the whole round trip of the slowest response.

    Build it: a tag allocator with a free list

    This is DCLink step 5's allocator, with DCLink's 2-bit tag field: 4 tags. The requester wants to send 8 reads back to back; the target answers each after its own latency, so completions come back out of order, at most one per cycle. The checker fails if a tag is ever given out while still in use.

    tb.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    // DCLink step 5: a tag allocator with a free list. The requester has 4 tags (the
    // 2-bit tag field). A request may go only when a tag is free; it takes the tag at
    // the head of the free list. The target answers out of order (each request has its
    // own latency, and at most one completion returns per cycle); a completion returns
    // its tag to the tail of the free list. The
    // checker makes sure no tag is ever given out while it is still in use.
    module tb;
      logic clk = 0;
      always #5 clk = ~clk;
      initial begin $dumpfile("wave.vcd"); $dumpvars(0, tb); end
    
      // Free list: a 4-entry FIFO of tags, full after reset
      logic [1:0] fl [0:3];
      logic [1:0] rp = 0, wp = 0;
      logic [2:0] free_cnt = 4;
      initial begin fl[0] = 0; fl[1] = 1; fl[2] = 2; fl[3] = 3; end
    
      // Requests: from cycle 1 the requester wants to send 8 requests back to back
      int sent = 0;
      int cyc = 0;
      wire       req = (cyc >= 1) && (sent < 8) && (free_cnt != 0);
      wire [1:0] tag = fl[rp];
      // Target: request k is answered after lat[k] cycles
      int lat [0:7];
      initial begin lat[0] = 6; lat[1] = 2; lat[2] = 4; lat[3] = 1; lat[4] = 3; lat[5] = 5; lat[6] = 2; lat[7] = 1; end
      int        due [0:3];              // cycle at which the request holding each tag completes
      logic [3:0] busy = 0;
      logic       cpl;
      logic [1:0] cpl_tag;
      // One completion per cycle: the lowest busy tag that is due (a later one waits a cycle)
      always_comb begin
        cpl = 0; cpl_tag = 0;
        for (int i = 3; i >= 0; i--) if (busy[i] && due[i] <= cyc) begin cpl = 1; cpl_tag = 2'(i); end
      end
    
      int errors = 0, completed = 0;
      always @(posedge clk) begin
        cyc <= cyc + 1;
        if (req) begin
          if (busy[tag]) errors++;                           // tag still in use: a bug
          busy[tag] <= 1; due[tag] <= cyc + lat[sent];
          rp <= rp + 1; sent <= sent + 1;
        end
        if (cpl) begin
          busy[cpl_tag] <= 0; fl[wp] <= cpl_tag; wp <= wp + 1; completed++;
        end
        free_cnt <= free_cnt - req + cpl;
      end
      initial begin
        #300;
        if (errors == 0 && completed == 8) $display("PASS: 8 requests completed out of order with 4 tags; no tag was reused while in flight");
        else $display("FAIL: errors %0d completed %0d", errors, completed);
        $finish;
      end
    endmodule

    The free list is a FIFO of tag numbers: a request takes the head, a completion appends its tag at the tail, so tags are reused in the order they were freed.

    Watch it run

    How to read it, slot by slot:

    • Slots 0 to 3: four requests take tags 0, 1, 2 and 3 (tag), and free_cnt counts down 4, 3, 2, 1.

    • Slot 3: the first completion arrives (cpl), for tag 1, not tag 0: request 1 had the shortest latency. A tag is taken and one returned in the same cycle, so free_cnt stays at 1, and in slot 4 the fifth request reuses tag 1.

    • Slots 4 and 5: tag 3 completes in slot 4 and the sixth request takes it in slot 5, leaving no free tags (free_cnt 0 in slot 6).

    • Slot 6: all four tags are in use and req is low: the requester must wait. Tag 0 completes in slot 6, and the seventh request takes it in slot 7. Read cpl_tag only in slots where cpl is high.

    How real protocols chose

    Protocol

    Labels

    AXI

    Ordering IDs on every channel, not unique tags: many transactions may share an ID and then complete in order; different IDs may complete in any order. ID width is chosen per design.

    PCI Express

    Each non-posted request carries a tag (with the requester's ID), and its completions echo both.

    DCLink (step 5)

    A 2-bit unique tag in the header: at most 4 requests outstanding, never two on the same tag.

    What breaks

    • Running out of tags: the requester stalls with the link idle, the opening problem of lesson 6.3 in disguise. The symptom is throughput that stops rising when latency rises.

    • Reusing a tag too early (illustrative): an allocator that freed a tag on the first of several completions for a split read (lesson 6.8) handed it to a new request, and the old read's remaining data was delivered to the new one. Free a tag only on the last response.

    • Lost completions: a tag whose completion never arrives is never freed; after enough losses the requester stops. A timeout that reclaims tags, with an error, prevents a silent leak.

    Why this matters

    Senior lens. Size the tag pool from the worst-case round trip at full rate, and check every other table indexed by tag (response buffers, reorder buffers, interconnect tracking) is as large, since the smallest sets the limit. Assert that no tag is allocated while in use and that every tag is eventually returned; a slow tag leak looks exactly like a credit leak (lesson 4.8).

    Interview

    Asked: "How many tag bits do you need?"

    Weak answer: Enough to count the requests.

    Strong answer: Enough tags to cover the outstanding requests needed at full rate, which Little's law gives as request rate times the worst-case round trip. At one request per cycle and a 300-cycle round trip that's 300 tags, so 9 bits. Fewer tags caps throughput at tags divided by round trip. Then the tag must stay reserved until the last response for it arrives, including split completions.

    Follow-ups: (1) What happens when you run out of tags? (2) When may a tag be reused?

    Check yourself A requester issues one read every 2 cycles and the worst round trip is 120 cycles. How many tags, and how many bits?

    60 tags (0.5 × 120), so 6 bits (64 tags).

    Check yourself An AXI manager issues three reads, all with ID 5. Could DCLink do the same with tag 1?

    No. AXI IDs are ordering IDs: the three reads complete in order and each response matches the oldest outstanding ID 5 read. A DCLink tag is unique while outstanding, so a second read on tag 1 must wait until the first one's completion frees it.

    Check yourself In the waveform, why does the fifth request get tag 1 rather than tag 0?

    Tag 0 is still in use (request 0 has the longest latency); tag 1 was the first returned to the free list.

    Key takeaways

    • Each outstanding request carries a tag that its response echoes; a tag is reserved until the last response.

    • Tags needed ≥ request rate × worst round trip; fewer tags cap throughput.

    • A free list hands out tags; assert none is reused while in use and none leaks.

    Keep going

    Next: Lesson 6.5, In-Order or Out-of-Order. Who pays for putting responses back in order, and why many protocols let them return out of order.

    Related topics

    • Part 6 overview: Concurrency and Performance

    Previous

    90 / 234

    Next: In-Order or Out-of-Order

    Up next

    Up nextIn-Order or Out-of-Order

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Latency, Throughput and Bandwidth
    • Little's Law
    • Reorder Buffers
    clkreqtag0123132012free_cnt432101234cplcpl_tag0130120130
    Simulation output: 8 requests share 4 tags. Completions return out of order and free their tags; with none free, the requester waits.