════════════════════════════
𓇲 Forth FPGA Synthesis 𓇲
════════════════════════════
November 15, 2025
FORTH DAY
Brad Nelson
Background
──────────
• Forth Day 2023
Christopher Lozinski offers a pico-ice:
- Raspberry Pi Pico (RP2040) - 264K RAM
- iCE40UP5K FPGA ★
- 4MB SPI Flash for CPU
- 4MB SPI Flash for FPGA
- 8MB low power qSPI RAM
- Shared RGB LED, All RP2040 + iCE40 pins exposed
- RP2040 can feed configuration to FPGA!
• By December 2023 ported uEforth
- and ability to send an FPGA image
@pico-ice.png
iCE40UP5K
─────────
• 5280 LUTs
• 1Mbit single port RAM
• 120Kb dual port RAM
• 8 x DSP blocks
• Part of a larger family with similar structure
icestorm/YoSYS
──────────────
• Community has reverse engineered
the iCE40 bitstream format!
• Built an open source verilog
• icepack capture fairly simple config layout
https://prjicestorm.readthedocs.io/en/latest/format.html
@ice40_structure.png
@ice40_plb.png
@ice40_cram_banks.png
@ice40_spans.png
.logic_tile_bitmap
Nobrrrr-rrrrrrbbbbbb-bbbbbbbbbbbbbbbllllllllllbbb--bbb
--orrrr-rrrrrrbbbbbb-bbbbbbbbbbbbbbbllllllllllbbbbCbbb
bbbrrrr-rrrrrrbbbbbb-bbbbbbbbbbbbbbbllllllllllbbb-bbbb
bbbrrrr-rrrrrrbbbbbb-bbbbbbbbbbbbbbbllllllllllbbb--bbb
bbbrrrr-rrrrrrbbbbbb-bbbbbbbbbbbbbbbllllllllllbbb-bbbb
bborrrr-rrrrrrbbbbbb-bbbbbbbbbbbbbbbllllllllllbbb--bbb
bbbrrrr-rrrrrrbbbbbb-bbbbbbbbbbbbbbbllllllllllbbb-bbbb
bborrrr-rrrrrrbbbbbb-bbbbbbbbbbbbbbbllllllllllbbb--bbb
bbbrrrr-rrrrrrbbbbbb-bbbbbbbbbbbbbbbllllllllllbbb-bbbb
bborrrr-rrrrrrbbbbbb-bbbbbbbbbbbbbbbllllllllllbbb--bbb
bbbrrrr-rrrrrrbbbbbb-bbbbbbbbbbbbbbbllllllllllbbb-bbbb
bborrrr-rrrrrrbbbbbb-bbbbbbbbbbbbbbbllllllllllbbb--bbb
bbbrrrr-rrrrrrbbbbbb-bbbbbbbbbbbbbbbllllllllllbbb-bbbb
bborrrr-rrrrrrbbbbbb-bbbbbbbbbbbbbbbllllllllllbbb--bbb
bbbrrrr-rrrrrrbbbbbb-bbbbbbbbbbbbbbbllllllllllbbb-bbbb
bborrrr-rrrrrrbbbbbb-bbbbbbbbbbbbbbbllllllllllbbb--bbb
- ... unknown bit
r ... routing
b ... buffer
l ... logic bits
o ... ColBufCtrl
C ... CarryInSet
N ... NegClk
@tilebits.jpg
chipdb-5k.txt
─────────────
.net 514
0 2 sp4_h_r_0
1 2 sp4_h_r_13
2 2 sp4_h_r_24
3 2 sp4_h_r_37
4 2 sp4_h_l_37
.routing 25 30 103375 B6[11] B6[13] B7[12]
001 86500
010 98041
011 103318
100 86493
101 98040
110 98045
111 103314
.buffer 25 30 103209 B6[14] B7[14] B7[15] B7[16] B7[17]
00001 103357
00011 78827
00101 103265
00111 90387
01001 103344
01011 64134
01111 103317
10011 98042
10101 94028
10111 98431
11001 103257
11011 98176
11101 94151
11111 98441
@ice40_graphviz.png
@visualize1.png
@visualize2.png
@visualize3.png
@visualize4.png
@visualize5.png
┌───────┐
────────┥ │ ┌─────┐
────────┥ LUT4 ┣──┥ FF? ┣──────
────────┥ │ └─────┘
────────┥ │
└───────┘
( optional carry )
output / ────➤ local_gX_Y ────➤ lutff_A/in_B ────➤ Span4 H V RV /
B T BL L / X=0..3 A=0..7 Span12 H V
TR TL BL R / Y=0..7 B=0..3
Span4 H V RV /
Span12 H V /
Globals
output / ────➤ local_gX_Y ────➤ io_A/OUT_B/OUT_ENB
Span4 H V / X=0..1 A=0..1
Span12 V / Y=0..7 B=0..a
io_A/IN_B / ────➤ Span4 H V
A=0..1 / Span12 V
B=0..1 / Neighbors
SuperFly!
─────────
• Need to represent each wire
- output, input, span, local
• A real object for each is too big
• Use Forth to create a special OO framework
for objects that fit in one CELL!
SEE SVFIG September 2025 Talk
WIRE INTERFACE
──────────────
method .create ( {various} o -- o )
method .optionCount ( o -- n )
method .optionWire ( i o -- wire )
method .getOption ( o -- n )
method .setOption ( n o -- )
method .print ( o -- )
ROUTING ALGORITHM
─────────────────
: route { src dst -- f }
src dst = if -1 exit then
dst .getOption { p }
p if src p dst .optionWire recurse exit then
dst .optionCount { n }
n 0 ?do
i dst .setOption
src i dst .optionWire recurse if -1 unloop exit then
0 dst .setOption
loop 0
;
@synth1.png
@synth2.png
@iopins.png
LUT4 ( wire1..4 table -- wire ) Allocate a LUT4
IN1PIN ( n -- wire ) Bring a single pin to a wire
OUT1PIN! ( wire n -- ) Output a single pin to a wire
BUS ( wire bus -- bus' ) Merge wire into a bus
UNBUS ( bus -- wire bus' ) Extract wire from bus
AND1 OR1 XOR1 INVERT1 - other operations on wires
PLACE ( x y -- ) Select coordinates to try to place
INPINS ( p1...pn n -- bus ) Input pins as a bus
OUTPINS ( bus p1...pn n -- ) Output bus to pins
REGISTER ( n -- "name" ) Create a register
AND OR XOR INVERT - other operations on buses
TBD - ENABLE / CLOCK / SET_RESET
8 5 PLACE
16 REGISTER constant v1
16 REGISTER constant v2
9 5 PLACE
v1 INVERT constant v1i
v2 INVERT constant v2i
10 6 PLACE
v1i v2i XOR constant xorval
60 ice40_allocation.fs
50 ice40_config.fs
3 ice40.fs
771 ice40_layout.fs
175 ice40_storage.fs
92 ice40_synthesis.fs
30 flyclasses.fs
1181 TOTAL
( DEMO )
42 1 INPINS constant a
a INVERT LED_B 1 OUTPINS!
a LED_R 1 OUTPINS!
@demo1.png
What's Missing?
───────────────
• Flip-flop enable, clock, set/reset
• Vertical-Horizonal cross-over routing
• Chip wide globals
• Phase Locked Loops, BRAM, DSP
QUESTIONS❓
🙏
Thank you!