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
  • Part 7: Ordering13
    • Why Ordering Matters
    • Ordering Scopes
    • The Point of Serialisation
    • Early and Final Acknowledgement
    • Fences and Barriers
    • Ordering Tables
    • Relaxed and ID-Based Ordering
    • Same-Address Hazards
    • Interrupt and Data Ordering
    • Memory Consistency Models
    • Ordering Across Bridges
    • Protocols Compared: Ordering
    • Drills: Ordering
  • 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 7: Ordering
  1. Topics
  2. First Principles of Hardware Protocols
  3. Part 7: Ordering
  4. Fences and Barriers

Part 7 · Ordering · Senior (S)

Fences and Barriers

A fence (or barrier) forces order on demand: nothing after it may become visible before everything before it has. How a fence works in hardware, what it waits for, and what it costs.

On this page

    Note

    Level: Senior (S) · Answers: In what order do results become visible?

    Before this: Early and Final Acknowledgement

    A system lets writes overtake each other freely, which makes it fast. Most of the time no one cares. But at the end of filling a buffer, the producer must be sure every data write has landed before it writes the flag. Keeping everything in order all the time would give up the speed. What the producer needs is a way to say, at that one point: "finish everything before this, then continue".

    How does a system force order only where it is needed?

    The obvious answer, and why it breaks

    One obvious way is to wait for the response to every data write before sending the flag. That works if the responses are final ones (lesson 7.4), but it means the producer must track every outstanding write itself. Another obvious way is to put everything on one ordered path, which gives up parallelism for all traffic, not just at the hand-off. A fence gives order exactly at the point it is placed, and nowhere else.

    The idea: a fence

    A fence (or barrier) divides a requester's accesses into before and after. No access after the fence may become visible until every access before it has completed to the point the fence promises (accepted on the path, or reached its destination). Between fences, accesses may be reordered freely.

    Kind

    Orders

    Typical use

    Write fence (store barrier)

    Earlier writes before later writes

    Data before flag; descriptors before doorbell

    Full fence

    All earlier accesses before all later ones

    Hand-offs that mix reads and writes

    Acquire / release

    Release: earlier accesses before the releasing write. Acquire: the acquiring read before later accesses

    Locks and message passing, at lower cost than full fences

    The fence completes only when everything before it has drained; the flag cannot overtake the data.

    Build it: a fence that drains a write buffer

    This is DCLink step 6. The producer posts three data writes into a write buffer that drains one write every 3 cycles, then issues a FENCE. The fence completes only when the buffer is empty; only then does the producer post the flag. The checker confirms the flag reaches memory after all three data writes.

    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
    56
    57
    58
    59
    60
    61
    62
    63
    64
    // DCLink step 6: a fence drains a posted-write buffer. The producer posts writes to
    // addresses 1, 2 and 3 into a 4-entry write buffer, which drains one write to memory
    // every 3 cycles. It then issues a FENCE: the fence completes (fence_cpl) only when
    // the buffer is empty, that is, when every earlier write has reached memory. Only
    // then does the producer post the flag write (address 9). The checker confirms the
    // flag reaches memory after all three data writes.
    module tb;
      logic clk = 0;
      always #5 clk = ~clk;
      initial begin $dumpfile("wave.vcd"); $dumpvars(0, tb); end
    
      int cyc = 0;
      always @(posedge clk) cyc <= cyc + 1;
    
      logic       fence_wait = 0, flag_sent = 0;
      // Write buffer: FIFO, drains one entry every 3 cycles; a fence completes when it is empty
      logic [3:0] buf_q [0:3];
      logic [1:0] rp = 0, wp = 0;
      logic [2:0] buf_cnt = 0;
      logic [1:0] tick = 0;
      wire        drain = (buf_cnt != 0) && (tick == 2);
      logic       mem_wr = 0;
      logic [3:0] mem_addr = 0;
      wire        fence_cpl = fence_wait && (buf_cnt == 0);
    
      // Producer
      logic       wr;
      logic [3:0] wr_addr;
      logic       fence;
      always_comb begin
        wr = 0; wr_addr = 0; fence = 0;
        case (cyc)
          1: begin wr = 1; wr_addr = 1; end
          2: begin wr = 1; wr_addr = 2; end
          3: begin wr = 1; wr_addr = 3; end
          4: fence = 1;
          default: ;
        endcase
        if (fence_cpl && !flag_sent) begin wr = 1; wr_addr = 9; end   // flag only after the fence completes
      end
    
      always @(posedge clk) begin
        tick <= (tick == 2) ? 0 : tick + 1;
        if (wr) begin buf_q[wp] <= wr_addr; wp <= wp + 1; end
        mem_wr <= drain;
        if (drain) begin mem_addr <= buf_q[rp]; rp <= rp + 1; end
        buf_cnt <= buf_cnt + wr - drain;
        if (fence) fence_wait <= 1;
        if (fence_cpl) begin fence_wait <= 0; flag_sent <= 1; end
      end
    
      // Checker: count data writes in memory; the flag must find all three there
      int landed = 0, errors = 0, flag_at = -1;
      always @(posedge clk) if (mem_wr) begin
        if (mem_addr == 9) begin if (landed != 3) errors++; flag_at = cyc; end
        else landed++;
      end
      initial begin
        #300;
        if (errors == 0 && flag_at > 0) $display("PASS: the flag reached memory after all 3 data writes; the fence waited for the buffer to drain");
        else $display("FAIL: errors %0d flag_at %0d", errors, flag_at);
        $finish;
      end
    endmodule

    The fence itself is one line: fence_cpl is high when a fence is waiting and the buffer is empty. Everything else is the buffer.

    Watch it run

    How to read it, slot by slot:

    • Slots 0 to 2: the producer posts writes to addresses 1, 2 and 3 (wr, wr_addr). The buffer starts draining at once: mem_wr shows address 1 reaching memory in slot 2.

    • Slot 3: the producer issues the FENCE (fence). Two writes are still in the buffer (buf_cnt 2).

    • Slots 5 and 8: addresses 2 and 3 reach memory (mem_wr, mem_addr); in slot 8 the buffer is empty and fence_cpl rises.

    • Slot 8: in the same cycle, and not before, the producer posts the flag (address 9). It drains and reaches memory in slot 11, after all three data writes.

    What a fence costs

    • A stall: everything after the fence waits for the slowest access before it, often a full round trip to memory.

    • Less overlap: accesses that could have proceeded in parallel across the fence cannot.

    • Implementation: the requester (or its buffers) must know when earlier accesses have completed, which needs final acknowledgements or a drain signal from each buffer on the path.

    That is why software uses the weakest fence that is correct (a write fence rather than a full fence, release rather than full ordering) and places it only at hand-offs.

    How real systems chose

    System

    Fences

    Processor instruction sets

    Arm has DMB and DSB barriers; x86 has MFENCE, SFENCE and LFENCE; RISC-V has FENCE with predecessor and successor sets. Release and acquire forms order only what a lock or flag needs.

    AXI

    No fence transaction: a requester waits for earlier write responses (from the destination, if they must have landed) before issuing ordered accesses.

    DCLink (step 6)

    A FENCE request whose completion means every earlier write has been performed.

    What breaks

    A driver (illustrative) put a fence between writing descriptors and ringing a doorbell, but used a fence that only ordered accesses to normal memory, while the doorbell was a device register. The processor treated device accesses as outside that fence's scope, and the doorbell occasionally overtook the descriptors. Fences have scopes too (lesson 7.2): the fence must cover both kinds of access it is meant to order.

    Why this matters

    Senior lens. Review fences like ordering scopes: what does this fence order, against what, and up to which point (accepted or arrived)? Then check the hardware path: every buffer between requester and destination must either drain on a fence or preserve order across it. A fence that the requester honours but a downstream bridge ignores is no fence at all.

    Interview

    Asked: "How does a barrier work in hardware?"

    Weak answer: It flushes the cache.

    Strong answer: It splits the requester's accesses into before and after, and holds the after ones until the before ones have completed to the point the barrier promises. In practice the requester stops issuing ordered accesses and waits until its write buffer has drained and all earlier writes are acknowledged, from the destination if the barrier promises they've landed. Every buffer on the path must either drain or keep order across it. It costs a stall of roughly the slowest earlier access, so code uses the weakest barrier that's correct, only at hand-offs.

    Follow-ups: (1) What is the difference between acquire, release and a full fence? (2) What happens if a bridge on the path ignores the fence?

    Check yourself In the waveform, why can't the flag be posted in slot 4?

    The fence has not completed: two data writes are still in the buffer (buf_cnt is not 0), so fence_cpl is low.

    Check yourself Why do locks use acquire and release instead of full fences?

    They order only what a lock needs (accesses inside the critical section against the lock's acquiring read and releasing write), so they stall less than a full fence.

    Key takeaways

    • A fence orders everything before it against everything after it, only at the point it is placed.

    • It works by holding later accesses until earlier ones have completed; every buffer on the path must drain or keep order.

    • Fences cost a stall; use the weakest correct one, and check its scope covers what it must order.

    Keep going

    Next: Lesson 7.6, Ordering Tables. Which transactions may pass which, and the reason behind every cell of the table.

    Related topics

    • Part 7 overview: Ordering

    • Architecture course: Fences, Acquire and Release

    Previous

    108 / 234

    Next: Ordering Tables

    Up next

    Up nextOrdering Tables

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Why Ordering Matters
    • Ordering Scopes
    • The Point of Serialisation
    clkwrwr_addr123090buf_cnt0121010mem_wrmem_addr01239fencefence_cpl
    Simulation output: three data writes enter the buffer, the fence waits until they have all drained, and only then is the flag written.