This project is the second installment of my “From VHDL Code to Real Hardware” series, in which we design simple electronic systems using a hardware-based approach and programmable logic ICs. These provide an excellent introduction to important design concepts that can also be applied to more complex projects.

Finite-State Machine Project Overview

In this project, I will cover the development of finite-state machines (FSM). Specifically, I will build a 4-bit binary counter with four output LEDs and a four-position DIP switch mode input as shown operating in this video:

Operation of the 4-bit binary counter finite-state machine with LED outputs. Video used courtesy of Kristijan Nelkovski

The logic will be written in VHDL, a hardware description programming language, and uploaded to a Complex Programmable Logic (CPLD) chip. The CPLD is a reprogrammable off-the-shelf logic gate IC similar to an FPGA.

This project will use the same hardware and software setup as my previous design of an 8-bit arithmetic logic unit (ALU):

  • Altera Max II EPM240 CPLD development board,
  • Intel’s Quartus Prime Lite Edition IDE.

If you want to brush up on the fundamentals of programmable logic ICs and the Quartus Prime suite, you will want to check out that previous project.

What is a Finite-State Machine?

Before we dive into the design, let’s do a quick review of finite-state machines. These are abstract mathematical models of sequential logic circuits that operate in exactly one out of a finite number of states at any given moment.

An FSM can transition between its states once every clock cycle. The next state is based on external inputs and the current state (meaning that it employs memory logic).

Finite-state machines can be used to detect or generate sequences and are the basis for systems such as elevators, traffic lights, vending machines, and electronic locks.

Binary Counters

One particular implementation of FSMs is a binary counter. These circuits are designed with the goal of iterating through and displaying a desired sequence of binary numbers, typically built using a series of interconnected flip-flops. Each new state of a binary counter must be triggered by an input pulse which can come from an outside source or from the circuit’s own clock signal.

With that background, let’s dive into the code for the 4-bit binary counter.

VHDL Code for the FSM Binary Counter

We’re going to start writing the code for our binary counter by declaring a couple of libraries at the top of the VHDL file:

Port Defintions

Next, we’re going to create an entity called fsm for our circuit and define its input and output ports:

In this code snippet we have defined:

  • The clock signal CLK input which is defined by the command “in std_logic”
  • A 4-bit output port called CNT using “out std_logic_vector(3 downto 0)”
  • A 4-bit input port called SEL using “in std_logic_vector(3 downto 0)”

Process and Data Type Defintions

Now that we have defined the fsm entity, we can create an architecture for our circuit. This architecture will have three processes:

  1. The state memory.
  2. The next state logic.
  3. The output logic.

Before we can create these processes, we need to define a new data type for our states and two signal variables of that data type for memorizing the current and next states (think of them as registers).

The State Memory Process

Now we can create our first process called STATE_MEMORY. Using a IF statement, it writes the value of the next state into the current state variable on every rising edge of the CLK signal.

The Next State Logic Process

We’re going to create a process called NEXT_STATE_LOGIC using the SEL input port and the CURRENT_STATE signal. Inside this process, we call one CASE-WHEN statement for the current state. We nest additional CASE-WHEN statements for the mode inside of each WHEN clause of the first CASE-WHEN statement.

This way, we have an architecture that first checks the current state of the FSM and then reads the value from the SEL port. When this process finishes, the last nested WHEN clause will write the correct next state value to the NEXT_STATE signal according to our code (to the sequence being counted.)

The code chunk below shows a simple up counter that is active when the SEL port value is equal to “0000”. For brevity here, I am only showing a few of these repeating case statements, but the full code is provided further below.

Skipping cases, C3 through C13 which are similar, we get to the end of the NEXT_STATE_LOGIC process where we have:

The Output Logic Process

Lastly, in order for the current state to be output on the 4-bit LED display, we need to write another process called OUTPUT_LOGIC. It enumerates each state type we defined earlier with a corresponding 4-bit binary number.

This is again done by using the CURRENT_STATE signal and calling another CASE-WHEN statement. The CNT output represents the current state as its 4-bit binary counterpart:

Compiling the Code

After writing our code, we first need to compile it. Then, we must assign the ports that we defined in our program to the physical pins of the Altera CPLD using Quartus’ Pin Planner tool using the mapping shown in Table 1.

Table 1. Port-to-pin mapping
Port Name Altera CPLD Pin
CLK PIN_52
CNT[3] PIN_99
CNT[2] PIN_97
CNT[1] PIN_95
CNT[0] PIN_91
SEL[3] PIN_92
SEL[2] PIN_96
SEL[1] PIN_98
SEL[0] PIN_100

Uploading to the Development Board

Next, we need to compile the code once again before we can finally upload it to the development board. This process is carried out using a USB Blaster connected to the Altera MAX II via JTAG as shown in Figure 1.

Altera board and USB Blaster connections

Figure 1. Altera board and USB Blaster connections. Image used courtesy of Kristijan Nelkovski

Adding an External Trigger

After uploading our code to the Altera board, we need to add a clock signal—some type of external trigger that allows our counter to iterate through its states. Here, I’m going to cheat a little bit and use a Raspberry Pi Pico running the default Arduino Blink sketch, which acts as a crude clock source for our circuit.

You can use any other MCU development board for this functionality; just ensure you send a 3.3 V logic-level signal to your Altera MAX II. The Raspberry Pi Pico natively outputs a 3.3 V logic signal.

In our circuit, the Blink sketch will provide a 0.5 Hz clock signal (you can change the frequency in the delay portion of the Arduino code). Instead of flashing an LED on for one second and then off for another second, it would tell our synchronous circuit when it’s time to switch to its next state, as defined in our VHDL code.

PCB Circuit Diagram

The schematic in Figure 2 shows the full circuit diagram of our project. Here, every LED is connected to an output pin through a current-limiting resistor, and every contact from the DIP switch that’s connected to an input pin is pulled down to ground using a resistor network. The clock pin is then connected to the supposed LED pin from the microcontroller board running Blink.

Circuit schematic of the 4-bit binary counter project

Figure 2. Circuit schematic of the 4-bit binary counter project. Image used courtesy of Kristijan Nelkovski [click to enlarge]

After wiring everything up and providing adequate power to the circuit, our FSM counter should start iterating through different binary sequences on its four LEDs according to the mode of counting selected through the DIP switch.

Designing Other Count Sequences

For other values of the SEL input you can create any arbitrary 4-bit number sequence. Inside the full code download at the end of this article, I’ve included 16 counting modes that can be selected using the 4-bit SEL input as described in Table 2.

Table 2. SEL control of counting modes
SEL Count Mode SEL Count Mode
0000 Binary Up 1000 Binary Down
0001 Ring Up 1001 Ring Down
0010 Johnson Up 1010 Johnson Down
0011 Gray Code Up 1011 Gray Down
0100 BCD Up 1100 BCD Down
0101 Even Up 1101 Even Down
0110 Odd Up 1110 Odd Down
0111 Fibonacci Up 1111 Fibonacci Down

Note: Technically, the Fibonacci sequences should have repeated 1’s, but for this simple project there is only a single 1 in those sequences.

Bill of Materials

Table 3 contains the project bill of materials (BOM).

Table 3. 4-bit counter bill of materials.
Part Description Quantity Notes
Altera Max II EPM240 development board and USB Blaster bundle (with a barrel jack and mini USB cable) 1 You need the main Altera board and the USB blaster to upload the code through Quartus via JTAG.
LEDs 4 Each bit is represented by an LED, so 4 bits for the FSM requires 4 LEDs.
Resistors (R_0 to R_3) 4 Current limiting resistors for our LEDs (220 Ω to 1 kΩ).
DIP Switch – 4 position 1 Two 8-bit inputs for our ALU.
DIP Switch – 4 position 1 A 4-bit mode select input for the FSM.
Resistor network 4 resistors (RESISTOR NETWORK_MODE) 1 Pull-down resistor network for the 4 position switches (4.7 kΩ and up).
Male-to-female jumper wires (or a custom IDC cable for the full PCB version) 30 Jumper wires from the Altera board to the breadboard/ perfboard.

Your Turn!

In this project, we created a finite-state machine circuit using the VHDL hardware description language and ran it on a CPLD development board. My goal with this series was to introduce programmable logic and give insights into the practical use of FPGA components using real hardware instead of circuit diagrams and computer simulations.

If you replicate this project, you can always play around with the code and switch up the counting modes or add and remove different sequences as you desire. Share your ideas and modifications in the comments below!

Downloadable Code

Here is the full code available for download from Github Gist using the “view raw” button in the lower right corner.