DS 2023 Project Assignment 1
Digital Clock
Professor Jonathan H. Manton
23 April 2023
According to Wikipedia, the first digital electronic watch was made in 1970, as a pro-
totype, and was available to the consumer in 1972. By the 1980s, digital watches were
ubiquitous. Back then, I remember a friend’s watch having the game Pac-Man on it.
The aim of this project, which comprises four assignments, is to design and implement
the functionality of a watch, just like digital watches of old. This includes a clock (with
the ability to set the time), an alarm, a stop-watch, a timer and possibly a game or
two.
This project follows on from the Practical Assignments 1 to 4. The skills learnt there
will be needed here.
Report Requirements
Refer to Practical Assignment 1 for report requirements.
Final Project Submission
In addition to the four project reports, you will also be required to submit your Quartus
project containing your final code, as a single Zip archive.
The Importance of Debugging
An essential part of being a good programmer is having the ability to find where the
mistakes are with your own code. Debugging is an essential skill to have. The lectures
and practical assignments have demonstrated how test benches along with $display
1
statements allow us to probe what is happening in the code. Another trick is developing
code one small step at a time and testing at each step, so if the code suddenly stops
working, you know it has to do with the last small step. If you are faced with a larger
chunk of code that is not working, then break it into smaller parts and test each part.
1 Time Display
1.1 Seconds Display
Our first objective is to implement a counter that counts from 0 to 59 once a second,
displaying the result to HEX[1] and HEX[0]. Forethought suggests it would be useful to
have generic up/down counters that can be used for other purposes (such as a stop-watch
with 1/100th of a second accuracy and a countdown timer).
Verilog has a parametrised module feature that allows us to create a family of modules.
This will allow us to specify what number the counter counts to. A minor challenge
though is that the number of bits needed to store the count will depend on this maximum
value.
Task 1
Write down a formula for the number of bits needed to store any number between
0 and N inclusive. You may make use of the function “ceil” which rounds a
number up to the nearest integer as well as any other standard functions.
Hint: With n bits, we can store any number between 0 and 2n − 1 inclusive.
Run the following code in an online Verilog simulator.
1 module test;
2 reg clk = 0;
3 initial forever #1 clk = !clk;
4
5 // Change the value of N
6 localparam N = 12;
7 localparam BW = $clog2(N+1);
8 wire [BW -1:0] cnt;
9 Counter #(. MAX(N), .WIDTH(BW))
10 cntr(.clk(clk), .enable(1’b1), .cnt(cnt ));
11
12 initial begin
13 #0 $display(cnt);
14 repeat (2*N+1)
15 #2 $display(cnt);
16 $stop;
17 end
2
18 endmodule
19
20 module Counter
21 #( parameter MAX=1, WIDTH =1) (
22 input clk ,
23 input enable ,
24 output reg [WIDTH -1:0] cnt
25 );
26
27 initial cnt = 0;
28
29 reg [WIDTH -1:0] next_cnt;
30
31 always @(posedge clk)
32 if (enable) cnt <= next_cnt;
33
34 always @(*)
35 next_cnt = (cnt == MAX) ? 1’d0 : (cnt + 1’d1);
36 endmodule
Task 2
Explain all aspects of the above code. Do not be afraid to google things like
“$clog2 verilog”.
Add an extra parameter to Counter, called UP. If UP is true (the default) then the
counter should count up; if UP is false the counter should count down (e.g., 9 8 7 6 5 4
3 2 1 0 9 8 . . . ). Make sure to test it works.
Task 3
Once tested, write down your module Counter.
To be able to count the number of seconds that have elapsed, we actually need two
counters. The first counter is needed to divide the 50 MHz clock into an enable pulse
that will enable the second counter once per second.
Since we now care about tracking time accurately, we use a ‘timescale statement in
our test bench. Complete the following code. Do not forget to include your Counter
module.
Test Bench
1 ‘timescale 1ns/1ns
2
3 module test;
4 // Generate a 50 MHz clock
5 reg clk = 0;
6 initial forever #... clk = !clk;
3
7
8 wire [5:0] out;
9 Time t(.clk(clk), .secs(out ));
10
11 initial begin
12 #0 $display(out);
13 repeat (120)
14 #500 _000_000 $display(out);
15 $stop;
16 end
17 endmodule
Top-level Module
1 module Watch(input CLOCK_50 );
2 wire [5:0] secs;
3 Time time(.clk(CLOCK_50), .secs(secs ));
4 endmodule
Time Module
1 module Time(input clk , output [5:0] secs);
2 localparam N = ...;
3 localparam BW = $clog2(N);
4 wire [BW -1:0] tick;
5 Counter #(. MAX(N-1), .WIDTH(BW))
6 divider (.clk(clk), .enable(1’b1), .cnt(tick ));
7 Counter #(. MAX(59), .WIDTH (6))
8 cs(.clk(clk), .enable(tick ==0), .cnt(secs ));
9 endmodule
Task 4
If you attempt to simulate the code as it is, you will find simulators will (normally)
time out. Therefore, apply the trick from lectures, namely, simulate a 50 Hz clock.
This necessitates changing your Time module to account for the fact the clock is
now 50 Hz and not 50 MHz. One the code works, copy the code as well as the
output of your test bench to your report. Justify why the output of the test bench
is correct.
Next we want to display the seconds on HEX[1] and HEX[0]. You will need the 7-segment
decoder module that you wrote earlier in your practical assignments. You will also need
to figure out mathematically how to take a number between 0 and 59 and find the first
digit and the second digit. Get this latter bit working first in an online Verilog simulator.
(Since you can draw a truth table for this, it can be implemented using combinational
logic.)
1 module jdoodle;
2 reg [5:0] sec;
4
3 reg [3:0] d1 , d2;
4 initial begin
5 sec = 32; // Change this number several times ,
6 // making sure each number works
7 d1 = ...; // Formula for determining first digit of sec
8 d2 = ...; // Formula for determining second digit of sec
9 $display("The first digit of ", sec , " is ", d1);
10 $display("The second digit of ", sec , " is ", d2);
11 end
12 endmodule
Hint: You can use arithmetic expressions to find the first and second digits. First read
up about how integer division (/) and the modulo operator (%) work.
Returning to your Quartus project, modify your top-level module to the following then
check it works on the board. You will need to fill in the details.
1 module Watch(input CLOCK_50 , output [6:0] HEX1 , HEX0);
2 wire [5:0] secs;
3 wire [3:0] secs_d1 = ...;
4 wire [3:0] secs_d2 = ...;
5 Time time(.clk(CLOCK_50), .secs(secs ));
6 SSeg ss0(. digit(secs_d1), .sseg(HEX1 ));
7 SSeg ss1(. digit(secs_d2), .sseg(HEX0 ));
8 endmodule
Task 5
Write down the completed modules Watch and SSeg.
1.2 Hours and Minutes Displays
Next we want to display the minutes as well as the seconds.
There is a minor problem to think about. We want the minutes to increment when the
seconds change from 59 to 0. A first idea might be to use secs == 0 but this will be true
for many cycles of the clock. Another idea might be secs == 0 && tick == 0, but will
this work?
Get the following code to work, and use a test bench to show that the minutes change
at precisely the same time as the seconds change from 59 to 0.
1 module Time(input clk , output [5:0] mins , secs);
2 localparam N = ...;
3 localparam BW = $clog2(N);
4 wire [BW -1:0] tick;
5 Counter #(. MAX(N-1), .WIDTH(BW))
6 divider (.clk(clk), .enable(1’b1), .cnt(tick ));
5
7 Counter #(. MAX(59), .WIDTH (6))
8 cs(.clk(clk), .enable(tick ==0), .cnt(secs ));
9 Counter #(. MAX(59), .WIDTH (6))
10 cm(.clk(clk), .enable (...), .cnt(mins ));
11 endmodule
Task 6
Copy the working module Time to your report, as well as the test bench and its
output that was used to verify that minutes updates at precisely the right time.
Task 7
Now add “hours” to Time. (It is a 24-hour clock; no AM or PM.) Show the
updated module, the relevant test bench and its output. Explain why this justifies
your design works.
Task 8
Modify the top-level module so hours, minutes and seconds are all displayed on
the board, using HEX6 down to HEX0 in the obvious way. Write down the top-level
module in your report.
Task 9
Explain how you can test your clock on the board without having to observe it
for 24 hours by speeding up how often the seconds display increments. Actually
carry out this test. Report the different speed-ups you used.
2 Seconds Advance
A bottom-up design approach will be taken initially. Once the individual parts are
working, we will switch to a top-down design later in the project. During this bottom-
up design phase, the top-level module will be continually changing so as to test each
small part. There are several ways of doing this, including commenting out the original
Watch module (highlight the whole module then right click with the mouse and select
Comment), or simply changing the name of the module from Watch to WatchOrig.
2.1 Individual Advance
For this part, our design will only display seconds, and it will remain on the same number
unless we press the “plus” key or the “minus” key. We will use KEY1 for “plus” and
6
KEY0 for “minus”. We will need to modify our Counter module, adding more inputs to
it, to allow us to adjust the value of the counter.
1 module Counter
2 #( parameter MAX=1, WIDTH=1, UP=1) (
3 input clk ,
4 input enable ,
5 input plus ,
6 input minus ,
7 output reg [WIDTH -1:0] cnt
8 );
9
10 // Fill in
11
12 endmodule
There is some freedom in how we define Counter to work, but we will go with the following
specifications.
• If plus is 1 then add one to cnt.
• If minus is 1 then subtract one from cnt.
• If both plus and minus are 1 then do not change cnt.
• If both plus and minus are 0, and enable is 1, then either increment or decrement
cnt in accordance with the parameter UP.
• Otherwise, do not change cnt.
Task 10
Write your Counter module in your report.
In one of your practical assignments, you wrote a module to convert the push of a button
into a single pulse; it was called a rising edge detector. Using this module as well as
your new Counter module, write a top-level module that displays the seconds (a number
between 0 and 59) and that increments or decrements this number according to whether
KEY1 or KEY0 is pressed, respectively.
Task 11
Test your code on the board, and when it works correctly, write your top-level
module in your report.
7
2.2 Fast Advance
If we need to increment the number of seconds by a large number, it can quickly become
tedious. We therefore want to be able to hold KEY1 down and have the number of
seconds increment rapidly. To achieve this, we want to convert a continuous press of a
button into a cycle of pulses. Write a module which outputs 0 if the input is 0, and
outputs the repeating sequence 1, 0, · · · , 0, 1, 0, · · · , 0, 1, 0, · · · while the input remains 1,
where we can choose the number of zeros after each 1 by using a parameter.
Task 12
Write this cyclic pulse module in your report.
Task 13
By replacing your rising edge detector with your cyclic pulse module, check on
the board that you can increment and decrement the seconds count at a rate of
precisely five digits per second. Write your code in your report.
2.3 Individual and Fast Advances
We wish to combine both the individual and fast advances in the previous two subsec-
tions. Specifically, we will develop an isolated module that translates a short push of a
button into a single pulse (. . . 0 1 0 . . . ) and a push-and-hold of the button into a cycle
of pulses (. . . 0 1 0 . . . 0 1 0 . . . ) with a specified period.
Start with a Moore state diagram which uses two counters for the state. If the button is
pressed, the first counter will count 0, 1, 2, 3 then remain at 3. As soon as the button is
released, the first counter returns to 0. This counter is used to distinguish an individual
advance (short press) from a fast advance (long press).
The second counter counts the period of the fast advance. While the button remains
pressed, and once the first counter has reached 3, the second counter will count 0, 1, 2,
3, 4, 0, 1, 2, 3, 4, . . . until the button is released, at which point both counters return to
0.
If the button is pressed for less than 3 clock cycles, we just want the output to be a
single pulse, occurring just as the button is pressed. And if the button is pressed for
more than 3 clock cycles then in addition to the single pulse occurring when the button
is first pressed, we also want pulses every time the second counter is 0.
Task 14
Draw the Moore state diagram for the above functionality.
8
Now implement your module and test it with the following test bench. (An online Verilog
simulator is fine provided you are happy to look at a long list of numbers as output.)
1 module test;
2 reg clk = 0;
3 initial forever #1 clk = !clk;
4
5 reg in;
6 wire out;
7 IFAdvance ifa(.clk(clk), .in(in), .out(out));
8
9 initial $monitor(clk , in , out);
10
11 initial begin
12 // One -clock -cycle presses
13 in = 0;
14 repeat (4) #2 in = !in;
15
16 // Two -clock -cycle presses
17 repeat (4) #4 in = !in;
18
19 // Three -clock -cycle presses
20 repeat (4) #6 in = !in;
21
22 // A 2 + 4 x 5 clock -cycle press
23 in = 1; #44 in = 0;
24
25 // A 3 + 4 x 5 clock -cycle press
26 in = 1; #46 in = 0;
27
28 #1 $stop;
29 end
30 endmodule
Once it works, modify your module so you can specify the number of clock cycles to
wait before the fast advance kicks in, and the period of the pulses produced under fast
advance mode.
1 module IFAdvance
2 #( parameter LONG=3, PERIOD =5) (
3 input clk ,
4 input in ,
5 output out);
6
7 localparam BWL = $clog2(LONG +1);
8 localparam BWP = $clog2(PERIOD );
9 reg [BWL -1:0] cl = 0, next_cl;
10 reg [BWP -1:0] cp = 0, next_cp;
11
12 always @(posedge clk)
9
13 {cl ,cp} <= {next_cl ,next_cp };
14
15
16 // Fill this in
17
18 endmodule
Test it again using the test bench, remembering to vary the parameters LONG and
PERIOD.
Task 15
After testing it works on the board, show the full code needed to display the
seconds count and be able to increment or decrement it either individually with
a short press of KEY1 or KEY0, or rapidly with a long press of KEY1 or KEY0.
Design it so that a short press is anything less than half a second, and the fast
advance will increment or decrement the seconds count at a rate of ten numbers
per second.
3 Edit and Select
Return to your Watch design from Section 1. We need a way of being able to edit first
the seconds, then the minutes, then the hours. The protocol will be that we press KEY2
for one second to enter edit mode, at which point the seconds digits will flash. If we
then press KEY2 again, the minutes digits will flash, and pressing it yet again will cause
the hours digits to flash. Pressing KEY2 at this stage will exit edit mode, and no digits
will flash.
We want the flashing to be at a rate of 2 Hz but with a duty cycle of 80% (meaning it
is ON for 80% of the time and off for 20% of the time).
Task 16
Once tested on the board, show the code needed to flash an LED at a rate of 2
Hz with a duty cycle of 80%.
Task 17
Once tested on the board, show the modifications needed to your Watch design
for the seconds to flash at a rate of 2 Hz with a duty cycle of 80%.
Hint: Do not modify your Time module in any way. Either add an enable pin to your
SSeg module, allowing you to blank the display, or insert a multiplexer (or equivalent)
between the SSeg instances in Watch and the outputs HEX1 and HEX0.
10
Task 18
Once tested on the board, show the modifications needed to your Watch design
so that SW2, SW1 and SW0 control whether hours, minutes and seconds flash,
respectively. (If SW2 is on then the hours digits should flash; if SW1 is on then
the minutes digits should flash; and the same for SW0 and the seconds digits.)
Task 19
Once tested on the board, show the code needed to implement the following
behaviour. Initially LED2, LED1 and LED0 are off. If KEY2 is pressed for one
second or longer, LED0 lights up. If KEY2 is pressed again (for however long or
short), LED0 goes off and LED1 lights. If KEY2 is pressed yet again, LED1 goes
off and LED2 goes on. And if KEY2 is pressed once more, all LEDs turn off, and
we return to the beginning, waiting for KEY2 to be pressed for one second or
longer.
Task 20
Combine the last two tasks, so that the switches are replaced by KEY2. If KEY2
is pressed for one second or longer then the seconds digits should flash. Another
press and the minutes digits will flash, and another press and the hours digits will
flash, and one more press will return to the beginning again.