Getting Started with the Low-Cost Cyclone IV EP4CE6E22C8N FPGA Board#
Keywords: Altera Cyclone IV FPGA, Quartus Prime Lite, EP4CE6E22C8N, VHDL Example
▷ Cyclone IV FPGA#
In March 2024, Intel (formerly Altera) announced the extension of the lifecycle for its legacy FPGA product lines, including the Cyclone IV series, through the year 2040. This extended support ensures continued availability of Cyclone IV FPGA devices, making them a practical and cost-effective choice for educational purposes and FPGA design learning. In addition, a variety of low-cost Cyclone IV FPGA boards manufactured in China are readily available on the market.

Figure: Intel/Altera FPGA Longevity Extended to 2040 (Source: Intel)
This blog provides a short tutorial on experimenting with a low-cost Cyclone IV (E-Series) FPGA board. VHDL source code is provided and intended for use with Intel/Altera Quartus Prime Lite (v24.x).

Figure: Intel/Altera Cyclone IV EP4CE6E22C8N FPGA Board

Figure: Device Selection in Quartus Prime Lite
▷ FPGA Board Information#
The FPGA development board used in this project integrates a mid-range Altera Cyclone IV device, basic peripheral interfaces, on-board storage, and power regulation circuits. The following list summarizes the key hardware specifications and pin assignments.
- FPGA: EP4CE6E22C8N
- Logic Elements (LEs): 6,272
- PLLs: 2
- Embedded Memory: 276,480 bits
- I/Os: 91
- Package: 144-LQFP
- USB-to-Serial Interface: CH340
- FPGA TX pin: PIN_10
- FPGA RX pin: PIN_23
- Clock Sources
- 50 MHz: PIN_24, PIN_25
- 27 MHz: PIN_90, PIN_91
- Push Buttons (Active-Low with Pull-Ups)
- FPGA Reset: PIN_88
- 4x User Buttons [3:0]: PIN_73, PIN_80, PIN_89, PIN_114
- LEDs (Active-Low)
- 5x LEDs [4:0]: PIN_1, PIN_2, PIN_3, PIN_7, PIN_11
- On-Board Storage
- Altera EPCS4N (Serial Configuration Device)
- Winbond 25Q64JVSIQ (64 Mbit / 8 MB Serial NOR Flash)
- Power Supply
- +5V via USB Type-C connector
- 4x AMS1117 LDO voltage regulators: +3.3 V, +2.5 V, +1.8 V, and +1.2 V outputs
- Bitstream Programming
- via a JTAG socket for USB Blaster

Figure: JTAG Socket for USB Blaster and Jumper settings for I/O Bank Voltages
▷ FPGA Demo Code#
The provided FPGA design demonstrates how to use the onboard 50 MHz clock input, the active-low reset button, four user push buttons, and five onboard LEDs. In addition, since the FPGA board includes a CH340 USB-to-serial interface chip, a simple serial transmitter module (baudrate = 115200) is implemented to send a predefined message ("Hello, World!") to the host computer whenever a button is pressed.
------------------------------------------------------------------
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;
------------------------------------------------------------------
ENTITY top IS
PORT (
CLK : IN STD_LOGIC; -- 50MHz clock
nRST : IN STD_LOGIC; -- active-low, asynchronous reset
LEDS : OUT STD_LOGIC_VECTOR(4 DOWNTO 0); -- active-low
BUTTONS : IN STD_LOGIC_VECTOR(3 DOWNTO 0); -- active-low
SERIAL_TX : OUT STD_LOGIC;
SERIAL_RX : IN STD_LOGIC
);
END top;
ARCHITECTURE SYNTH OF top IS
CONSTANT BIT_WIDTH : INTEGER := 5;
CONSTANT ALL_ONES : UNSIGNED := TO_UNSIGNED(2**BIT_WIDTH-1,BIT_WIDTH);
CONSTANT COUNT_PERIOD : INTEGER := 10_000_000;
SUBTYPE count_t IS INTEGER RANGE 0 TO (COUNT_PERIOD - 1);
SIGNAL count : count_t := 0;
SIGNAL reg : STD_LOGIC_VECTOR(BIT_WIDTH-1 DOWNTO 0);
SIGNAL shift_en : STD_LOGIC;
-- Define constants
CONSTANT CLK_FREQ : INTEGER := 50e6; -- 50 MHz clock
CONSTANT BAUD_RATE : INTEGER := 115200; -- 115200 baud rate
CONSTANT BAUD_PERIOD : INTEGER := CLK_FREQ / BAUD_RATE - 1;
-- ASCII message to be transmitted
TYPE ascii_array_t IS ARRAY (0 TO 13) OF STD_LOGIC_VECTOR(7 DOWNTO 0);
CONSTANT MESSAGE : ascii_array_t := (
x"48", -- H
x"65", -- e
x"6C", -- l
x"6C", -- l
x"6F", -- o
x"20", -- (space)
x"57", -- W
x"6F", -- o
x"72", -- r
x"6C", -- l
x"64", -- d
x"21", -- !
x"0D", -- \r
x"0A" -- \n
);
-- FSM states
TYPE state_t IS (ST_IDLE, ST_LOAD_DATA, ST_SEND_DATA, ST_STOP, ST_WAIT);
SIGNAL state : state_t := ST_IDLE;
SIGNAL next_state : state_t := ST_IDLE;
SIGNAL wait_counter : INTEGER := 0;
SIGNAL bit_counter : INTEGER RANGE 0 TO 9 := 0;
SIGNAL char_counter : INTEGER RANGE 0 TO MESSAGE'length := 0;
SIGNAL shift_reg : STD_LOGIC_VECTOR(9 DOWNTO 0) := (OTHERS => '1');
SIGNAL btn_capture : STD_LOGIC_VECTOR(1 DOWNTO 0) := (OTHERS => '1');
SIGNAL txd : STD_LOGIC := '1';
BEGIN
SERIAL_TX <= txd;
LEDS <= NOT reg WHEN BUTTONS = "1111" ELSE "10101";
PROCESS (nRST, CLK) BEGIN
IF nRST = '0' THEN
count <= 0;
shift_en <= '0';
ELSIF rising_edge(CLK) THEN
-- check whether the counter reaches the max. value.
IF count = (COUNT_PERIOD - 1) THEN
count <= 0; -- reset the counter.
shift_en <= '1'; -- enable register shift.
ELSE
count <= count + 1; -- increment counter by 1.
shift_en <= '0'; -- disable register shift.
END IF;
END IF;
END PROCESS;
PROCESS (nRST, CLK) BEGIN
IF nRST = '0' THEN
reg <= (OTHERS => '0'); -- clear the shift register.
ELSIF rising_edge(CLK) THEN
IF shift_en = '1' THEN -- register shifting is enabled.
IF reg = STD_LOGIC_VECTOR(ALL_ONES) THEN
-- clear the shift register.
reg <= (OTHERS => '0');
ELSE
-- shift left, insert '1' as LSB.
reg <= reg(reg'left - 1 DOWNTO 0) & '1';
END IF;
END IF;
END IF;
END PROCESS;
PROCESS (CLK, nRST)
BEGIN
IF nRST = '0' THEN
state <= ST_IDLE;
bit_counter <= 0;
wait_counter <= 0;
char_counter <= 0;
btn_capture <= "11";
shift_reg <= (OTHERS => '1');
txd <= '1'; -- TXD idle (high)
ELSIF rising_edge(CLK) THEN
btn_capture <= btn_capture(0) & BUTTONS(0);
CASE state IS
WHEN ST_IDLE =>
TXD <= '1';
IF btn_capture = "01" THEN -- button click
state <= ST_LOAD_DATA;
char_counter <= 0;
END IF;
WHEN ST_LOAD_DATA =>
shift_reg <= '1' & MESSAGE(char_counter) & '0';
state <= ST_SEND_DATA;
bit_counter <= 0;
WHEN ST_SEND_DATA =>
txd <= shift_reg(0); -- LSB bit
shift_reg <= '1' & shift_reg(9 DOWNTO 1); -- Shift data
state <= ST_WAIT;
wait_counter <= BAUD_PERIOD;
IF bit_counter = 9 THEN
next_state <= ST_STOP;
bit_counter <= 0;
ELSE
next_state <= ST_SEND_DATA;
bit_counter <= bit_counter + 1;
END IF;
WHEN ST_STOP =>
txd <= '1';
IF char_counter = MESSAGE'length - 1 THEN
state <= ST_IDLE;
ELSE
char_counter <= char_counter + 1;
state <= ST_LOAD_DATA;
END IF;
WHEN ST_WAIT =>
IF wait_counter = 0 THEN
state <= next_state;
ELSE
wait_counter <= wait_counter - 1;
END IF;
WHEN OTHERS =>
state <= ST_IDLE;
END CASE;
END IF;
END PROCESS;
END SYNTH;
A Tcl script file is also provided to set up the FPGA pin assignments automatically.
#set_global_assignment -name FAMILY "Cyclone IV E"
#set_global_assignment -name DEVICE EP4CE6E22C8
#set_global_assignment -name TOP_LEVEL_ENTITY top
set_location_assignment PIN_24 -to CLK
set_location_assignment PIN_88 -to nRST
set_location_assignment PIN_1 -to LEDS[0]
set_location_assignment PIN_2 -to LEDS[1]
set_location_assignment PIN_3 -to LEDS[2]
set_location_assignment PIN_7 -to LEDS[3]
set_location_assignment PIN_11 -to LEDS[4]
set_location_assignment PIN_73 -to BUTTONS[3]
set_location_assignment PIN_80 -to BUTTONS[2]
set_location_assignment PIN_89 -to BUTTONS[1]
set_location_assignment PIN_114 -to BUTTONS[0]
set_location_assignment PIN_10 -to SERIAL_TX
set_location_assignment PIN_23 -to SERIAL_RX
set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to LEDS[4]
set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to LEDS[3]
set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to LEDS[2]
set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to LEDS[1]
set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to LEDS[0]
set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to BUTTONS[3]
set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to BUTTONS[2]
set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to BUTTONS[1]
set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to BUTTONS[0]
set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to nRST
set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to CLK
set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to SERIAL_TX
set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to SERIAL_RX
To upload the bitstream file (.sof) to the FPGA board, you'll need a USB Blaster (or a compatible clone). Make sure the USB Blaster driver is properly installed on your Windows 10 or 11 system. The USB driver is included with the Quartus Prime software package.


Figure: Device Programming using USB Blaster
▷ Conclusion#
Low-cost FPGA boards, such as the one based on the EP4CE6E22C8N, offer a practical entry point into the world of digital design. They provide just enough logic resources and peripherals to let beginners explore essential concepts — from basic combinational circuits to complete embedded systems.
However, it’s important to understand the trade-offs. Entry-level FPGA boards may have limited memory, fewer I/O pins, or reduced clock speeds compared to their higher-end counterparts. Beginners should also consider factors such as toolchain compatibility, availability of learning resources, and community support when selecting a board. Choosing a platform with good documentation and an active user base often makes the learning process much smoother.
As skills develop, moving to higher-capacity or more advanced FPGA devices opens the door to a wider range of projects — including high-speed signal processing, or custom system-on-chip designs. The foundations built on low-cost boards translate naturally to these larger systems, helping learners grow into confident and capable FPGA developers.
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Created: 2025-10-26 | Last Updated: 2025-10-26