Skip to content

§ 2.1. Arithmetic Components

The arithmetic path of ALU8 provides addition, subtraction, carry-aware arithmetic, increment, decrement, and negation. This page specifies its components in construction order: one-bit adders, the eight-bit carry chain, shared addition/subtraction, and arithmetic-operation selection. Each reference covers IO pins, behavior, circuit structure, and examples. Multiplication and division are not included.

Component Description Structural Behavioral
HalfAdder Adds two bits; produces a sum bit and carry without a carry input Yes No
FullAdder Adds two bits and a carry input using two half adders and an OR gate Yes No
Adder8 Adds two eight-bit operands and a carry input; produces a sum and carry output Yes Yes
AddSub8 Shares an addition/subtraction path with independent carry input, carry/no-borrow output, and signed overflow Yes Yes
ArithmeticUnit8 Selects and prepares operands for addition, subtraction, carry-aware arithmetic, increment, decrement, and negation Yes Yes

These columns indicate registered simulator implementations; the simplified C++ examples below are not counted.

Conventions

  • HalfAdder and FullAdder have one-bit pins. Their carry outputs represent the second bit of the unsigned sum, not signed-overflow flags.
  • Eight-bit pins carry bit patterns from 0x00 through 0xFF; bit 0 is the least significant bit.
  • Arithmetic results wrap modulo 256. Carry and overflow report information lost from the eight-bit result; they do not change or saturate it.
  • Signed interpretation uses two's complement: 0x00–0x7F represent 0–127, and 0x80–0xFF represent −128–−1.
  • A subtraction carry output of 1 means no unsigned borrow. An incoming carry of 0 supplies an incoming borrow for SBC.
  • The components are combinational. They have no clock, reset, stored flags, or completion signal. Results below describe settled outputs, not intermediate propagation states.

The simplified C++ examples under Behavior describe binary values, not the simulator implementation. They omit event scheduling, gate delays, and X/Z handling. The functions can be compiled together in the order shown.

The Behavioral implementation subsections describe the registered compact evaluators and how they compute four-state outputs. These are separate from the simplified C++ examples. The structural implementation remains the default for the embedded simulations and checkpoint tables.

When selected through the component catalog, an ALU8 behavioral part receives an output delay table characterized from its structural counterpart. The table distinguishes input-to-output paths and, for the complete ALU, operation codes. A supported delay twin follows the current input bits through a flat gate list and can publish when that case settles, within the table's bound. The compact part still omits intermediate gate glitches. A directly created part without an attached table retains its constructor delay. These are simulator ticks, not a CPU clock period or calibrated hardware time.

2.1.1. HalfAdder

HalfAdder adds two one-bit operands. It has no carry input, so it cannot by itself be a general cell in a multi-bit carry chain.

IO pins

Pin Direction Width Description
A Input 1 First operand bit
B Input 1 Second operand bit
Sum Output 1 Low bit of the sum
Carry Output 1 Carry into the next bit position

The output is named Carry, not Carry_out or Cout. Pin names are case-sensitive.

Behavior

For binary inputs,

\[ \begin{aligned} \mathrm{Sum} &= A \oplus B, \\ \mathrm{Carry} &= A \land B. \end{aligned} \]

Interpreting the bits as unsigned integers, A + B = Sum + 2*Carry. For example, 1 + 1 gives Sum = 0, Carry = 1. The component has no clock, stored state, or flags beyond this carry.

C++ example

struct HalfAdderResult {
    bool Sum;
    bool Carry;
};

HalfAdderResult half_adder_ref(bool a, bool b) {
    return {a != b, a && b};
}

This binary-only function describes the settled outputs. It is not a registered behavioral implementation.

Unknown inputs and timing

The simulator's XOR produces X if either input is X or Z. The AND produces 0 if either input is definitely 0, 1 only if both are definitely 1, and X otherwise. Neither output drives Z.

A B Settled Sum Settled Carry
0 X or Z X 0
1 X or Z X X

Both gates have a one-tick delay; the settling analysis proves that no output changes later than one tick after its inputs. These are abstract simulator ticks, not calibrated hardware timing. The recorded checkpoint times include the test's stimulus spacing and settling margin, so they are not the gate delay itself.

Implementation

Structural implementation

Both inputs fan out to two gates, which operate in parallel:

Child Type Inputs → output
XOR1 XORGate A, B → Sum
AND1 ANDGate A, B → Carry

There is no internal carry chain or storage. HalfAdder currently has only this structural implementation; the two gates remain visible in the simulator.

Examples

All 16 recorded rows of HalfAdderTest, in checkpoint order. Outputs are test expectations verified against the simulation. Time is the absolute settled checkpoint time in simulator units, not milliseconds or CPU cycles.

Default scenario profile: canonical-default-preview / structural (7fa4118a10b2bf2c). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units AInput · 1 bit BInput · 1 bit CarryExpected output · 1 bit SumExpected output · 1 bit
#0row-01 0 0 0 0
#1row-13 0 1 0 1
#2row-25 0 X 0 X
#3row-37 0 Z 0 X
#4row-49 1 0 0 1
#5row-511 1 1 1 0
#6row-613 1 X X X
#7row-715 1 Z X X
#8row-817 X 0 0 X
#9row-919 X 1 X X
#10row-1021 X X X X
#11row-1123 X Z X X
#12row-1225 Z 0 0 X
#13row-1327 Z 1 X X
#14row-1429 Z X X X
#15row-1531 Z Z X X

Scroll within the table for all rows and pins. Buses use hexadecimal; values containing X/Z use MSB-first binary. Inputs show the declared values in effect at each checkpoint, including retained inputs. Sequential tests also replay preparation changes between checkpoints, such as lowering the clock. Use the matching checkpoint label in the simulation below.

The table contains all 16 combinations of A and B from {0, 1, X, Z}: four binary cases and twelve nonbinary cases. Each row checks both outputs. The checkpoint number and time identify the same settled observation in the simulation below.

Embedded simulation

The half-adder scenario runs HalfAdderTest. Inspect XOR1 and AND1 for the sum and carry paths. Compare (A, B) = (0, X) with (1, X) to see how a known zero suppresses uncertainty in the AND output without making the sum known.

Open HalfAdder in the simulator

Source and tests

Test Coverage
HalfAdderTest / halfAdderRows() All 16 four-state input combinations; verifies settled Sum and Carry
SettlingAnalysisTest Proves a one-tick settling bound
DocumentationExamplesTest Checks the reference pattern, implementation availability, and complete example-table export; compiles the actual C++ example and checks all four binary cases

The binary C++ check does not verify X/Z behavior. Those cases are verified by the structural scenario and retained in its table and simulation.

2.1.2. FullAdder

FullAdder adds two one-bit operands and a carry input. Its carry output can feed the next bit position; eight such cells form Adder8.

IO pins

Pin Direction Width Description
A Input 1 First operand bit
B Input 1 Second operand bit
Carry_in Input 1 Carry from the previous bit position
Sum Output 1 Low bit of the three-input sum
Carry_out Output 1 Carry into the next bit position

Carry_in and Carry_out are the actual pins; they are not named Cin and Cout as on Adder8.

Behavior

Define the first half adder's sum as P = A XOR B. For binary inputs,

\[ \begin{aligned} \mathrm{Sum} &= P \oplus \mathrm{Carry\_in}, \\ \mathrm{Carry\_out} &= (A \land B) \\ &\quad \lor (P \land \mathrm{Carry\_in}). \end{aligned} \]

Thus A + B + Carry_in = Sum + 2*Carry_out. With three input ones, the result is Sum = 1, Carry_out = 1; with exactly two ones, it is Sum = 0, Carry_out = 1. The carry is not a signed-overflow indication.

C++ example

struct FullAdderResult {
    bool Sum;
    bool Carry_out;
};

FullAdderResult full_adder_ref(bool a, bool b, bool carry_in) {
    const auto first = half_adder_ref(a, b);
    const auto second = half_adder_ref(first.Sum, carry_in);
    return {second.Sum, first.Carry || second.Carry};
}

This example reuses half_adder_ref above and follows the actual two-half-adder composition. It describes binary settled values, not simulator events or a selectable behavioral implementation.

Unknown inputs and timing

X/Z inputs propagate through the two half adders and final OR gate. Use the stated XOR/AND/OR network, not an arbitrary binary-equivalent rewrite, to determine four-state results. For example:

A B Carry_in Settled Sum Settled Carry_out
0 0 Z X 0
1 1 X X 1
1 X 1 X X

In the last row, both the first sum and both intermediate carries are unknown. The binary-equivalent majority expression (A AND B) OR (A AND Carry_in) OR (B AND Carry_in) would instead yield carry 1 because A AND Carry_in = 1. That is not this structural circuit's four-state contract. Neither output drives Z.

The longest structural path is HA1.XOR1 → HA2.AND1 → OR1, totaling three abstract delay ticks. The sum path through two XOR gates is shorter, so sum and carry need not settle simultaneously. The settling analysis proves the three-tick bound; the example table records later settled checkpoints, not each internal transition.

Implementation

Structural implementation

Child Type Inputs → output
HA1 HalfAdder External A, B → intermediate sum and first carry
HA2 HalfAdder HA1.Sum, external Carry_in → external Sum and second carry
OR1 ORGate HA1.Carry, HA2.Carry → external Carry_out

Expanding the two half adders reveals five primitive gates in total: two XORs, two ANDs, and one OR. There is no stored carry or clock. FullAdder currently has only this structural implementation.

In the eight-bit chain, Adder8.Cin drives FA0.Carry_in, each cell's Carry_out drives the next cell's Carry_in, and FA7.Carry_out drives Adder8.Cout. This is a ripple-carry connection, not eight independent additions.

Examples

All 64 recorded rows of FullAdderTest, in checkpoint order. Outputs are test expectations verified against the simulation. Time is the absolute settled checkpoint time in simulator units, not milliseconds or CPU cycles.

Default scenario profile: canonical-default-preview / structural (25ccf9ef8ae550ea). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units AInput · 1 bit BInput · 1 bit Carry_inInput · 1 bit Carry_outExpected output · 1 bit SumExpected output · 1 bit
#0row-02 0 0 0 0 0
#1row-14 0 0 1 0 1
#2row-26 0 0 X 0 X
#3row-38 0 0 Z 0 X
#4row-411 0 1 0 0 1
#5row-514 0 1 1 1 0
#6row-617 0 1 X X X
#7row-719 0 1 Z X X
#8row-822 0 X 0 0 X
#9row-925 0 X 1 X X
#10row-1027 0 X X X X
#11row-1129 0 X Z X X
#12row-1232 0 Z 0 0 X
#13row-1335 0 Z 1 X X
#14row-1437 0 Z X X X
#15row-1539 0 Z Z X X
#16row-1642 1 0 0 0 1
#17row-1745 1 0 1 1 0
#18row-1848 1 0 X X X
#19row-1950 1 0 Z X X
#20row-2053 1 1 0 1 0
#21row-2155 1 1 1 1 1
#22row-2257 1 1 X 1 X
#23row-2359 1 1 Z 1 X
#24row-2462 1 X 0 X X
#25row-2565 1 X 1 X X
#26row-2667 1 X X X X
#27row-2769 1 X Z X X
#28row-2872 1 Z 0 X X
#29row-2975 1 Z 1 X X
#30row-3077 1 Z X X X
#31row-3179 1 Z Z X X
#32row-3282 X 0 0 0 X
#33row-3385 X 0 1 X X
#34row-3487 X 0 X X X
#35row-3589 X 0 Z X X
#36row-3692 X 1 0 X X
#37row-3795 X 1 1 X X
#38row-3897 X 1 X X X
#39row-3999 X 1 Z X X
#40row-40102 X X 0 X X
#41row-41105 X X 1 X X
#42row-42107 X X X X X
#43row-43109 X X Z X X
#44row-44112 X Z 0 X X
#45row-45115 X Z 1 X X
#46row-46117 X Z X X X
#47row-47119 X Z Z X X
#48row-48122 Z 0 0 0 X
#49row-49125 Z 0 1 X X
#50row-50127 Z 0 X X X
#51row-51129 Z 0 Z X X
#52row-52132 Z 1 0 X X
#53row-53135 Z 1 1 X X
#54row-54137 Z 1 X X X
#55row-55139 Z 1 Z X X
#56row-56142 Z X 0 X X
#57row-57145 Z X 1 X X
#58row-58147 Z X X X X
#59row-59149 Z X Z X X
#60row-60152 Z Z 0 X X
#61row-61155 Z Z 1 X X
#62row-62157 Z Z X X X
#63row-63159 Z Z Z X X

Scroll within the table for all rows and pins. Buses use hexadecimal; values containing X/Z use MSB-first binary. Inputs show the declared values in effect at each checkpoint, including retained inputs. Sequential tests also replay preparation changes between checkpoints, such as lowering the clock. Use the matching checkpoint label in the simulation below.

The table contains all 64 combinations of A, B, and Carry_in from {0, 1, X, Z}: eight binary cases and 56 nonbinary cases. Both outputs are checked at every checkpoint. The cases are exhaustive for settled four-state input combinations, not for every possible sequence of propagation glitches.

Embedded simulation

The full-adder scenario runs FullAdderTest. Expand HA1 and HA2 to inspect their gates, or keep them collapsed to trace the two intermediate carries into OR1. Compare (1, 1, X) with (1, X, 1) to inspect the four-state distinction described above.

Open FullAdder in the simulator

Source and tests

Test Coverage
FullAdderTest / fullAdderRows() All 64 four-state input combinations; verifies settled Sum and Carry_out against the two-half-adder network
SettlingAnalysisTest Proves the three-tick settling bound and reports its path
DocumentationExamplesTest Checks the reference pattern, implementation availability, and complete example-table export; compiles this C++ example with half_adder_ref and checks all eight binary cases

The embedded simulation and table use the structural test's complete sequence. The C++ function checks only binary values; it does not replace the four-state structural test.

2.1.3. Adder8

Adder8 adds two eight-bit operands and a one-bit carry input. It produces the low eight bits of the sum and a separate carry output.

IO pins

Pin Direction Width Description
A Input 8 First operand
B Input 8 Second operand
Cin Input 1 Carry added to the operands
Sum Output 8 Low eight bits of the sum
Cout Output 1 Carry beyond bit 7

Pin names are case-sensitive. Cin, Sum, and Cout are the existing component's names; they are not aliases for the uppercase pins on the new components.

Behavior

Treating A and B as unsigned values, let

\[ T = A + B + \mathrm{Cin}. \]

Then

\[ \mathrm{Sum} = T \bmod 256, \qquad \mathrm{Cout} = [T \geq 256]. \]

Here, brackets denote a Boolean condition: 1 when true, otherwise 0. Adder8 does not expose signed overflow, zero, or negative flags.

C++ example

#include <cstdint>

struct Adder8Result {
    std::uint8_t Sum;
    bool Cout;
};

Adder8Result adder8_ref(std::uint8_t a, std::uint8_t b, bool cin) {
    const unsigned total = unsigned(a) + unsigned(b) + unsigned(cin);
    return {
        static_cast<std::uint8_t>(total & 0xFFu),
        total >= 0x100u,
    };
}

The intermediate value must be wider than eight bits so that computing Cout does not lose the carry.

Unknown inputs and timing

The structural implementation preserves gate-level four-state behavior. An unknown bit need not make every output unknown. For example, with bits written from bit 7 to bit 0:

A B Cin Settled Sum Settled Cout
0000000X 00000000 0 0000000X 0
0000000Z 00000000 0 0000000X 0

In this table, Z denotes high impedance, not the ALU's zero flag. The examples are covered by the current Adder8Test rows. The C++ example cannot represent these inputs.

Output changes pass through the splitter, full-adder gates, and joiner. There is no single declared structural Adder8 latency; consumers must allow the structural circuit to settle before interpreting its outputs.

Implementation

Structural implementation

The current component contains:

Internal component Count Connection
BitSplitter<8> 2 Splits A and B into individual bits
FullAdder 8 Adds corresponding operand bits and the incoming carry
BitJoiner<8> 1 Assembles the eight sum bits into Sum

Cin connects to FA0.Carry_in. Each FAi.Carry_out connects to the next cell's Carry_in; FA7.Carry_out drives Cout. Each full adder consists of two half adders and an ORGate.

The implementation currently instantiates eight full adders directly. It does not contain a separate four-bit slice component.

Behavioral implementation

Behavioral8<Adder8> calls the four-state add function. It computes each sum bit and the carry chain using the same XOR/AND/OR expressions as the full-adder cells, without constructing eight FullAdder objects.

The evaluator is defined in ALU8Behavioral.cpp; its value functions are in ALU8ValueModel.hpp. It retains the public pins without functional circuit children. Catalog construction supplies a delay table characterized from the gates; where a delay twin is available and agrees with the computed value, publication can follow the current inputs’ gate-level settling time within that bound, subject to older pending results. The compact evaluator does not reproduce transient gate glitches.

The recorded scenario runs against both implementations in CTest. ALU8FidelityContractTest checks binary/X/Z settled-output equivalence against the structural circuit. The example table uses the default structural run; its checkpoint times need not match the behavioral run.

Examples

All 14 recorded rows of Adder8Test, in checkpoint order. Outputs are test expectations verified against the simulation. Time is the absolute settled checkpoint time in simulator units, not milliseconds or CPU cycles.

Default scenario profile: canonical-default-preview / structural (7f3b5bb631aed64b). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units AInput · 8 bit BInput · 8 bit CinInput · 1 bit CoutExpected output · 1 bit SumExpected output · 8 bit
#0row-05 0x00 0x00 0 0 0x00
#1row-19 0x01 0x02 0 0 0x03
#2row-220 0x0F 0x01 0 0 0x10
#3row-331 0xFF 0x01 0 1 0x00
#4row-450 0xFF 0x00 1 1 0x00
#5row-555 0x7F 0x00 1 0 0x80
#6row-661 0x80 0x80 0 1 0x00
#7row-780 0xAA 0x55 1 1 0x00
#8row-886 0x00 0x00 1 0 0x01
#9row-991 0xFF 0xFF 0 1 0xFE
#10row-10110 0x80 0x7F 1 1 0x00
#11row-11127 0x55 0xAA 0 0 0xFF
#12row-12131 0b0000000X 0x00 0 0 0b0000000X
#13row-13134 0b0000000Z 0x00 0 0 0b0000000X

Scroll within the table for all rows and pins. Buses use hexadecimal; values containing X/Z use MSB-first binary. Inputs show the declared values in effect at each checkpoint, including retained inputs. Sequential tests also replay preparation changes between checkpoints, such as lowering the clock. Use the matching checkpoint label in the simulation below.

For wider addition, connect the lower byte's Cout to the upper byte's Cin. For example, 0x00FF + 0x0001 produces a low-byte result of 0x00 with carry 1, followed by an upper-byte result of 0x01.

Embedded simulation

The existing adder8 scenario exposes Adder8Test. Its 14 recorded cases include ordinary sums, carry propagation, and two unknown-value cases; it is not an exhaustive input sweep. The table above contains the complete recorded sequence. Match its checkpoint labels and times with the timeline to inspect settled outputs.

Open Adder8 in the simulator

Source and tests

Test Coverage
Adder8Test / adder8Rows() 14 recorded cases: 12 binary cases and two cases with bit 0 of A driven to X or Z. Checks Sum and Cout; not an exhaustive sweep.

The embedded simulation runs this same recorded scenario. The C++ example is binary-only and does not verify four-state propagation or settling time.

2.1.4. AddSub8

AddSub8 extends an eight-bit addition path with conditional inversion of B and signed-overflow detection. SUB controls inversion; C_IN independently controls the initial carry.

IO pins

Pin Direction Width Description
A Input 8 First operand
B Input 8 Second operand before conditional inversion
SUB Input 1 0: use B; 1: use the eight-bit complement of B
C_IN Input 1 Initial carry into the addition path
OUT Output 8 Low eight bits of the result
CARRY_OUT Output 1 Addition carry-out; no-borrow indicator for subtraction
OVERFLOW Output 1 Signed result lies outside −128–127

Unlike AddSub32, whose initial carry is tied to SUB, AddSub8 exposes the carry independently.

Behavior

Define the effective second operand as

\[ B' = \begin{cases} B, & \mathrm{SUB}=0, \\ 255-B, & \mathrm{SUB}=1. \end{cases} \]

The unsigned addition path computes

\[ T = A + B' + \mathrm{C\_IN}, \qquad \mathrm{OUT} = T \bmod 256, \qquad \mathrm{CARRY\_OUT} = [T \geq 256]. \]
SUB C_IN Arithmetic operation
0 0 A + B
0 1 A + B + 1
1 1 A - B
1 0 A - B - 1

Subtraction does not automatically supply the carry

Setting SUB = 1 only complements B. Ordinary subtraction also requires C_IN = 1. SUB = 1, C_IN = 0 includes an incoming borrow.

For subtraction, the carry condition can also be written as

\[ \mathrm{CARRY\_OUT} = [A \geq B + (1-\mathrm{C\_IN})], \]

where the comparison is evaluated without eight-bit truncation. In particular, B = 0xFF, C_IN = 0 gives a threshold of 256, not zero.

Let \(s(x)\) interpret an eight-bit pattern as a signed integer:

\[ s(x)=\begin{cases}x,&x<128,\\x-256,&x\geq128.\end{cases} \]

OVERFLOW is 1 when the corresponding signed mathematical result is outside −128–127:

\[ R_s = \begin{cases} s(A)+s(B)+\mathrm{C\_IN}, & \mathrm{SUB}=0, \\ s(A)-s(B)-(1-\mathrm{C\_IN}), & \mathrm{SUB}=1. \end{cases} \]

Carry and overflow are independent outputs. There is no signed/unsigned mode pin.

C++ example

struct AddSub8Result {
    std::uint8_t OUT;
    bool CARRY_OUT;
    bool OVERFLOW;
};

AddSub8Result addsub8_ref(
    std::uint8_t a, std::uint8_t b, bool sub, bool c_in) {
    const auto effective_b = static_cast<std::uint8_t>(
        b ^ (sub ? 0xFFu : 0x00u));
    const auto sum = adder8_ref(a, effective_b, c_in);
    const bool same_sign = ((a ^ effective_b) & 0x80u) == 0;
    const bool result_changed_sign = ((a ^ sum.Sum) & 0x80u) != 0;
    return {sum.Sum, sum.Cout, same_sign && result_changed_sign};
}

Unknown inputs and timing

The structural simulator variant evaluates the actual XOR gates, adder, and sign-bit logic, rather than the C++ example. It supports four signal states: 0, 1, X (unknown), and Z (high impedance). Gates interpret X and Z as unknown logic, not as an assumed 0 or 1; some outputs can still be determined from the other inputs.

After an input changes, values propagate through the internal gates. OUT, CARRY_OUT, and OVERFLOW can change at different times, so intermediate timeline positions can show temporary values or unknown bits. The operation tables describe settled outputs. Recorded test checkpoints occur after the circuit has settled; they are the appropriate points for checking those tables.

Implementation

Structural implementation

Internal component Function
Eight XORGate instances Compute B'[i] = B[i] XOR SUB
One Adder8 Compute A + B' + C_IN
Splitters and joiners Expose operand/result bits and assemble B'
Sign-bit logic Compute signed overflow from A[7], B'[7], and OUT[7]

The output wiring is Adder8.Sum → OUT and Adder8.Cout → CARRY_OUT. Overflow follows

\[ \mathrm{OVERFLOW} = \neg(A_7 \oplus B'_7) \land (A_7 \oplus \mathrm{OUT}_7). \]

This arrangement reuses Adder8 without requiring access to its internal carry wires. No separate subtractor or stored state is needed.

Behavioral implementation

Behavioral8<AddSub8> calls addsub: it XORs each bit of B with SUB, evaluates the shared addition function with C_IN, and derives signed overflow from the operand and result sign bits. These operations use four-state logic rather than integer subtraction.

The evaluator is defined in ALU8Behavioral.cpp; its value functions are in ALU8ValueModel.hpp. It retains the public pins without functional circuit children. Catalog construction supplies a delay table characterized from the gates; where a delay twin is available and agrees with the computed value, publication can follow the current inputs’ gate-level settling time within that bound, subject to older pending results. The compact evaluator does not reproduce transient gate glitches.

The recorded scenario runs against both implementations in CTest. ALU8FidelityContractTest checks binary/X/Z settled-output equivalence against the structural circuit. The example table uses the default structural run; its checkpoint times need not match the behavioral run.

Examples

All 80 recorded rows of AddSub8Test, in checkpoint order. Outputs are test expectations verified against the simulation. Time is the absolute settled checkpoint time in simulator units, not milliseconds or CPU cycles.

Default scenario profile: canonical-default-preview / structural (d2f4800e277ccc71). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units AInput · 8 bit BInput · 8 bit C_INInput · 1 bit SUBInput · 1 bit CARRY_OUTExpected output · 1 bit OUTExpected output · 8 bit OVERFLOWExpected output · 1 bit
#0row-010 0x00 0x00 0 0 0 0x00 0
#1row-113 0x00 0x00 1 0 0 0x01 0
#2row-221 0x00 0x00 0 1 0 0xFF 0
#3row-340 0x00 0x00 1 1 1 0x00 0
#4row-450 0x00 0x01 0 0 0 0x01 0
#5row-555 0x00 0x01 1 0 0 0x02 0
#6row-663 0x00 0x01 0 1 0 0xFE 0
#7row-766 0x00 0x01 1 1 0 0xFF 0
#8row-875 0x00 0x80 0 0 0 0x80 0
#9row-978 0x00 0x80 1 0 0 0x81 0
#10row-1086 0x00 0x80 0 1 0 0x7F 0
#11row-11105 0x00 0x80 1 1 0 0x80 1
#12row-12123 0x00 0xFF 0 0 0 0xFF 0
#13row-13142 0x00 0xFF 1 0 1 0x00 0
#14row-14152 0x00 0xFF 0 1 0 0x00 0
#15row-15155 0x00 0xFF 1 1 0 0x01 0
#16row-16164 0x01 0x00 0 0 0 0x01 0
#17row-17169 0x01 0x00 1 0 0 0x02 0
#18row-18190 0x01 0x00 0 1 1 0x00 0
#19row-19193 0x01 0x00 1 1 1 0x01 0
#20row-20203 0x01 0x01 0 0 0 0x02 0
#21row-21206 0x01 0x01 1 0 0 0x03 0
#22row-22227 0x01 0x01 0 1 0 0xFF 0
#23row-23246 0x01 0x01 1 1 1 0x00 0
#24row-24256 0x01 0x80 0 0 0 0x81 0
#25row-25261 0x01 0x80 1 0 0 0x82 0
#26row-26282 0x01 0x80 0 1 0 0x80 1
#27row-27285 0x01 0x80 1 1 0 0x81 1
#28row-28307 0x01 0xFF 0 0 1 0x00 0
#29row-29310 0x01 0xFF 1 0 1 0x01 0
#30row-30320 0x01 0xFF 0 1 0 0x01 0
#31row-31325 0x01 0xFF 1 1 0 0x02 0
#32row-32347 0x7F 0x00 0 0 0 0x7F 0
#33row-33366 0x7F 0x00 1 0 0 0x80 1
#34row-34374 0x7F 0x00 0 1 1 0x7E 0
#35row-35377 0x7F 0x00 1 1 1 0x7F 0
#36row-36399 0x7F 0x01 0 0 0 0x80 1
#37row-37402 0x7F 0x01 1 0 0 0x81 1
#38row-38410 0x7F 0x01 0 1 1 0x7D 0
#39row-39415 0x7F 0x01 1 1 1 0x7E 0
#40row-40437 0x7F 0x80 0 0 0 0xFF 0
#41row-41456 0x7F 0x80 1 0 1 0x00 0
#42row-42464 0x7F 0x80 0 1 0 0xFE 1
#43row-43467 0x7F 0x80 1 1 0 0xFF 1
#44row-44477 0x7F 0xFF 0 0 1 0x7E 0
#45row-45480 0x7F 0xFF 1 0 1 0x7F 0
#46row-46501 0x7F 0xFF 0 1 0 0x7F 0
#47row-47520 0x7F 0xFF 1 1 0 0x80 1
#48row-48531 0x80 0x00 0 0 0 0x80 0
#49row-49534 0x80 0x00 1 0 0 0x81 0
#50row-50542 0x80 0x00 0 1 1 0x7F 1
#51row-51561 0x80 0x00 1 1 1 0x80 0
#52row-52571 0x80 0x01 0 0 0 0x81 0
#53row-53576 0x80 0x01 1 0 0 0x82 0
#54row-54584 0x80 0x01 0 1 1 0x7E 1
#55row-55587 0x80 0x01 1 1 1 0x7F 1
#56row-56596 0x80 0x80 0 0 1 0x00 1
#57row-57599 0x80 0x80 1 0 1 0x01 1
#58row-58607 0x80 0x80 0 1 0 0xFF 0
#59row-59626 0x80 0x80 1 1 1 0x00 0
#60row-60644 0x80 0xFF 0 0 1 0x7F 1
#61row-61663 0x80 0xFF 1 0 1 0x80 0
#62row-62673 0x80 0xFF 0 1 0 0x80 0
#63row-63676 0x80 0xFF 1 1 0 0x81 0
#64row-64698 0xFF 0x00 0 0 0 0xFF 0
#65row-65717 0xFF 0x00 1 0 1 0x00 0
#66row-66725 0xFF 0x00 0 1 1 0xFE 0
#67row-67728 0xFF 0x00 1 1 1 0xFF 0
#68row-68750 0xFF 0x01 0 0 1 0x00 0
#69row-69753 0xFF 0x01 1 0 1 0x01 0
#70row-70761 0xFF 0x01 0 1 1 0xFD 0
#71row-71766 0xFF 0x01 1 1 1 0xFE 0
#72row-72788 0xFF 0x80 0 0 1 0x7F 1
#73row-73807 0xFF 0x80 1 0 1 0x80 0
#74row-74815 0xFF 0x80 0 1 1 0x7E 0
#75row-75818 0xFF 0x80 1 1 1 0x7F 0
#76row-76828 0xFF 0xFF 0 0 1 0xFE 0
#77row-77831 0xFF 0xFF 1 0 1 0xFF 0
#78row-78852 0xFF 0xFF 0 1 0 0xFF 0
#79row-79871 0xFF 0xFF 1 1 1 0x00 0

Scroll within the table for all rows and pins. Buses use hexadecimal; values containing X/Z use MSB-first binary. Inputs show the declared values in effect at each checkpoint, including retained inputs. Sequential tests also replay preparation changes between checkpoints, such as lowering the clock. Use the matching checkpoint label in the simulation below.

For A = B = 0xFF, SUB = 1, and C_IN = 0, the calculation is 255 - 255 - 1 = -1 under unsigned arithmetic, so it requires a borrow. Under signed interpretation it is -1 - (-1) - 1 = -1, which does not overflow.

For wider subtraction, initialize the lowest byte with SUB = 1, C_IN = 1, then connect each byte's CARRY_OUT to the next byte's C_IN. For 0x0100 - 0x0001, the low byte produces 0xFF with carry 0; the upper byte consumes that borrow and produces 0x00 with carry 1.

Embedded simulation

The addsub8 scenario exposes AddSub8Test, with 80 recorded cases covering both SUB values and both carry inputs. Use the timeline checkpoints to inspect settled results. In particular, SUB = 1, C_IN = 0 computes A - B - 1; ordinary A - B requires C_IN = 1.

Open AddSub8 in the simulator

Source and tests

Test Coverage
AddSub8Test 80 recorded binary cases: five A values × four B values × both SUB values × both C_IN values. Checks result, carry, and overflow.
ALU8ContractTest: standalone AddSub8 sweep All 262,144 binary input combinations: 256 × 256 × 2 × 2. Checks OUT, CARRY_OUT, and OVERFLOW on both structural and behavioral AddSub8 implementations.

The embedded simulation contains the 80 AddSub8Test cases, not the 262,144-case sweep. The sweep disables history recording and drains the event queue between cases; it is exhaustive for binary inputs, not for all four-state input patterns.

2.1.5. ArithmeticUnit8

ArithmeticUnit8 selects operands and controls for one AddSub8. It implements all seven arithmetic operations of ALU8 without instantiating a separate datapath for every opcode.

IO pins

Pin Direction Width Description
A Input 8 Primary operand
B Input 8 Secondary operand; ignored by unary operations
OP Input 3 Local arithmetic operation selector
C_IN Input 1 Carry input; used only by ADC and SBC
OUT Output 8 Selected arithmetic result
CARRY Output 1 Carry-out or no-borrow indication
OVERFLOW Output 1 Signed overflow for the selected operation
INVALID_OP Output 1 Local opcode 0b111 is selected

ZERO and NEGATIVE are not outputs of this subcomponent. The complete ALU8 computes them from its final selected OUT, which may come from an arithmetic, logic, shift, or comparison component.

Behavior

OP selects one of seven arithmetic operations. The result wraps modulo 256; CARRY and OVERFLOW report the unsigned carry/no-borrow condition and signed overflow for that operation.

Local OP Operation Mathematical result before wrapping Uses B? Uses C_IN?
0b000 ADD A + B Yes No
0b001 ADC A + B + C_IN Yes Yes
0b010 SUB A - B Yes No
0b011 SBC A - B - (1 - C_IN) Yes Yes
0b100 INC A + 1 No No
0b101 DEC A - 1 No No
0b110 NEG 0 - A No No
0b111 Reserved OUT = 0, CARRY = 0, OVERFLOW = 0, INVALID_OP = 1 No No

For supported, fully known opcodes, INVALID_OP = 0, independently of the operand values. It reports an invalid encoding, not unknown data or an unsettled circuit.

The complete ALU8 has a five-bit OP. Its arithmetic encodings 0x00–0x06 match this local selector, but the top-level decoder must also check the operation group. A logic opcode must not become an arithmetic operation merely because its low three bits match one of these values.

Arithmetic flags

All comparisons in the carry column use unsigned operand values. As in AddSub8, \(s(x)\) denotes the signed interpretation of an eight-bit pattern.

Operation CARRY = 1 when OVERFLOW = 1 when
ADD A + B >= 256 s(A) + s(B) is outside −128–127
ADC A + B + C_IN >= 256 s(A) + s(B) + C_IN is outside −128–127
SUB A >= B s(A) - s(B) is outside −128–127
SBC A >= B + (1 - C_IN) s(A) - s(B) - (1 - C_IN) is outside −128–127
INC A == 0xFF A == 0x7F
DEC A != 0x00 A == 0x80
NEG A == 0x00 A == 0x80

NEG follows the same no-borrow convention as SUB: subtracting zero from zero requires no borrow, whereas subtracting a nonzero unsigned operand from zero does.

C++ example

struct Arithmetic8Result {
    std::uint8_t OUT;
    bool CARRY;
    bool OVERFLOW;
    bool INVALID_OP;
};

Arithmetic8Result arithmetic8_ref(
    std::uint8_t a, std::uint8_t b, std::uint8_t op, bool c_in) {
    std::uint8_t x = a;
    std::uint8_t y = b;
    bool sub = false;
    bool carry_in = false;

    switch (op) {
    case 0x00: break;                                      // ADD
    case 0x01: carry_in = c_in; break;                     // ADC
    case 0x02: sub = true; carry_in = true; break;          // SUB
    case 0x03: sub = true; carry_in = c_in; break;          // SBC
    case 0x04: y = 0; carry_in = true; break;               // INC
    case 0x05: y = 1; sub = true; carry_in = true; break;   // DEC
    case 0x06: x = 0; y = a; sub = true;
               carry_in = true; break;                    // NEG
    default: return {0, false, false, true};
    }

    const auto result = addsub8_ref(x, y, sub, carry_in);
    return {result.OUT, result.CARRY_OUT, result.OVERFLOW, false};
}

The function's op argument represents the three-bit local selector. Its default branch also rejects out-of-range integer arguments, although those cannot occur on a three-bit binary pin.

Unknown inputs and timing

The selected operands pass through multiplexers before reaching AddSub8, so unknown values follow the underlying gates' four-state behavior. Unused inputs are isolated by the selection logic: for example, during NEG, unknown bits on B or C_IN do not affect the settled result or flags. With the known reserved opcode 0b111, output masking produces zero result, carry, and overflow, and INVALID_OP = 1, even when data inputs are unknown.

Changing OP can change the internal operands and carry controls before the final outputs settle. There is no single declared structural ArithmeticUnit8 latency; the result and flags can change at different times. Use the recorded checkpoints to inspect settled behavior. The C++ example does not model these propagation states.

Implementation

Structural implementation

The component consists of opcode-controlled operand/control multiplexers, constants, one AddSub8, and output masking for the reserved opcode. On a valid opcode, AddSub8.OUT, CARRY_OUT, and OVERFLOW drive the corresponding arithmetic outputs.

The following table defines the signals supplied to the shared AddSub8 instance. X and Y are local eight-bit operand wires, not additional external pins.

Operation AddSub8.A (X) AddSub8.B (Y) AddSub8.SUB AddSub8.C_IN
ADD A B 0 0
ADC A B 0 C_IN
SUB A B 1 1
SBC A B 1 C_IN
INC A 0x00 0 1
DEC A 0x01 1 1
NEG 0x00 A 1 1

Behavioral implementation

Behavioral8<ArithmeticUnit8> calls arithmetic. Four-state multiplexer functions select the operands, subtraction control, and initial carry for one addsub evaluation. The reserved-opcode result masks OUT, CARRY, and OVERFLOW to zero and drives INVALID_OP.

The evaluator is defined in ALU8Behavioral.cpp; its value functions are in ALU8ValueModel.hpp. It retains the public pins without functional circuit children. Catalog construction supplies a delay table characterized from the gates; where a delay twin is available and agrees with the computed value, publication can follow the current inputs’ gate-level settling time within that bound, subject to older pending results. The compact evaluator does not reproduce transient gate glitches.

The recorded scenario runs against both implementations in CTest. ALU8FidelityContractTest checks binary/X/Z settled-output equivalence against the structural circuit. The example table uses the default structural run; its checkpoint times need not match the behavioral run.

Examples

All 80 recorded rows of ArithmeticUnit8Test, in checkpoint order. Outputs are test expectations verified against the simulation. Time is the absolute settled checkpoint time in simulator units, not milliseconds or CPU cycles.

Default scenario profile: canonical-default-preview / structural (a46e324fa954d9a1). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units AInput · 8 bit BInput · 8 bit C_INInput · 1 bit OPInput · 3 bit CARRYExpected output · 1 bit INVALID_OPExpected output · 1 bit OUTExpected output · 8 bit OVERFLOWExpected output · 1 bit
#0row-018 0x00 0x01 0 0x0 0 0 0x01 0
#1row-120 0x00 0x01 1 0x0 0 0 0x01 0
#2row-234 0x01 0x01 0 0x0 0 0 0x02 0
#3row-336 0x01 0x01 1 0x0 0 0 0x02 0
#4row-463 0x7F 0x01 0 0x0 0 0 0x80 1
#5row-565 0x7F 0x01 1 0x0 0 0 0x80 1
#6row-681 0x80 0x01 0 0x0 0 0 0x81 0
#7row-783 0x80 0x01 1 0x0 0 0 0x81 0
#8row-8110 0xFF 0x01 0 0x0 1 0 0x00 0
#9row-9112 0xFF 0x01 1 0x0 1 0 0x00 0
#10row-10129 0x00 0x01 0 0x1 0 0 0x01 0
#11row-11141 0x00 0x01 1 0x1 0 0 0x02 0
#12row-12155 0x01 0x01 0 0x1 0 0 0x02 0
#13row-13165 0x01 0x01 1 0x1 0 0 0x03 0
#14row-14192 0x7F 0x01 0 0x1 0 0 0x80 1
#15row-15202 0x7F 0x01 1 0x1 0 0 0x81 1
#16row-16218 0x80 0x01 0 0x1 0 0 0x81 0
#17row-17230 0x80 0x01 1 0x1 0 0 0x82 0
#18row-18257 0xFF 0x01 0 0x1 1 0 0x00 0
#19row-19267 0xFF 0x01 1 0x1 1 0 0x01 0
#20row-20298 0x00 0x01 0 0x2 0 0 0xFF 0
#21row-21300 0x00 0x01 1 0x2 0 0 0xFF 0
#22row-22328 0x01 0x01 0 0x2 1 0 0x00 0
#23row-23330 0x01 0x01 1 0x2 1 0 0x00 0
#24row-24343 0x7F 0x01 0 0x2 1 0 0x7E 0
#25row-25345 0x7F 0x01 1 0x2 1 0 0x7E 0
#26row-26374 0x80 0x01 0 0x2 1 0 0x7F 1
#27row-27376 0x80 0x01 1 0x2 1 0 0x7F 1
#28row-28392 0xFF 0x01 0 0x2 1 0 0xFE 0
#29row-29394 0xFF 0x01 1 0x2 1 0 0xFE 0
#30row-30421 0x00 0x01 0 0x3 0 0 0xFE 0
#31row-31431 0x00 0x01 1 0x3 0 0 0xFF 0
#32row-32444 0x01 0x01 0 0x3 0 0 0xFF 0
#33row-33469 0x01 0x01 1 0x3 1 0 0x00 0
#34row-34482 0x7F 0x01 0 0x3 1 0 0x7D 0
#35row-35494 0x7F 0x01 1 0x3 1 0 0x7E 0
#36row-36520 0x80 0x01 0 0x3 1 0 0x7E 1
#37row-37530 0x80 0x01 1 0x3 1 0 0x7F 1
#38row-38546 0xFF 0x01 0 0x3 1 0 0xFD 0
#39row-39558 0xFF 0x01 1 0x3 1 0 0xFE 0
#40row-40575 0x00 0x01 0 0x4 0 0 0x01 0
#41row-41577 0x00 0x01 1 0x4 0 0 0x01 0
#42row-42592 0x01 0x01 0 0x4 0 0 0x02 0
#43row-43594 0x01 0x01 1 0x4 0 0 0x02 0
#44row-44621 0x7F 0x01 0 0x4 0 0 0x80 1
#45row-45623 0x7F 0x01 1 0x4 0 0 0x80 1
#46row-46639 0x80 0x01 0 0x4 0 0 0x81 0
#47row-47641 0x80 0x01 1 0x4 0 0 0x81 0
#48row-48669 0xFF 0x01 0 0x4 1 0 0x00 0
#49row-49671 0xFF 0x01 1 0x4 1 0 0x00 0
#50row-50701 0x00 0x01 0 0x5 0 0 0xFF 0
#51row-51707 0x00 0x01 1 0x5 0 0 0xFF 0
#52row-52735 0x01 0x01 0 0x5 1 0 0x00 0
#53row-53741 0x01 0x01 1 0x5 1 0 0x00 0
#54row-54754 0x7F 0x01 0 0x5 1 0 0x7E 0
#55row-55760 0x7F 0x01 1 0x5 1 0 0x7E 0
#56row-56789 0x80 0x01 0 0x5 1 0 0x7F 1
#57row-57795 0x80 0x01 1 0x5 1 0 0x7F 1
#58row-58811 0xFF 0x01 0 0x5 1 0 0xFE 0
#59row-59817 0xFF 0x01 1 0x5 1 0 0xFE 0
#60row-60847 0x00 0x01 0 0x6 1 0 0x00 0
#61row-61849 0x00 0x01 1 0x6 1 0 0x00 0
#62row-62879 0x01 0x01 0 0x6 0 0 0xFF 0
#63row-63881 0x01 0x01 1 0x6 0 0 0xFF 0
#64row-64896 0x7F 0x01 0 0x6 0 0 0x81 0
#65row-65898 0x7F 0x01 1 0x6 0 0 0x81 0
#66row-66929 0x80 0x01 0 0x6 0 0 0x80 1
#67row-67931 0x80 0x01 1 0x6 0 0 0x80 1
#68row-68950 0xFF 0x01 0 0x6 0 0 0x01 0
#69row-69952 0xFF 0x01 1 0x6 0 0 0x01 0
#70row-70964 0x00 0x01 0 0x7 0 1 0x00 0
#71row-71970 0x00 0x01 1 0x7 0 1 0x00 0
#72row-72977 0x01 0x01 0 0x7 0 1 0x00 0
#73row-73983 0x01 0x01 1 0x7 0 1 0x00 0
#74row-74990 0x7F 0x01 0 0x7 0 1 0x00 0
#75row-75996 0x7F 0x01 1 0x7 0 1 0x00 0
#76row-761003 0x80 0x01 0 0x7 0 1 0x00 0
#77row-771009 0x80 0x01 1 0x7 0 1 0x00 0
#78row-781016 0xFF 0x01 0 0x7 0 1 0x00 0
#79row-791022 0xFF 0x01 1 0x7 0 1 0x00 0

Scroll within the table for all rows and pins. Buses use hexadecimal; values containing X/Z use MSB-first binary. Inputs show the declared values in effect at each checkpoint, including retained inputs. Sequential tests also replay preparation changes between checkpoints, such as lowering the clock. Use the matching checkpoint label in the simulation below.

Embedded simulation

The arithmetic8 scenario exposes ArithmeticUnit8Test, with 80 recorded cases including unary boundary cases, both carry inputs, and the reserved local opcode. Each checkpoint shows the settled result and flags for one case.

Open ArithmeticUnit8 in the simulator

Source and tests

Test Coverage
ArithmeticUnit8Test 80 recorded binary cases: all eight local opcodes × five A values × both C_IN values, with B = 1. Checks OUT, CARRY, OVERFLOW, and INVALID_OP.
ALU8ContractTest: integrated ALU8 checks 5,184 boundary cases across all 32 top-level opcodes, 2,048 seeded random cases, and 28 targeted four-state cases. These include arithmetic results and flags, reserved encodings, and isolation of selected ignored inputs. Also verifies that the ALU contains one shared AddSub8, one Adder8, and eight FullAdder instances.

The embedded simulation contains the 80 ArithmeticUnit8Test cases. The integrated checks are not an exhaustive sweep of ArithmeticUnit8 or ALU8; the 262,144-case exhaustive sweep described above targets the standalone AddSub8. The contract-test harness disables history recording, while the recorded component scenarios retain it.