Teach Remote lab lessons

Teach lesson

Digital Logic on the DE1-SoC (2/6): Truth tables, SOP/POS, K-maps, and a multiplexer

Students connect truth tables, Boolean forms, Karnaugh maps, and multiplexers, then test minimized combinational logic on DE1-SoC hardware.

  • Altera DE1-SoC
  • 70 min
  • Undergraduate, introductory
  • English
  • Digital systems & FPGA

First time activating Teach? You can run one class session at no cost. Students join with a class code.

Karnaugh map of F(C,B,A) with minterms 1, 3, 4, and 6 marked as 1. Two adjacent pairs are outlined, but the reduced terms are not written in the image. Students must decide which variable changes inside each group and write the simplified terms themselves.
Altera DE1-SoC

Learning Outcomes

  • Derive the sum-of-products (SOP) and product-of-sums (POS) forms from a truth table.

  • Minimize a Boolean function with a Karnaugh map and read off the simplified expression.

  • Compare a canonical form with its minimal form on real hardware and confirm they match.

  • Build and verify a 4-to-1 multiplexer that routes one of four inputs to the output.

Student activity preview

Activity Content

Preview only. In a class session, students can fill in responses and submit their work to the teacher.

1

From truth table to circuit

8 min

In Lesson 1 you wired single gates to switches. Real designs start one level up: you decide what a circuit should do — usually as a truth table — and then find a gate network that produces it. This lesson works one function from both directions and then makes it smaller.

The function for the first two exercises is F(C,B,A), a function of three inputs. You will wire SW[0]=A, SW[1]=B, SW[2]=C, and show the result on LEDR[0]. Here is its truth table. The rows are listed in binary counting order C B A from 000 to 111:

Truth table for F(C,B,A), in binary counting order:

- Row 0: C B A = 0 0 0 -> F=0
- Row 1: C B A = 0 0 1 -> F=1
- Row 2: C B A = 0 1 0 -> F=0
- Row 3: C B A = 0 1 1 -> F=1
- Row 4: C B A = 1 0 0 -> F=1
- Row 5: C B A = 1 0 1 -> F=0
- Row 6: C B A = 1 1 0 -> F=1
- Row 7: C B A = 1 1 1 -> F=0

F is 1 on four of the eight rows and 0 on the other four — read them off the table yourself before you answer below. This split drives the whole lesson: the F=1 rows give you the sum of products, and the F=0 rows give you the product of sums. Keep this table in front of you for both exercises — everything below refers back to it.

Recall the board conventions you used in Lesson 1: a switch up is logic 1, and LEDR is active-high, so the LED lights when the output is 1. These exercises are purely combinational — the output depends only on the current switch positions, so there is no clock to declare.

On how many of the eight rows does F equal 1, and which rows are they?

2

Sum of products and the K-map

14 min

Minterm. A *minterm* is a single AND (product) term built from all the inputs — each input appears once, plain or negated — that is 1 for exactly one row of the truth table. For three inputs there are eight minterms, one per row. Row 1 is C=0, B=0, A=1, so its minterm is (~C & ~B & A): this product is 1 only when C=0, B=0, and A=1, and 0 everywhere else.

Canonical sum of products (SOP). To build F, OR together the minterm of every row where F=1. Because each minterm is 1 for exactly its own row, the OR is 1 on precisely the F=1 rows and 0 elsewhere. Use the truth table above to write the four minterms for F yourself. Do not skip the full form: you will test it on the board before you test the reduced form.

Minimizing with a Karnaugh map. A *K-map* is the truth table redrawn so that physically adjacent cells differ in exactly one input. When two adjacent 1s sit next to each other, the variable that *changes* between them does not matter for that pair and can be dropped, leaving a smaller term. The diagram below marks the four 1 cells and shows two possible groups. Your job is to read each group: identify which input stays fixed, which input changes, and what product term remains.

Karnaugh map of F(C,B,A) with minterms 1, 3, 4, and 6 marked as 1. Two adjacent pairs are outlined, but the reduced terms are not written in the image. Students must decide which variable changes inside each group and write the simplified terms themselves.

The K-map for F. Each outlined pair contains two adjacent 1s. For each group, compare the two cells and cross out the one variable that changes; the variables that stay fixed form the reduced term.

Write the full canonical SOP expression for F(C,B,A) from the four truth-table rows where F=1. Use ~ for NOT, & for AND, and | for OR.

Using the K-map, write the reduced product term for each of the two groups. For each group, state which variable changes and therefore disappears. Then combine the two reduced terms into one minimal expression for F.

Does any input variable disappear from your minimal expression? Explain what that tells you about how F depends on its three inputs. Then find two truth-table rows that differ only in that variable, and give their row numbers and F values to confirm your claim.

3

Build it: canonical SOP, then the minimal form

14 min

Now put the function on the real board. You will run the canonical SOP first, verify all eight rows, then edit the same starter to test the minimal expression you derived from the K-map.

The module below is intentionally incomplete. Replace the TODO_CANONICAL_SOP_EXPRESSION placeholder with your full SOP. After you verify it on the board, replace that same line with your minimized expression and synthesize again.

// Lesson 2 - Exercise 1-B: Sum of Products (SOP) starter
// F(C,B,A): use the truth-table rows where F=1. SW[0]=A, SW[1]=B, SW[2]=C.
// Fill the canonical SOP first. Then replace it with your K-map minimized expression.
module leds_mirror(SW, LEDR);
    input  [9:0] SW;
    output [9:0] LEDR;

    wire A = SW[0];
    wire B = SW[1];
    wire C = SW[2];

    // Pass 1: canonical SOP. Write one product term for each F=1 row.
    assign LEDR[0] = TODO_CANONICAL_SOP_EXPRESSION;

    // Pass 2: after you verify the canonical form, replace the line above with
    // your minimized K-map expression and synthesize again.

    assign LEDR[9:1] = 9'b0;
endmodule
  1. Open the DE1-SoC Verilog lab. The editor shows a file leds_mirror.v.

  2. Select all of the contents of leds_mirror.v and replace them with the starter module above. Keep the module named leds_mirror.

  3. Fill TODO_CANONICAL_SOP_EXPRESSION with your canonical SOP expression. Keep exactly one assign LEDR[0] = ...; line.

  4. Click Synthesize. If a guided tour covers the button, close or skip the tour first. Wait for it to finish (about 1-3 minutes). If it reports an error, check for a missing semicolon or mismatched parenthesis, fix it, and Synthesize again. Continue only when the final status says the build succeeded with 0 errors.

  5. Click Upload to FPGA and wait for the live board session and camera.

  6. Step SW[2:0] (C B A) through all eight combinations 000, 001, 010, 011, 100, 101, 110, 111, reading LEDR[0] on the camera each time. Record each result in the table below.

  7. For the second pass, edit the same assign LEDR[0] line so it uses your minimized K-map expression. Synthesize and Upload again, then spot-check at least three rows, including one where F=0 and one where F=1, and confirm the minimized version matches the canonical version.

Set SW[2:0] = C B A for each row and record the LEDR[0] value you read on the camera (1 = lit, 0 = off). Use your own truth table to check whether the canonical SOP and your minimized expression agree.

C = SW2 B = SW1 A = SW0 F = LEDR0 (observed)

Did your canonical SOP and your minimized expression produce the same LEDR[0] for the rows you checked? In one sentence, say what that confirms about the two expressions.

Upload your evidence. Capture a camera screenshot of your minimized expression running on any row where your truth table predicts F=1, and attach it below. In the caption or text field, state the C B A row you chose.

> Optional bonus (no upload required). Quartus reports how many logic elements a design uses. If your lab surfaces that resource report, synthesize your canonical SOP and your minimized expression and compare their logic-element counts. Treat this as exploration only; the in-IDE resource report is not guaranteed on every lab session, so it is not part of the required evidence.

4

The same function as a product of sums

14 min

The SOP built F from its 1 rows. The product of sums (POS) builds the same F from its 0 rows instead.

Maxterm. A *maxterm* is a single OR (sum) term using all the inputs — each plain or negated — that is 0 for exactly one row. It is the mirror image of a minterm. For row 0 (C=0, B=0, A=0) the maxterm is (A | B | C): this OR is 0 only when A, B, and C are all 0, and 1 on every other row.

Canonical product of sums. AND together the maxterm of every row where F=0. Each maxterm forces the output to 0 on its own row and leaves it 1 elsewhere, so the AND of all of them is 0 on exactly the F=0 rows. Read the F=0 rows from the truth table and write the four maxterms yourself.

// Lesson 2 - Exercise 1-C: Product of Sums (POS) starter
// Same F(C,B,A). Use the truth-table rows where F=0 to write the maxterms.
module leds_mirror(SW, LEDR);
    input  [9:0] SW;
    output [9:0] LEDR;

    wire A = SW[0];
    wire B = SW[1];
    wire C = SW[2];

    // Canonical POS: one sum term for each F=0 row, ANDed together.
    assign LEDR[0] = TODO_CANONICAL_POS_EXPRESSION;

    assign LEDR[9:1] = 9'b0;
endmodule
  1. In the DE1-SoC Verilog lab, select all of the contents of leds_mirror.v and replace them with the POS starter above. Keep the module named leds_mirror.

  2. Fill TODO_CANONICAL_POS_EXPRESSION with the canonical POS for F.

  3. Click Synthesize and wait for it to finish. Fix any reported syntax error and Synthesize again.

  4. Click Upload to FPGA and wait for the live camera.

  5. Step SW[2:0] (C B A) through all eight combinations 000 ... 111, reading LEDR[0] each time. Confirm that the POS produces the same observed rows as the SOP table you recorded earlier.

  6. If a row disagrees, recheck both possibilities: the POS maxterm for that row and the physical switch positions SW[2]=C, SW[1]=B, SW[0]=A.

For this particular F, how does the canonical POS compare in size with the canonical SOP?

5

A 4-to-1 multiplexer

12 min

A multiplexer (MUX) is a selector: it routes one of several data inputs to a single output, chosen by a set of select lines. A 4-to-1 MUX has four data inputs and needs two select lines (because two bits choose among 2^2 = 4 inputs). It behaves like a rotary switch: the select value picks which data line is connected to the output.

The pin assignment for this exercise — stated explicitly so the bit order is unambiguous:

- Data inputs: SW[3]=D3, SW[2]=D2, SW[1]=D1, SW[0]=D0
- Select lines: SW[5]=S1, SW[4]=S0
- Output: LEDR[0] = Y

The select value {S1, S0} chooses the data line: 00 → D0, 01 → D1, 10 → D2, 11 → D3. The module uses a case statement inside always @(*). The default branch is not decorative — in a combinational always block, if some input combination left Y unassigned, the synthesizer would infer a latch to "remember" the old value, turning your intended pure-combinational logic into accidental memory. Assigning Y in every branch (here via default) guarantees Y always has a defined value and no latch is created.

// Lesson 2 - Exercise 1-D: 4-to-1 multiplexer starter
// Data inputs: SW[3]=D3, SW[2]=D2, SW[1]=D1, SW[0]=D0
// Select lines: SW[5]=S1, SW[4]=S0. Output: LEDR[0]=Y.
module leds_mirror(SW, LEDR);
    input  [9:0] SW;
    output [9:0] LEDR;

    wire D0 = SW[0], D1 = SW[1], D2 = SW[2], D3 = SW[3];
    wire S0 = SW[4], S1 = SW[5];

    reg Y;
    always @(*) begin
        case ({S1, S0})
            2'b00:   Y = TODO_SELECTED_INPUT_00;
            2'b01:   Y = TODO_SELECTED_INPUT_01;
            2'b10:   Y = TODO_SELECTED_INPUT_10;
            2'b11:   Y = TODO_SELECTED_INPUT_11;
            default: Y = 1'b0;   // default avoids an inferred latch
        endcase
    end

    assign LEDR[0]   = Y;
    assign LEDR[9:1] = 9'b0;
endmodule
  1. In the DE1-SoC Verilog lab, select all of the contents of leds_mirror.v and replace them with the multiplexer module above. Keep the module named leds_mirror.

  2. Click Synthesize and wait for it to finish. Fix any reported syntax error and Synthesize again.

  3. Click Upload to FPGA and wait for the live camera.

  4. Set the four data switches to the pattern SW[3:0] = 1010 — that is D3=1 (SW[3] up), D2=0 (SW[2] down), D1=1 (SW[1] up), D0=0 (SW[0] down).

  5. Now cycle the two select switches through S1:S0 = 00, 01, 10, 11 using SW[5] (S1) and SW[4] (S0). For each select value, read LEDR[0] (Y) and record it in the table below. With data 1010, the output should follow the selected data line: 0, 1, 0, 1.

  6. As a sanity check, pick one select value, change the corresponding data switch, and confirm Y tracks that one data line while the others have no effect.

Keep the data switches at SW[3:0] = 1010 (D3 D2 D1 D0 = 1 0 1 0). For each select value, set SW[5]=S1 and SW[4]=S0, note which data input is selected, and record the observed LEDR[0] (Y). With this data pattern the expected Y is 0, 1, 0, 1.

S1 = SW5 S0 = SW4 Selected input (D0..D3) Y = LEDR0 (observed)

Upload your evidence. Capture a camera screenshot of the multiplexer with data SW[3:0] = 1010 and select S1:S0 = 01 (SW[4] up, SW[5] down) — this selects D1 = 1, so LEDR[0] is lit — and attach it below.

Imagine you deleted the default: Y = 1'b0; line so that some input combination left Y unassigned. What unwanted piece of hardware would the synthesizer infer, and why does that conflict with the pure-combinational multiplexer you intended?

6

Bonus: a two-digit decimal 7-segment decoder

4 min

This exercise is optional. If you have time, it is a satisfying jump from a single LED to formatted output on the seven-segment displays.

A seven-segment display has seven bars — labelled a through g — that light in combinations to draw a digit. On the DE1-SoC each HEX display is active-low and the bit order is {g, f, e, d, c, b, a}: a segment lights when you drive its bit to 0. So the pattern for the digit 0 (segments a,b,c,d,e,f on, g off) is 7'b1000000 — six 0s for the lit segments and a 1 for the unlit g.

Seven-segment display layout. The seven bars are labelled a (top), b (upper right), c (lower right), d (bottom), e (lower left), f (upper left), and g (middle). Bits are ordered g f e d c b a, active-low, so a segment lights when its bit is driven 0; the digit 0 lights segments a through f and turns g off.

Segment naming and the active-low {g,f,e,d,c,b,a} bit order on the DE1-SoC HEX displays. A bit of 0 lights its segment; a 1 turns it off. The displayed digit 0 lights every segment except g, giving the encoding 7'b1000000.

The design below reads SW[3:0] as a value 0..15, shows the ones digit on HEX0 and the tens digit (0 or 1) on HEX1. Note one thing the default project does not give you for free: to drive HEX1 you must add output [6:0] HEX1 to the module's port list — the starter leds_mirror exposes only HEX0. The module below already declares both HEX0 and HEX1, so this is the example that makes the extra port concrete.

// Lesson 2 - Exercise 1-E (optional): two-digit decimal decoder starter
// Complete the active-low digit patterns and the tens/ones equations.
module leds_mirror(SW, HEX0, HEX1);
    input  [9:0] SW;        // SW[3:0] = value 0..15
    output [6:0] HEX0;      // ones digit
    output [6:0] HEX1;      // tens digit (0 or 1)

    wire [4:0] v = {1'b0, SW[3:0]};

    function [6:0] seg;
        input [3:0] d;
        case (d)
            4'd0: seg = 7'b1000000;
            4'd1: seg = TODO_SEGMENT_PATTERN_1;
            4'd2: seg = TODO_SEGMENT_PATTERN_2;
            4'd3: seg = TODO_SEGMENT_PATTERN_3;
            4'd4: seg = TODO_SEGMENT_PATTERN_4;
            4'd5: seg = TODO_SEGMENT_PATTERN_5;
            4'd6: seg = TODO_SEGMENT_PATTERN_6;
            4'd7: seg = TODO_SEGMENT_PATTERN_7;
            4'd8: seg = TODO_SEGMENT_PATTERN_8;
            4'd9: seg = TODO_SEGMENT_PATTERN_9;
            default: seg = 7'b1111111;
        endcase
    endfunction

    wire [3:0] tens = TODO_TENS_DIGIT;
    wire [3:0] ones = TODO_ONES_DIGIT;

    assign HEX0 = seg(ones);
    assign HEX1 = seg(tens);
endmodule
  1. Optional. In the DE1-SoC Verilog lab, replace all of the contents of leds_mirror.v with the decoder module above. Keep the module named leds_mirror. Confirm the port list reads module leds_mirror(SW, HEX0, HEX1); — the added output [6:0] HEX1 is what lets you drive the tens display.

  2. Click Synthesize, fix any reported error, and Synthesize again.

  3. Click Upload to FPGA and wait for the live camera.

  4. Set SW[3:0] to a few values and read both displays: 0000 should show 0 on HEX0 and 0 on HEX1; 1001 (decimal 9) shows 9 and 0; 1111 (decimal 15) shows 5 on HEX0 and 1 on HEX1.

What one change to the module's port list was required before this design could light HEX1, and why was it necessary?

7

What you built

4 min

In a short paragraph, summarize what you did with F(C,B,A): how the canonical SOP and POS each came from the truth table, how the K-map reduced F to your minimal expression, and what any disappearing variable told you. Mention that you confirmed the canonical and minimal forms gave identical outputs on the board.

For the multiplexer with data SW[3:0] = 1010, what sequence of Y values did you observe as S1:S0 went 00, 01, 10, 11, and why is that sequence exactly the selected data input each time?

Turn this preview into a live class session

Activate LabsLand Teach to use this lesson with your students. The first time you activate Teach, you can run one class session at no cost.