A complete Motorola 68000-compatible System-on-Chip implementation for Lattice iCE40 FPGAs, featuring the j68 CPU core with external SRAM, LED control, and comprehensive ModelSim verification.
- Features
- Architecture
- Resource Utilization
- Performance
- Hardware Requirements
- Software Requirements
- Quick Start
- Building the SOC
- Simulation
- Firmware Development
- Memory Map
- Project Structure
- Verification
- Known Issues
- Contributing
- License
- Acknowledgments
- Motorola 68000-compatible instruction set
- Microcode-based architecture - not cycle-accurate but functionally equivalent
- Stack-based with Forth-like microcode
- All M68000 instructions implemented
- Auto-vector interrupts supported
- 3× frequency overhead - requires ~3× higher clock than real MC68000
- 4 KB Boot ROM @ 0x00000000 (on-chip BRAM)
- 512 KB External SRAM @ 0x00001000 (via unified controller)
- 16-bit data bus for efficient memory access
- Single-cycle BRAM reads for boot ROM
- 4-cycle SRAM accesses (controlled by unified SRAM controller)
- 2× GPIO LEDs @ 0xFF000000 (memory-mapped)
- Debug UART register @ 0xFF000004 (simulation only)
- Extensible I/O space at 0xFF000000+ (1,392 LCs / 12 BRAMs available)
- Complete ModelSim test suite (PicoRV32-style)
- LED toggle pattern verification (10 cycles, 40 state changes)
- Icarus Verilog support for open-source simulation
- Waveform dumps (VCD format)
- Automated test scripts with progress monitoring
┌─────────────────────────────────────────────────────────────┐
│ M68000 SOC Top Level │
│ (m68k_soc_top.v) │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌────────────────┐ │
│ │ j68 CPU │◄────►│ Boot ROM │ │
│ │ (M68000) │ │ (4 KB BRAM) │ │
│ │ │ └────────────────┘ │
│ │ - 32-bit │ │
│ │ - Microcode │ ┌────────────────┐ │
│ │ - 20-bit µOP│◄────►│ SRAM Controller│◄───► SRAM │
│ │ - 2KB µROM │ │ (Unified) │ (512KB) │
│ │ - 256B Dec │ └────────────────┘ │
│ └──────────────┘ │
│ ▲ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Peripherals │ │
│ ├──────────────┤ │
│ │ LED Control │ @ 0xFF000000 │
│ │ Debug UART │ @ 0xFF000004 │
│ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
The j68 is a microcode-based reimplementation of the Motorola 68000:
- Not cycle-accurate - functional compatibility prioritized
- ~3-120 microcode cycles per instruction (average ~20)
- 2048×20-bit microcode ROM (5× 2048×4-bit BRAMs)
- 256×36-bit decode ROM (1× BRAM)
- Stack-based execution with internal stacks for data/instructions
- Forth-like microcode for compact implementation
| Resource | Used | Available | Utilization |
|---|---|---|---|
| Logic Cells (LCs) | 6,288 | 7,680 | 81.9% |
| Block RAMs | 20 | 32 | 62.5% |
| PLBs | 786 | 960 | 81.9% |
| I/O Pins | ~40 | 256 | ~15.6% |
CPU Core (j68):
- Logic Cells: ~5,500 LCs (estimated)
- Block RAMs: 18 BRAMs
- 5× BRAMs for microcode (2048×20-bit split into 5× 4-bit)
- 1× BRAM for decode ROM (256×36-bit)
- 12× BRAMs for internal stacks and registers
SOC Infrastructure:
- Logic Cells: ~788 LCs
- SRAM Controller: ~400 LCs
- Boot ROM: 1 BRAM (4KB)
- LED/Peripheral logic: ~100 LCs
- Clock/Reset: ~50 LCs
Available for Expansion:
- 1,392 Logic Cells (18.1% of device)
- 12 Block RAMs (37.5% of device)
- Sufficient for: UART, SPI, Timers, DMA, Interrupt Controller
| Clock Target | Status | Measured | Timing Margin |
|---|---|---|---|
| 25 MHz (design target) | ✓ PASS | 46.53 MHz | +21.53 MHz |
| 40 MHz (typical) | ✓ PASS | 46.53 MHz | +6.53 MHz |
| 47.93 MHz (max) | ✓ PASS | 47.93 MHz | 0 MHz |
Recommended operating frequency: 25-40 MHz
Due to the ~3× microcode frequency overhead:
| FPGA Clock | Equivalent M68K Speed | Notes |
|---|---|---|
| 25 MHz | ~8.3 MHz | Conservative, guaranteed timing |
| 40 MHz | ~13.3 MHz | Typical operation, good margin |
| 47 MHz | ~15.7 MHz | Maximum, tight timing |
Comparison to original MC68000:
- Original MC68000: 8 MHz (1979), 16 MHz (1982)
- This implementation @ 40 MHz ≈ 13.3 MHz MC68000
| Instruction Type | Microcode Cycles | Notes |
|---|---|---|
| Register operations | 6-12 cycles | MOV, ADD, SUB, etc. |
| Memory access | 15-30 cycles | Load/Store with addressing |
| Branches | 8-15 cycles | Conditional/unconditional |
| Complex instructions | 40-120 cycles | DIVU, MULU, shifts |
Average CPI (Cycles Per Instruction): ~20 microcode cycles
Critical path: 20.86 ns (47.93 MHz)
Logic delay: 5.10 ns (24%)
Routing delay: 15.77 ns (76%)
Bottleneck: Memory interface routing (CPU ↔ SRAM controller)
Optimization opportunities:
- Register CPU-SRAM interface (+5-10 MHz potential)
- Reduce fanout on memory bus (+2-5 MHz potential)
- Pipeline memory controller (+10-15 MHz potential)
- Lattice iCE40HX8K-CT256 evaluation board
- 512 KB SRAM (16-bit data bus, 18-bit address)
- 50 MHz oscillator (on-board)
- 2× LEDs for status indication
- iCE40HX4K or larger (7,680+ LCs, 32+ BRAMs)
- 256 KB+ SRAM (16-bit or 8-bit bus)
- 25 MHz+ clock source
- JTAG programmer (or SPI flash programmer)
See ice40hx8k_ct256.pcf for complete pin configuration.
Critical Signals:
CLK: H16 (50 MHz oscillator)
SRAM_ADDR: A3-D3, A2-D2 (18-bit address bus)
SRAM_DATA: A1-D1 (16-bit bidirectional data bus)
SRAM_CS_N: E1 (Chip select, active low)
SRAM_OE_N: F1 (Output enable, active low)
SRAM_WE_N: G1 (Write enable, active low)
LED1: P12 (GPIO output)
LED2: P11 (GPIO output)
Required:
- Yosys (>= 0.9) - Verilog synthesis
- nextpnr-ice40 (>= 0.2) - Place & Route
- icepack - Bitstream packing
- icetime - Timing analysis
Installation (Ubuntu/Debian):
# Option 1: oss-cad-suite (recommended)
wget https://github.com/YosysHQ/oss-cad-suite-build/releases/download/latest/oss-cad-suite-linux-x64-latest.tgz
tar -xzf oss-cad-suite-linux-x64-latest.tgz
export PATH="$(pwd)/oss-cad-suite/bin:$PATH"
# Option 2: Distribution packages
sudo apt install fpga-icestorm yosys nextpnr-ice40Required:
- m68k-elf-gcc - Cross-compiler
- m68k-elf-as - Assembler
- m68k-elf-ld - Linker
- m68k-elf-objcopy - Binary utilities
- m68k-elf-objdump - Disassembler
Installation:
# Option 1: marsdev toolchain (recommended)
git clone https://github.com/andwn/marsdev.git
cd marsdev && make && cd ..
export PATH="$(pwd)/marsdev/m68k-elf/bin:$PATH"
# Option 2: Build from source (GNU)
# See: https://github.com/m68k-elf-gccModelSim (commercial):
# Intel ModelSim-Intel FPGA Edition
# Download from: https://www.intel.com/content/www/us/en/software/programmable/quartus-prime/modelsim.html
export PATH="/path/to/modelsim_ase/bin:$PATH"Icarus Verilog (open-source):
sudo apt install iverilog gtkwavegit clone https://github.com/yourusername/m68k-fpga-soc.git
cd m68k-fpga-socmake clean
make build
# Output: build/m68k_soc.bin# Using iceprog (direct FTDI programmer)
sudo iceprog build/m68k_soc.bin
# Using OpenOCD (JTAG)
openocd -f interface/ftdi/olimex-arm-usb-ocd-h.cfg \
-f target/ice40.cfg \
-c "init; svf build/m68k_soc.svf; exit"# LEDs should toggle in pattern:
# LED1 → LED2 → BOTH → OFF (repeating)# Full build (synthesis + P&R + bitstream)
make build
# Individual stages
make synth # Synthesis only
make pnr # Place & Route only
make pack # Bitstream generation only
make timing # Timing analysis only
# Cleanup
make clean # Remove build artifacts
make distclean # Remove all generated filesbuild/
├── m68k_soc.bin # FPGA bitstream (program this!)
├── m68k_soc.json # Synthesis output (Yosys)
├── m68k_soc.asc # Place & route output (nextpnr)
├── synth.log # Synthesis log
├── pnr.log # Place & route log
└── timing.rpt # Timing analysis report
Edit Makefile to customize:
# Clock frequency target (MHz)
FREQ = 25
# FPGA device
DEVICE = hx8k
# Package
PACKAGE = ct256
# Synthesis options
SYNTH_OPTS = -dsp # Enable DSP blocks (not used in this design)Full test with monitoring:
cd sim
./run_test_sim_long.sh
# Expected output:
# ✓✓✓ TEST PASSED ✓✓✓
# All 10 LED pattern cycles completed successfully!Interactive simulation:
cd sim
vsim -c -do "do compile_test_sim.do; do run_test_sim.do"
# View waveforms:
vsim -view m68k_test_sim.vcdQuick test:
cd sim
./run_test_iverilog.sh
# Output: test_sim_run.log, m68k_test_sim.vcdView waveforms:
gtkwave m68k_test_sim.vcdThe test firmware (firmware/test_sim.S) performs:
- Prints "Sim Startup\r\n" to debug UART
- Toggles LEDs in pattern: LED1 → LED2 → BOTH → OFF
- Repeats for 10 cycles (40 LED state changes)
- Prints "Test Complete\r\n"
- Halts with STOP instruction
Expected simulation time: ~965 µs @ 50 MHz Pattern cycle time: ~59 µs per cycle
cd firmware
make clean
make
# Output: test_sim.hex (installed to ../sim/)0x00000000: Exception vectors (8 bytes)
0x00000000: Initial SSP (Stack Pointer)
0x00000004: Initial PC (Program Counter = _start)
0x00000008: Boot code (.text section)
- LED toggle routines
- Delay loops
- Debug output functions
0x00001000: RAM (.data section)
- String constants
- Variables
0x00004000: Stack (grows down from here)
- 16 KB initial stack space
The linker script (m68k.ld) defines:
- ROM: 4 KB @ 0x00000000 (code + vectors)
- RAM: 508 KB @ 0x00001000 (data + bss + heap + stack)
- Stack: 512 KB - 16 KB = 0x0007C000 (top of RAM)
| M68000 Exception Vector Table
.section .vectors, "ax"
.long 0x00080000 | Initial SSP (top of SRAM @ 512KB)
.long _start | Initial PC (reset vector)
.text
.globl _start
_start:
| Initialize stack pointer
lea 0x00004000, %sp
| Write to LED register
move.w #0x01, 0xFF000000 | LED1 ON, LED2 OFF
| Simple delay loop
moveq #10, %d0
delay:
subq.l #1, %d0
bne delay
| Infinite loop
halt:
stop #0x2700
bra halt# Generate disassembly
make
# View disassembly
cat test_sim.lst
# Check symbol table
m68k-elf-nm test_sim.elf
# Verify memory layout
m68k-elf-objdump -h test_sim.elf0x00000000 - 0x00000FFF : Boot ROM (4 KB, BRAM, read-only)
Vector table, boot code, constants
0x00001000 - 0x0007FFFF : External SRAM (508 KB, read-write)
RAM data, BSS, heap, stack
0xFF000000 - 0xFF0000FF : Peripheral I/O (256 bytes)
0xFF000000: LED Control Register (16-bit)
Bit 0: LED1 (0=OFF, 1=ON)
Bit 1: LED2 (0=OFF, 1=ON)
Bits 2-15: Reserved
0xFF000004: Debug UART Register (8-bit, simulation only)
Write: Send character to stdout
Read: Always returns 0x00
0xFF000008+: Reserved for future peripherals
| Region | Cycles | Notes |
|---|---|---|
| Boot ROM | 1 | Single-cycle BRAM access |
| SRAM | 4 | Via unified SRAM controller |
| Peripherals | 1 | Direct register access |
m68k-fpga-soc/
├── README.md # This file
├── LICENSE # ISC License
├── Makefile # Top-level build system
│
├── hdl/ # Hardware Description (Verilog)
│ ├── m68k_soc_top.v # Top-level SOC integration
│ ├── m68k_boot_rom.v # Boot ROM with initialization
│ ├── sram_controller_unified.v # External SRAM controller
│ └── j68_cpu/ # j68 M68000 CPU core (upstream)
│ ├── cpu_j68.v # CPU top-level
│ ├── j68_*.v # CPU submodules
│ └── *.mem, *.mif # Microcode ROM initialization
│
├── firmware/ # M68000 Firmware
│ ├── test_sim.S # Original assembly test
│ ├── m68k.ld # Original linker script
│ ├── Makefile # Original build system
│ └── examples/ # Firmware examples
│ ├── 01_asm_simple/ # Assembly LED blink
│ └── 02_c_blink/ # C LED blink with crt0
│
├── sim/ # Simulation Environment
│ ├── m68k_test_sim_tb.v # ModelSim testbench
│ ├── sram_model_16bit.v # SRAM behavioral model
│ ├── compile_test_sim.do # ModelSim compile script
│ ├── run_test_sim.do # ModelSim run script
│ ├── run_test_sim_long.sh # Long-running test monitor
│ ├── run_test_iverilog.sh # Icarus Verilog test script
│ └── convert_mif_to_hex.sh # MIF to HEX converter
│
├── build/ # Build outputs (generated)
│ ├── m68k_soc.bin # FPGA bitstream
│ ├── m68k_soc.json # Synthesis netlist
│ ├── m68k_soc.asc # Place & route output
│ └── *.log, *.rpt # Build logs and reports
│
└── docs/ # Documentation
├── TEST_REPORT.md # ModelSim verification report
└── PERFORMANCE_ANALYSIS.md # Performance documentation
Test Coverage:
- ✓ CPU reset sequence (8192 cycles)
- ✓ Boot ROM execution
- ✓ SRAM read/write operations
- ✓ LED register writes (41 writes verified)
- ✓ LED pattern sequencing (40 state changes)
- ✓ Pattern cycle completion (10/10 cycles)
- ✓ Memory controller state machine
- ✓ Clock domain stability
Test Results:
✓✓✓ TEST PASSED ✓✓✓
Execution Time: 964.99 µs
LED Pattern Cycles: 10/10 (100%)
LED State Changes: 40
CPU Activity:
- Data Reads: 1,473
- Data Writes: 45
- LED Writes: 41
cd sim
./run_test_sim_long.sh
# Monitor progress:
tail -f /tmp/m68k_test_sim.log
# Verify success:
grep "TEST PASSED" /tmp/m68k_test_sim.logCritical signals to observe:
clk_50mhz- System clockrst_n- Reset (active low)cpu_address[31:0]- CPU address buscpu_rd_data[15:0]- CPU read datacpu_wr_data[15:0]- CPU write dataled1, led2- LED outputssram_addr[17:0]- SRAM addresssram_data[15:0]- SRAM data (bidirectional)
1. Instruction Fetch Counter Always Zero
- Status: Testbench detection issue
- Impact: None (CPU verified working via LED toggling)
- Cause: j68 microcode may not assert FC=010 for instruction fetches
- Workaround: Use data read/write counts instead
2. No Debug UART Output in Simulation
- Status: $write/$fflush mechanism issue
- Impact: None (not critical for LED toggle test)
- Expected: "Sim Startup\r\n" message
- Workaround: LED pattern verification sufficient
- Add real UART peripheral (16550-compatible)
- Implement SPI master for SD card interface
- Add timer/counter peripherals
- Implement interrupt controller
- Add DMA controller for memory transfers
- Pipeline memory controller for higher frequency
- Support 8-bit SRAM devices
- Add Wishbone bus interface
- Implement hardware breakpoint support
Contributions are welcome! Please follow these guidelines:
- Use GitHub Issues
- Include synthesis/simulation logs
- Provide FPGA board details
- Specify toolchain versions
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing-feature) - Add tests for new functionality
- Ensure builds pass (
make clean build) - Run simulation tests (
cd sim && ./run_test_sim_long.sh) - Update documentation (README.md, comments)
- Commit with clear messages
- Push to branch (
git push origin feature/amazing-feature) - Open Pull Request
- Verilog: Follow IEEE 1364-2001 standard
- Comments: Document all module interfaces
- Naming:
snake_casefor signals,CamelCasefor modules - Indentation: 4 spaces (no tabs)
- License: Add ISC license header to new files
Copyright (c) 2025 Michael Wolak
This project (SOC integration and supporting infrastructure) is licensed under the ISC License.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
SPDX-License-Identifier: ISC
j68 CPU Core
Copyright (c) 2011-2018 Frederic Requin
The j68 core is included under its original license terms.
See hdl/j68_cpu/ directory for complete license information.
- Frederic Requin - j68 M68000-compatible CPU core (GitHub)
- Motorola/Freescale/NXP - Original MC68000 architecture and documentation
- Lattice Semiconductor - iCE40 FPGA family and development tools
- Yosys/nextpnr teams - Open-source FPGA synthesis and PnR tools
M68000 Documentation:
j68 CPU Core:
FPGA Tools:
M68K Resources:
Michael Wolak Email: mikewolak@gmail.com
Project Link: https://github.com/yourusername/m68k-fpga-soc
Current Status: ✓ Verified in ModelSim simulation Hardware Testing: Pending (synthesis successful, timing closed) Next Milestone: Physical FPGA board testing
Last Updated: November 2025