Teach Remote lab lessons

Teach lesson

Digital Logic on the DE1-SoC (3/6): Memory — flip-flops and registers

Students build D flip-flop and register circuits in Verilog, test clock and reset behavior, and distinguish combinational logic from stored state.

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

Learning Outcomes

  • Explain why sequential logic needs memory while combinational logic does not.

  • Describe a D flip-flop as sample-and-hold on the clock edge, with synchronous reset.

  • Build an 8-bit parallel-load register from D flip-flops sharing a clock and reset.

  • Use non-blocking assignment in clocked blocks and recognize active-low KEY inputs.

Student activity preview

Activity Content

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

1

Why circuits need memory

10 min

Every design so far has been combinational: the LEDs were a pure function of the switches *right now*. Change a switch and the output changes the instant the signal propagates through the gates. A combinational circuit is stateless — it has no idea what the inputs were a moment ago, and it cannot remember anything.

But most useful systems need memory. A counter has to know its current value to produce the next one. A CPU has to hold operands and results between instructions. A traffic light has to know which colour is showing now to decide what comes next. None of that is possible with gates alone, because gates only ever reflect their present inputs.

Sequential logic is logic that remembers. Its output depends not only on the current inputs but also on stored state — what happened before. The building block that stores one bit of state is the flip-flop, and the timekeeper that tells every flip-flop *when* to update is the clock.

The DE1-SoC provides a free-running 50 MHz clock called CLOCK_50: a square wave that rises and falls 50,000,000 times per second. From this lesson on, your sequential designs must list it as an input. The default leds_mirror.v template lists only switches, buttons, and LEDs and has no clock, so you will add input CLOCK_50; to the module yourself — forgetting it is the most common error in sequential designs.

Which of these circuits is sequential (its output can depend on the past), rather than combinational?

The combinational designs in Lessons 1–2 listed only switches and LEDs, but a clocked design like a flip-flop must also list input CLOCK_50;. Why does a flip-flop or register need the clock as an input, and what tends to go wrong if you leave it out? (One or two sentences.)

2

The D flip-flop: sample-and-hold on the clock edge

11 min

A D flip-flop is the simplest memory element. It has a data input D, a clock input, and an output Q. Its entire rule is:

> On every rising edge of the clock, Q captures the value of D and then holds it until the next rising edge.

Between edges, nothing the input does matters — Q is frozen. That is sample-and-hold: the flip-flop samples D at the instant the clock goes from 0 to 1, then holds that sample steady. This is exactly how a circuit gains memory: Q can stay 1 long after D has gone back to 0, because it remembers the sample.

Timing diagram of a D flip-flop: a clock square wave, a data signal D that changes at arbitrary times, and the output Q which only changes to match D at each rising clock edge and holds that value flat until the next rising edge — illustrating sample-and-hold.

Sample-and-hold on the clock edge: Q updates only at each rising edge of CLOCK_50, copying whatever D is at that instant, then holds that value flat until the next rising edge. Changes to D between edges are ignored.

Writing this in Verilog — two new ideas.

First, the clocked block. We describe edge-triggered behaviour with always @(posedge CLOCK_50): the statements inside run once at each rising edge, not continuously. A signal assigned inside such a block must be declared reg (it holds a value between edges).

Second, non-blocking assignment <=. Inside a clocked block, always use <=, not =. The <= operator means "schedule all the right-hand sides to be sampled now, then update the left-hand sides together at the edge" — which is exactly how real flip-flops behave (they all sample at once). Using = (blocking) inside a clocked block is a classic Verilog bug: the assignments take effect in sequence within the edge, so one flip-flop can see another's *new* value instead of its old one, producing hardware that does not match your intent. Rule of thumb: <= in clocked blocks, = only in combinational assign/always @(*).

Synchronous reset. We also want a way to force Q back to a known value. A synchronous reset is checked *inside* the clocked block, so it only takes effect on a clock edge:

always @(posedge CLOCK_50) begin
    if (!KEY[0]) Q <= 1'b0;   // synchronous reset, evaluated at the edge
    else         Q <= SW[0];  // otherwise capture D
end

Because KEY is active-low, the button reads 0 when pressed, so !KEY[0] is true exactly while the reset button is held down.

Suppose you wrote Q = SW[0]; with a blocking = (instead of <=) inside always @(posedge CLOCK_50). Which operator should you have used, and what can go wrong when several flip-flops in one clocked block update together with =?

Suppose D pulses to 1 only briefly *between* two rising clock edges and is back to 0 before the next edge. Does Q ever become 1? Explain your answer using the sample-and-hold rule.

3

Exercise 2-A — D flip-flop (reset in action)

12 min

Build a single D flip-flop on the real board. The wiring is SW[0] = D, KEY[0] = reset (active-low), LEDR[0] = Q, and LEDR[1] = ~Q.

Read this before you run it — important. At 50 MHz, Q follows D 50,000,000 times per second, far faster than your eye or the camera can ever see. So even though this circuit *does* store D, you cannot *watch* it store: flip SW[0] and LEDR[0] appears to follow instantly. That is why this exercise really demonstrates reset, not storage. The behaviour you will actually observe:

- With SW[0] up (D=1), LEDR[0] is on (and LEDR[1], showing ~Q, is off).
- Hold KEY[0] (pressed reads 0, so reset is active): LEDR[0] goes off even though SW[0] is still up. The reset wins over the data.
- Release KEY[0]: LEDR[0] follows SW[0] again.

You will *see* memory in the next exercise (2-B). For now, focus on the reset overriding the data input.

// Lesson 3 - Exercise 2-A: D flip-flop
// SW[0]=D, KEY[0]=reset (active-low), LEDR[0]=Q, LEDR[1]=~Q.
// Note: at 50 MHz, Q follows D faster than the eye/camera can see, so the
// clearly visible behavior here is RESET. Storage is demonstrated in 2-B.
module leds_mirror(CLOCK_50, KEY, SW, LEDR);
    input        CLOCK_50;
    input  [3:0] KEY;       // KEY[0] = reset_n (active-low)
    input  [9:0] SW;        // SW[0]  = D
    output [9:0] LEDR;

    reg Q;
    always @(posedge CLOCK_50) begin
        if (!KEY[0]) Q <= 1'b0;    // synchronous reset: takes effect on the clock edge
        else         Q <= SW[0];   // capture D on every rising clock edge
    end

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

  2. Select all of the contents of leds_mirror.v and replace them with the D flip-flop module above. Keep the module named leds_mirror. Notice it now declares input CLOCK_50; — that line is required for any clocked design.

  3. 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, the most common cause is a missing CLOCK_50 in the port list or a missing semicolon; fix it and Synthesize again. Continue only when the final status says the build succeeded with 0 errors.

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

  5. Set SW[0] up (D=1). Confirm LEDR[0] (Q) is on and LEDR[1] (~Q) is off.

  6. Demonstrate reset: with SW[0] still up, press and hold KEY[0]. Watch LEDR[0] turn off while the switch is unchanged — reset overrides the data. Release KEY[0] and confirm LEDR[0] comes back on.

Set SW[0] and KEY[0] as in each row and record what LEDR[0] (Q) and LEDR[1] (~Q) show on the camera (1 = lit, 0 = off). Remember KEY is active-low: pressed = 0, released = 1.

SW[0] (D) KEY[0] (0=pressed, 1=released) LEDR[0] = Q LEDR[1] = ~Q

What is the difference between this flip-flop and a plain wire from SW[0] to LEDR[0]? Answer in terms of memory.

This design uses a synchronous reset. Point to the line of code that performs the reset, and explain what "synchronous" means about *when* the reset takes effect. (You will meet asynchronous reset in Lesson 5.)

Why does LEDR[1] always show the opposite of LEDR[0]?

Exercise 2-A really does store D, yet on the camera you could only see the reset working, never the storing. Using the 50 MHz clock rate, explain why the storing is invisible while the reset is clearly visible.

4

Exercise 2-B — 8-bit parallel-load register (memory you can see)

12 min

A register is just N D flip-flops side by side, all sharing the same clock and the same reset. An 8-bit register stores 8 bits. A parallel load copies all 8 input bits into the register at once on a clock edge — every flip-flop captures its own data bit simultaneously.

The trick that makes memory visible here is the load control. Instead of capturing the data on *every* edge (as the bare flip-flop did), this register only captures when you tell it to. So you can change the switches all you like and the stored value stays put — until you load. The wiring is SW[7:0] = data, KEY[0] = reset (active-low), KEY[1] = load (active-low).

Because KEY is active-low, you press and release KEY[1] to load: pressing makes KEY[1] read 0, and the code tests !KEY[1] to detect that. Note KEY[1] is *sampled on the clock*, not a hardware-debounced clock signal — the design simply checks the button's level at each rising edge, which is fine for this exercise.

// Lesson 3 - Exercise 2-B: 8-bit parallel-load register
// SW[7:0]=data, KEY[0]=reset (active-low), KEY[1]=load (active-low). LEDR[7:0]=stored value.
// Because KEY is active-low, "press and release KEY[1]" loads the switches; the code tests !KEY[1].
module leds_mirror(CLOCK_50, KEY, SW, LEDR);
    input        CLOCK_50;
    input  [3:0] KEY;       // KEY[0]=reset_n, KEY[1]=load_n
    input  [9:0] SW;        // SW[7:0] = data to load
    output [9:0] LEDR;

    reg [7:0] Q;
    always @(posedge CLOCK_50) begin
        if (!KEY[0])      Q <= 8'h00;     // reset overrides load
        else if (!KEY[1]) Q <= SW[7:0];   // parallel load while KEY[1] is held
        // else: hold (Q keeps its value even when the switches change)
    end

    assign LEDR[7:0] = Q;
    assign LEDR[9:8] = 2'b0;
endmodule
  1. In leds_mirror.v, replace all contents with the 8-bit register module above. Keep the module named leds_mirror and keep input CLOCK_50;.

  2. Click Synthesize, wait for success, then click Upload to FPGA.

  3. Set the switches to SW[7:0] = 10101010 (SW[7] is the most significant bit). The LEDs will not yet show this — nothing has been loaded.

  4. Load: press and release KEY[1]. LEDR[7:0] now shows 10101010. The register has captured the switches.

  5. See the memory: now change the switches to SW[7:0] = 01010101 without touching KEY[1]. Watch the LEDs — they do not change. The register is holding its stored value even though the inputs are different. This is memory.

  6. Update: press and release KEY[1] again. Now LEDR[7:0] updates to 01010101.

  7. Reset: press and release KEY[0]. All eight LEDs go off (Q = 00000000), regardless of the switches.

Carry out the load sequence and record the 8 LEDs (LEDR[7:0]) at each stage. The key observation is the row 'switches changed, no load': the stored value must stay the same as the row before it.

Stage SW[7:0] set to Action on KEY LEDR[7:0] observed

Upload your evidence. Capture a camera screenshot that proves the register is *holding*: LEDR[7:0] reads 10101010 (the previously loaded value) while the on-screen switches SW[7:0] are set to a different pattern such as 01010101 and you have not pressed KEY[1]. Show both the held LEDs and the different switch positions, and attach it below.

In step 5 you changed the switches but the LEDs did not move. Explain why, using the words "store" and "load".

If you held both reset (KEY[0]) and load (KEY[1]) at the same moment of a clock edge, what value would the register take, and why? Point to the line of code that decides this.

What is the practical purpose of a parallel-load register inside a CPU? Give one concrete example of what such a register might hold.

5

What you built, and a note on reset styles

8 min

You built the two foundations of all memory in digital systems: the D flip-flop (one bit, sample-and-hold on the clock edge) and the register (many flip-flops loaded in parallel, holding their value until told otherwise). You used always @(posedge CLOCK_50) with non-blocking <=, and you saw an active-low KEY act as a control input.

A note carried forward — two styles of reset. Both designs in this lesson used a synchronous reset: the reset was checked *inside* the clocked block, so it only took effect on a rising clock edge. That is clean and predictable, and it is what you will keep using for the simple storage elements in this lesson and the next. However, the finite-state-machine lessons (5 and 6) will switch to an active-low asynchronous reset (written as always @(posedge CLOCK_50 or negedge KEY[0])), which forces the circuit to a safe state immediately, without waiting for a clock edge — useful for getting a stuck state machine back under control on the board. The detailed difference, and why FSMs prefer the asynchronous style, will be explained there. For now, just know the two styles exist and that this lesson deliberately used the synchronous one.

Which sequence matches what you did for each exercise in this lesson?

In three or four sentences: explain how Exercise 2-B *proved* that the register has memory (cite your data), why Exercise 2-A could only demonstrate reset and not storage, and name one place where active-low KEY polarity mattered in your steps.