Teach lesson
Digital Logic on the DE1-SoC with VHDL (2/6): Truth tables, SOP, K-maps, and optional extensions
Students implement a Boolean function in VHDL as a canonical SOP, minimize it with a K-map, and try optional POS/MUX extensions on the DE1-SoC if time permits.
New to LabsLand? Create your teacher account
Learning Outcomes
Derive a canonical sum-of-products (SOP) form 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.
Recognize POS, multiplexer, and two-digit display work as optional extensions if time permits.
Student activity preview
Activity Content
Preview only. In a class session, students can fill in responses and submit their work to the teacher.
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?
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 (not C and not B and 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 — which is exactly F. The four F=1 rows (1, 3, 4, 6) give:
- Row 1 C=0,B=0,A=1 → (not C and not B and A)
- Row 3 C=0,B=1,A=1 → (not C and B and A)
- Row 4 C=1,B=0,A=0 → (C and not B and not A)
- Row 6 C=1,B=1,A=0 → (C and B and not A)
So the canonical SOP is F = (not C and not B and A) or (not C and B and A) or (C and not B and not A) or (C and B and not A). That is correct but wasteful: four 3-input AND terms feeding a 4-input OR.
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.
Group the four F=1 minterms into two pairs:
- Group minterms 1 and 3. Row 1 is C=0, B=0, A=1; row 3 is C=0, B=1, A=1. Note which variables stay the same and which one changes. The changing variable is eliminated.
- Group minterms 4 and 6. Row 4 is C=1, B=0, A=0; row 6 is C=1, B=1, A=0. Make the same comparison. This group wraps around the edge of the map because columns 00 and 10 are also adjacent in Gray-code order.
Write one reduced term for each group and combine the two terms with or. If you recognize a known gate, you may write the equivalent form; otherwise keep the two-product-term expression. Also check which input disappears from both groups and what that says about how F depends on its inputs.
The K-map for F. Use the groups as a guide: for each group, identify the variables that keep the same value and eliminate the variable that changes.
Apply the same minterm method to a *different* function. A function G(C,B,A) is 1 on exactly rows 0, 2, 5, 7 (and 0 on rows 1, 3, 4, 6). Write the full canonical sum-of-products expression for G — the OR of one minterm per G=1 row. Use the VHDL operators not, and, and or.
In your minimal expression for F, which variable disappears from both groups? Explain what its absence tells you about how F depends on its three inputs. Then find — on your own — two truth-table rows that differ only in that variable, and give their row numbers and F values to confirm it.
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 switch to your minimized expression and confirm you get the identical outputs.
The starter entity below deliberately leaves the expression for LEDR(0) as TODOs. For the first pass, complete the active canonical SOP assignment from the four minterms. For the second pass, replace it with the minimized expression from the K-map, then synthesize again and compare.
-- Student starter: fill the TODO lines before synthesizing.
-- Lesson 2 - Exercise 1-B: Sum of Products.
-- F(C,B,A): minterms 1,3,4,6. SW(0)=A, SW(1)=B, SW(2)=C. Output on LEDR(0).
library ieee;
use ieee.std_logic_1164.all;
entity blink is
port (
SW : in std_logic_vector(9 downto 0);
LEDR : out std_logic_vector(9 downto 0)
);
end entity blink;
architecture rtl of blink is
signal A, B, C : std_logic;
begin
A <= SW(0);
B <= SW(1);
C <= SW(2);
-- First pass: complete the canonical SOP expression from the four minterms.
LEDR(0) <= TODO_CANONICAL_SOP;
-- Second pass: replace the line above with the minimized expression from the K-map.
-- LEDR(0) <= TODO_MINIMIZED_EXPRESSION;
LEDR(9 downto 1) <= (others => '0');
end architecture rtl;Open the DE1-SoC VHDL lab. The editor shows a file
blink.vhd.Select all of the contents of
blink.vhdand replace them with the SOP starter above. Fill the TODO assignment for the current pass, then keep the entity namedblink.For the first pass, complete the active
TODO_CANONICAL_SOPline using the four minterms. Exactly one line that drivesLEDR(0)must be active.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.
Click Upload to FPGA and wait for the live board session and camera.
Step
SW(2 downto 0)(C B A) through all eight combinations000, 001, 010, 011, 100, 101, 110, 111, readingLEDR(0)on the camera each time. Record each result in the table below. (LeaveSW(1)=Btoggling as part of the count even though you expect it not to matter.)For the second pass, edit the file again: replace the canonical assignment with the minimized expression you obtained from the K-map. Synthesize and Upload again, then spot-check a few rows (for example
001,100,111) and confirmLEDR(0)is identical to the canonical version.
Set SW(2 downto 0) = C B A for each row and record the LEDR(0) value from the canonical SOP run (1 = lit, 0 = off). After you replace it with your minimized expression, spot-check three rows and answer the follow-up question below.
| C = SW2 | B = SW(1) | A = SW(0) | Canonical SOP LEDR0 (observed) |
|---|---|---|---|
Which three rows did you spot-check after using your minimized expression, and did LEDR(0) match the canonical SOP in all three? In one sentence, say what that confirms about the two expressions.
Upload your evidence. Capture a camera screenshot of your minimized expression running with the switches set to a row where F = 1 (for example C B A = 100: SW(2) up, SW(1) and SW(0) down, so LEDR(0) is lit), and attach it below.
> Optional bonus (no upload required). Quartus reports how many logic elements a design uses. If your lab surfaces that resource report, synthesize the four-term canonical SOP and the one-operator your minimized expression and compare their logic-element counts — the minimal form should use fewer. 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.
Optional extension: the same function as a product of sums
10 min
Optional extension if your teacher has time: 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 or B or 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 — again exactly F. The four F=0 rows are 0, 2, 5, 7:
- Row 0 C=0,B=0,A=0 → (A or B or C)
- Row 2 C=0,B=1,A=0 → (A or not B or C)
- Row 5 C=1,B=0,A=1 → (not A or B or not C)
- Row 7 C=1,B=1,A=1 → (not A or not B or not C)
In each maxterm, a variable appears *negated* when it is 1 in that F=0 row (so the term goes to 0 there). The entity below implements this canonical POS directly. It describes the very same function, so its eight outputs must match what you already recorded: rows 0..7 give 0, 1, 0, 1, 1, 0, 1, 0.
-- Student starter: fill the TODO lines before synthesizing.
-- Lesson 2 - Exercise 1-C: Product of Sums (POS).
-- Same F(C,B,A). Maxterms (F=0 rows): 0, 2, 5, 7. SW(0)=A, SW(1)=B, SW(2)=C.
library ieee;
use ieee.std_logic_1164.all;
entity blink is
port (
SW : in std_logic_vector(9 downto 0);
LEDR : out std_logic_vector(9 downto 0)
);
end entity blink;
architecture rtl of blink is
signal A, B, C : std_logic;
begin
A <= SW(0);
B <= SW(1);
C <= SW(2);
LEDR(0) <= TODO_POS_EXPRESSION; -- TODO: product of maxterm sums
LEDR(9 downto 1) <= (others => '0');
end architecture rtl;In the DE1-SoC VHDL lab, select all of the contents of
blink.vhdand replace them with the POS entity above. Keep the entity namedblink.Click Synthesize and wait for it to finish. Fix any reported syntax error and Synthesize again.
Click Upload to FPGA and wait for the live camera.
Step
SW(2 downto 0)(C B A) through all eight combinations000 … 111, readingLEDR(0)each time. Confirm the sequence of outputs is0, 1, 0, 1, 1, 0, 1, 0, matching the SOP results you recorded earlier.If any row disagrees with
0, 1, 0, 1, 1, 0, 1, 0, the mismatch is almost certainly a switch-setting slip rather than a logic error — recheck which switch isC,B, andAand re-read that row.
For this particular F, how does the canonical POS compare in size with the canonical SOP?
Optional extension: a 4-to-1 multiplexer
10 min
Optional extension if your teacher wants a selector example: a multiplexer (MUX) 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 mapping 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 entity uses VHDL with sel select, ending with when others. That final branch is not decorative: in combinational VHDL, every output must receive a value for every possible input. If some path left Y unassigned, the synthesizer would infer a latch to remember the old value, turning pure combinational logic into accidental memory.
-- Student starter: fill the TODO lines before synthesizing.
-- Lesson 2 - Exercise 1-D: 4-to-1 multiplexer using selected signal assignment.
-- Data: SW(3)=D3, SW(2)=D2, SW(1)=D1, SW(0)=D0. Select: SW(5)=S1, SW(4)=S0.
library ieee;
use ieee.std_logic_1164.all;
entity blink is
port (
SW : in std_logic_vector(9 downto 0);
LEDR : out std_logic_vector(9 downto 0)
);
end entity blink;
architecture rtl of blink is
signal D0, D1, D2, D3 : std_logic;
signal S0, S1, Y : std_logic;
signal sel : std_logic_vector(1 downto 0);
begin
D0 <= SW(0); D1 <= SW(1); D2 <= SW(2); D3 <= SW(3);
S0 <= SW(4); S1 <= SW(5);
sel <= S1 & S0;
with sel select
Y <= TODO_FOR_00 when "00",
TODO_FOR_01 when "01",
TODO_FOR_10 when "10",
TODO_FOR_11 when "11",
'0' when others;
LEDR(0) <= Y;
LEDR(9 downto 1) <= (others => '0');
end architecture rtl;In the DE1-SoC VHDL lab, select all of the contents of
blink.vhdand replace them with the multiplexer entity above. Keep the entity namedblink.Click Synthesize and wait for it to finish. Fix any reported syntax error and Synthesize again.
Click Upload to FPGA and wait for the live camera.
Set the four data switches to the pattern
SW(3 downto 0) = 1010— that isD3=1(SW(3)up),D2=0(SW(2)down),D1=1(SW(1)up),D0=0(SW(0)down).Before looking at the camera, predict which data input each select value will choose. Then cycle the two select switches through
S1:S0 = 00, 01, 10, 11usingSW(5)(S1) andSW(4)(S0). For each select value, readLEDR(0)(Y) and record your prediction and observation in the table below.As a sanity check, pick one select value, change the corresponding data switch, and confirm
Ytracks that one data line while the others have no effect.
Keep the data switches at SW(3 downto 0) = 1010 (D3 D2 D1 D0 = 1 0 1 0). For each select value, set SW(5)=S1 and SW(4)=S0, predict which data input is selected and what Y will be, then record the observed LEDR(0).
| S1 = SW5 | S0 = SW4 | Selected input (D0..D3) | Y predicted before camera | Y = LEDR0 (observed) |
|---|---|---|---|---|
Upload your evidence. Capture one camera screenshot from your multiplexer test. In the table above, mark the row that matches the screenshot so your teacher can compare the predicted and observed Y.
Imagine you removed the when others => '0' branch or otherwise left some select value without a value for Y. What unwanted piece of hardware could the synthesizer infer, and why would that conflict with the pure-combinational multiplexer you intended?
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 "1000000" — six 0s for the lit segments and a 1 for the unlit g.
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 "1000000".
The design below reads SW(3 downto 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 HEX1 : out std_logic_vector(6 downto 0) to the entity's port list — the starter blink exposes only HEX0. The entity below already declares both HEX0 and HEX1, so this is the example that makes the extra port concrete.
-- Student starter: fill the TODO lines before synthesizing.
-- Lesson 2 - Exercise 1-E (optional): two-digit decimal decoder.
-- SW(3 downto 0) selects 0..15. HEX0 shows ones; HEX1 shows tens.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity blink is
port (
SW : in std_logic_vector(9 downto 0);
HEX0 : out std_logic_vector(6 downto 0);
HEX1 : out std_logic_vector(6 downto 0)
);
end entity blink;
architecture rtl of blink is
signal v : unsigned(4 downto 0);
signal tens : unsigned(3 downto 0);
signal ones : unsigned(3 downto 0);
function seg(d : unsigned(3 downto 0)) return std_logic_vector is
begin
case d is
when "0000" => return "1000000"; -- 0
when "0001" => return TODO_SEG_1;
when "0010" => return TODO_SEG_2;
when "0011" => return TODO_SEG_3;
when others => return "1111111";
end case;
end function;
begin
v <= resize(unsigned(SW(3 downto 0)), 5);
tens <= TODO_TENS;
ones <= TODO_ONES;
HEX0 <= seg(ones);
HEX1 <= seg(tens);
end architecture rtl;Optional. In the DE1-SoC VHDL lab, replace all of the contents of
blink.vhdwith the decoder entity above. Keep the entity namedblink. Confirm that the entity port list includesHEX1 : out std_logic_vector(6 downto 0);— that added port is what lets you drive the tens display.Click Synthesize, fix any reported error, and Synthesize again.
Click Upload to FPGA and wait for the live camera.
Set
SW(3 downto 0)to a few values and read both displays:0000should show0onHEX0and0onHEX1;1001(decimal 9) shows9and0;1111(decimal 15) shows5onHEX0and1onHEX1.
What one change to the entity's port list was required before this design could light HEX1, and why was it necessary?
What you built
4 min
In a short paragraph, summarize what you did with F(C,B,A): how the canonical SOP came from the truth table, how the K-map reduced F to your minimized expression, and what the disappearance of B told you. Mention that you confirmed the canonical and minimal forms gave identical outputs on the board. If you also tried the optional POS, MUX, or 7-segment extension, add one extra sentence about it.
(Optional MUX extension) For the multiplexer with data SW(3 downto 0) = 1010, what sequence of Y values did you observe as S1:S0 went 00, 01, 10, 11, and how did your prediction compare with the camera?