Combinational Logic in HDL

Continuous assignment, always @(*) and VHDL processes, if and case, vectors and arithmetic, and the accidental latch — the classic beginner bug.

1. Combinational logic: outputs that depend only on present inputs

A combinational circuit has no memory: its outputs are a function of its inputs right now, like the gates, multiplexers, decoders and adders of Chapter 2. On an FPGA every such function, up to six inputs, fits into one LUT; larger ones become a few LUTs plus, for arithmetic, the carry chain (Chapter 5). This chapter shows the HDL constructs that describe combinational logic, and the one classic way of accidentally describing memory instead.

Both languages offer two styles. Concurrent statements sit directly in the module or architecture body — one statement per output, like the assign and <= of Chapter 7. Procedural blocks (always in Verilog, process in VHDL) group several statements that are read top to bottom, which is convenient for if and case. Both describe hardware; the procedural style is not “more software”.

d0[3:0]0d1[3:0]1d2[3:0]2d3[3:0]3MUXsel[1:0]y[3:0]after synthesis: one LUT6 per output bitLUT6y[0]d0..d3[0],sel[1:0]LUT6y[1]d0..d3[1],sel[1:0]LUT6y[2]d0..d3[2],sel[1:0]LUT6y[3]d0..d3[3],sel[1:0]
A 4-to-1 multiplexer: the 2-bit select chooses which 4-bit input appears on y. Each output bit depends on 6 signals (4 data bits and 2 select bits), so a 4-bit mux4 fits exactly into four 6-input LUTs.
mux4 — concurrent style: conditional operator and selected assignment
// mux4: choose one of four 4-bit inputs. Concurrent style.
module mux4 (
    input  wire [3:0] d0, d1, d2, d3,
    input  wire [1:0] sel,
    output wire [3:0] y
);
    // Conditional operator  cond ? if_true : if_false, nested
    assign y = (sel == 2'd0) ? d0 :
               (sel == 2'd1) ? d1 :
               (sel == 2'd2) ? d2 :
                               d3;
endmodule

Verilog's cond ? x : y (borrowed from C) is literally a 2-to-1 multiplexer; nesting three of them describes the 4-to-1 mux. VHDL has two concurrent forms: the conditional assignment y <= x when cond else z; and the selected assignment with sel select y <= … when …; used here. The when others choice is required because std_logic has nine values, so "00"…"11" do not cover every possible value of sel — others covers the remaining ones (including "11").

2. Procedural blocks: always @(*) and process

A procedural block is a container for statements read in order. For combinational logic:

  • Verilog: always @(*). The @(*) means “re-evaluate whenever any signal read inside changes”. Signals assigned in an always block must be declared reg — a historical name; here the reg becomes plain wires and gates, not a register. Use the blocking assignment = in combinational blocks (Chapter 9 explains why).
  • VHDL: process (list). The sensitivity list names the signals whose change wakes the process up. VHDL-2008 allows process (all), but Vivado treats .vhd files as VHDL-93 by default, so this course writes the list out in full: every signal read in the process must appear in it.
prio_enc — priority encoder with if / else if
// prio_enc: index of the highest-numbered active request.
// Procedural style: always @(*) with if / else if.
module prio_enc (
    input  wire [3:0] req,
    output reg  [1:0] idx,
    output reg        valid
);
    always @(*) begin
        valid = 1'b1;                       // default ...
        if      (req[3]) idx = 2'd3;        // highest priority, tested first
        else if (req[2]) idx = 2'd2;
        else if (req[1]) idx = 2'd1;
        else if (req[0]) idx = 2'd0;
        else begin
            idx   = 2'd0;                   // every branch assigns idx
            valid = 1'b0;                   // ... overridden here
        end
    end
endmodule

Two rules make the procedural style predictable. First, the last assignment wins: the block sets valid to 1 at the top and overrides it in the final else. That is not “assign 1 then change it” in time — the synthesiser works out, for each combination of inputs, which assignment is the last one executed, and builds logic that produces that value. Second, in VHDL a signal assigned in a process does not change until the process suspends, so reading valid right after writing it would still give the old value; Chapter 9 makes use of this.

Common mistake: an incomplete sensitivity list
If a VHDL-93 process reads b but its list says only (sel, a), the simulator does not wake the process when b changes, so the output holds its old value — a latch-like behaviour. The synthesiser ignores the list and builds the combinational logic you wrote. Simulation and hardware now disagree. Vivado warns about this; take the warning seriously. Verilog's @(*) avoids the problem, which is why you should never write a hand-made list like @(a or b).

3. if versus case — priority versus parallel

An if / else if chain describes priority: the first true condition wins, so req[3] above masks every lower request. Synthesis builds it as a chain of 2-to-1 muxes (or the equivalent LUT logic). A case statement compares one expression with a list of constant values that cannot overlap, which describes a parallel selection — a wide multiplexer or a look-up table. When the conditions are mutually exclusive, use case: it says what you mean, and for small functions the result is the same LUTs anyway.

A 7-segment display shows a digit with seven bar-shaped LEDs labelled a to g. The decoder below turns a 4-bit value into the seven segment enables, with hexadecimal letters A–F. It is the archetypal case: sixteen constants, one per input value. Seven outputs of four inputs each means seven LUTs (Vivado can pair some of them into dual-output LUTs).

seg7 — hexadecimal 7-segment decoder
// seg7: hexadecimal digit to 7-segment pattern.
// seg = {g, f, e, d, c, b, a}, 1 = segment lit (common-cathode display).
//      a
//    f   b
//      g
//    e   c
//      d
module seg7 (
    input  wire [3:0] hex,
    output reg  [6:0] seg
);
    always @(*) begin
        case (hex)
            4'h0: seg = 7'b0111111;
            4'h1: seg = 7'b0000110;
            4'h2: seg = 7'b1011011;
            4'h3: seg = 7'b1001111;
            4'h4: seg = 7'b1100110;
            4'h5: seg = 7'b1101101;
            4'h6: seg = 7'b1111101;
            4'h7: seg = 7'b0000111;
            4'h8: seg = 7'b1111111;
            4'h9: seg = 7'b1101111;
            4'hA: seg = 7'b1110111;
            4'hB: seg = 7'b1111100;   // lower-case b
            4'hC: seg = 7'b0111001;
            4'hD: seg = 7'b1011110;   // lower-case d
            4'hE: seg = 7'b1111001;
            default: seg = 7'b1110001; // F
        endcase
    end
endmodule

The default (Verilog) or when others (VHDL) branch is good practice even when every value is listed: in VHDL it is mandatory for the reason given in Section 1, and in both languages it guarantees that the output is assigned whatever the input. The testbench drives all sixteen values, checks that each pattern lights the right number of segments, and draws the result:

seg7_tb — exhaustive test with ASCII-art output
`timescale 1ns/1ps
// Drives all 16 digits, checks the number of lit segments of each,
// and draws the patterns in ASCII art.
module seg7_tb;
    reg  [3:0] hex;
    wire [6:0] seg;
    reg  [6:0] pat [0:15];
    reg  [63:0] nlit = 64'h4554_5667_3654_5526;  // lit segments, digit F..0
    integer i, k, lit, errors = 0;

    seg7 dut (.hex(hex), .seg(seg));

    initial begin
        for (i = 0; i < 16; i = i + 1) begin
            hex = i;
            #10;
            pat[i] = seg;
            lit = 0;
            for (k = 0; k < 7; k = k + 1) lit = lit + seg[k];
            if (lit != nlit[4*i +: 4]) errors = errors + 1;
        end
        // seg = {g, f, e, d, c, b, a}: three text rows per digit
        for (i = 0; i < 16; i = i + 1)
            $write(" %s  ", pat[i][0] ? "_" : " ");
        $write("\n");
        for (i = 0; i < 16; i = i + 1)
            $write("%s%s%s ", pat[i][5] ? "|" : " ", pat[i][6] ? "_" : " ", pat[i][1] ? "|" : " ");
        $write("\n");
        for (i = 0; i < 16; i = i + 1)
            $write("%s%s%s ", pat[i][4] ? "|" : " ", pat[i][3] ? "_" : " ", pat[i][2] ? "|" : " ");
        $write("\n");
        if (errors == 0) $display("PASS: 16 digits, segment counts correct");
        else             $display("FAIL: %0d digits wrong", errors);
        $finish;
    end
endmodule
Output (identical from Icarus and GHDL)
 _       _   _       _   _   _   _   _   _       _       _   _
| |   |  _|  _| |_| |_  |_    | |_| |_| |_| |_  |    _| |_  |_
|_|   | |_   _|   |  _| |_|   | |_|  _| | | |_| |_  |_| |_  |
PASS: 16 digits, segment counts correct

The PYNQ-Z2 has no 7-segment display on board; you can connect a PMOD display, or reuse seg7 in later chapters to drive one through a PMOD connector.

4. Vectors: slicing, concatenation, replication

Buses are manipulated as a whole or in parts. None of these operations creates a single gate: slicing and concatenation are just wiring, renaming which physical wire is which bit. They are free in hardware.

OperationVerilogVHDLHardware
Declare 8-bit buswire [7:0] dsignal d : std_logic_vector(7 downto 0)eight wires
One bitd[7]d(7)one wire
Sliced[7:4]d(7 downto 4)four wires
Concatenate{a, b}a & bwires side by side
Replicate{4{x}}(3 downto 0 => x)one wire fanned out
All zeros8'd0 or 0(others => '0')tied to ground
Swap nibbles{d[3:0], d[7:4]}d(3 downto 0) & d(7 downto 4)crossed wires
Shift left by 1{d[6:0], 1'b0}d(6 downto 0) & '0'wires, no gates
Reduction XOR (parity)^dxor d (VHDL-2008 only)XOR tree

Shifting by a constant is wiring; shifting by a variable amount (d << n, shift_left(d, n)) is a barrel shifter, a layer of multiplexers per bit of n. The ALU in Section 7 contains one.

5. Arithmetic: numeric_std, widths, carry and sign

A vector is just bits; whether 1011 means 11 or \(-5\) is an interpretation you choose (Chapter 1). Verilog vectors are unsigned unless declared signed or cast with $signed(). VHDL is stricter: std_logic_vector has no arithmetic at all. The standard package ieee.numeric_std provides two types with the same bits but different meaning, unsigned and signed, and you convert explicitly:

ConversionVHDL (numeric_std)Verilog
bits → unsigned / signedunsigned(v) signed(v)v / $signed(v)
number → bitsstd_logic_vector(x)(automatic)
integer → n-bit unsignedto_unsigned(i, n)n'd i
n-bit → integerto_integer(x)(automatic)
change widthresize(x, n){zeros or sign copies, x}

The width of a result matters, because hardware has a fixed number of wires. Adding two \(n\)-bit numbers can produce an \((n+1)\)-bit result; if you store it in \(n\) bits, the top bit — the carry out — is silently dropped and the result wraps around modulo \(2^n\). To keep it, widen an operand first. How you widen depends on the interpretation:

\[ \text{zero extension: } 1011 \to 0\,1011 \;(11 \to 11), \qquad \text{sign extension: } 1011 \to 1\,1011 \;(-5 \to -5) \]
widths — the same bits, three different additions
// widths: the same two 4-bit codes added as unsigned and as signed numbers.
module widths (
    input  wire [3:0] a,
    input  wire [3:0] b,
    output wire [3:0] wrap,     // 4-bit result: overflows silently
    output wire [4:0] usum,     // 5 bits: carry out kept (zero extension)
    output wire [4:0] ssum,     // 5 bits: signed, sign extension
    output wire [7:0] rep       // replication: a copied twice
);
    assign wrap = a + b;                                   // truncated to 4 bits
    assign usum = {1'b0, a} + {1'b0, b};                   // extend with 0
    assign ssum = {a[3], a} + {b[3], b};                   // extend with sign bit
    assign rep  = {2{a}};                                  // = {a, a}
endmodule

In VHDL, numeric_std's + returns the width of its wider operand, so resize(unsigned(a), 5) + unsigned(b) is a 5-bit addition, and resize on a signed value copies the sign bit. Verilog is more permissive: it extends both operands to the width of the largest expression in the statement, including the left-hand side. Writing the extension explicitly, as here, makes the intent obvious in both languages. With a = 1011, b = 0111 both simulators print:

widths_tb — print the three results
`timescale 1ns/1ps
// a = 1011, b = 0111: as unsigned 11 + 7 = 18, as signed -5 + 7 = 2.
module widths_tb;
    reg  [3:0] a = 4'b1011, b = 4'b0111;
    wire [3:0] wrap;
    wire [4:0] usum, ssum;
    wire [7:0] rep;

    widths dut (.a(a), .b(b), .wrap(wrap), .usum(usum), .ssum(ssum), .rep(rep));

    initial begin
        #1;
        $display("wrap = %b (%0d)", wrap, wrap);
        $display("usum = %b (%0d)", usum, usum);
        $display("ssum = %b (%0d)", ssum, $signed(ssum));
        $display("rep  = %b", rep);
        if (wrap == 4'd2 && usum == 5'd18 && $signed(ssum) == 2 && rep == 8'b10111011)
            $display("PASS");
        else
            $display("FAIL");
        $finish;
    end
endmodule
Output
wrap = 0010 (2)      11 + 7 = 18 does not fit in 4 bits: 18 mod 16 = 2
usum = 10010 (18)    zero-extended: the carry is kept
ssum = 00010 (2)     sign-extended: -5 + 7 = 2
rep  = 10111011
PASS
Common mistake: mixing signed and unsigned in Verilog
In Verilog, if any operand of an expression is unsigned, the whole expression is evaluated as unsigned. So $signed(a) < b with an unsigned b compares as unsigned, and the sign of a is ignored. VHDL would refuse to compile the mix; Verilog silently does the wrong thing. Cast every operand, or declare the signals signed.

6. The inferred latch

Here is the classic beginner bug. The intent is “y follows d while en is high”; nothing is said about what happens when en is low.

BUGGY: y is not assigned when en is 0
// fragment: BUGGY - y is not assigned when en = 0, so a latch is inferred
always @(*) begin
    if (en)
        y = d;
end

A combinational block must give every output a value for every combination of inputs. This one does not: when en = 0 the code assigns nothing, and the language rules say a reg or signal that is not assigned keeps its previous value. Keeping a value requires memory. Since there is no clock, the synthesiser builds a level-sensitive latch: transparent while en = 1, holding while en = 0. Vivado reports it as a warning (“inferring latch for variable y”); the open-source GHDL synthesiser refuses outright with latch infered for net "y".

if (en) y = d; — latchdenLATCHDGQyen = 0: y remembers its last valuetiming analysis through it is unreliabley = 0; if (en) y = d; — muxd4'd010enyno memory:one LUT per bit
Left: what the buggy code describes — a latch whose enable is a data signal. Right: the fix — a default assignment makes y defined on every path, and the result is a pure multiplexer (here an AND of each bit with en).
gate_fixed — a default assignment removes the latch
// gate_fixed: y follows d when en = 1, and is 0 otherwise.
// A default assignment at the top makes y defined on every path.
module gate_fixed (
    input  wire       en,
    input  wire [3:0] d,
    output reg  [3:0] y
);
    always @(*) begin
        y = 4'd0;          // default: no path leaves y unassigned
        if (en)
            y = d;
    end
endmodule

Why are latches bad on an FPGA? The fabric is built around edge-triggered flip-flops; a latch uses the same storage element in a different mode, but its enable is now an ordinary logic signal that can glitch, and a transparent latch creates a timing path that passes straight through it, which the timing analyser handles poorly. Intentional latches are almost never needed in FPGA design. Every latch in your synthesis report should be treated as a bug until proven otherwise.

Key idea: three ways to never infer a latch
(1) Start every combinational block with a default assignment for every output it drives. (2) Give every if an else, and every case a default / when others. (3) Make sure each output is assigned in every branch, not just some. Rule (1) alone is enough and is the easiest to check by eye.

7. Complete example: an 8-bit ALU

An arithmetic and logic unit is the calculating heart of a processor: a 3-bit opcode selects one of eight operations on two 8-bit operands, and four status flags describe the result. It combines everything in this chapter — a case on the opcode, defaults against latches, widened arithmetic for the carry, signed and unsigned interpretation, and a variable shift.

opOperationcarryoverflow
000y = a + bcarry out of bit 7signed result out of −128…127
001y = a − b1 if a ≥ b (unsigned): no borrowsigned result out of range
010y = a AND b00
011y = a OR b00
100y = a XOR b00
101y = 1 if a < b (signed)00
110y = 1 if a < b (unsigned)00
111y = a shifted right arithmetically by b[2:0]00

zero is 1 when y is all zeros and neg is bit 7 of y. The two kinds of “too big” are different: carry is about the unsigned reading (\(200 + 100 = 300 > 255\)), overflow about the signed one (\(100 + 100 = 200 > 127\)). For addition, signed overflow happens exactly when both operands have the same sign and the result has the other:

\[ V_{a+b} = \overline{(a_7 \oplus b_7)} \cdot (y_7 \oplus a_7), \qquad V_{a-b} = (a_7 \oplus b_7) \cdot (y_7 \oplus a_7) \]
alu — 8 operations, carry / overflow / zero / negative flags
// alu: 8-bit arithmetic and logic unit with status flags.
//  op   operation              carry            overflow
//  000  y = a + b              carry out        signed overflow
//  001  y = a - b              1 if a >= b (no borrow, unsigned)  signed overflow
//  010  y = a & b              0                0
//  011  y = a | b              0                0
//  100  y = a ^ b              0                0
//  101  y = (a < b) signed     0                0
//  110  y = (a < b) unsigned   0                0
//  111  y = a >>> b[2:0]       0                0   (arithmetic shift right)
module alu (
    input  wire [7:0] a,
    input  wire [7:0] b,
    input  wire [2:0] op,
    output reg  [7:0] y,
    output reg        carry,
    output reg        overflow,
    output wire       zero,
    output wire       neg
);
    // 9-bit results keep the carry / borrow in bit 8
    wire [8:0] sum9 = {1'b0, a} + {1'b0, b};
    wire [8:0] dif9 = {1'b0, a} - {1'b0, b};

    always @(*) begin
        carry    = 1'b0;                 // defaults: no latches
        overflow = 1'b0;
        case (op)
            3'b000: begin
                y        = sum9[7:0];
                carry    = sum9[8];
                overflow = (a[7] == b[7]) && (sum9[7] != a[7]);
            end
            3'b001: begin
                y        = dif9[7:0];
                carry    = ~dif9[8];     // bit 8 is the borrow
                overflow = (a[7] != b[7]) && (dif9[7] != a[7]);
            end
            3'b010:  y = a & b;
            3'b011:  y = a | b;
            3'b100:  y = a ^ b;
            3'b101:  y = {7'd0, $signed(a) < $signed(b)};
            3'b110:  y = {7'd0, a < b};
            default: y = $signed(a) >>> b[2:0];
        endcase
    end

    assign zero = (y == 8'd0);
    assign neg  = y[7];
endmodule

Notes on the two versions. The sums are computed once, outside the case, in 9 bits so that bit 8 holds the carry (for subtraction, bit 8 is set when a borrow occurred, so carry is its inverse — the ARM convention). Verilog's >>> on a $signed value is an arithmetic shift (copies the sign bit in); VHDL uses shift_right on a signed. The VHDL version computes the result into an internal signal r because zero and neg need to read it, and VHDL-93 forbids reading an out port. The process sensitivity list contains every signal it reads: a, b, op, sum9, dif9.

A 3-input function of 8-bit operands has only \(8 \times 256 \times 256 = 524\,288\) input combinations, so the testbench simply tries them all. It computes the expected value with ordinary integer arithmetic — a completely different method from the design — and also folds every output into a 32-bit signature so that the two simulations, and the Python model below, can be compared with one number.

alu_tb — exhaustive self-checking testbench (524 288 vectors)
`timescale 1ns/1ps
// Exhaustive ALU test: all 8 ops x 256 x 256 operands = 524 288 vectors.
// The expected results are computed with plain integer arithmetic.
module alu_tb;
    reg  [7:0] a, b;
    reg  [2:0] op;
    wire [7:0] y;
    wire       carry, overflow, zero, neg;
    integer ia, ib, iop, sa, sb, r, errors = 0;
    reg  [7:0] ey;
    reg        ec, ev;
    reg [31:0] sig = 0;   // signature of all outputs, compared with Python

    alu dut (.a(a), .b(b), .op(op), .y(y), .carry(carry),
             .overflow(overflow), .zero(zero), .neg(neg));

    initial begin
        for (iop = 0; iop < 8; iop = iop + 1)
            for (ia = 0; ia < 256; ia = ia + 1)
                for (ib = 0; ib < 256; ib = ib + 1) begin
                    op = iop; a = ia; b = ib;
                    #1;
                    sa = (ia > 127) ? ia - 256 : ia;   // two's-complement value
                    sb = (ib > 127) ? ib - 256 : ib;
                    ec = 0; ev = 0;
                    case (iop)
                        0: begin r = ia + ib; ec = r > 255;
                                 ev = (sa + sb > 127) || (sa + sb < -128); end
                        1: begin r = ia - ib; ec = ia >= ib;
                                 ev = (sa - sb > 127) || (sa - sb < -128); end
                        2: r = ia & ib;
                        3: r = ia | ib;
                        4: r = ia ^ ib;
                        5: r = sa < sb;
                        6: r = ia < ib;
                        default: r = sa >>> (ib % 8);
                    endcase
                    ey = r;                              // keep the low 8 bits
                    if (y !== ey || carry !== ec || overflow !== ev ||
                        zero !== (ey == 0) || neg !== ey[7]) begin
                        if (errors < 5)
                            $display("op=%0d a=%0d b=%0d: y=%0d expected %0d", iop, ia, ib, y, ey);
                        errors = errors + 1;
                    end
                    sig = sig * 31 + {y, carry, overflow, zero, neg};
                end
        if (errors == 0) $display("PASS: 524288 vectors, signature %h", sig);
        else             $display("FAIL: %0d mismatches", errors);
        $finish;
    end
endmodule
Output
Icarus:  PASS: 524288 vectors, signature 4624fe68      (about 3 s)
GHDL:    PASS: 524288 vectors, signature 4624FE68      (about 45 s)

(GHDL also prints one harmless metavalue detected warning at time 0, when the inputs are still 'U' and the shifter converts b to an integer.)

8. Python: a bit-accurate model and two readings of one byte

A bit-accurate model reproduces the hardware's results bit for bit, using the same operations the hardware uses — masking to 8 bits, a 9-bit sum, sign-bit comparisons — rather than high-level arithmetic. Here it is checked against a reference written with Python's unbounded integers, over all 524 288 cases at once with NumPy, and it reproduces the testbench signature. The plots show the same 8-bit code read as unsigned and as signed, and which operand pairs make \(a + b\) set the carry flag, the overflow flag, or both.

Python
script.py86 lines

Click Run to execute the Python code

Code will be executed with Python 3 on the server

The right-hand plot makes the difference between the flags visible. Carry (sky and amber) is set above the anti-diagonal \(a + b = 256\). Overflow occupies two triangles: both operands positive (codes below 0x80) with a sum above 127 (orange), or both negative with a sum below \(-128\) (amber, where the carry is also set). Mixed signs can never overflow. Exactly a quarter of all pairs overflow — the 16 384 printed by the program.

9. Summary

You wantVerilogVHDL (VHDL-93 compatible)
One output, simple expressionassign y = ...;y <= ...;
Two-way choice, concurrentassign y = c ? a : b;y <= a when c = '1' else b;
Choice on a value, concurrent(nested ?: or always + case)with s select y <= ... when ...;
Several outputs, if / casealways @(*) begin ... endprocess (every input) begin ... end process;
Arithmeticon wire/reg, $signed() for signedunsigned()/signed() from numeric_std
Avoid latchesdefault assignment at the topdefault assignment at the top

Everything in this chapter is memoryless. Chapter 9 adds the clock: flip-flops, registers, counters and shift registers, and the second kind of assignment that goes with them.

Practice Problems

Progress: 0/5 (0%)
1
⭐ Easy
Given wire [7:0] d = 8'hB6; (VHDL: d = x"B6"), what are d[7:4], {d[3:0], d[7:4]} and {4{d[0]}}? Write the VHDL equivalents of the last two expressions.
2
⭐ Easy
Two 8-bit unsigned numbers are added. How many bits does the result need so that it can never overflow? What about the product of two 8-bit unsigned numbers, and the sum of sixteen 8-bit numbers?
3
⭐⭐ Medium
This Verilog block was meant to be a 2-to-1 multiplexer: always @(sel or a) if (sel) y = a; else y = b;. It simulates wrongly but synthesises to a correct multiplexer. Explain both halves of that sentence, then give two fixes (one Verilog, one for the VHDL-93 equivalent).
4
⭐⭐ Medium
The 4-bit patterns \(a = 1100_2\) and \(b = 1010_2\) are added by the widths module. Give wrap, usum and ssum in binary and in decimal, and say whether the signed 4-bit addition overflowed.
5
⭐⭐⭐ Hard
Prove that the ALU's overflow rule for addition, “a[7] == b[7] and y[7] != a[7]”, is equivalent to carry into bit 7 XOR carry out of bit 7. Then explain why the subtraction rule compares a[7] != b[7].

Problem Set Statistics

Total Problems
5
Attempted
0
Completion
0%
Share:XRedditLinkedIn
Rate this chapter: