Week 5 · Day 25 · 7 hours

Day 25 — SystemVerilog Processes & IPC

Implement and debug concurrent stimulus using fork/join variants, events, mailboxes, semaphores and watchdog timeouts.

01

Concept lesson

Understand the language feature before coding

Day 25 — Processes and IPC

Simulation status (28 Oct 2026): The full lab passes on Verilator 5.052, including mailbox, semaphore, event and fork. Icarus Verilog 12 runs only the tb/day25_processes_ipc_icarus.sv subset. Details: Week 5 validation record (VALIDATION.md). Workspace: project_workspace/week5_systemverilog/day25_processes_ipc

Theory (2 h)

Explain fork...join, join_any, join_none, wait fork, disable fork, events, mailbox, bounded mailbox, semaphore and producer/consumer patterns.

Guided lab (2 h)

Demonstrate the six XLS exercises: fork/join; join_any; join_none; wait fork; disable fork; event trigger/wait. Record ordering and termination.

Independent lab (2 h)

Demonstrate mailbox put/get/try_get, bounded mailbox, semaphore locking, producer/consumer, parallel stimulus, and timeout/watchdog.

Debug / assignment (1 h)

Intentionally block a consumer and start a worker that never completes; capture watchdog evidence, correct synchronization/termination and rerun.

Suggested execution

verilator --binary --timing --top-module day25_processes_ipc tb/day25_processes_ipc.sv -o day25_sim ./obj_dir/day25_sim | tee logs/day25.log

For the Icarus-supported process/event and queue-based producer-consumer subset, run .venv/bin/python scripts/check_week5_simulator.py --simulator iverilog --run. This fallback does not validate native mailbox, semaphore, join_any, join_none, wait fork or disable fork behavior; run the full lab on a simulator that supports those constructs.

Compatibility: the full lab passes on Verilator 5.052. On another simulator, check its support for SystemVerilog mailbox, semaphore, event and fork constructs first.

Evidence

Keep source, logs, transaction ordering, deadlock reproduction, fix and final PASS/FAIL report. Do not use UVM or Week 6 constrained-random features.

02

Visual explanation

Connect syntax with simulator behavior

Concurrent process join modes: fork...join: wait for all; fork...join_any: wait for first; fork...join_none: do not wait immediately
Concurrent process join modes
Producer-consumer IPC: Producer -> bounded mailbox; Consumer -> semaphore-protected stats; Sentinel termination + watchdog timeout
Producer-consumer IPC
03

Worked example

Read and run the supplied SystemVerilog source

Example source · Day 25
// Week 5 Day 25 — SystemVerilog process and IPC demonstration.
// Simulator compatibility must be checked before claiming runtime PASS.
module day25_processes_ipc;
  mailbox #(int) channel = new(2);
  semaphore gate = new(1);
  event start_event;
  int received = 0;
  int total = 0;
  bit done = 0;

  task automatic producer();
    for (int i = 1; i <= 4; i++) begin
      channel.put(i);
      $display("PRODUCER put %0d", i);
    end
    channel.put(-1); // sentinel
  endtask

  task automatic consumer();
    int value;
    forever begin
      channel.get(value);
      if (value == -1) break;
      gate.get(1);
      received += value;
      total++;
      gate.put(1);
      $display("CONSUMER get %0d", value);
    end
    done = 1;
  endtask

  initial begin : main
    fork
      begin
        #1;
        -> start_event;
      end
      begin
        @start_event;
        $display("EVENT received");
      end
    join

    fork
      producer();
      consumer();
    join

    if (!done || total != 4 || received != 10)
      $fatal(1, "DAY25 FAIL: count=%0d sum=%0d", total, received);

    fork
      begin #2; $display("join_any fast"); end
      begin #4; $display("join_any slow"); end
    join_any
    wait fork;

    fork
      begin #1; $display("join_none worker"); end
    join_none
    wait fork;

    fork
      begin forever #1; end
      begin #3; $display("disable fork cleanup"); end
    join_any
    disable fork;

    $display("DAY25 PASS");
    $finish;
  end

  initial begin : watchdog
    #100;
    $fatal(1, "DAY25 FAIL: watchdog timeout");
  end
endmodule

Compile and run

  1. mkdir -p logs
  2. verilator --binary --timing --top-module day25_processes_ipc tb/day25_processes_ipc.sv -o day25_sim
  3. ./obj_dir/day25_sim | tee logs/day25.log

What to verify

  • fork/join
  • join_any
  • join_none
  • wait fork
  • disable fork
  • event synchronization
  • mailbox put/get/try_get
  • bounded mailbox
  • semaphore exclusion
  • producer-consumer
  • parallel stimulus
  • timeout/watchdog
  • deadlock recovery

Pass condition: Expected values and transaction counts match; no hanging process; watchdog catches intentional timeout; final DAY25 PASS is printed.

04

Hands-on practice

Complete the exercises in order

    Written assignment

    1. Compare fork/join, join_any and join_none with timing diagrams.
    2. Explain event missed-trigger risk and a safe synchronization pattern.
    3. Document bounded mailbox backpressure and semaphore mutual exclusion.
    4. Provide deadlock/runaway before-and-after logs, simulator version and command.

    Debug challenge

    Reproduce a blocked mailbox consumer and a runaway worker, record the timeout, then fix the producer/consumer termination protocol and rerun.

    Completion criteria

    12 XLS activities covered; concurrent processes terminate deterministically; bounded mailbox, semaphore and watchdog tests pass.

    05

    Knowledge check

    Check your understanding

    How do fork...join, join_any and join_none differ?

    join waits for all children; join_any waits for any one; join_none starts children without waiting.

    Why can an event trigger be missed?

    A process waiting on an event after its trigger does not receive the earlier transient trigger; establish synchronization before triggering.

    How does a bounded mailbox provide backpressure?

    A blocking put waits when the mailbox reaches its capacity until a consumer removes an item.

    What problem does a semaphore solve in concurrent stimulus?

    It limits access to a shared resource so critical sections cannot be entered by too many processes simultaneously.

    What does wait fork do after processes were started with join_none?

    It blocks until all outstanding child processes of the current process complete.

    Why use a typed mailbox such as mailbox #(transaction)?

    It restricts transferred values to the expected transaction type.

    Evidence to retain

    • SystemVerilog source
    • Build and run commands
    • PASS/FAIL transcript
    • producer-consumer ordering proof
    • watchdog timeout proof
    • deadlock/runaway fix evidence