════════════════════════════
 𓇲 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!