LUTs, Flip-Flops & Routing

A look-up table is a tiny memory holding a truth table. Slices, configurable logic blocks, carry chains, switch matrices and I/O blocks.

1. The trick: a truth table stored in memory

Chapter 4 said that an FPGA's logic blocks compute with look-up tables. Here is the whole idea in one sentence: any Boolean function of \(k\) inputs is completely described by its truth table, which has \(2^k\) rows; so store the output column in a \(2^k\)-bit memory and use the inputs as the address. Reading the memory is evaluating the function.

\[ y = \text{INIT}\big[\,\text{address}\,\big], \qquad \text{address} = \sum_{j=0}^{k-1} x_j\, 2^{\,j} \quad\Rightarrow\quad 2^{2^k} \text{ possible functions per LUT} \]

A 6-input LUT therefore holds 64 bits, called its INIT value, and can be any one of\(2^{64} \approx 1.8\times10^{19}\) functions. An AND gate, a parity check, a 4-to-1 multiplexer and a random-looking function all cost the same: one LUT, one LUT delay. This is very different from the gates of Chapter 1, where a bigger expression meant more gates. Inside an FPGA, the question is never “how many gates?” but “how many inputs?”.

0addr 00addr 10addr 21addr 30addr 41addr 51addr 61addr 7y = 1I0 = 1I1 = 0I2 = 1majority(I2,I1,I0)INIT = 8'b1110_1000= 8'hE8SRAM: set bythe bitstreammux tree: fixedsilicon
Inside a LUT: the INIT bits sit in SRAM cells, and a tree of 2:1 multiplexers driven by the inputs selects one of them. Shown for k = 3 (8 bits) programmed with the majority function; a LUT6 has the same structure with 64 cells and six levels. The highlighted path is the input 101 (I2=1, I1=0, I0=1), address 5.
Key idea: in an FPGA, logic costs inputs, not gates
A LUT6 computes any function of up to six inputs in the same time and area. Synthesis tools therefore cut your design into pieces with at most six inputs each (“technology mapping”), and the cost of a circuit is roughly the number of such pieces and the number of LUT levels a signal must cross.

2. Programming a LUT, in HDL and in Python

Since a LUT is just memory, we can model one in a few lines. The model below takes the 64-bit INIT value as a parameter (Verilog) or generic (VHDL) and returns the bit at the address formed by the six inputs. Next to it is a 4-to-1 multiplexer: four data inputs plus two select inputs make exactly six, so Vivado maps it to one LUT6.

lut6_model — a LUT is a 64-bit memory addressed by its inputs
// Behavioural model of a 6-input look-up table: a 64-bit memory
// (the INIT value) read at the address formed by the six inputs.
module lut6_model #(
    parameter [63:0] INIT = 64'h0000_0000_0000_0000
) (
    input  wire [5:0] i,      // i[5] is the most significant address bit
    output wire       o
);
    assign o = INIT[i];
endmodule
mux4 — a six-input function that fits in exactly one LUT6
// A 4-to-1 multiplexer: 4 data + 2 select = 6 inputs, one output.
// Any function of six inputs fits in ONE LUT6, so this is one LUT.
module mux4 (
    input  wire [3:0] d,
    input  wire [1:0] s,
    output wire       y
);
    assign y = d[s];
endmodule

What INIT value turns the LUT into mux4? Order the inputs as address bits i[3:0] = d, i[5:4] = s. When s = 0 the output follows d[0]= address bit 0, which is 1 on every odd address: the pattern …1010, i.e. AAAA in the lowest 16 bits. Doing the same for s = 1, 2, 3 gives CCCC, F0F0 andFF00. The testbench programs lut6_model with 64'hFF00_F0F0_CCCC_AAAA and checks it against mux4 for all 64 inputs.

lut6_tb — the programmed LUT equals the multiplexer
`timescale 1ns / 1ps
// Program a LUT model with the mux4 truth table and compare it with mux4
// for all 64 input combinations.
module lut6_tb;
    reg  [5:0] i;
    wire       y_mux, y_lut;
    integer    n, errors;

    mux4 ref_mux (.d(i[3:0]), .s(i[5:4]), .y(y_mux));
    lut6_model #(.INIT(64'hFF00_F0F0_CCCC_AAAA)) lut (.i(i), .o(y_lut));

    initial begin
        errors = 0;
        for (n = 0; n < 64; n = n + 1) begin
            i = n;
            #1;
            if (y_lut !== y_mux) errors = errors + 1;
        end
        if (errors == 0)
            $display("PASS: LUT6 with INIT=FF00F0F0CCCCAAAA equals mux4 on all 64 inputs");
        else
            $display("FAIL: %0d mismatches", errors);
        $finish;
    end
endmodule

Both simulators print PASS: LUT6 with INIT=FF00F0F0CCCCAAAA equals mux4 on all 64 inputs. If you open a synthesised design in Vivado and click on a LUT, its properties show exactly such an INIT string. (In real designs you never write INIT values by hand — synthesis computes them — but knowing what they mean demystifies the device.)

The Python program does what synthesis does for a single LUT: it evaluates a function on every input combination, packs the results into an INIT value, and verifies it. It then asks how many LUT6s an \(n\)-input function needs, comparing a “tree” function such as a wide AND (each LUT absorbs six signals and emits one, so\(\lceil (n-1)/5 \rceil\) LUTs) with the worst case of an arbitrary function, built by Shannon expansion\(f = x\,f_1 + \bar x\, f_0\) on one input at a time.

Python
script.py91 lines

Click Run to execute the Python code

Code will be executed with Python 3 on the server

The orange curve grows like \(2^n/64\): an arbitrary function of 16 inputs needs about a thousand LUTs, because it really is a 64-Kbit table — the job of block RAM (Chapter 6). Luckily, real logic is rarely arbitrary; adders, comparators and AND trees grow slowly.

3. The fracturable LUT6: one LUT or two LUT5s

A 7-series LUT is built as two 5-input LUTs (32 bits each) sharing inputs I0–I4, plus a final 2:1 multiplexer controlled by I5. It has two outputs:

  • O6 — the full 6-input function (the mux selects the upper or lower LUT5 using I5);
  • O5 — the output of the lower LUT5 alone.

So a LUT6 can implement one function of six inputs, or two functions of up to five inputs each as long as they share the same (at most five) input signals; in that case I5 is tied high by the tools. Adders use this: one LUT produces both the propagate signal for the carry chain and a second signal from the same inputs. Vivado's utilization report counts “LUT as Logic” per physical LUT6, whether one or both outputs are used.

LUT5 (upper)INIT[63:32]LUT5 (lower)INIT[31:0]I0 … I4shared1 0I5O6O5any f(I0..I5)g(I0..I4)
The fracturable 7-series LUT6: two LUT5s share inputs I0–I4. O6 is the 6-input function selected by I5; O5 is the lower LUT5 alone, available when the LUT is used as two 5-input functions.

4. Flip-flops, carry chains and wide multiplexers: the slice

A LUT alone only does combinational logic. Xilinx groups LUTs with the other things every design needs into a slice. A 7-series slice contains:

  • 4 LUT6 (named A, B, C, D), each fracturable as above;
  • 8 storage elements: two per LUT. All eight can be D flip-flops; four of them can alternatively be latches. They share the slice's clock, clock enable and set/reset signals — which is why designs with many different enables or resets pack less densely;
  • a CARRY4: a dedicated 4-bit carry chain, the fast ripple-carry logic of Chapter 2 in hard silicon, linked vertically to the slices above and below;
  • wide multiplexers F7AMUX, F7BMUX and F8MUX, which combine LUT outputs into functions of 7 or 8 inputs (or 8:1 and 16:1 multiplexers) without spending more LUTs.
SLICECOUT to the slice aboveCIN from the slice belowDLUTLUT6 / 2×LUT56MUX/XORFFFF / latchCLUTLUT6 / 2×LUT56MUX/XORFFFF / latchBLUTLUT6 / 2×LUT56MUX/XORFFFF / latchALUTLUT6 / 2×LUT56MUX/XORFFFF / latchF7BF8F7A+ outputselect muxeswide muxesCLK, CE, SR shared by all 8CARRY4carry in green, LUT outputs in orange
A simplified 7-series slice. Each LUT can feed its two flip-flops, the CARRY4 (which runs vertically into the slices above and below), and the F7/F8 multiplexers. All eight flip-flops share one clock, one clock enable (CE) and one set/reset (SR).

Two slices form a configurable logic block (CLB), the tile repeated across the chip. Slices come in two flavours. In a SLICEL (“logic”) the LUTs are only function generators. In a SLICEM (“memory”) the LUTs can also be written while the design is running:

  • as distributed RAM: each LUT becomes a 64 × 1 bit RAM with a synchronous write and an asynchronous (combinational) read; the four LUTs of a slice combine into deeper or multi-port memories;
  • as a shift register (SRL32): each LUT becomes a 32-stage shift register with a selectable tap, replacing up to 32 flip-flops.

About one slice in three is a SLICEM; every CLB contains either two SLICELs or one SLICEL and one SLICEM. The XC7Z020's 53,200 LUTs live in 13,300 slices (6,650 CLBs), with twice as many flip-flops: 106,400.

ResourcePer slicePer CLBXC7Z020 total
LUT64853,200
Flip-flops816106,400
CARRY4 (4 bits of carry)1213,300
F7 mux / F8 mux2 / 14 / 2—
Can be RAM / SRLSLICEM onlyat most 1 slicea fraction of the LUTs

5. Arithmetic: why adders are cheap

Recall the full adder from Chapter 2: \(s_i = a_i \oplus b_i \oplus c_i\) and\(c_{i+1} = a_i b_i + c_i(a_i \oplus b_i)\). Write \(p_i = a_i \oplus b_i\) (“propagate”). Then the carry can be computed with a multiplexer:

\[ c_{i+1} = \begin{cases} c_i & \text{if } p_i = 1 \\ a_i & \text{if } p_i = 0 \end{cases} \qquad\qquad s_i = p_i \oplus c_i \]

That is exactly how the CARRY4 is built: per bit, one dedicated multiplexer (MUXCY) for the carry and one XOR gate (XORCY) for the sum. The LUT only has to compute \(p_i\) (and pass \(a_i\) through), so an\(N\)-bit adder costs \(N\) LUTs and \(\lceil N/4 \rceil\) CARRY4s. Because the carry hops between dedicated cells instead of through general routing, it is very fast: a few tens of picoseconds per bit, so a 32-bit add easily runs well above the 125 MHz of the PYNQ-Z2.

add8 — a registered adder: 8 LUTs, 2 CARRY4, 9 flip-flops
// Registered 8-bit adder with carry out. Synthesis maps it to
// 8 LUTs (one per bit) + 2 CARRY4 blocks + 9 flip-flops.
module add8 (
    input  wire       clk,
    input  wire [7:0] a,
    input  wire [7:0] b,
    output reg  [8:0] sum = 9'd0     // sum[8] is the carry out
);
    always @(posedge clk) begin
        sum <= a + b;                // 9-bit result: widths extended
    end
endmodule

Synthesis turns a + b into exactly that structure: eight LUTs computing\(a_i \oplus b_i\), two CARRY4 blocks in two vertically adjacent slices, and nine flip-flops for the registered sum and carry out. Notice the VHDL resize to 9 bits: in numeric_std the result of + has the width of the wider operand, so without it the carry would be thrown away. In Verilog the addition takes the width of the 9-bit destination. An exhaustive testbench checks all 65,536 sums in both languages.

add8_tb — all 65,536 additions, one clock of latency
`timescale 1ns / 1ps
module add8_tb;
    reg        clk = 1'b0;
    reg  [7:0] a = 8'd0, b = 8'd0;
    wire [8:0] sum;
    integer    n, errors;

    add8 dut (.clk(clk), .a(a), .b(b), .sum(sum));
    always #4 clk = ~clk;                     // 125 MHz

    initial begin
        errors = 0;
        for (n = 0; n < 65536; n = n + 1) begin
            @(negedge clk);
            {a, b} = n;
            @(posedge clk); #1;               // result is registered: one cycle later
            if (sum !== a + b) errors = errors + 1;
        end
        if (errors == 0)
            $display("PASS: all 65536 additions correct");
        else
            $display("FAIL: %0d errors", errors);
        $finish;
    end
endmodule
Common mistake: hand-building an adder from gates
Writing an adder bit by bit with explicit XOR/AND/OR equations (as in Chapter 2) can stop the synthesiser from recognising it, and you get a chain of LUTs routed through the general fabric — slower and larger. Write a + b and let the tools use the carry chain.

6. LUTs as shift registers

A delay line — a value that must come out exactly \(N\) clock cycles after it went in — is common in signal processing and pipelines. Written naively, it is a chain of \(N\) flip-flops. But if the chain has no reset, Vivado can put up to 32 stages in one SLICEM LUT configured as an SRL (shift register LUT).

delay16 — a 16-cycle delay line that maps to a single SRL
// A 16-cycle delay line. With no reset, synthesis packs the whole
// shift register into ONE LUT of a SLICEM used as an SRL (SRL16/SRL32),
// instead of 16 flip-flops.
module delay16 #(
    parameter DEPTH = 16
) (
    input  wire clk,
    input  wire ce,          // clock enable: shift only when high
    input  wire d,
    output wire q
);
    reg [DEPTH-1:0] sr = {DEPTH{1'b0}};

    always @(posedge clk) begin
        if (ce)
            sr <= {sr[DEPTH-2:0], d};
    end

    assign q = sr[DEPTH-1];
endmodule

The testbench shifts a 100-bit pattern through and checks that q is d delayed by exactly 16 cycles (both languages: PASS: q equals d delayed by 16 cycles (84 bits checked)). Vivado typically implements this as one SRL16E/SRLC32E LUT; depending on settings it may keep the final stage in an ordinary flip-flop, because a flip-flop output is faster than an SRL output.

delay16_tb — checking the delay
`timescale 1ns / 1ps
module delay16_tb;
    reg  clk = 1'b0, ce = 1'b1, d = 1'b0;
    wire q;
    reg  [99:0] pattern = 100'h9_A3F0_5C1E_7724_B0D9_8E61_35AC;
    integer n, errors;

    delay16 dut (.clk(clk), .ce(ce), .d(d), .q(q));
    always #4 clk = ~clk;

    initial begin
        errors = 0;
        for (n = 0; n < 100; n = n + 1) begin
            @(negedge clk);
            if (n >= 16 && q !== pattern[n-16]) errors = errors + 1;
            d = pattern[n];
        end
        if (errors == 0)
            $display("PASS: q equals d delayed by 16 cycles (84 bits checked)");
        else
            $display("FAIL: %0d errors", errors);
        $finish;
    end
endmodule
Common mistake: adding a reset to every register
An SRL cannot be reset — its stages are SRAM cells, not flip-flops. If you give the shift register a reset, synthesis must use real flip-flops: 16 instead of one LUT. Reset only what really needs a known value; data pipelines usually do not.

7. Routing: where the time goes

Between the CLBs runs the programmable interconnect. Next to every CLB sits a switch matrix: a dense block of multiplexers (controlled, like everything else, by configuration bits) that connects the CLB's inputs and outputs to wire segments of different lengths — short ones reaching the neighbouring tiles, medium ones spanning a few tiles, and long lines crossing large parts of the chip. A signal travelling from one LUT to another crosses one or more of these multiplexers and segments.

Each crossing adds delay, and wires have resistance and capacitance. In a modern FPGA a LUT costs on the order of a tenth of a nanosecond, while a single routing hop costs several times that. On typical paths routing accounts for half or more of the total delay. That changes how you think about speed:

\[ t_\text{path} = t_\text{clk}\!\to_{Q} + \sum_\text{LUT levels} t_\text{LUT} + \sum_\text{hops} t_\text{route} + t_\text{setup} \;\le\; T_\text{clk} \]
  • fewer LUT levels between flip-flops means fewer hops, so logic depth matters more than logic size;
  • placement matters: the tools place connected logic close together, and a congested or very full device slows down;
  • signals that fan out to hundreds of destinations (enables, resets) need long wires and often limit speed.

Chapter 15 measures all this with Vivado's timing reports, where every path is broken down into “logic” and “net” delay. Dedicated paths — carry chains, F7/F8 muxes, and the cascades inside DSP and block RAM columns (Chapter 6) — bypass the general routing and are much faster.

Key idea: area is LUTs, speed is levels and wires
To estimate the cost of a design, count LUTs and flip-flops. To estimate its speed, count LUT levels between registers and remember that each level brings a wire with it. Inserting registers (pipelining) shortens the paths — at the price of more cycles of latency.

8. I/O blocks: the edge of the fabric

Signals enter and leave the chip through I/O blocks (IOBs) arranged in banks around the fabric. Each IOB contains an input buffer, an output driver with tri-state control, and programmable settings for the voltage standard (LVCMOS33, LVCMOS18, LVDS…), drive strength, slew rate and pull-up/pull-down resistors. All pins of one bank share a supply voltage, which is why the XDC files of Chapters 13 and 14 set IOSTANDARD LVCMOS33 on every PYNQ-Z2 pin: those banks are powered at 3.3 V on the board.

Next to each IOB is dedicated logic for fast interfaces: input and output registers (so the flip-flop sits right at the pin, for predictable timing), double-data-rate registers (IDDR/ODDR) that capture on both clock edges, programmable delay lines (IDELAY), and serialisers/deserialisers (ISERDES/OSERDES) that convert between a slow wide bus and a fast narrow one. Their use is beyond this course, but it is good to know they exist when you read a data sheet.

What Vivado reports for add8 (illustrative utilization summary)
+-------------------------+------+
| Site Type               | Used |
+-------------------------+------+
| Slice LUTs              |    8 |
|   LUT as Logic          |    8 |
| Slice Registers         |    9 |
| CARRY4                  |    2 |
| Bonded IOB              |   26 |   <- 8 + 8 + 9 data pins + clk
+-------------------------+------+

Notice that the I/O count, not the logic, is what such a small design “uses up”. In real designs the inner signals stay inside the fabric, and only a handful of pins connect to LEDs, buttons and connectors.

Practice Problems

Progress: 0/5 (0%)
1
⭐ Easy
Write the 4-bit INIT value (as bits, address 3 down to 0) of a 2-input LUT that computes \(y = a \,\bar{b}\), where \(a\) is input I0 (the least significant address bit) and \(b\) is I1.
2
⭐ Easy
How many 7-series slices does a 32-bit registered adder need at minimum? Count LUTs, CARRY4 blocks and flip-flops.
3
⭐⭐ Medium
A design needs two independent functions: \(f(a,b,c,d)\) and \(g(a,b,c,e)\). Can they share one fracturable LUT6? What about \(f(a,b,c,d,e)\) and \(g(p,q)\)?
4
⭐⭐ Medium
Using the 7-series wide multiplexers, how many LUT6s and which muxes implement a 16-to-1 multiplexer? How many slices is that? What about a 32-to-1 multiplexer?
5
⭐⭐⭐ Hard
A path goes FF → LUT → LUT → LUT → FF. Assume a clock-to-out of 0.4 ns, 0.12 ns per LUT, a setup time of 0.05 ns, and 0.5 ns for each of the four routing hops. (a) What is the maximum clock frequency? (b) What fraction of the delay is routing? (c) A register is inserted after the second LUT, splitting the path in two; estimate the new maximum frequency.

Problem Set Statistics

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