This directory contains board-specific wrappers that enable the FROST RISC-V processor to run on real FPGA hardware. Each subdirectory targets a specific development board with its own clock configuration, pin constraints, and Xilinx IP cores.
| Board | FPGA | CPU Clock | Features |
|---|---|---|---|
| Genesys2 | Xilinx Kintex-7 (xc7k325t) | 133.33 MHz | Entry-level development |
| Nexys A7 | Xilinx Artix-7 (xc7a100t) | 80 MHz | Entry-level development |
| X3 | Xilinx Alveo X3522PV (UltraScale+) | 300 MHz | High-performance target |
Each board wrapper handles clock generation and instantiates a common xilinx_frost_subsystem module:
┌───────────────────────────────────────────────────────────────────────────┐
│ Board Top Module │
│ (genesys2_frost, nexys_a7_frost, x3_frost) │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Clock Generation │ │
│ │ │ │
│ │ Clock ┌────────┐ ┌──────┐ ┌────────┐ │ │
│ │ Input ────>│ IBUF/ │──>│ MMCM │──>│ BUFG │──> CPU Clock │ │
│ │ │ IBUFDS │ └──┬───┘ └────────┘ (80-300 MHz) │ │
│ │ └────────┘ │ │ │
│ │ └──────>┌────────┐ │ │
│ │ │ BUFG │──> /4 Clock │ │
│ │ └────────┘ (20-80 MHz) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ xilinx_frost_subsystem │ │
│ │ │ │
│ │ ┌───────────────────────────────────────────────────────────────┐ │ │
│ │ │ JTAG Software Loading (/4 clock) │ │ │
│ │ │ │ │ │
│ │ │ JTAG ┌─────────────┐ AXI ┌─────────────────┐ │ │ │
│ │ │ Port ───>│ jtag_axi_0 │───────>│ axi_bram_ctrl_0 │ │ │ │
│ │ │ └─────────────┘ └────────┬────────┘ │ │ │
│ │ │ │ │ │ │
│ │ │ ┌────────────────────────────────────┘ │ │ │
│ │ │ │ BRAM Interface │ │ │
│ │ │ v (en, we, addr, wrdata) │ │ │
│ │ └──────┼────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────┼────────────────────────────────────────────────────────┐ │ │
│ │ │ │ FROST CPU │ │ │
│ │ │ v │ │ │
│ │ │ ┌─────────────┐ ┌────────────────────┐ ┌─────────────┐ │ │ │
│ │ │ │ Instruction │<──│ 6-Stage Pipeline │ │ UART TX │───┼──┼─>│
│ │ │ │ Memory │ │ (IF-PD-ID-EX-MA-WB)│ │ UART RX │<──┼──┼──│
│ │ │ │ (BRAM) │ └────────────────────┘ └─────────────┘ │ │ │
│ │ │ └─────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │
│ │ │ │ Image Load Reset Logic │ │ │ │
│ │ │ │ • Detects JTAG writes to instruction memory │ │ │ │
│ │ │ │ • Holds CPU in reset during software loading │ │ │ │
│ │ │ │ • Releases reset after counter expires │ │ │ │
│ │ │ └─────────────────────────────────────────────────────────┘ │ │ │
│ │ └───────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────────┘
Programs are loaded into instruction memory via JTAG without reprogramming the FPGA bitstream. This enables rapid software iteration:
- Synthesize and program the FPGA bitstream once
- Load new software via Vivado Hardware Manager as needed
- CPU automatically resets during loading and starts execution when complete
The board wrappers include logic that:
- Detects when software is being written to instruction memory
- Holds the CPU in reset during the load process
- Releases reset automatically when loading completes
This prevents the CPU from executing partially-loaded or stale instructions.
boards/
├── README.md # This file
├── xilinx_frost_subsystem.sv # Common subsystem (JTAG, BRAM, CPU, reset)
├── genesys2/
│ ├── genesys2_frost.sv # Top-level board wrapper (clock generation)
│ ├── genesys2_frost.f # File list for synthesis tools
│ └── constr/
│ └── genesys2.xdc # Pin assignments & timing constraints
├── nexys_a7/
│ ├── nexys_a7_frost.sv # Top-level board wrapper (clock generation)
│ ├── nexys_a7_frost.f # File list for synthesis tools
│ └── constr/
│ └── nexys_a7.xdc # Pin assignments & timing constraints
└── x3/
├── x3_frost.sv # Top-level board wrapper (clock generation)
├── x3_frost.f # File list for synthesis tools
└── constr/
└── x3.xdc # Pin assignments & timing constraints
Note: Xilinx IP cores (jtag_axi, axi_bram_ctrl) are generated on-the-fly during synthesis to ensure compatibility across Vivado versions.
- Xilinx Vivado (see main README for validated versions)
- Target FPGA development board
- USB cable for JTAG programming
For automated builds, use the build script (recommended):
./fpga/build/build.py <board> # e.g., genesys2, nexys_a7, x3For manual Vivado project setup:
- Create a new Vivado project targeting your board's FPGA
- Add the RTL sources:
- All files from
hw/rtl/(the CPU core) boards/xilinx_frost_subsystem.sv(common subsystem)- The board-specific wrapper (e.g.,
genesys2/genesys2_frost.sv)
- All files from
- Add the constraint file from
constr/ - Generate the required Xilinx IP cores (jtag_axi_0, axi_bram_ctrl_0) - see
fpga/build/build.tclfor configuration - Set the top module (e.g.,
genesys2_frost) - Run synthesis and implementation
- Generate the bitstream
After the FPGA is programmed with the bitstream:
- Open Vivado Hardware Manager
- Connect to the target board
- Use the JTAG-AXI core to write your program to instruction memory:
# Example: Write instruction words starting at address 0 create_hw_axi_txn write_txn [get_hw_axis hw_axi_1] \ -type write -address 0x00000000 -data {<your_program_hex>} run_hw_axi write_txn
| Signal | Direction | Pin | Description |
|---|---|---|---|
i_sysclk_p |
Input | AD12 | 200 MHz differential clock (positive) |
i_sysclk_n |
Input | AD11 | 200 MHz differential clock (negative) |
i_pb_resetn |
Input | R19 | Push-button reset (active-low) |
o_uart_tx |
Output | Y23 | UART transmit for debug console |
i_uart_rx |
Input | Y20 | UART receive for debug console input |
o_fan_pwm |
Output | W19 | Fan PWM control (disabled, prevents noise) |
| Signal | Direction | Pin | Description |
|---|---|---|---|
i_sysclk |
Input | E3 | 100 MHz single-ended clock |
i_pb_resetn |
Input | C12 | Push-button reset (active-low) |
o_uart_tx |
Output | C4 | UART transmit for debug console |
i_uart_rx |
Input | D4 | UART receive for debug console input |
| Signal | Direction | Pin | Description |
|---|---|---|---|
i_sysclk_p |
Input | AL23 | 300 MHz differential clock (positive) |
i_sysclk_n |
Input | AK23 | 300 MHz differential clock (negative) |
o_uart_tx |
Output | AP24 | UART transmit for debug console |
i_uart_rx |
Input | AR24 | UART receive for debug console input |
All boards use an MMCM (Mixed-Mode Clock Manager) to generate the CPU clock from the board's reference oscillator:
| Board | Input Clock | VCO Freq | CPU Clock | Calculation |
|---|---|---|---|---|
| Genesys2 | 200 MHz | 800 MHz | 133.33 MHz | 200 × 4 / 6 |
| Nexys A7 | 100 MHz | 800 MHz | 80 MHz | 100 × 8 / 10 |
| X3 | 300 MHz | 1200 MHz | 300 MHz | 300 × 4 / 1 / 4 |
Note: All boards generate a /4 clock for JTAG and UART clock domain crossing.
| Feature | Genesys2 | Nexys A7 | X3 |
|---|---|---|---|
| FPGA Family | Kintex-7 | Artix-7 | UltraScale+ |
| FPGA Part | xc7k325tffg900-2 | xc7a100tcsg324-1 | xcux35-vsva1365-3-e |
| CPU Clock | 133.33 MHz | 80 MHz | 300 MHz |
| Div4 Clock | 33.33 MHz | 20 MHz | 75 MHz |
| Reset | Push-button + JTAG | Push-button + JTAG | JTAG load only |
| Use Case | Development/learning | Development/learning | Production/high-performance |
To add support for a new Xilinx FPGA board:
- Create a new subdirectory named after the board
- Copy an existing wrapper (e.g.,
genesys2_frost.sv) as a starting point - Modify the clock generation:
- Adjust MMCM parameters for your board's input clock frequency
- Configure CLKOUT0 for CPU clock and CLKOUT1 for /4 clock
- Use appropriate clock buffers (BUFG for 7-series, BUFGCE_DIV for UltraScale+)
- Instantiate
xilinx_frost_subsystemwith your clocks and reset - Create a constraint file with your board's pin assignments
- Update the file list (
.ffile) to include the subsystem - Update
fpga/build/build.tclto handle the new board name - Update this README with the new board's specifications
Key considerations:
- Match the MMCM VCO frequency to your target CPU clock
- Ensure timing constraints match your input clock period
- Verify I/O voltage standards match your board's bank voltages
- For non-Xilinx FPGAs (Altera, Lattice), a new subsystem would be needed