Project 4: Programmable Lights
Due Tuesday October 20 2026
Purpose: The purpose of this project is to build a very simple programmable light display. We will define a machine model with a small instruction set and write a state machine to implement it.
The project (excluding the testbench file used for simulation) uses two entities - lights.vhd and lightrom.vhd. lights.vhd implements a state machine that uses lightrom as a component. lightrom implements the read-only memory that defines the program executed by lights.vhd. There are several internal signals (PC, etc.) that will be explained below in the detailed instructions.)
Tasks
The programmable light display will be a simple state machine that can execute eight possible instructions and read a sequential program that is 16 instructions long. In addition to the program counter (PC) and instruction register (IR), the state machine has one register, the light register [LR], which holds 8-bits. The eight possible instructions and their bit codes are as follows.
| Code | Meaning |
|---|---|
| 000 | Load "00000000" into the LR |
| 001 | shift the LR right by one position, fill from the left with a '0' |
| 010 | shift the LR left by one position, fill from the right with a '0' |
| 011 | add 1 to the LR |
| 100 | subtract 1 from the LR |
| 101 | invert all of the bits of the LR |
| 110 | rotate the LR right by one position (rightmost bit becomes leftmost bit) |
| 111 | rotate the LR left by one position (leftmost bit becomes rightmost bit) |
- Setup
Use Quartus to create a new directory and new project, using a name such as lights. For the sake of simplicity, I'll refer to the project as lights (which means I assume the top level entity is also called lights).
- Create a ROM to hold a program
Create a simple read-only memory [ROM] using VHDL. Make a new VHDL file and drop in a template for a complete design such as the unsigned adder. Remove the generic part of the entity statement and rename the entity as lightrom in both the entity and architecture statements. Save the file as lightrom.vhd and add it to the current project.
The lightrom should take one input signal addr that should be a 4-bit std_logic_vector. This is the address of the instruction to return. It should have one output signal data that should be a 3-bit std_logic_vector. This is the instruction to be executed.
The content of your architecture should be a single conditional signal assignment. You can use the following for testing. Later, you will need to write your own program. The 3-bit values stored at each address location are the program.
data <= "000" when addr = "0000" else -- move 0s to LR 00000000 "101" when addr = "0001" else -- bit invert LR 11111111 "101" when addr = "0010" else -- bit invert LR 00000000 "101" when addr = "0011" else -- bit invert LR 11111111 "001" when addr = "0100" else -- shift LR right 01111111 "001" when addr = "0101" else -- shift LR right 00111111 "111" when addr = "0110" else -- rotate LR left 01111110 "111" when addr = "0111" else -- rotate LR left 11111100 "111" when addr = "1000" else -- rotate LR left 11111001 "111" when addr = "1001" else -- rotate LR left 11110011 "010" when addr = "1010" else -- shift LR left 11100110 "010" when addr = "1011" else -- shift LR left 11001100 "011" when addr = "1100" else -- add 1 to LR 11001101 "100" when addr = "1101" else -- sub 1 from LR 11001100 "101" when addr = "1110" else -- bit invert LR 00110011 "011"; -- add 1 to LR 00110100 - Create the control circuit to execute the program
- Begin a VHDL state machine
Create a new VHDL file lights.vhd. Use the full design template for the Moore state machine. Modify the entity and architecture statements to use lights as the name of the circuit.
- Define the control circuit port statement
The entity port statement should have four signals. The inputs should be a clock and a reset, both type std_logic. The outputs should be a lights signal (with a name like lightsig) that is an 8 bit std_logic_vector and a signal called IRView that is a 3-bit std_logic_vector. If you wish, you can add an output signal called PCView that would be a 4-bit unsigned. The IRView and PCView signals will let you monitor the values of the IR and PC registers. The lights output is the output signal that will drive the LED display on the board.
- Define the internal signals
The lights circuit needs four internal signals. These should be declared inside the architecture, before the begin statement. The signals are: IR, a 3-bit std_logic_vector; PC, a 4 bit unsigned; LR, an 8 bit unsigned; ROMvalue, a 3-bit std_logic_vector; and state which is of type state_type.
When using the unsigned type, you will need to add a use statement to the top of your file that imports the numeric_std package to define it.
use ieee.numeric_std.all; -- defines the unisgned type
- Define the state machine
- The reset condition should set the PC, IR and LR to all zeros and the state to sFetch.
- The state machine will have only two states: sFetch and sExecute.
- The sFetch state should assign ROMvalue to the IR, add one to the PC and then set the state to sExecute.
- The sExecute state should use a case statement on the value of the IR and execute the proper action given the machine instruction table above. Each case should consist of a single signal assignment statement to LR. It also needs to set the state to sFetch.
Note that the shift and rotate operations should be implemented by concatenating the appropriate parts of the LR together using the & operator, e.g. '0' & LR(7 downto 1)
- Connect the internal signals to the output signals
Using concurrent signal assignments, outside the main state machine process, assign the IR to the IRview output and assign the LR to the lights output. When you assign the LR to the lights output you will need to cast it to a std_logic_vector, as below.
lightsig <= std_logic_vector(LR);
- Begin a VHDL state machine
- Connect the ROM to the control circuit
Right after the architecture statement, add the following template.
component lightrom <port statement> end component;Copy the port statement from the lightrom circuit and paste it into the component statement in place of <port statement>.
In the body of the light architecture, make an instance of the lightrom using the port map statement. (There are details in the lab instructions, or, if you are using Quartus, you can use the component instantiation template via VHDL->Constructs->Concurrent Statements->Instances.) Connect the addr input to the PC signal, cast as a std_logic_vector. Connect the data output to the ROMvalue signal.
- Simulate the circuit
To simulate the input for the circuit, we need a testbench file (lightsbench)
Download the following lightsbench.vhd test file and save it in your working directory. Then open a terminal, cd to your working directory and run the following commands.
ghdl -a lightsbench.vhd lights.vhd lightrom.vhd ghdl -e lightsbench ghdl -r lightsbench --vcd=lightsbench.vcd gtkwave lightsbench.vcd &
Insert the lightsig signal from lightbench, then open the tree (press the plus button next to lightsbench in the upper left window), select the lights instance (l0), and insert the PC and IR signals. After zooming the view to fit, you should get a result like the following.
- Demonstrate the circuit
If you are using the DE0 board, demonstrate the circuit there.
Set up the pins so the light signal drives 8 of the green lights and one of the buttons or switches controls reset.
You have two options for providing clock input to the circuit - you can manually press a button, with each button release indicating a new clock cycle, or you can use the on-board clock (pin G21). If you use the on-board clock, then you need to use a counter to slow it down so that you can see the operations at work (see below for details).
+ (more detail)
Before testing your circuit on the board, you will need to slow down the circuit. Add the following process and assignment to your architecture.
-- used to slow down the clock process(clk, reset) begin if reset = '0' then counter <= (others => '0'); elsif (rising_edge(clk)) then counter <= counter + 1; end if; end process; slowclock <= counter(26);Add the following to your set of internal signal declarations.
signal slowclock : std_logic; signal counter: unsigned (26 downto 0);
Then modify your main state process sensitivity list to slowclock instead of clk. (Your process was probably something like process(clk, reset). Change that to process(slowclock, reset). Also, change the rising_edge test from clk to slowclock. Now your state machine is running 67 million times slower than the clock. You can adjust the speed of your circuit by assigning different bits from the counter to the slowclock signal.
Run the circuit on the board and take a movie of it to link to in your report.
Note: when you switch back and forth between testing in ghdl and testing on the board you need to either bypass the counter (ghdl) or use the counter to slow down the clock (board). You can bypass the counter by changing the slowclock assignment statement to the following.
slowclock <= clk;If you are not using the DE0 board, then include a detailed description in your report of the simulation output and how you can tell it is correct.
- Write two programs for your circuit
Write two different programs of 16 instructions each by copying and modifying the lightrom circuit. You must update the comments to reflect the expected output of these programs. Be sure to save copies of your work in different files so you can demonstrate them later. Test out your programs and include videos/video links in your report. You can also simulate your programs using the lightsbench test file. Include your programs in your report along with a description of what they are supposed to do and how you can tell they are correct.
Extensions
- Make longer programs.
- Add instructions to the system. Make branch instructions, for example.
- Add a hold/free button to the display.
- Add a button that lets you speed up or slow down the display.
- Be creative.
Report
Create a Word/Pages/etc document for your report. For each task, write a short description of the task, in your own words.
- Include a description of your top-level design.
- Include the two programs you wrote using the simple instruction set. Please DO NOT include your other VHDL design files in your report. Describe what you intended the programs to do.
- Include a video/video link (DE0) for each of the three programs demonstrating they work.
- Describe the hardware testing you undertook to prove the circuit works. I.e. provide your overall strategy.
- Include a description and video link (demonstrating it works) of any extensions.
- If you use simulation, include gtkWaves and justify how you can tell they are correct.
- Include an Acknowledgements section in which you list any resources you con sulted. These could be on-line (movies or web pages) or people (peers, TA's, professors). This is your opportunity to celebrate the community you were able to plug into.
Handin
Put your vhd and write-up files in a folder called Project4_<username>, zip it up (so its name will be Project4_<username>), and upload the zip file to the Google classroom assignment for this project.
Fall 2026
