Project 1 A Basic Digital System

Verilog-based digital system to monitor and filter reaction times



This final project present a more complex digital system that reuses many of the circuits designed in earlier projects. This project provides an opportunity to design a more complex digital system that will use all the knowledge and skill you’ve accumulated so far. As you proceed through the design, focus on creating a good design partition with a well-constructed hierarchy, and use structural Verilog wherever possible.

Before you begin, you should:

  • Be a reasonably competent digital design engineer;
  • Know the Vivado IDE well, and know how to use all the relevant tools;
  • Know how to design and implement state machines and sequential circuits.

After you’re done, you should:

  • Be able to implement complex digital circuits;
  • Be looking forward to continuing your education.

Digital circuits built from two or more combinational and/or sequential circuit components are commonly referred to as digital systems. Simple digital systems might use a state machine, a few registers, and some combinational logic blocks, while more involved systems may include complex custom IP blocks, processor cores and software components. A simple digital system can be designed and implemented by a single engineer working alone, but a more complex system may require a team of several engineers. In any case, digital system designs are generally more complex that component designs. As designs become more complex, the use of good, methodical design practices becomes essential.

In a typical digital system, the top-level design is structural. The top-level Verilog module serves as a block diagram, or netlist, that instantiates and interconnects the needed sub-modules. Some components may themselves be constructed from subcomponents, resulting in more than two levels of hierarchy. Sub-modules are typically designed, simulated, and validated independently, so their functionality is known and they can be included in the top-level module with little risk. High-level designs that use known-good, proven components are far easier to validate and debug than designs built with unproven components.

Digital systems are multilevel, hierarchical designs, where higher-level components are built using lower-level components. Sometimes, pre-existing components can be used, like the four-digital decimal counter you designed in a previous project. Sometimes, you will need to create new components, and any new components should be designed and simulated independently. And further, whenever possible, any new components should be designed so that they too can be reused in other designs. Choosing which sub-blocks to use, and which functions are implemented in which blocks, is known as design partitioning.

Choosing a design partition is one of the most important decisions you will make. A good partition uses components (or modules) that encapsulate functions that can be independently conceived, designed, and verified, and whose interfaces to other modules are minimal and easy to define; a poor partition uses modules that implement partial functions, with interfaces that expose signals that could have been contained inside the module. Whenever possible, a good partition uses components that can be reused in other designs; a poor partition uses components that are specific to the current design, and that fail to recognize and use more generally useful interfaces. Components in a good partition can be independently and meaningfully simulated to verify their function; components in a poor partition cannot be. In the end, practice, experience, and working with others will guide your choices. For now, be aware of the partitioning choices you are making, try to identify alternatives, and look for the benefits or detriments to your overall design as you proceed.


In this project, you will create a digital system that implements a “Reaction Time Measurement” (RTM) system. Along with a few new components, the RTM can reuse many of the components from previous projects, including parts of the stopwatch, a counter, clock divider, and several registers. New components include a pseudo-random number generator, an 8-location memory component called a “register file”, and, if you do the challenge, an accessory state machine for processing input signals from pushbuttons.

Like the previous stopwatch project, this project uses the seven-segment display to show the state of a timer/counter. In the stopwatch project, a BCD counter was used, and its digits could directly drive a seven-segment decoder. In this project, you must perform arithmetic operations on the counter value, as well as display the value on the seven-segment display. This presents a challenge, because BCD values do not use binary codes 1010 – 1111. This means that BCD codes can’t be added using a regular adder, and they can’t be shifted to multiply or divide by powers of 2. The figure below illustrates adding 29 and 7 using BCD, decimal, and binary representations, and shows that when BCD codes are submitted to a binary adder, the results are incorrect.

Figure 1. BCD vs. Binary Arithmetic
Figure 1. BCD vs. Binary Arithmetic

BCD data is a good format for driving displays with numerical data, and binary data is a good format for use with digital circuits. Since humans aren’t ready to consume binary or hexadecimal numbers, we must solve this dilemma: either work in BCD, and create new arithmetic circuits that can consume and produce BCD numbers; or work in binary, and convert to BCD for the purpose of displaying data to humans. Either solution would work, but the latter offers the advantage of not reworking several arithmetic circuits (e.g., shifters that preserved magnitude through BCD codes would be challenging). If you use this solution, then the timer will be a simple binary counter instead of a BCD counter as was used in the stopwatch, and the binary counter outputs will need to be converted to BCD before they can be shown on the seven-segment display.


1. Switch Debounce

When a mechanical switch opens or closes (for example, when user depresses a pushbutton), it is very typical that the mechanical apparatus used to “close” or “make” the circuit bounces for a short time. That is, contact is made initially between the switch contacts, but then due to mechanical elasticity, imperfections in manufacturing, and other factors, contact is then broken for short period, then remade, then broken again, etc., for some amount of time. This “switch bounce” phenomenon is typically very short lived, with each make-break cycle happening over a few microseconds, and with perhaps one to several such cycles per switch actuation. Smaller switches might exhibit bounce activity for 10-100us, while large switches might bounce for upwards of 1ms.

Design a circuit that can debounce a mechanical switch. Your circuit should receive the clock, data, and reset inputs, and produce a single debounced output. Your circuit should be reusable in other digital systems, but for this system, you can assume a switch bounce time of no more than 200us (so choose an appropriate clock). Note this problem is related to homework problem #1.

2. Reaction Time Monitor

Create a Reaction Time Monitor (RTM) that can indicate how quickly an user can respond to a stimulus. In operation, the RTM is initialized when a “start” button is pressed. Immediately after the start button is pressed, the 7seg display is set to show all 0’s, and then a random time later (between about 1 and 10 seconds), a “react now” LED illuminates and a millisecond timer starts. When the timer starts, the 7seg display shows the timer value as it counts up in milliseconds.

As quickly as possible after the react LED illuminates, the user must press a “react” button to stop the timer. The stopped timer will contain the number of milliseconds between the react LED being illuminated and the button being pressed, and that time will be shown on the 7seg display. Pressing the start button again will clear the timer and begin a new round.

Note: This first requirement can use either a BCD counter or a binary counter, but the next requirement will benefit from using a binary counter. You are free to use either a binary or BCD counter for requirement 1.

3. Averaging Filter

Repeat a single reaction time measurement eight times, and store the eight measured reaction time values in temporary holding registers. A “start average” button is pressed to reset the holding registers and RTM circuit. After a reset, eight individual reaction time measurements are performed using the RTM circuit (and as before, each individual reaction time is shown on the 7seg display). Each of the eight consecutive reaction time measurements must be stored in one of eight holding registers. After the eighth value is recorded, another LED should illuminate to show the user that eight values have been recorded. Then, pressing an average button should switch the 7seg display to show the average of the eight individual runs. If the “average” button is pressed prior to recording all eight reaction time measurements, you should still show the average, even though some of the entries will still be zero. Pressing the start average button again will clear all registers and allow a new session to begin.

You will probably want to use a binary counter, so that the count values can be added and shifted to find an average. This means the binary counter outputs must be converted to BCD for display. A background topic document discusses binary-to-BCD conversion.


To find an average value, all inputs must be added into an accumulator, and then the accumulator must be divided by the number of points accumulated. Note that if a “power of 2” number of points is accumulated, the necessary division can be accomplished by right-shifting the accumulator value by log base 2 of N (where N is the number of points accumulated).

Note: The averaging circuit must add the eight 8-bit register values into an 11-bit accumulator, and then shift the accumulated number right 3 bits (or, just use the upper 8 bits of the accumulator). If your circuit uses a three-bit counter to generate register addresses, and if that counter simply rolls over and keeps counting if more than eight reaction times are recorded, then the average will always use the eight most recently recorded reaction times.

But if you start a new averaging session, there may still be older reaction times stored in the registers, and so pressing the average button before eight new reaction times have been recorded will use incorrect data. This can be solved by clearing all eight registers before they are used. Clearing the eight registers can be accomplished by adding a “clear” signal input to the register file that will synchronously clear all registers at the same time, or by writing 0’s to all eight registers by cycling through all eight addresses, and writing 0’s to each register in sequence.


1. Measure switch bounce time

The pushbuttons and slide switches on your board can exhibit switch bounce under normal operating conditions. Design and implement a circuit that can measure switch bounce on any slide switch or push button on your circuit board, with 10ns resolution. You only need to measure bounce on a 0-1 transition, and you only need to measure the initial, single period of bounce (i.e, measure the first make-break-make cycle). Note that due to differences in manufacturing, some switches may not exhibit measurable bounce.