Skip to content
STIMSMITH

Loop Scenario (Backward Branch)

Concept WIKI v1 · 6/5/2026

A constrained-random verification technique used in processor testbenches to handle backward branches by treating them as bounded loop scenarios. By structuring an ADDI (add immediate) operation immediately before the branch and incrementing a loop-index register inside the loop body, the scenario avoids endless execution while still exercising the branch condition evaluation logic across a controllable range of loop iterations.

Overview

In constrained-random verification (CRV) of microprocessors, automatic instruction generation must deal with the fact that purely random register values almost never satisfy the comparison operands of a backward branch. For example, a BNE R3, R4 instruction with random contents in R3 and R4 will almost never fall through, so program execution becomes stuck in a loop for a very large (effectively unbounded) number of iterations. The Loop Scenario (Backward Branch) technique addresses this by deliberately constructing the instructions around the branch so that the loop terminates after a small, controllable number of iterations.

Problem With Pure Random Backward Branches

A sample program trace with a backward branch (BNE) is shown in Figure 7 of the source article. Because random 32-bit values are extremely unlikely to make R3 equal R4, the test program would be trapped in the loop for a practically infinite number of cycles, preventing the rest of the program from executing and starving the testbench of meaningful coverage of the branch-not-taken path and of the code following the loop.

Loop-Scenario Construction

Backward branches are handled by treating them as loop scenarios. The construction rule, expressed as SystemVerilog constraints on the instruction sequence, has three parts:

  1. Pre-branch ADDI. The operation immediately preceding the backward branch must be an ADDI (add immediate) that uses the same two operand registers as the branch and a small negative immediate value. This sets up the loop-index relationship between the two branch operands.
  2. In-loop increment. Inside the loop body, the loop-index register is incremented by 1 in the instruction just before the branch operation. Each iteration brings the two compared registers one step closer together.
  3. Register isolation. The two registers used as branch operands must not be modified by any other instruction inside the loop. No other destination register in the loop body may alias with either of the branch's source registers. This ensures the comparison converges predictably.

Worked Example (Figure 10)

Consider the example program trace from the source article:

  • If the immediate value at line 7 of the trace is -1, then R3 and R4 are equal at the comparison on line 12, so the BNE falls through and the loop exits immediately.
  • If the immediate value is less than -1 (e.g., -3), the ADDI on line 11 brings the register values closer by 1 on each iteration. The BNE on line 12 is taken for every iteration until R3 equals R4, at which point the branch falls through and the loop exits.

This single construction therefore spans the full range of behaviors the verifier cares about — from a single fall-through (zero iterations of the taken path) to loops with several taken iterations — without producing the absurdly long loops that pure randomness would create.

Operand Variation (Figure 11)

The article also illustrates a variant in which the backward branch uses different operand registers, and the preceding ADDI is structured accordingly. The same constraints (small negative immediate, no aliasing writes inside the loop, in-loop increment of the index) apply.

Boundary-Condition Considerations

The processor testbench must additionally consider boundary cases that could make a backward branch degenerate into a trivial always-taken or never-taken path. For example, a BGT R1, R2, LABEL_X must not be constructed when R2 holds the smallest representable value, because the branch would then be always taken, again turning the loop into an effectively unbounded construct. Similar care is needed for the maximum-value and equal-value edges of every supported branch condition.

Role in the Stimulus-Generation Strategy

Loop scenarios are one of three classes of scenarios typically supported by a microprocessor scenario generator, alongside:

  • Constrained-random scenarios — for example, restricting an instruction stream to purely computational operations (no branches, loads, or stores) to focus on arithmetic coverage.
  • Directed-random scenarios — for example, preloading data memory with walking-1s/0s patterns, near-zero values, and near-minimum/maximum values before an arithmetic stream, to surface rounding and overflow bugs.
  • Directed scenarios — loading a pre-assembled program trace from a file, including leveraged tests from the processor's software team.

A common scenario base class encapsulates shared methods (such as loading a directed scenario from a file) and the constraints that relate instruction objects to one another, including the branch-scenario constraints described above. A scenario generator randomly selects and randomizes scenario objects from this pool until a user-specified stopping condition is reached.

Summary

The Loop Scenario (Backward Branch) pattern converts an otherwise dangerous source of unbounded execution into a well-behaved, controllable coverage point. By constraining the instruction immediately preceding a backward branch to be an ADDI with a small negative immediate on the same operand registers as the branch, incrementing the loop index by 1 per iteration, and prohibiting other writes to the two branch operands inside the loop, the testbench produces loops that range from immediate fall-through to several taken iterations — exercising the branch condition evaluation logic, the taken/not-taken paths, and the code after the loop exit.

CITATIONS

6 sources
6 citations
[1] Purely random 32-bit register values are extremely unlikely to make the operands of a backward BNE branch equal, so program execution would be stuck in a backward-branch loop for a very long time. Applying Constrained-Random Verification to Microprocessors
[2] Backward branches should be handled as loop scenarios, with the operation preceding a backward branch constrained to be an ADDI with the same operands and a small negative immediate value, the operand (loop index) incremented by 1 inside the loop just before the branch, and the two branch-operand registers prohibited from being modified elsewhere inside the loop. Applying Constrained-Random Verification to Microprocessors
[3] In the Figure 10 worked example, an immediate of -1 at line 7 makes R3 equal R4 at the line-12 comparison so the BNE falls through immediately, while an immediate less than -1 causes the line-11 ADDI to bring R3 and R4 closer by 1 per iteration until they are equal and the loop exits. Applying Constrained-Random Verification to Microprocessors
[4] Boundary conditions must be considered, e.g., a BGT R1, R2, LABEL_X is always taken if R2 holds the smallest possible number, which would re-create an unbounded loop. Applying Constrained-Random Verification to Microprocessors
[5] Forward branches are addressed symmetrically by requiring the preceding operation to be an ADDI with the same operands and a small immediate value (positive, negative, or zero), so that the probability of a forward branch being taken becomes reasonable. Applying Constrained-Random Verification to Microprocessors
[6] Three types of scenarios are typically supported — constrained-random, directed-random (e.g., preloading memory with walking patterns and boundary values), and directed (loading a pre-assembled program trace) — and a scenario generator randomly selects and randomizes among them until a user-specified stopping condition. Applying Constrained-Random Verification to Microprocessors