Check power-domain clamps, retention and restart ordering

How-to Last tested 24 October 2026

Use plain RTL simulation to check the control contract at a power-domain boundary: what output is clamped, when state is saved, when it is restored, and when the boundary may reopen. Model those choices explicitly and make incorrect ordering fail an assertion. This example executes that digital contract; it does not execute UPF or establish that physical power cells and supplies are connected correctly.

Define the boundary before the sequence

Write down the domain’s useful state, its externally visible outputs and the controller signals that change their meaning. The example has one byte of state, a separately modeled retained byte and one output. power_good is a testbench-controlled Boolean, not a measured voltage. isolate selects an output clamp. save and restore move a value between the model’s two storage roles, while reset gives the working state a defined restart value.

Those roles are deliberately separate. An output can be clamped while the internal state is still valid, which allows preparation before power removal. A retained copy can remain available while the working state has been cleared. Restoring that copy changes the working state but does not itself release isolation. Keeping the controls independent makes ordering errors observable instead of allowing one convenient control signal to hide several assumptions.

Power intent has a wider scope. IEEE’s public P1801 description covers power-management information alongside HDL and library models, including isolation, level shifting and retention1. The code here is an original behavioral model of selected control obligations. It contains no supply network, voltage value, power-state table, library-cell mapping or power-aware corruption semantics. A successful run therefore supports the stated sequence only; it is not evidence that a power-intent file was interpreted.

Choose a clamp from the receiving interface’s meaning. Zero is easy to see in this byte-wide example, but a production receiver may interpret zero as a valid command or active control. Record which combination leaves the receiver inactive, which signals must stay meaningful during shutdown, and which domain supplies the isolation control. The example’s output is data, and its testbench never interprets the clamped byte as a request to perform work.

Run the explicit RTL model

Save this complete module and self-checking testbench as example.sv. State updates happen at the positive clock edge. Stimulus steps wait until after that edge, and the sequence assertions inspect the controls at the negative edge. That arrangement gives the model’s non-blocking updates time to settle before observations and avoids using an ordering race as a demonstration of a power failure.

// SPDX-License-Identifier: MIT
`timescale 1ns/1ps
module power_model(
  input logic clk, reset, power_good, isolate, save, restore, write_enable,
  input logic [7:0] write_data,
  output logic [7:0] output_data,
  output logic [7:0] state=0, retained=0,
  output logic retained_valid=0, restored=0
);
  assign output_data=isolate ? 8'h00 : state;
  always @(posedge clk) begin
    if (save && power_good && !reset) begin
      retained <= state;
      retained_valid <= 1;
    end
    if (reset || !power_good) begin
      state <= 0;
      restored <= 0;
    end else if (restore) begin
      state <= retained;
      restored <= 1;
    end else if (write_enable) state <= write_data;
  end
endmodule
module tb;
  logic clk=0, reset=1, power_good=1, isolate=1;
  logic save=0, restore=0, write_enable=0;
  logic [7:0] write_data=0, output_data, state, retained;
  logic retained_valid, restored;
  bit returning=0;
  power_model dut(.*);
  always #5 clk=~clk;
  always @(negedge clk) begin
    assert(!save || (power_good && !reset)) else $fatal(1,"save ordering");
    assert(!restore || (power_good && !reset && retained_valid))
      else $fatal(1,"restore ordering");
    assert(isolate || power_good) else $fatal(1,"unclamped while off");
    assert(!isolate || output_data===0) else $fatal(1,"clamp mismatch");
    assert(!returning || isolate || restored) else $fatal(1,"early release");
  end
  task tick;
    @(posedge clk); #1;
  endtask
  initial begin
    tick(); reset=0; write_enable=1; write_data=8'h37;
    tick(); write_enable=0; save=1;
    tick(); save=0;
    assert(retained===8'h37) else $fatal(1,"save value");
    $display("TRACE saved=%h clamp=%h",retained,output_data);
    power_good=0; returning=1;
    tick();
    assert(state===0 && output_data===0) else $fatal(1,"off model");
    $display("TRACE off_state=%h clamp=%h",state,output_data);
    if ($test$plusargs("BAD")) begin
      isolate=0;
      $display("TRACE injecting unclamp while power_good=0");
      tick();
      $fatal(1,"checker did not stop bad ordering");
    end
    power_good=1; reset=1;
    tick(); reset=0;
    tick(); restore=1;
    tick(); restore=0;
    assert(state===8'h37 && restored) else $fatal(1,"restore value");
    isolate=0;
    tick();
    assert(output_data===8'h37) else $fatal(1,"release value");
    $display("TRACE restored=%h released=%h",state,output_data);
    $display("PASS RTL clamp/save/restore/order checks; no UPF execution");
    $finish;
  end
  initial begin #300; $fatal(1,"timeout"); end
endmodule

The Icarus library binding below is installation-specific; use the ivl directory belonging to the selected installation. The command references document language selection and assertion controls23.

Icarus invocation:

iverilog -B "${ICARUS_LIB}" -g2012 -gstrict-expr-width -Wall -s tb -o run.vvp example.sv
vvp -M "${ICARUS_LIB}" run.vvp

Verilator invocation:

verilator --binary --timing --assert --language 1800-2012 --x-assign 0 --x-initial 0 -Wno-fatal --quiet-build --quiet-stats --quiet-exit --top-module tb --Mdir obj example.sv
./obj/Vtb +verilator+quiet

The normal run produced the following application output in both tools. Both compilers and both normal executions returned zero. The finish banners differ by simulator and remain separate from the result comparison.

TRACE saved=37 clamp=00
TRACE off_state=00 clamp=00
TRACE restored=37 released=37
PASS RTL clamp/save/restore/order checks; no UPF execution

The saved value is hexadecimal 37. During the modeled off state the working byte is zero and the output remains clamped to zero. After the explicit restore, both the state and the released output are 37. These are observed digital values at selected points, not analog measurements or a proof that every possible intervening sequence is valid.

Check isolation and save before shutdown

The initial phase holds isolation active while the testbench leaves reset, writes the working byte and requests a save. The save occurs on a later positive clock edge. Only after the testbench checks the retained value does it lower power_good. That order matters: the model clears working state whenever reset is asserted or power is not good, so saving after that clearing would preserve the restart value rather than the intended byte.

The assertion on save requires power to be good and reset to be inactive. A separate value check verifies the actual retained byte. Neither condition alone is sufficient. A correctly ordered save could still capture the wrong data; a coincidentally correct retained byte could survive even when an illegal control sequence should have been rejected. Keep ordering and value checks as separate claims so that either can expose a different integration error.

The isolation assertion checks the visible consequence: whenever isolate is asserted, output_data must equal zero. Another assertion prohibits releasing the clamp while power_good is false. The latter expresses a controller requirement even though this simplified model has already forced the working state to zero. Without it, an illegal release could look harmless simply because the off-state model happens to produce the same value as the clamp.

The model is intentionally conservative about its own meaning. It clears an off-domain byte rather than injecting unknown values. That keeps this example portable across the selected configurations, but it removes an entire category of power-aware behavior. A real power-aware run must apply its own specified off-state and corruption semantics. Do not infer successful corruption propagation, isolation-cell insertion or retention-cell connectivity from the clean zero observed here.

Restore only after the required restart state

The return sequence raises power_good with reset still asserted. It then releases reset, waits another step, and requests restore. The restore assertion requires good power, inactive reset and a valid saved copy. The normal run checks that state becomes 37 and restored becomes one before it releases isolation. This makes the order a visible part of the test rather than an assumption buried in the controller.

returning marks the period after shutdown in this testbench. During that period, the checker allows either continued isolation or an explicit restored state. It rejects reopening the boundary merely because power has returned. The marker is a testbench aid, not a complete power-management controller; a design with cold boot, warm restart and multiple retained regions needs a contract for each path.

The reset arrangement also distinguishes working storage from retained storage. Reset clears state and the model’s restore-completed flag, but the retained byte remains in its separately modeled storage. That is a deliberate modeling decision for this scenario. If a project intends a reset to invalidate retained state, its RTL model and assertions must express that different contract rather than inheriting this example’s choice by accident.

Write a sequence table before extending the model. For each phase, identify permitted writes, clamp state, saved-copy validity, reset state and the event that permits advancement. Include a rejected path as well as the normal one. A compact table makes disagreements about save versus reset priority easier to find than a long waveform in which several controls change at the same timestamp.

PhaseWorking stateRetained copyBoundary condition
PrepareWrite 37Save pendingClamp active
Save37Captured 37Clamp active
Off modelCleared to 00Preserved 37Release prohibited
RestartReset then idleValid 37Clamp active
RestoreReloaded 37Valid 37Release permitted after check

Make a bad release fail

Run the same compiled model with +BAD; no source edit is required. After the off-state check, the testbench releases isolation while power_good remains zero. At the next negative edge the ordering assertion terminates the run. The final fallback fatal in the testbench exists to catch a checker that failed to stop the bad sequence; it was not the fatal reached in these executions.

# icarus
vvp -M "${ICARUS_LIB}" run.vvp +BAD
# verilator
./obj/Vtb +verilator+quiet +BAD

Both executions returned status one. Their literal failure messages identify the intended check:

icarus
TRACE saved=37 clamp=00
TRACE off_state=00 clamp=00
TRACE injecting unclamp while power_good=0
FATAL: example.sv:37: unclamped while off
       Time: 40000  Scope: tb

verilator
TRACE saved=37 clamp=00
TRACE off_state=00 clamp=00
TRACE injecting unclamp while power_good=0
[40000] %Fatal: example.sv:37: Assertion failed in tb: unclamped while off
%Error: example.sv:37: Verilog $stop
Aborting...

This negative run establishes that the off-state release check was active and observed this injected violation. It does not establish that every assertion has been fault-injected. The save-order, restore-order and early-return-release checks are present and enabled, but this edition does not include separate failing runs for each branch. Treat adding those controlled failures as a distinct extension, with the expected assertion and exit status named before running it.

A regression should distinguish expected failure from unexpected failure. Here the negative case is successful as a test only when it stops on unclamped while off; a compile error or timeout would not satisfy that expectation. Preserve the normal and negative logs separately, together with the exact source and options, so an automation system cannot report a generic nonzero status as proof that the intended property was checked.

For another boundary, begin by changing the contract deliberately rather than changing several controls together. If the clamp should be one, update the output expression and its checker together, then retain a separate mutation that makes them disagree. If no retained state is required, remove the save-and-restore expectation and specify the cold-start value that must be visible before release. A passing result has meaning only relative to those selected requirements.

Also decide how the testbench observes the receiver. This example observes the boundary byte directly. A production test might need to show that the receiving block neither accepted a false transaction during shutdown nor consumed stale state during restart. That requires an additional receiver model or monitor and an explicit acceptance rule. Merely checking that the output eventually equals the saved byte would miss an earlier unintended transaction.

Carry the contract into power-aware verification

Use this model to settle the digital sequence and its observable requirements before comparing it with a richer implementation. Map each abstract control to its real controller signal, and map each retained state item to the intended implementation boundary. Identify which outputs require isolation, where that isolation is powered, and which receivers must remain operational during the transition. Those are project-specific integration facts, not facts established by this example.

Then keep the evidence layers distinct. RTL assertions can demonstrate that a chosen stimulus obeyed or violated the modeled ordering. A power-aware simulation must demonstrate the selected power-intent semantics and their interaction with the design. Structural and implementation checks must address the actual cells, supplies and connectivity. A check at one layer may expose a useful problem without replacing the checks at the others.

For a handoff, preserve the normal sequence, the intentional failure, the clamp interpretation and the reset-versus-retention policy together. State whether a saved value is valid after each reset class and whether restoration is mandatory before release. An engineer integrating the block can then change one assumption deliberately and rerun the relevant checks, rather than treating a single passing power-down sequence as a universal permission to switch the domain off.

Not covered on this page

Sources

  1. IEEE P1801: power-intent scope and IEEE 1801-2024 listing, public project page, active PAR dated 11 December 2024, opened 24 October 2026; full standard not accessed. ↩

  2. Icarus Verilog command-line flags, current documentation opened 24 October 2026; executed version 13.0. ↩

  3. Verilator executable arguments, guide 5.052, opened 24 October 2026. ↩

How we test: methodology. Found a mistake? Corrections.