Imported from arielybarak/agent-lab (
references/hakathon_sendbox/.claude/skills/de10lite-board-and-build/SKILL.md). Install upstream withnpx skills add arielybarak/agent-lab --skill de10lite-board-and-build. Copyright stays with the author.
De10Lite Board And Build
CrashTech VLSI-2026 — FPGA BKM (Verified Reference)
Authoritative reference for all FPGA projects in this repo.
All commands, pin assignments, and flows verified working on the actual CrashTech DE10-Lite kit (May 2026).
Board Identity
| Field | Value |
|---|---|
| Board | DE10-Lite |
| Device | Intel MAX 10 — 10M50DAF484C7G |
| Clock | 50 MHz on MAX10_CLK1_50 (PIN_P11) |
| Toolchain | Quartus Prime Lite 17.1 |
| Programmer | USB-Blaster (detected as USB-Blaster [USB-0]) |
| Driver path | C:\intelFPGA_lite\17.1\quartus\drivers\usb-blaster |
Project File Templates
Minimal .qpf:
QUARTUS_VERSION = "17.1"
DATE = "2026.05.04"
PROJECT_REVISION = "my_project"
Minimal .qsf header (copy and extend):
set_global_assignment -name FAMILY "MAX 10"
set_global_assignment -name DEVICE 10M50DAF484C7G
set_global_assignment -name TOP_LEVEL_ENTITY my_top
set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files
set_global_assignment -name SYSTEMVERILOG_FILE src/my_top.sv
set_global_assignment -name LAST_QUARTUS_VERSION "17.1.0 Lite Edition"
Full DE10-Lite Pin Assignments (Copy-Paste Ready)
# ---- Clock ----
set_location_assignment PIN_P11 -to MAX10_CLK1_50
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to MAX10_CLK1_50
# ---- Switches SW[9:0] ----
set_location_assignment PIN_C10 -to SW[0]
set_location_assignment PIN_C11 -to SW[1]
set_location_assignment PIN_D12 -to SW[2]
set_location_assignment PIN_C12 -to SW[3]
set_location_assignment PIN_A12 -to SW[4]
set_location_assignment PIN_B12 -to SW[5]
set_location_assignment PIN_A13 -to SW[6]
set_location_assignment PIN_A14 -to SW[7]
set_location_assignment PIN_B14 -to SW[8]
set_location_assignment PIN_F15 -to SW[9]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SW[*]
# ---- Keys KEY[1:0] (active-low) ----
set_location_assignment PIN_B8 -to KEY[0]
set_location_assignment PIN_A7 -to KEY[1]
set_instance_assignment -name IO_STANDARD "3.3 V SCHMITT TRIGGER" -to KEY[*]
# ---- Red LEDs LEDR[9:0] ----
set_location_assignment PIN_A8 -to LEDR[0]
set_location_assignment PIN_A9 -to LEDR[1]
set_location_assignment PIN_A10 -to LEDR[2]
set_location_assignment PIN_B10 -to LEDR[3]
set_location_assignment PIN_D13 -to LEDR[4]
set_location_assignment PIN_C13 -to LEDR[5]
set_location_assignment PIN_E14 -to LEDR[6]
set_location_assignment PIN_D14 -to LEDR[7]
set_location_assignment PIN_A11 -to LEDR[8]
set_location_assignment PIN_B11 -to LEDR[9]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[*]
# ---- 7-Segment HEX0..HEX5 (active-low, [7]=dp) ----
set_location_assignment PIN_C14 -to HEX0[0]
set_location_assignment PIN_E15 -to HEX0[1]
set_location_assignment PIN_C15 -to HEX0[2]
set_location_assignment PIN_C16 -to HEX0[3]
set_location_assignment PIN_E16 -to HEX0[4]
set_location_assignment PIN_D17 -to HEX0[5]
set_location_assignment PIN_C17 -to HEX0[6]
set_location_assignment PIN_D15 -to HEX0[7]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to HEX0[*]
set_location_assignment PIN_C18 -to HEX1[0]
set_location_assignment PIN_D18 -to HEX1[1]
set_location_assignment PIN_E18 -to HEX1[2]
set_location_assignment PIN_B16 -to HEX1[3]
set_location_assignment PIN_A17 -to HEX1[4]
set_location_assignment PIN_A18 -to HEX1[5]
set_location_assignment PIN_B17 -to HEX1[6]
set_location_assignment PIN_A16 -to HEX1[7]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to HEX1[*]
set_location_assignment PIN_B20 -to HEX2[0]
set_location_assignment PIN_A20 -to HEX2[1]
set_location_assignment PIN_B19 -to HEX2[2]
set_location_assignment PIN_A21 -to HEX2[3]
set_location_assignment PIN_B21 -to HEX2[4]
set_location_assignment PIN_C22 -to HEX2[5]
set_location_assignment PIN_B22 -to HEX2[6]
set_location_assignment PIN_A19 -to HEX2[7]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to HEX2[*]
set_location_assignment PIN_F21 -to HEX3[0]
set_location_assignment PIN_E22 -to HEX3[1]
set_location_assignment PIN_E21 -to HEX3[2]
set_location_assignment PIN_C19 -to HEX3[3]
set_location_assignment PIN_C20 -to HEX3[4]
set_location_assignment PIN_D19 -to HEX3[5]
set_location_assignment PIN_E17 -to HEX3[6]
set_location_assignment PIN_D22 -to HEX3[7]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to HEX3[*]
set_location_assignment PIN_F18 -to HEX4[0]
set_location_assignment PIN_E20 -to HEX4[1]
set_location_assignment PIN_E19 -to HEX4[2]
set_location_assignment PIN_J18 -to HEX4[3]
set_location_assignment PIN_H19 -to HEX4[4]
set_location_assignment PIN_F19 -to HEX4[5]
set_location_assignment PIN_F20 -to HEX4[6]
set_location_assignment PIN_F17 -to HEX4[7]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to HEX4[*]
set_location_assignment PIN_J20 -to HEX5[0]
set_location_assignment PIN_K20 -to HEX5[1]
set_location_assignment PIN_L18 -to HEX5[2]
set_location_assignment PIN_N18 -to HEX5[3]
set_location_assignment PIN_M20 -to HEX5[4]
set_location_assignment PIN_N19 -to HEX5[5]
set_location_assignment PIN_N20 -to HEX5[6]
set_location_assignment PIN_L19 -to HEX5[7]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to HEX5[*]
# ---- Arduino Header — ARDUINO_IO[15:0] ----
# ARDUINO_IO[0] — CrashTech: UART RX from ESP32 (GPIO16)
# ARDUINO_IO[1] — CrashTech: UART TX to ESP32 (GPIO17)
# GND pin on Arduino header — connect ESP32 GND here
set_location_assignment PIN_AB5 -to ARDUINO_IO[0]
set_location_assignment PIN_AB6 -to ARDUINO_IO[1]
set_location_assignment PIN_AB7 -to ARDUINO_IO[2]
set_location_assignment PIN_AB8 -to ARDUINO_IO[3]
set_location_assignment PIN_AB9 -to ARDUINO_IO[4]
set_location_assignment PIN_Y10 -to ARDUINO_IO[5]
set_location_assignment PIN_AA11 -to ARDUINO_IO[6]
set_location_assignment PIN_AA12 -to ARDUINO_IO[7]
set_location_assignment PIN_AB17 -to ARDUINO_IO[8]
set_location_assignment PIN_AA17 -to ARDUINO_IO[9]
set_location_assignment PIN_AB19 -to ARDUINO_IO[10]
set_location_assignment PIN_AA19 -to ARDUINO_IO[11]
set_location_assignment PIN_Y19 -to ARDUINO_IO[12]
set_location_assignment PIN_AB20 -to ARDUINO_IO[13]
set_location_assignment PIN_AB21 -to ARDUINO_IO[14]
set_location_assignment PIN_AA20 -to ARDUINO_IO[15]
set_location_assignment PIN_F16 -to ARDUINO_RESET_N
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ARDUINO_IO[*]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to ARDUINO_RESET_N
7-Segment Encoding (Active-Low)
Bit order: [7]=dp [6]=g [5]=f [4]=e [3]=d [2]=c [1]=b [0]=a
Set 1 = segment OFF, 0 = segment ON.
| Char | 8'b value | Hex |
|---|---|---|
| 0 | 8'b1100_0000 |
C0 |
| 1 | 8'b1111_1001 |
F9 |
| 2 | 8'b1010_0100 |
A4 |
| 3 | 8'b1011_0000 |
B0 |
| 4 | 8'b1001_1001 |
99 |
| 5 | 8'b1001_0010 |
92 |
| 6 | 8'b1000_0010 |
82 |
| 7 | 8'b1111_1000 |
F8 |
| 8 | 8'b1000_0000 |
80 |
| 9 | 8'b1001_0000 |
90 |
| A | 8'b1000_1000 |
88 |
| b | 8'b1000_0011 |
83 |
| C | 8'b1100_0110 |
C6 |
| d | 8'b1010_0001 |
A1 |
| E | 8'b1000_0110 |
86 |
| F | 8'b1000_1110 |
8E |
| H | 8'b1000_1001 |
89 |
| i | 8'b1100_1111 |
CF |
| L | 8'b1100_0111 |
C7 |
| n | 8'b1010_1011 |
AB |
| o | 8'b1010_0011 |
A3 |
| P | 8'b1000_1100 |
8C |
| r | 8'b1010_1111 |
AF |
| U | 8'b1100_0001 |
C1 |
| blank | 8'b1111_1111 |
FF |
- |
8'b1011_1111 |
BF |
CLI Compile & Program (PowerShell — Verified)
# Compile (from project folder containing .qsf/.qpf)
cd c:\Projects\TechCrash2026\demos\alive_test\fpga
& "C:\intelFPGA_lite\17.1\quartus\bin64\quartus_sh.exe" --flow compile alive_test
# Check .sof was produced
Test-Path "output_files\alive_test.sof"
# List available programmers (should show "USB-Blaster [USB-0]")
& "C:\intelFPGA_lite\17.1\quartus\bin64\quartus_pgm.exe" --list
# Program (volatile SRAM — fast, lost on power-off)
& "C:\intelFPGA_lite\17.1\quartus\bin64\quartus_pgm.exe" -c "USB-Blaster [USB-0]" -m JTAG -o "P;output_files\alive_test.sof"
Note on cable name: Always use
"USB-Blaster [USB-0]"(with the[USB-0]suffix). Using just"USB-Blaster"returns error 87.
UART in RTL — Verified Pattern (9600 baud, 50 MHz)
// CLKS_PER_BIT = 50_000_000 / 9600 = 5208
// ARDUINO_IO[0] = input (RX from ESP32)
// ARDUINO_IO[1] = output (TX to ESP32)
assign ARDUINO_IO[0] = 1'bz; // input mode
assign uart_rx_in = ARDUINO_IO[0];
assign ARDUINO_IO[1] = uart_tx_out;
assign ARDUINO_IO[15:2] = 14'bz; // unused = high-Z
// Double-flop sync on RX input (mandatory for async inputs)
always @(posedge clk) begin
rx_d1 <= uart_rx_in;
rx_d2 <= rx_d1;
end
Verified Status (May 2026)
| Feature | Status | Notes |
|---|---|---|
Compile via CLI (quartus_sh) |
✅ Working | --flow compile <project> |
Program via CLI (quartus_pgm) |
✅ Working | Cable name = "USB-Blaster [USB-0]" |
LEDR[9:0] |
✅ Working | Sweep pattern |
HEX5..HEX0 |
✅ Working | "ALivE " verified on hardware |
SW[9:0] |
✅ Working | |
KEY[1:0] |
✅ Working | Active-low reset |
GPIO[0]/[1] |
✅ Working | UART TX/RX to ESP32 via Arduino header (ARDUINO_IO[0]/[1]) |
Reference Demo
See demos/alive_test/fpga/ — canonical working project with full QSF.
Quartus Prime Lite 17.1 — Complete Installation Guide
Everything you need to design, compile, simulate, and program the DE10-Lite FPGA. Follow these steps exactly.
Step 1: Download Quartus Prime Lite 17.1
- Go to: https://www.intel.com/content/www/us/en/software-kit/669444/intel-quartus-prime-lite-edition-design-software-version-17-1-for-windows.html
- If the direct link doesn't work, go to https://www.intel.com/content/www/us/en/products/details/fpga/development-tools/quartus-prime/resource.html and select Version 17.1 under "Quartus Prime Lite"
- Download the Combined Files tab option (single ~5 GB installer that includes Quartus + ModelSim + device support), OR download individually:
- Quartus Prime Lite Edition (main IDE)
- ModelSim-Intel FPGA Edition (simulation)
- MAX 10 device support (required for the DE10-Lite board)
Why version 17.1? It is the last version that bundles ModelSim-Altera for free and has proven stability with the DE10-Lite (MAX 10) device. Newer versions work but require separate ModelSim licensing.
Step 2: Install Quartus Prime Lite 17.1
- Run the downloaded installer (
QuartusLiteSetup-17.1.0.590-windows.exeor similar) - Choose installation directory:
C:\intelFPGA_lite\17.1\(default — keep it) - Select components:
- Quartus Prime Lite Edition
- ModelSim-Intel FPGA Edition (simulation)
- MAX 10 FPGA device support (required)
- Other device families — not needed, skip to save space
- Click Install and wait (~10–20 minutes depending on your system)
- When done, verify these paths exist:
| Tool | Path |
|---|---|
| Quartus IDE | C:\intelFPGA_lite\17.1\quartus\bin64\quartus.exe |
| Quartus Shell (CLI) | C:\intelFPGA_lite\17.1\quartus\bin64\quartus_sh.exe |
| Quartus Programmer | C:\intelFPGA_lite\17.1\quartus\bin64\quartus_pgm.exe |
| ModelSim | C:\intelFPGA_lite\17.1\modelsim_ase\win32aloem\vsim.exe |
Step 3: Install the USB-Blaster Driver
The USB-Blaster driver lets your PC communicate with the DE10-Lite over USB for programming. Without it, you cannot load designs onto the FPGA.
- Connect the DE10-Lite to your PC via the USB cable
- Windows may show "Unknown device" or "USB-Blaster" in Device Manager — either way, proceed:
- Open Device Manager (right-click Start → Device Manager)
- Find the unrecognized device — it will be under "Other devices" or "Universal Serial Bus controllers"
- Right-click → Update driver → Browse my computer for drivers
- Browse to:
C:\intelFPGA_lite\17.1\quartus\drivers\usb-blaster - Click Next → Windows will install the driver
- Verify: Device Manager should now show "Altera USB-Blaster" under "Universal Serial Bus controllers"
Alternative (auto-detect): Open Quartus → Tools → Programmer → Hardware Setup → click "Auto Detect". If it finds the USB-Blaster, the driver is working.
Troubleshooting driver issues:
- If Windows refuses the unsigned driver: temporarily disable "Driver Signature Enforcement" in Windows advanced startup options
- If the device doesn't appear: try a different USB port (use USB 2.0 if available, avoid USB hubs)
- If using Windows 11: the driver from 17.1 works — just point to the same folder above
Step 4: Verify the Full Toolchain
Run these checks to confirm everything is installed:
# Check Quartus
& "C:\intelFPGA_lite\17.1\quartus\bin64\quartus_sh.exe" --version
# Check ModelSim
& "C:\intelFPGA_lite\17.1\modelsim_ase\win32aloem\vsim.exe" -version
# Check Programmer can see the board (board must be connected)
& "C:\intelFPGA_lite\17.1\quartus\bin64\quartus_pgm.exe" --auto
Expected output from quartus_sh --version:
Quartus Prime Shell
Version 17.1.0 Build 590 ...
Step 5: Quick Smoke Test — LED Blink
Verify the entire flow (design → compile → program) with a minimal project:
- Create a folder:
C:\FPGA\led_test\ - Create file
led_test.sv:
module led_test (
input MAX10_CLK1_50,
input [9:0] SW,
output [9:0] LEDR
);
// Switches directly control LEDs
assign LEDR = SW;
endmodule
- Create file
led_test.qpf:
QUARTUS_VERSION = "17.1"
PROJECT_REVISION = "led_test"
- Create file
led_test.qsf:
set_global_assignment -name FAMILY "MAX 10 FPGA"
set_global_assignment -name DEVICE 10M50DAF484C7G
set_global_assignment -name TOP_LEVEL_ENTITY led_test
set_global_assignment -name SYSTEMVERILOG_FILE led_test.sv
set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files
set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0
set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85
# Clock
set_location_assignment PIN_P11 -to MAX10_CLK1_50
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to MAX10_CLK1_50
# Switches
set_location_assignment PIN_C10 -to SW[0]
set_location_assignment PIN_C11 -to SW[1]
set_location_assignment PIN_D12 -to SW[2]
set_location_assignment PIN_C12 -to SW[3]
set_location_assignment PIN_A12 -to SW[4]
set_location_assignment PIN_B12 -to SW[5]
set_location_assignment PIN_A13 -to SW[6]
set_location_assignment PIN_A14 -to SW[7]
set_location_assignment PIN_B14 -to SW[8]
set_location_assignment PIN_F15 -to SW[9]
# LEDs
set_location_assignment PIN_A8 -to LEDR[0]
set_location_assignment PIN_A9 -to LEDR[1]
set_location_assignment PIN_A10 -to LEDR[2]
set_location_assignment PIN_B10 -to LEDR[3]
set_location_assignment PIN_D13 -to LEDR[4]
set_location_assignment PIN_C13 -to LEDR[5]
set_location_assignment PIN_E14 -to LEDR[6]
set_location_assignment PIN_D14 -to LEDR[7]
set_location_assignment PIN_A11 -to LEDR[8]
set_location_assignment PIN_B11 -to LEDR[9]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SW[*]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LEDR[*]
- Compile:
cd C:\FPGA\led_test
& "C:\intelFPGA_lite\17.1\quartus\bin64\quartus_sh.exe" --flow compile led_test
- Program the board:
& "C:\intelFPGA_lite\17.1\quartus\bin64\quartus_pgm.exe" -m jtag -o "P;output_files/led_test.sof@1"
- Test: Flip the slide switches on the DE10-Lite — the corresponding LEDs should light up. If they do, your entire toolchain is working end-to-end.
DE10-Lite Board Overview & Pin Mapping
For Future Projects: Complete reference for the Intel DE10-Lite (MAX 10 FPGA) board — pin assignments, peripherals, and golden top module pattern.
Board Hardware
| Resource | Details |
|---|---|
| FPGA | Intel MAX 10 (10M50DAF484C7G) |
| Clock | 50 MHz oscillator (MAX10_CLK1_50) |
| Switches | 10× slide switches SW[9:0] (active-high, 3.3V LVTTL) |
| Keys | 2× push-buttons KEY[0], KEY[1] (active-low, Schmitt trigger) |
| LEDs | 10× red LEDs LEDR[9:0] |
| 7-Segment | 6× displays HEX[5:0], each 8 bits (active-low, includes DP) |
| VGA | 4-bit per channel: VGA_R[3:0], VGA_G[3:0], VGA_B[3:0], VGA_HS, VGA_VS |
| Arduino Header | 16 I/O: ARDUINO_IO[15:0], active-low reset: ARDUINO_RESET_N |
| GPIO | 36 pins: GPIO[35:0] |
| ADC | 6 analog channels (12-bit, onboard MAX 10 ADC) |
| SDRAM | 64MB (optional usage, 16-bit bus) |
| Accelerometer | Onboard ADXL345 via SPI |
Golden Top Module Pattern
All DE10-Lite projects use the same port declaration:
module top (
input MAX10_CLK1_50,
input [9:0] SW,
input [1:0] KEY,
output [9:0] LEDR,
output [7:0] HEX0, HEX1, HEX2, HEX3, HEX4, HEX5,
output [3:0] VGA_R, VGA_G, VGA_B,
output VGA_HS, VGA_VS,
inout [15:0] ARDUINO_IO,
inout ARDUINO_RESET_N
);
From working projects:
- `SW[9]` → `resetN` (active-low reset via slide switch)
- `SW[8:7]` → `cfg[1:0]` (display mode selection)
- `SW[6]` → CPU reset / secondary function
- `SW[0]` → Manual start / debug trigger
## Common Instantiation Hierarchy
top_module ├── pll25 (50→25/50/100 MHz) ├── vga_ctrl (VGA display hub) │ ├── vga_controller (sync gen) │ ├── text_screen (80×60 chars) │ ├── game_unit (sprite engine) │ └── pattern_gen (test bars) ├── lcd_ctrl (addon PCB LCD mirror) ├── analog_input (6-ch ADC FSM) ├── periphery_control (joystick/buttons) ├── seven_segment (hex decoder ×6) └── one_sec (1-second timer)
## Quartus Project Setup
- **Tool**: Intel Quartus Prime Lite 17.1 (`C:\intelFPGA_lite\17.1\`)
- **Project files**: `.qpf` (project), `.qsf` (settings + pin assignments), `.qar` (archive)
- **IP cores**: Generated via Qsys (Platform Designer)
- **Compilation**: Full compilation flow: Analysis → Fitter → Assembler → Timing
---
## DE10-Lite Quartus Build, Program & Simulation Flow
**For Future Projects**: Complete step-by-step guide for compiling, programming (burning), and simulating DE10-Lite FPGA projects using Quartus Prime Lite and ModelSim.
---
## Toolchain Paths
| Tool | Path |
|------|------|
| **Quartus Prime Lite 17.1** | `C:\intelFPGA_lite\17.1\quartus\bin64\` |
| **ModelSim-Altera** | `C:\intelFPGA_lite\17.1\modelsim_ase\win32aloem\` |
| **Quartus Shell** | `quartus_sh.exe` (command-line compilation) |
| **Quartus Programmer** | `quartus_pgm.exe` (JTAG programming) |
| **Quartus CPF** | `quartus_cpf.exe` (file conversion SOF↔POF) |
| **Platform Designer** | `qsys-edit.exe` (IP core generation) |
## Project File Structure
Every Quartus project needs at minimum:
project_dir/ ├── top.qpf # Project file (names the project) ├── top.qsf # Settings: device, pins, source files ├── DE10_LITE_Golden_Top.v # Top-level module ├── src/ # RTL source files ├── output_files/ # Compilation output (auto-generated) │ ├── top.sof # SRAM Object File (volatile) │ ├── top.pof # Programmer Object File (persistent) │ └── top.fit.summary # Fitter report ├── sim/ # Simulation scripts │ ├── run_sim.do # ModelSim .do script │ └── run_tests.bat # Batch launcher └── db/ # Quartus database (auto-generated)
### QPF File (Minimal)
```tcl
QUARTUS_VERSION = "17.1"
PROJECT_REVISION = "top"
The PROJECT_REVISION must match the .qsf filename (e.g., top.qsf → "top").
QSF File Key Assignments
# Device
set_global_assignment -name FAMILY "MAX 10 FPGA"
set_global_assignment -name DEVICE 10M50DAF484C6GES
set_global_assignment -name TOP_LEVEL_ENTITY DE10_LITE_Golden_Top
# Source files (one per RTL file)
set_global_assignment -name VERILOG_FILE DE10_LITE_Golden_Top.v
set_global_assignment -name VERILOG_FILE src/my_module.v
set_global_assignment -name SYSTEMVERILOG_FILE src/my_module.sv
# Qsys IP cores (reference the .qip file)
set_global_assignment -name QIP_FILE pll25.qip
set_global_assignment -name QIP_FILE adc/synthesis/adc.qip
set_global_assignment -name QIP_FILE font_rom.qip
# Output directory
set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files
# Timing / thermal
set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0
set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85
set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW"
set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)"
# Pin assignments — see bkm-de10lite-board-overview for full list
# Example:
set_location_assignment PIN_P11 -to MAX10_CLK1_50
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to MAX10_CLK1_50
Multiple QSF Revisions
You can maintain multiple .qsf files in the same project directory as named snapshots:
| Convention | Purpose |
|---|---|
rev5-initial_working.qsf |
Known-good baseline |
lcd-working.qsf |
After LCD addon integration |
lcd_connect.qsf |
LCD pin connection variant |
CPU_added.qsf |
After Hack CPU integration |
my_cpu.qsf |
Active development revision |
Switch active revision in the .qpf:
PROJECT_REVISION = "lcd-working"
Compilation (Full Flow)
Command-Line Compilation
@echo off
set QUARTUS_PATH="C:\intelFPGA_lite\17.1\quartus\bin64"
REM Full flow: Analysis → Fitter → Assembler → Timing Analyzer
%QUARTUS_PATH%\quartus_sh --flow compile top
The --flow compile argument runs all four stages:
- Analysis & Synthesis (
quartus_map) — parses RTL, infers logic - Fitter (
quartus_fit) — place & route onto the MAX 10 device - Assembler (
quartus_asm) — generates.sofprogramming file - Timing Analyzer (
quartus_sta) — static timing analysis
You can also run stages individually:
%QUARTUS_PATH%\quartus_map --read_settings_files=on top
%QUARTUS_PATH%\quartus_fit --read_settings_files=on top
%QUARTUS_PATH%\quartus_asm top
%QUARTUS_PATH%\quartus_sta top
run.bat Template (Proven Pattern)
@echo off
echo Starting Quartus compilation for DE10_LITE_Golden_Top project...
echo Target Device: MAX 10 FPGA (10M50DAF484C6GES)
echo Top Level Entity: DE10_LITE_Golden_Top
echo.
REM Optional: clean previous outputs
if exist output_files (
echo Cleaning previous output files...
rmdir /s /q output_files
)
set QUARTUS_PATH="C:\intelFPGA_lite\17.1\quartus\bin64"
echo Starting full compilation flow...
%QUARTUS_PATH%\quartus_sh --flow compile top
if %ERRORLEVEL% EQU 0 (
echo.
echo COMPILATION COMPLETED SUCCESSFULLY
echo Programming file: output_files/top.sof
) else (
echo.
echo COMPILATION FAILED WITH ERRORS
echo Check compilation reports in output_files/
exit /b %ERRORLEVEL%
)
Programming the Board ("Burning")
Prerequisites
- USB-Blaster driver installed (comes with Quartus)
- DE10-Lite connected via USB cable
- Board powered on (USB provides power)
Volatile Programming (SRAM — lost on power-off)
set QUARTUS_PATH="C:\intelFPGA_lite\17.1\quartus\bin64"
%QUARTUS_PATH%\quartus_pgm -m jtag -o "P;output_files/top.sof@1"
| Flag | Meaning |
|---|---|
-m jtag |
Use JTAG interface (USB-Blaster) |
-o "P;..." |
Program operation |
@1 |
Device index 1 on the JTAG chain |
This is the fast method — takes seconds. Use during development.
Persistent Programming (Flash — survives power cycles)
REM Step 1: Convert SOF to POF
%QUARTUS_PATH%\quartus_cpf -c output_files/top.sof output_files/top.pof
REM Step 2: Program the flash
%QUARTUS_PATH%\quartus_pgm -m jtag -o "P;output_files/top.pof@1"
Use this for final deployment when the board should boot with your design.
program.bat Template
@echo off
set QUARTUS_PATH="C:\intelFPGA_lite\17.1\quartus\bin64"
if not exist output_files\top.sof (
echo ERROR: No .sof file found. Run compilation first.
exit /b 1
)
echo Programming DE10-Lite via USB-Blaster (JTAG)...
%QUARTUS_PATH%\quartus_pgm -m jtag -o "P;output_files/top.sof@1"
if %ERRORLEVEL% EQU 0 (
echo PROGRAMMING SUCCESSFUL
) else (
echo PROGRAMMING FAILED
echo Check: USB cable connected? Driver installed? Board powered?
exit /b %ERRORLEVEL%
)
Compile + Program Combined
@echo off
set QUARTUS_PATH="C:\intelFPGA_lite\17.1\quartus\bin64"
echo === COMPILE ===
%QUARTUS_PATH%\quartus_sh --flow compile top
if %ERRORLEVEL% NEQ 0 ( echo COMPILE FAILED & exit /b 1 )
echo === PROGRAM ===
%QUARTUS_PATH%\quartus_pgm -m jtag -o "P;output_files/top.sof@1"
if %ERRORLEVEL% NEQ 0 ( echo PROGRAM FAILED & exit /b 1 )
echo === DONE ===
Simulation (ModelSim)
ModelSim Path
C:\intelFPGA_lite\17.1\modelsim_ase\win32aloem\vsim.exe
ModelSim .do Script Template (Proven Pattern)
# Quit any running simulation
quit -sim
# Create/reset work library
if {[file exists work]} { vdel -all -lib work }
vlib work
# Compile RTL
vlog -work work +acc ../src/my_module.sv
# Compile Testbench
vlog -work work +acc ../testbench/tb_my_module.sv
# Load simulation
vsim -novopt work.tb_my_module
# Add waves
add wave -divider "Clock & Reset"
add wave -hex /tb_my_module/clk
add wave -hex /tb_my_module/rst_n
add wave -divider "I/O"
add wave -hex /tb_my_module/dut/*
# Run
run -all
Batch Simulation (No GUI)
set MODELSIM_PATH="C:\intelFPGA_lite\17.1\modelsim_ase\win32aloem"
%MODELSIM_PATH%\vsim -c -do run_sim.do
The -c flag runs in console mode (no GUI). Useful for CI or batch regression.
PowerShell Test Runner Pattern
From CoCode_clock — scan for ModelSim in common paths:
$ModelSimPaths = @(
"C:\intelFPGA\17.1\modelsim_ase\win32aloem\vsim.exe",
"C:\intelFPGA_lite\17.1\modelsim_ase\win32aloem\vsim.exe",
"C:\ModelSim\win32aloem\vsim.exe"
)
foreach ($Path in $ModelSimPaths) {
if (Test-Path $Path) { $VsimPath = $Path; break }
}
MIF-Based ROM Programming (Software → Hardware)
For projects with a CPU (Hack, RISC-V), the code is loaded via .mif files:
- Write assembly program (
.sor.asm) - Assemble to
.mif(Memory Initialization File) - Place
.mifin project root (referenced by Qsys ROM IP) - Recompile Quartus project
- Program the board
REM Example: assemble_for_fpga.bat
python assembler.py program.s -o program_rom.mif -s 1024
copy program_rom.mif ..\..\project_dir\program_rom.mif
The ROM IP core in Qsys references the .mif file. Changing the .mif requires recompilation — the data is baked into the bitstream.
Qsys IP Core Generation
IP cores (PLL, RAM, ROM, ADC) are created in Platform Designer (Qsys):
- Open Quartus → Tools → Platform Designer
- Add component (e.g., ALTPLL, RAM: 2-PORT, ADC)
- Configure parameters
- Generate HDL → produces
.qipfile - Add
.qipto project:set_global_assignment -name QIP_FILE pll25.qip
Common IP Cores Used
| IP Core | QIP File | Purpose |
|---|---|---|
| ALTPLL | pll25.qip |
50→25/50/100 MHz clock generation |
| ADC | adc/synthesis/adc.qip |
MAX 10 onboard ADC controller |
| RAM: 2-PORT | text_ram_a.qip |
Dual-port RAM (text screen, etc.) |
| ROM: 1-PORT | font_rom.qip |
Font bitmap ROM |
| ROM: 1-PORT | cpu_rom.qip |
CPU program ROM (.mif) |
| RAM: 1-PORT | cpu_ram.qip |
CPU data RAM |
| ROM: 1-PORT | lcd_cmd.qip |
LCD init command sequence |
Troubleshooting
| Problem | Solution |
|---|---|
quartus_pgm fails: "No device found" |
Check USB cable, install USB-Blaster driver, try different USB port |
quartus_pgm fails: "JTAG chain broken" |
Power-cycle the board, ensure no other Quartus instance has the programmer open |
| Compilation fails: "can't find module" | Check all source files listed in .qsf with correct paths |
| Compilation fails: "multiple drivers" | Check for conflicting assign statements or multiple always blocks driving same signal |
| Timing violations (setup/hold) | Check output_files/top.sta.summary — may need to reduce clock frequency or add pipeline stages |
.mif changes not reflected |
Must recompile — .mif is baked into the SRAM bitstream |
| Pin assignment mismatch | Compare .qsf pin assignments against DE10-Lite manual / golden top module |
| Qsys IP "file not found" | Regenerate IP → re-generate HDL, ensure .qip path in .qsf is correct |
Quick Reference: New Project Checklist
- Copy
DE10_LITE_Golden_Top.v(or.sv) from a working project - Copy
top.qpf— editPROJECT_REVISIONif needed - Copy
top.qsf— has all 400+ pin assignments already set - Add your source files to
.qsf:set_global_assignment -name SYSTEMVERILOG_FILE src/myfile.sv - Generate any needed IP cores in Platform Designer → add
.qipreferences - Write your logic modules in
src/ - Instantiate everything in
DE10_LITE_Golden_Top run.bat→ compileprogram.bat→ burn to board- Test on hardware
FPGA Design Patterns Cookbook (DE10-Lite)
For Future Projects: Cross-cutting design patterns observed across all DE10-Lite FPGA projects — timing, resets, FSMs, and common pitfalls.
1. Asynchronous Active-Low Reset
Used in: Every single module across all projects.
always @(posedge clk or negedge resetN) begin
if (!resetN) begin
// Reset state
end else begin
// Normal operation
end
end
- DE10-Lite convention:
SW[9]is the system reset (active-low via slide switch) - KEY buttons are also active-low with Schmitt trigger
- ALL flip-flops must have consistent reset polarity
2. Counter-Based Timing
Divide 50 MHz clock for slower operations:
| Desired Rate | Counter Max | Bits Needed |
|---|---|---|
| 1 Hz | 49,999,999 | 26 bits |
| 2 Hz | 24,999,999 | 25 bits |
| 60 Hz (frame) | 833,332 | 20 bits |
| 1 kHz | 49,999 | 16 bits |
| ~50 Hz debounce | 999,999 (~20ms) | 20 bits |
Pattern: Counter + pulse (single-cycle) or toggle (clock output).
3. Edge Detection (CDC Safe)
Sample slow signal in fast clock domain:
reg signal_d;
always @(posedge clk) signal_d <= signal;
wire rising_edge = signal && !signal_d;
wire falling_edge = !signal && signal_d;
Used for: v_sync → start_of_frame, clk_1hz in 50 MHz domain, debounced button edges.
4. FSM Encoding Styles
Localparam (most common)
localparam IDLE = 2'd0, RUNNING = 2'd1, DONE = 2'd2;
reg [1:0] state;
One-Hot (analog_input.sv)
localparam IDLE = 12'b000000000001;
localparam WRITE_0 = 12'b000000000010;
// Faster decode, more FFs, common in FPGA
Enum (lcd_ctrl.sv — Altera recommended)
enum logic [3:0] {ST_IDLE, ST_INIT, ST_STREAM, ST_DONE} state;
// Best for readability, Quartus optimizes encoding
5. Pipeline Delay Buffering
When reading RAM/ROM takes N cycles, buffer the address/control signals:
// 3-stage pipeline example (text_screen)
reg [9:0] pxl_x_d1, pxl_x_d2;
always @(posedge clk) begin
pxl_x_d1 <= pxl_x; // Delay 1: RAM read
pxl_x_d2 <= pxl_x_d1; // Delay 2: ROM read
end
// Use pxl_x_d2 for bit-select in cycle 3
6. Memory-Mapped I/O
Address decode pattern for CPU-attached peripherals:
// Write routing
assign ram_we = we && (addr < 16'h4000);
assign vga_we = we && (addr >= 16'h4000) && (addr < 16'h6000);
// Read mux
always @(*) begin
casez (addr)
16'b00??????????????: data_out = ram_data;
16'b010?????????????: data_out = vga_data; // text screen
16'h6000: data_out = keyboard;
16'h6001: data_out = {8'b0, switches};
default: data_out = 16'h0000;
endcase
end
7. Signed vs Unsigned Sprite Math
Use signed for sprite positions (allow negative for off-screen):
input signed [31:0] topLeft_x; // Can be negative!
wire signed [31:0] rightX = topLeft_x + WIDTH;
// This correctly handles sprites partially off-screen
assign drawingRequest = (pxl_x >= topLeft_x) && (pxl_x < rightX);
8. Combinational Boundary Clamping
Prevent sprite from going off-screen:
// Clamp to valid range in one expression
assign safe_x = (raw_x < 0) ? 0 :
(raw_x > 640-WIDTH) ? 640-WIDTH : raw_x;
9. Dual-Port RAM Pattern
For CPU-write + VGA-read scenarios:
Port A (read): VGA scan address → character/pixel data
Port B (write): CPU address + data + write_enable
Always separate read and write concerns to different ports.
10. I/O Assignment Conventions
| Resource | Typical Assignment |
|---|---|
SW[9] |
System resetN |
SW[8:7] |
Display mode selection |
SW[6] |
CPU/subsystem reset |
SW[0] |
Manual start trigger |
KEY[0] |
Mode button |
KEY[1] |
Increment button |
LEDR[7:0] |
Input state indicators |
LEDR[9] |
Heartbeat (1-second blink) |
HEX[5:4] |
Hours / high value |
HEX[3:2] |
Minutes / mid value |
HEX[1:0] |
Seconds / low value |
Common Pitfalls
| Pitfall | Fix |
|---|---|
| Combinational loop | Ensure all paths through always @(*) assign to output |
| Missing reset | Every always @(posedge clk) needs reset branch |
| Multi-driven net | Only one always block can drive a signal |
| Clock domain crossing | Sample slow clock in fast domain with edge detect |
| RAM read latency | Buffer address/control to match pipeline delay |
| Unsigned overflow | Use signed for sprite positions, boundary checks |
Reusable IP Blocks for DE10-Lite Projects
For Future Projects: Catalog of proven, reusable SystemVerilog/Verilog modules used across multiple DE10-Lite projects.
1. Seven-Segment Decoder
Hex digit (4-bit) to active-low 7-segment display:
module seven_segment (
input [3:0] data,
output reg [7:0] seg // Active-low: {DP, G, F, E, D, C, B, A}
);
always @(*) begin
case (data)
4'h0: seg = 8'b11000000; 4'h1: seg = 8'b11111001;
4'h2: seg = 8'b10100100; 4'h3: seg = 8'b10110000;
4'h4: seg = 8'b10011001; 4'h5: seg = 8'b10010010;
4'h6: seg = 8'b10000010; 4'h7: seg = 8'b11111000;
4'h8: seg = 8'b10000000; 4'h9: seg = 8'b10010000;
4'hA: seg = 8'b10001000; 4'hB: seg = 8'b10000011;
4'hC: seg = 8'b11000110; 4'hD: seg = 8'b10100001;
4'hE: seg = 8'b10000110; 4'hF: seg = 8'b10001110;
default: seg = 8'b11111111; // All off
endcase
end
endmodule
Blanking variant (CoCode_clock): Add blink_enable input — when LOW, output 8'hFF (all off).
2. One-Second Timer
Generates 1-second pulses from 50 MHz clock:
module one_sec (
input clk, // 50 MHz
input resetN,
output reg pulse, // Single-cycle pulse every 1 second
output reg [3:0] counter // 0-15 rolling counter
);
localparam DIVISOR = 50_000_000;
reg [25:0] count;
always @(posedge clk or negedge resetN) begin
if (!resetN) begin
count <= 0; pulse <= 0; counter <= 0;
end else begin
pulse <= 0;
if (count == DIVISOR - 1) begin
count <= 0;
pulse <= 1;
counter <= counter + 1;
end else
count <= count + 1;
end
end
endmodule
3. Clock Divider (Parameterized)
Generate arbitrary frequency from 50 MHz:
module clock_divider #(
parameter COUNT_MAX = 25_000_000 - 1 // 1 Hz default
)(
input clk_50mhz,
input reset_n,
output reg clk_out
);
reg [25:0] counter;
always @(posedge clk_50mhz or negedge reset_n) begin
if (!reset_n) begin counter <= 0; clk_out <= 0; end
else if (counter == COUNT_MAX) begin counter <= 0; clk_out <= ~clk_out; end
else counter <= counter + 1;
end
endmodule
4. Button Debounce with Edge Detection
Clean single-cycle pulse from noisy pushbutton:
module button_debounce (
input clk, // 50 MHz
input reset_n,
input btn_raw, // Active-low raw button
output reg btn_pulse // Single-cycle clean pulse
);
reg [19:0] counter; // ~20 ms debounce @ 50 MHz
reg stable, prev;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin counter <= 0; stable <= 1; prev <= 1; btn_pulse <= 0; end
else begin
btn_pulse <= 0;
if (btn_raw != stable) begin
if (counter == 20'hFFFFF) begin
stable <= btn_raw;
counter <= 0;
end else
counter <= counter + 1;
end else
counter <= 0;
prev <= stable;
if (prev && !stable) // Falling edge = button press
btn_pulse <= 1;
end
end
endmodule
5. PLL25 (Quartus IP)
Generate 25/50/100 MHz from 50 MHz input:
| Output | Frequency | Use |
|---|---|---|
c0 |
25 MHz | VGA pixel clock |
c1 |
50 MHz | System clock (pass-through) |
c2 |
100 MHz | LCD controller fast clock |
Setup: Quartus → IP Catalog → ALTPLL → 50 MHz input → configure 3 outputs.
6. Obj_Rect (Parameterized Bounds Checker)
Reusable sprite bounds test with signed coordinates:
module obj_rect #(
parameter OBJECT_WIDTH_X = 32,
parameter OBJECT_HEIGHT_Y = 16
)(
input signed [31:0] pxl_x, pxl_y,
input signed [31:0] topLeft_x, topLeft_y,
output [10:0] offsetX, offsetY,
output drawingRequest
);
// Supports negative topLeft (sprite partially off-screen)
7. Sync Gen (Simple VGA)
All-in-one sync generator with built-in 50→25 MHz divider:
module sync_gen (
input clk_50, // 50 MHz input
input resetN,
output vga_h_sync, vga_v_sync,
output disp_ena,
output [9:0] pixel_x, pixel_y
);
// Internal: divides 50→25 MHz, counts 800×525
Cross-Reference: Which Module Where
| Module | nand2tetris | Arcade Template | CoCode_clock |
|---|---|---|---|
| seven_segment | ✓ | — | ✓ |
| one_sec | ✓ | — | — |
| clock_divider | — | — | ✓ |
| button_debounce | — | — | ✓ |
| pll25 | ✓ | ✓ | — |
| obj_rect | ✓ | — | — |
| vga_controller | ✓ | ✓ | — |
| analog_input | ✓ | ✓ | — |
| lcd_ctrl | ✓ | ✓ | — |
DE10-Lite Arcade Game Template (Quick-Start)
For Future Projects: Complete starter template for building arcade-style games on the DE10-Lite with VGA, LCD, joystick, buttons, and sprites.
Template File Structure
Project/
├── Top_template.sv ← Top-level: wires everything together
├── Screens_dispaly.sv ← VGA + LCD display manager
├── periphery_control.sv ← Joystick/button decoder (analog→boolean)
├── Drawing_priority.sv ← Sprite priority compositor
├── Intel_unit.sv ← Player sprite (Move + Draw)
│ ├── Move_Intel.sv ← Player movement logic
│ └── Draw_Intel.sv ← Player pixel renderer (128×64 bitmap)
├── Ghost_unit.sv ← Enemy sprite (Move + Draw)
│ ├── Move_Ghost.sv ← Autonomous bounce movement
│ └── Draw_Ghost.sv ← Enemy pixel renderer (64×64 bitmap)
├── vga_controller.v ← Parameterized VGA sync generator
├── lcd_ctrl.sv ← LCD mirror controller
├── analog_input.sv ← ADC FSM for 6 channels
├── adc/ (Qsys-generated) ← ADC IP block
└── pll25/ (Qsys-generated) ← PLL IP block (50→25/50/100 MHz)
Top-Level Wiring Pattern
module Top_template (
input MAX10_CLK1_50,
input [9:0] SW, [1:0] KEY,
output [9:0] LEDR,
output [7:0] HEX0..HEX5,
output [3:0] VGA_R, VGA_G, VGA_B,
output VGA_HS, VGA_VS,
inout [15:0] ARDUINO_IO, ARDUINO_RESET_N
);
// Clock generation
pll25 pll_inst (.inclk0(MAX10_CLK1_50), .c0(clk_25), .c1(clk_50), .c2(clk_100));
// Display output (VGA + LCD mirror)
Screens_display display_inst (
.clk_25, .clk_100, .resetN(SW[9]),
.Red_level, .Green_level, .Blue_level,
.pxl_x, .pxl_y,
.VGA_R, .VGA_G, .VGA_B, .VGA_HS, .VGA_VS,
.ARDUINO_IO
);
// Input controls
periphery_control controls_inst (
.clk(clk_25), .resetN(SW[9]),
.A, .B, .Select, .Start,
.Right, .Left, .Up, .Down, .Wheel,
.ARDUINO_IO
);
// Player sprite
Intel_unit player_inst (
.clk(clk_25), .resetN(SW[9]),
.pxl_x, .pxl_y,
.Wheel, .Up, .Down,
.Red(player_R), .Green(player_G), .Blue(player_B), .Draw(player_draw)
);
// Enemy sprite
Ghost_unit enemy_inst (
.clk(clk_25), .resetN(SW[9]),
.pxl_x, .pxl_y,
.collision(1'b0), // Wire to collision detection
.Red(enemy_R), .Green(enemy_G), .Blue(enemy_B), .Draw(enemy_draw)
);
// Priority compositor (player > enemy > background)
Drawing_priority compositor_inst (
.clk(clk_25), .resetN(SW[9]),
.RGB_1({player_R, player_G, player_B}), .draw_1(player_draw),
.RGB_2({enemy_R, enemy_G, enemy_B}), .draw_2(enemy_draw),
.RGB_bg(12'hFFF), // White background
.Red_level, .Green_level, .Blue_level
);
How to Customize
Add a New Sprite
- Copy
Intel_unit.sv→My_unit.sv - Replace bitmap in
Draw_Intel.svwith your sprite image - Modify
Move_Intel.svfor your movement logic - Add to
Drawing_priority.svchain (wire newdraw_N/RGB_N) - Instantiate in top module
Add Background
Replace RGB_bg(12'hFFF) with a background module:
// Tiled background, scrolling, etc.
bg_unit bg_inst (.pxl_x, .pxl_y, .bg_select(SW[2:0]), .RGB(bg_RGB));
Add Collision Detection
// Simple overlap check
wire collision = player_draw && enemy_draw;
// Feed back to enemy to trigger bounce
Add Scoring / Game State
Use text_screen module for score display, or seven-segment for simple counters.
LED Debug Mapping
assign LEDR[0] = A; // Button A pressed
assign LEDR[1] = B; // Button B pressed
assign LEDR[2] = Select; // Select pressed
assign LEDR[3] = Start; // Start pressed
assign LEDR[4] = Right; // Joystick right
assign LEDR[5] = Left; // Joystick left
assign LEDR[6] = Up; // Joystick up
assign LEDR[7] = Down; // Joystick down
Source Project
Template from: C:\Projects\FPGA\VGAstarter_DE10_lite\Arcade_template_restored\
Designed by: Mor (Mordechai) Dahan, Sep. 2022 (Technion IIT course)
Pipeline Accumulator — Single-Adder FSM Design (ADDER_PIPE)
For Future Projects: Demonstrates constrained datapath design — accumulating N inputs using exactly one adder, controlled by a 3-state FSM.
Design Constraint
Use exactly one physical adder to accumulate 1–16 unsigned 32-bit inputs streamed one per clock.
Interface
module pipeline_accumulator (
input clk,
input resetN,
input start, // Single-cycle trigger
input [3:0] num_inputs, // N-1 (0→1 input, 15→16 inputs)
input [31:0] data_in, // Streamed input (one per cycle)
output reg [31:0] data_out, // Accumulated sum
output reg valid_out // Single-cycle result pulse
);
FSM States
IDLE ──(start)──► ACCUMULATING ──(counter==0)──► DONE ──► IDLE
| State | Duration | Action |
|---|---|---|
IDLE |
Until start | Wait for start pulse |
ACCUMULATING |
N cycles | Add data_in to accumulator each cycle |
DONE |
1 cycle | Assert valid_out, output final sum |
Key Pattern: Single-Resource Datapath
// THE ONLY ADDER in the design
wire [31:0] adder_a = clear_acc ? 32'b0 : accumulator;
wire [31:0] adder_b = data_in;
assign adder_sum = adder_a + adder_b;
// Accumulator register with conditional enable
always @(posedge clk or negedge resetN) begin
if (!resetN) accumulator <= 32'b0;
else if (enable_acc) accumulator <= adder_sum;
end
How it works:
clear_acc = 1(first cycle):adder_a = 0, sosum = 0 + data_in[0]clear_acc = 0(subsequent):adder_a = accumulator, sosum = running_total + data_in[n]
2-Process FSM Pattern (Textbook)
// Process 1: State register
always @(posedge clk or negedge resetN) begin
if (!resetN) state <= IDLE;
else state <= next_state;
end
// Process 2: Next-state + output logic (combinational)
always @(*) begin
next_state = state; // Default: stay
enable_acc = 0; clear_acc = 0; valid_out = 0;
case (state)
IDLE: begin
if (start) begin
next_state = ACCUMULATING;
enable_acc = 1;
clear_acc = 1; // First add: 0 + data_in
end
end
ACCUMULATING: begin
enable_acc = 1;
if (counter == 0) next_state = DONE;
end
DONE: begin
valid_out = 1;
next_state = IDLE;
end
endcase
end
Timing Diagram
clk: _|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_
start: ___|‾‾‾|___________________
data_in: XX| A | B | C | D |XXX
state: IDLE|ACM |ACM |ACM |DONE|IDLE
accum: 0 | A |A+B |A+B+C|sum|0
valid_out: ______________________|‾‾‾|___
data_out: XXXXXXXXXXXXXXXXXXXXX|sum |XXX
For num_inputs = 3 (4 inputs): Latency = 4+1 = 5 cycles.
Design Lessons
- Resource sharing: One adder shared across N additions via temporal multiplexing
- Clear + enable pattern:
clear_accinitializes accumulator without separate state - Down-counter: Simpler than up-counter for "remaining inputs" tracking
- Single-cycle result:
valid_outis exactly 1 cycle — safe for downstream handshake - 2-process FSM: Clean separation of state register (sequential) and logic (combinational)
Source Location
C:\Projects\FPGA\ADDER_PIPE\src\pipeline_accumulator.sv
Digital Clock Project — FSM + BCD + 7-Segment (CoCode_clock)
For Future Projects: Modular digital clock design on DE10-Lite showing clean FSM-based time counter with mode setting, BCD display, and button debounce.
Architecture
clk_50mhz ──► clock_divider ──► clk_1hz (counting)
──► clk_2hz (blink)
KEY[0] ──► button_controller ──► mode_pulse ──┐
KEY[1] ──► ──► inc_pulse ──┤
▼
time_counter
│ hours[4:0] (0-23)
│ minutes[5:0] (0-59)
│ seconds[5:0] (0-59)
│ setting_mode[1:0]
▼
display_controller
│ digit[5:0][3:0] (BCD)
│ blink_enable[5:0]
▼
seven_segment_decoder ×6 ──► HEX[5:0]
Mode FSM (time_counter.sv)
MODE_NORMAL ──(mode_pulse)──► MODE_SET_HOURS ──(mode_pulse)──► MODE_SET_MINUTES ──(mode_pulse)──► MODE_NORMAL
| Mode | Behavior | inc_pulse Action |
|---|---|---|
MODE_NORMAL (0) |
Clock runs normally | (ignored) |
MODE_SET_HOURS (1) |
Clock paused, hours blink | Increment hours (0-23 wrap) |
MODE_SET_MINUTES (2) |
Clock paused, minutes blink | Increment minutes (0-59 wrap) |
Key Patterns Demonstrated
1. Clock Domain Crossing (CDC)
1 Hz and 2 Hz signals sampled in 50 MHz domain:
reg clk_1hz_prev;
always @(posedge clk_50mhz) begin
clk_1hz_prev <= clk_1hz;
if (clk_1hz && !clk_1hz_prev) // Rising edge in 50 MHz domain
// ... do 1-second action
end
2. Cascaded Rollover Counters
// seconds → minutes → hours cascade
if (seconds == 59) begin
seconds <= 0;
if (minutes == 59) begin
minutes <= 0;
if (hours == 23) hours <= 0;
else hours <= hours + 1;
end else minutes <= minutes + 1;
end else seconds <= seconds + 1;
3. Binary to BCD Conversion
function [3:0] tens; input [5:0] val; tens = val / 10; endfunction
function [3:0] ones; input [5:0] val; ones = val % 10; endfunction
// HEX5:HEX4 = hours, HEX3:HEX2 = minutes, HEX1:HEX0 = seconds
digit[5] = tens(hours); digit[4] = ones(hours);
digit[3] = tens(minutes); digit[2] = ones(minutes);
digit[1] = tens(seconds); digit[0] = ones(seconds);
4. Selective Digit Blink
// blink_enable: 1=show, 0=blank
case (setting_mode)
MODE_NORMAL: blink_enable = 6'b111111; // All visible
MODE_SET_HOURS: blink_enable = {blink_state, blink_state, 4'b1111};
MODE_SET_MINUTES: blink_enable = {2'b11, blink_state, blink_state, 2'b11};
endcase
blink_state toggles at 2 Hz, making the active digit pair flash.
Test Infrastructure
The CoCode_clock project includes comprehensive test scripts:
| Script | Purpose |
|---|---|
run_simple_test.ps1 |
Basic functional test |
run_enhanced_test.ps1 |
Extended test coverage |
run_final_test.ps1 |
Full regression |
run_system_test.ps1 |
System-level integration |
run.bat |
Quick compilation + program |
Documentation
| File | Contents |
|---|---|
ARCHITECTURE_SPEC.md |
High-level system architecture |
MICROARCHITECTURE_SPEC.md |
Module-level detail |
IMPLEMENTATION_SUMMARY.md |
Feature status and notes |
HARDWARE_TEST_CHECKLIST.md |
Manual test procedures |
TEST_STRATEGY.md |
Test methodology |
Source Location
C:\Projects\FPGA\CoCode_clock\
Hack CPU on DE10-Lite (nand2tetris System)
For Future Projects: Complete nand2tetris Hack computer implementation on DE10-Lite — CPU, memory arbiter, ALU, and memory-mapped I/O.
System Architecture
cpu_rom (32K×16) ──► hack_cpu ──► mem_space_arbiter ──┬── cpu_ram (16K×16)
│ ├── text_screen (8K)
│ ├── keyboard (addr 0x6000)
alu ──┘ └── switches (addr 0x6001)
Hack CPU (hack_cpu.sv)
| Feature | Specification |
|---|---|
| Word size | 16 bits |
| Architecture | Harvard (separate instruction/data ROM) |
| Cycles | Dual-cycle: Fetch (stage=0) → Execute (stage=1) |
| Registers | A (16-bit address/data), D (16-bit data), PC (14-bit) |
| ROM | 16K × 16 instruction ROM |
Instruction Format
A-instruction: 0vvv_vvvv_vvvv_vvvv (load 15-bit value into A)
C-instruction: 111a_cccc_ccdd_djjj
│ │ │ └── jump condition (3 bits)
│ │ └───── destination (3 bits)
│ └───────────── computation (6 bits)
└─────────────── a=0: use A, a=1: use M[A]
Destination Decode
| d2 d1 d0 | Destination |
|---|---|
| 000 | null (no store) |
| 001 | M (RAM[A]) |
| 010 | D register |
| 011 | M and D |
| 100 | A register |
| 101 | A and M |
| 110 | A and D |
| 111 | A, M, and D |
Jump Conditions
| j2 j1 j0 | Mnemonic | Condition |
|---|---|---|
| 000 | (none) | No jump |
| 001 | JGT | out > 0 |
| 010 | JEQ | out == 0 |
| 011 | JGE | out >= 0 |
| 100 | JLT | out < 0 |
| 101 | JNE | out != 0 |
| 110 | JLE | out <= 0 |
| 111 | JMP | Always |
ALU Operations (alu.sv)
18 operations selected by 6-bit comp code:
| comp[5:0] | Operation | Description |
|---|---|---|
| 101010 | 0 | Zero |
| 111111 | 1 | One |
| 111010 | -1 | Minus one |
| 001100 | D | D register |
| 110000 | A (or M) | A register (or memory) |
| 001101 | !D | Bitwise NOT D |
| 110001 | !A | Bitwise NOT A |
| 001111 | -D | Negate D |
| 110011 | -A | Negate A |
| 011111 | D+1 | Increment D |
| 110111 | A+1 | Increment A |
| 001110 | D-1 | Decrement D |
| 110010 | A-1 | Decrement A |
| 000010 | D+A | Add |
| 010011 | D-A | Subtract |
| 000111 | A-D | Reverse subtract |
| 000000 | D&A | Bitwise AND |
| 010101 | D|A | Bitwise OR |
Memory Space Arbiter (mem_space_arbiter.sv)
| Address Range | Size | Target | Description |
|---|---|---|---|
0x0000–0x3FFF |
16K | cpu_ram | General-purpose RAM |
0x4000–0x5FFF |
8K | text_screen | Character display RAM |
0x6000 |
1 | keyboard | Keyboard input (returns 0) |
0x6001 |
1 | switches | SW[7:0] (8-bit input) |
Write Routing
assign ram_we = we && (addr < 16'h4000);
assign vga_text_wr = we && (addr >= 16'h4000) && (addr < 16'h6000);
Read Mux
always @(*) begin
if (addr < 16'h4000) data_out = ram_data;
else if (addr == 16'h6000) data_out = keyboard_data;
else if (addr == 16'h6001) data_out = {8'b0, SW[7:0]};
else data_out = 16'h0000;
end
Integration on DE10-Lite
// CPU instance
hack_cpu cpu_inst (
.clk(clk_25), .resetN(resetN),
.instruction(rom_data),
.mem_in(arbiter_data_out),
.mem_out(cpu_data), .mem_addr(cpu_addr),
.mem_we(cpu_we), .pc(cpu_pc)
);
// Instruction ROM (pre-loaded with .mif file)
cpu_rom rom_inst (.address(cpu_pc), .clock(clk_25), .q(rom_data));
// Memory arbiter
mem_space_arbiter arb_inst (
.clk(clk_25), .addr(cpu_addr), .data_in(cpu_data),
.we(cpu_we), .SW(SW[7:0]),
.data_out(arbiter_data_out),
.vga_text_wr(vga_text_wr)
);
Programming the CPU
- Write Hack assembly (
.asm) or Jack high-level language - Compile to
.hackbinary using nand2tetris tools - Convert to Quartus
.mif(Memory Initialization File) - Load as init file for
cpu_romIP block - Recompile Quartus project → program FPGA