7. Stopwatch
This lab implements a stopwatch which displays the elapsed time since we activated a switch. We’ll split the task of designing our stopwatch into three steps. First, in Section 7.1 we’ll design a modulo-k counter which produces a tick every \(k\) cycles of an input clock signal. Second, in Section 7.2 we’ll implement our own clock signals which have frequencies of interest to us. Third, in Section 7.3 we’ll use our custom clock signals and count the number of elapsed time units which the final stopwatch displays.
7.1. Counters
Our stopwatch will show typical time units since the last reset, i.e., tenths of a second, seconds, tens of seconds, minutes and tens of minutes. The clock signals available in the MAX 10 FPGA have much higher frequencies. This means that we require a way to generate clock signals with (much) lower frequencies from the provided ones.
Fig. 7.1.1 illustrates the challenge. Here, clk0 has the highest frequency and we are interested in deriving new clock signals from clk0. For example, we could be interested in generating the signal clk3 which has a four times lower frequency than clk0. Putting some numbers behind this, assume that clk0 has a frequency of 100MHz. This means that we have \(100 \cdot 10^6\) cycles per second as input. Now, we’d like to generate the 25MHz signal clk3 which therefore has \(25 \cdot 10^6\) cycles per second. How can we design a circuit which takes clk0 as input and outputs clk3?
The conceptual answer to this is a rather simple one: We simply count the number of elapsed cycles of the higher-frequency clock and generate our new clock signal based on that counter.
Fig. 7.1.2 illustrates the counter-based generation of the clock signal clk3 from signal clk0. The used counter ctr3 is an instantiation of a modulo-k counter where \(k=2\) for ctr3. Our targeted modulo-k counter counts from 0 to \(k-1\). When the counter has the value \(k-1\) in a clock cycle, it will have the value 0 in the next clock cycle.
As shown in Fig. 7.1.2 ctr3 has the initial value 0 during the first clock cycle of clk0 with id 0. Then, in the second clock cycle of clk0 with id 1, the value of ctr3 is 1. This means that ctr3 now has the value \(k-1\). Thus, in the next clock cycle of clk0 which has id 2, the value of ctr3 is 0. Similarly, ctr3 has the value zero in clk0’s clock cycles with ids 4, 6, 8 and 10.
Another modulo-k counter with \(k=4\) is illustrated in Fig. 7.1.3. Here, ctr4 counts from 0 to 3. Respectively, ctr4’s value is 0 in clk0’s clock cycles with ids 0, 4 and 8.
1/**
2 * Counter which has the number of elapsed clock cycles modulo k as output.
3 *
4 * @param i_clk input clock.
5 * @param i_reset reset signal which resets the counter's value to zero.
6 * @param i_k value k which is used for the modulo computation.
7 * @param o_count set to number of elapsed clock cycles modulo k.
8 **/
9module counter_mod_k_count #(parameter N=8) ( input logic i_clk,
10 input logic i_reset,
11 input logic [N-1:0] i_k,
12 output logic [N-1:0] o_count );
13 // current value of the counter
14 logic [N-1:0] m_count;
15
16 // on positive edge of input clock or reset
17 // either reset counter or assign next value
18 always_ff @(posedge i_clk, posedge i_reset)
19 begin
20 if( i_reset == 1'b1 )
21 begin
22 m_count <= {N{1'b0}};
23 end
24 else
25 begin
26 if( m_count == i_k-1 )
27 begin
28 m_count <= {N{1'b0}};
29 end
30 else
31 begin
32 m_count <= m_count + 1;
33 end
34 end
35 end
36
37 // assign output bus
38 assign o_count = m_count;
39endmodule
An implementation of a SystemVerilog module which implements a modulo-k counter is given Listing 7.1.1.
We see that the module modifies the counter’s value m_count
only on a rising edge of the input clock signal i_clk
or the reset signal i_reset
(line 18 - 35).
If the reset signal is high, the counter’s value m_count
is set to zero (lines 20 - 23).
Additionally, if the reset signal is low, m_count
is also set to zero if the counters value is \(k-1\) (lines 26-29).
Lastly, if neither of the previous cases occurred, the counter is simply incremented (lines 30-33).
The output of the counter is its value (line 38).
In this part of the lab, we’ll implement and test a slightly different module.
Instead of outputting the value of the counter, we output a 1-bit rollover signal.
We set this rollover signal to high in the clock cycle of the input clock after a rollover ocurred, i.e., when the counter’s value changed from \(k-1\) to \(0\).
In all other cases the rollover signal is low.
The illustrated signals of ctr3 in Fig. 7.1.2 and ctr4 in Fig. 7.1.3 show the rollover.
A template of the targeted module counter_mod_k_ro
is given in Listing 7.1.2.
To simplify your initial efforts, a code frame
for the stopwatch is provided as starting point.
1/**
2 * Counter which has the rollover signal as output.
3 * The signal o_roll_over is high in every clock cycle after a rollover occurred and low in all other cycles.
4 *
5 * Assume k=3, then:
6 * cycle | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ...
7 * mod k | 0 | 1 | 2 | 0 | 1 | 2 | 0 | 1 | 2 | 0 | 1 | ...
8 * rollover: | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | ...
9 *
10 *
11 * @param i_clk input clock.
12 * @param i_reset reset signal which resets the counter's value to zero.
13 * @param i_k value k which is used for the modulo computation.
14 * @param o_roll_over set to rollover signal which is high in every clock cycle after a rollover occurred; low else.
15 **/
16module counter_mod_k_ro #(parameter N=8) ( input logic i_clk,
17 input logic i_reset,
18 input logic [N-1:0] i_k,
19 output logic o_roll_over );
20 // value of the counter
21 logic [N-1:0] m_count;
22
23 // TODO: finish implementation of modulo-k counter which outputs a rollover signal
24endmodule
Tasks
Finish the implementation of the module
counter_mod_k_ro
and the testbenchcounter_mod_k_ro_tb
in the stopwatch code frame. Use \(k=4\) in the testbench and check the output of the counter for at least ten clock cycles.Generate a waveform plot illustrating the behavior of
counter_mod_k_ro
with \(k=4\) in the first ten clock cycles of the input clocki_clk
. The plot shall show all inputs, i.e.,i_clk
,i_reset
,i_k
, the value of the counter, i.e.,m_count
, and the outputo_roll_over
.
7.2. Clock Signals
In this task we use our modulo-k counter with rollover output to derive custom clock signals. For this, we’ll write a SystemVerilog module which takes a rollover signal as an input and outputs a clock signal. Specifically, on each rising edge of the rollover signal our clock changes its state.
We see in Fig. 7.1.2 that we may obtain the clock signal clk3 from ct3’s rollover output. Here, the state of clk3 changes for the first time from high to low when clk0 transitions from cycle 1 to cycle 2. This coincides with the first shown rising edge of ctr3’s rollover signal. Since a full clock cycle implies two state changes, the resulting clock signal of ctr3 has a four times lower frequency when compared to clk0. The clock clk4 in Fig. 7.1.3 is derived analogously from the rollover output signal of ctr4.
1/**
2 * Outputs a clock signal based on the given rollover signal.
3 * The clock signal changes on every positive edge of the rollover signal.
4 * On a positive edge of the reset signal, the clock is reset to high.
5 *
6 * @param i_roll_over rollover signal.
7 * @param i_reset reset signal.
8 * @param o_clk set to generated clock signal.
9 **/
10module clock( input logic i_roll_over,
11 input logic i_reset,
12 output logic o_clk );
13 logic m_clk;
14
15 // TODO: finish implementation
16
17 assign o_clk = m_clk;
18endmodule
A template for the clock is given in Listing 7.2.1.
The module shall only change its state on a rising edge of input i_roll_over
or i_reset
.
If i_reset
is high, the clock should set its state m_clk
to high.
Otherwise it should simply change its state, i.e., if m_clk
is high it should change to low and if m_clk
is low it should change to high.
Once our custom clock is implemented and tested, we can combine it with the modulo-k counter given through the module counter_mod_k_ro
.
Combined both modules allow us to lower the frequency of the clock signal MAX10_CLK1_50
which can be accessed in the MAX 10 FPGA.
In the last part of this task, we’ll generate a 10Hz clock signal, an 1Hz signal and a 0.1Hz one from MAX10_CLK1_50
.
We’ll display the 10Hz clock signal using LEDR5
, the 1Hz signal using LEDR6
, and the 0.1Hz one using LEDR7
.
The signal of the first switch SW0
will drive the reset signal of all modules (reset on low).
An exemplary video of the targeted functionality is shown at the top of this task.
Hint
Base all your custom clocks on the MAX10_CLK1_50 clock signal. In other words: Do not chain your clocks!
Tasks
Finish the implementation of the module
clock
and the testbenchclock_tb
in the stopwatch code frame.Generate a waveform plot illustrating the behavior of the module
clock
. Manually provide the rollover signal when generating the plot and show three full cycles of the clock. The plot shall show all inputs, i.e.,i_roll_over
,i_reset
and the outputo_clk
.Look up the frequency of the clock signal
MAX10_CLK1_50
in the DE10-Lite User Manual.Derive appropriate values for \(k\) such that you can use
MAX10_CLK1_50
as clock input for your modulo-k countercounter_mod_k_ro
followed by the clock moduleclock
to generate clock signals with 10Hz, 1Hz and 0.1Hz.Finish the top-level module
clocks_de10_lite
of the stopwatch code frame. Use the smallest possible values for the parameterN
in the instantiations of the modulecounter_mod_k_ro
.Program the FPGA of a DE10-Lite board. Explain and illustrate your programmed FPGA to the teaching personnel. In your submission include a scan of this week’s tailored coversheet which represents the deliverable of this subtask.
7.3. Elapsed Time
This final part of the lab combines the pieces of Section 7.1 and Section 7.2 into an actual stopwatch.
For this we write the top-level module stopwatch_de10_lite
.
The short video above shows the deployed stopwatch.
The stopwatch uses switch SW0
as input.
If SW0
is high the watch runs and measures the elapsed time, if it is low the stopwatch holds and resets to zero.
The outputs of the stopwatch are as follows:
LEDR5
displays the 10Hz clock signal which drives a tenth of a second counterHEX0
displays the output of the tenth of a second counterLEDR6
displays the 1Hz clock signal which drives a seconds counterHEX1
displays the output of the seconds counter. Further,HEX1
has an always-on decimal dot. Check the DE10-Lite User Manual on how to activate the dot.LEDR7
displays the 0.1Hz clock signal which drives a tens of seconds counterHEX2
displays the output of the tens of seconds counterHEX3
displays a blinking dash which separates the displays showing seconds from the ones showing minutesLEDR8
displays the 1/60Hz clock signal which drives a minutes counterHEX4
displays the output of the minutes counterLEDR9
displays the 1/600Hz clock signal which drives a tens of minutes counterHEX5
displays the output of the tens of minutes counter
In general, one of the time-showing HEX displays can be programmed by combining four modules:
A modulo-k counter which generates a suitable rollover signal from
MAX10_CLK1_50
. In Section 7.2 we already derived proper values for \(k\) for the seconds-related parts. Respective values of \(k\) for minutes and tens of minutes still have to be derived.A clock which takes the rollover output of the modulo-k counter as input and generates a respective clock signal.
Another modulo-k counter which has the generated clock signal as input and outputs the counter’s value. Hint: For this counter \(k\) is either 6 or 10.
A decoder which wires the output bits of the second counter to the respective HEX display.
Tasks
Implement the top-level module
stopwatch_de10_lite
in the filestopwatch_de10_lite.sv
. As in Section 7.2 use minimal values for the parameterN
in all instantiations of the counterscounter_mod_k_count
andcounter_mod_k_ro
.Program the FPGA of a DE10-Lite board. Explain and illustrate your programmed FPGA to the teaching personnel. In your submission include a scan of this week’s tailored coversheet which represents the deliverable of this subtask.