A complete working example of PicoRV32 RISC-V processor on Digilent Arty A7-100T FPGA board.
- PicoRV32 RISC-V processor running at 50MHz
- 8KB RAM for program and data
- 4 single-color LEDs for output display
- 4 RGB LEDs for status indication
- 4 slide switches for mode selection
- 4 push buttons for input
- Array operations similar to NTT butterfly operations
- Multiple operation modes controlled by switches
- FPGA: Xilinx Artix-7 XC7A100T
- Clock: 100MHz (divided to 50MHz for CPU)
- Block RAM: 4,860 Kb
- LEDs: 4 single + 4 RGB
- Switches: 4 slide switches
- Buttons: 4 push buttons + 1 reset
arty_project/
├── picorv32_arty_top.v # Top module (Verilog)
├── arty_a7_100t.xdc # Pin constraints
├── firmware.c # C firmware code
├── startup.s # Assembly startup
├── linker.ld # Linker script
├── Makefile # Build firmware
├── tb_picorv32_arty.v # Testbench
├── build_vivado.tcl # Vivado build script
└── README_ARTY.md # This file
# Option 1: Ubuntu/Debian package
sudo apt-get install gcc-riscv64-unknown-elf
# Option 2: Build from source
git clone https://github.com/riscv/riscv-gnu-toolchain
cd riscv-gnu-toolchain
./configure --prefix=/opt/riscv --with-arch=rv32im --with-abi=ilp32
make
export PATH=$PATH:/opt/riscv/bin# Download PicoRV32
wget https://raw.githubusercontent.com/YosysHQ/picorv32/master/picorv32.v- Vivado 2019.1 or later
- Make sure Arty A7-100T board files are installed
# Compile firmware
make clean
make
# Verify output
ls -lh firmware.hex firmware.elf firmware.lst# Run simulation with Icarus Verilog
iverilog -o sim tb_picorv32_arty.v picorv32_arty_top.v picorv32.v
./sim
# View waveforms
gtkwave picorv32_arty.vcd# Run Vivado in batch mode
vivado -mode batch -source build_vivado.tcl
# Or create manually in GUI
vivado &
# Then: File -> Project -> New
# Add all .v files and .xdc file
# Set picorv32_arty_top as top modulecp firmware.hex vivado_project/GUI Method:
- Open Vivado project
- Click "Run Synthesis"
- Wait for synthesis to complete
- Click "Run Implementation"
- Click "Generate Bitstream"
Command Line Method:
# In Vivado TCL console
launch_runs synth_1
wait_on_run synth_1
launch_runs impl_1 -to_step write_bitstream
wait_on_run impl_1Using Vivado Hardware Manager:
- Connect Arty A7-100T via USB
- Open Hardware Manager
- Auto-connect to target
- Program device with generated
.bitfile - Located at:
vivado_project/picorv32_arty.runs/impl_1/picorv32_arty_top.bit
Using Command Line:
# Generate programming file
vivado -mode batch -source program_fpga.tcl| Address | Device | Description |
|---|---|---|
| 0x00000000 | RAM | 8KB program/data memory |
| 0x02000000 | LED Register | Control 4 single LEDs |
| 0x02000004 | GPIO Register | Read switches & buttons |
| 0x02000008 | RGB Register | Control 4 RGB LEDs |
| 0x0200000C | UART Register | UART data (future use) |
Control mode with slide switches (SW0-SW3):
- Performs butterfly array operations
- Displays result on LEDs
- Cycles RGB LED patterns
- Similar to NTT algorithm flow
- Shows incrementing counter on LEDs
- Blue RGB indicator
- Good for testing LED functionality
- LED chase/knight rider pattern
- Green RGB indicator
- Tests sequential LED control
- LEDs mirror button states
- Red RGB indicator
- Tests button input reading
- Cycles through array values
- Shows each element on LEDs
- Different RGB colors per element
- Power On: Connect USB cable, FPGA should enumerate
- Program: Load bitstream (LEDs may flicker during config)
- Reset: Press reset button (BTN0 on Arty)
- Observe:
- RGB LEDs will cycle through startup animation
- Single LEDs will show operation results
- Switch Modes: Use slide switches to change modes
- Press Buttons: In mode 3, buttons control LEDs directly
After programming and reset:
- RGB LED Animation (8 patterns, ~0.5s each)
- Mode 0 starts automatically:
- LEDs blink showing calculated values
- RGB LEDs cycle through colors
- Pattern repeats continuously
- Change switch settings to see different modes
# Check toolchain
riscv32-unknown-elf-gcc --version
# Should show: riscv32-unknown-elf-gcc (GCC) 10.x.x or later- Check pin constraints match your board variant (A7-100T vs A7-35T)
- Verify bitstream programmed successfully
- Press reset button
- Check Vivado routing completed without errors
# Ensure firmware.hex exists
ls firmware.hex
# If missing, run: make- Ensure
picorv32.vis in project directory - Check all files are added to project
- Verify
firmware.hexis copied to Vivado project folder
- Press reset button (BTN0)
- Check power LED is on
- Verify correct bitstream file loaded
- Check Vivado implementation completed without critical warnings
Approximate resource usage on Arty A7-100T:
| Resource | Used | Available | Utilization |
|---|---|---|---|
| LUTs | ~1800 | 63,400 | ~3% |
| Registers | ~800 | 126,800 | ~1% |
| Block RAM | 4 | 135 | ~3% |
| DSPs | 3 | 240 | ~1% |
Very lightweight - plenty of room for expansion!
This project is designed as a foundation. To implement full NTT:
// Change in picorv32_arty_top.v
ram_memory #(
.ADDR_WIDTH(13) // 32KB instead of 8KB
) ram (- Implement modular reduction hardware
- Add as memory-mapped accelerator
- Typical NTT uses q = 12289 or similar prime
module twiddle_rom (
input [9:0] addr,
output reg [15:0] twiddle
);
// Precomputed twiddle factors
endmodule- Current code is sequential
- Can parallelize with hardware accelerator
- Or unroll loops in C code
- Add bit-reversal permutation
- Can be done in software or hardware
#define CYCLE_COUNTER (*(volatile unsigned int*)0x02000010)
// Read cycle counter before/after NTT- PicoRV32: https://github.com/YosysHQ/picorv32
- Arty Reference: https://reference.digilentinc.com/arty-a7
- RISC-V Spec: https://riscv.org/specifications/
- NTT Papers: Search for "Number Theoretic Transform FPGA"
// In picorv32_arty_top.v
// For 25MHz CPU clock (divide by 4):
reg [1:0] clk_div_counter;
always @(posedge clk) begin
clk_div_counter <= clk_div_counter + 1;
end
assign clk_cpu = clk_div_counter[1]; // 25MHz// Add to memory map
wire custom_out_sel = (mem_addr == 32'h02000010);
reg [7:0] custom_out_reg;
// Add to port list
output wire [7:0] custom_out
assign custom_out = custom_out_reg;// In firmware.c
int data[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
simple_array_ops(data, 16);This project uses:
- PicoRV32: ISC License
- Arty constraints: BSD License
For issues:
- Check this README thoroughly
- Review Vivado synthesis/implementation reports
- Check firmware.lst for disassembly
- Simulate before implementing on hardware
Happy FPGA development! 🚀