Decoding ICE40 ══════════════ ══════════ ══════ ══ July 26, 2025 Background ══════════ • Forth 2023 gifted a pico-ice by Christopher Lozinski - Raspberry Pi Pico (RP2040) + 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 ported uEforth - and ability to send an FPGA image So what next? ═════════════ • Wouldn't it be cool to interactively create gates? • Toolchains, even open source ones are slow 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 Time passes... More time passess... What's taking so long? ══════════════════════ • Better understand the mapping between bits and routing • Some progress on simulation BUT... • Strugged to unravel + simplify open source representation - things are represented as data instead of code • Realized routing constraints may fail fairly easily iCE40UP5K ═════════ • 5280 LUTs • 1Mbit single port RAM • 120Kb dual port RAM • 8 x DSP blocks @ice40-top.png@ice40-plb.png
@ice40-viewer.png
@ice40-spans.png
@arouting1.jpg
https://knielsen.github.io/ice40_viewer/ice40_viewer.html iCE40 Format ════════════ • icepack capture fairly simple config layout: if (right_half) cram_x = bank_xoff + column_width - 1 - bit_x; else cram_x = bank_xoff + bit_x; if (top_half) cram_y = bank_yoff + (15 - bit_y); else cram_y = bank_yoff + bit_y; .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 @tilebits1.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 .logic_tile_bits 54 16 CarryInSet B1[50] ColBufCtrl.glb_netwk_0 B9[7] ColBufCtrl.glb_netwk_1 B8[7] ColBufCtrl.glb_netwk_2 B11[7] ColBufCtrl.glb_netwk_3 B10[7] ColBufCtrl.glb_netwk_4 B13[7] ColBufCtrl.glb_netwk_5 B12[7] ColBufCtrl.glb_netwk_6 B15[7] ColBufCtrl.glb_netwk_7 B14[7] LC_0 B0[36] B0[37] B0[38] B0[39] B0[40] B0[41] B0[42] B0[43] B0[44] B0[45] B1[36] B1[37] B1[38] B1[39] B1[40] B1[41] B1[42] B1[43] B1[44] B1[45] LC_1 B2[36] B2[37] B2[38] B2[39] B2[40] B2[41] B2[42] B2[43] B2[44] B2[45] B3[36] B3[37] B3[38] B3[39] B3[40] B3[41] B3[42] B3[43] B3[44] B3[45] LC_2 B4[36] B4[37] B4[38] B4[39] B4[40] B4[41] B4[42] B4[43] B4[44] B4[45] B5[36] B5[37] B5[38] B5[39] B5[40] B5[41] B5[42] B5[43] B5[44] B5[45] LC_3 B6[36] B6[37] B6[38] B6[39] B6[40] B6[41] B6[42] B6[43] B6[44] B6[45] B7[36] B7[37] B7[38] B7[39] B7[40] B7[41] B7[42] B7[43] B7[44] B7[45] LC_4 B8[36] B8[37] B8[38] B8[39] B8[40] B8[41] B8[42] B8[43] B8[44] B8[45] B9[36] B9[37] B9[38] B9[39] B9[40] B9[41] B9[42] B9[43] B9[44] B9[45] LC_5 B10[36] B10[37] B10[38] B10[39] B10[40] B10[41] B10[42] B10[43] B10[44] B10[45] B11[36] B11[37] B11[38] B11[39] B11[40] B11[41] B11[42] B11[43] B11[44] B11[45] LC_6 B12[36] B12[37] B12[38] B12[39] B12[40] B12[41] B12[42] B12[43] B12[44] B12[45] B13[36] B13[37] B13[38] B13[39] B13[40] B13[41] B13[42] B13[43] B13[44] B13[45] LC_7 B14[36] B14[37] B14[38] B14[39] B14[40] B14[41] B14[42] B14[43] B14[44] B14[45] B15[36] B15[37] B15[38] B15[39] B15[40] B15[41] B15[42] B15[43] B15[44] B15[45] NegClk B0[0] DEMO Visualizer @hrouting1.jpg
@vrouting1.jpg
@vrrouting1.jpg
Issues... ═════════ • Haven't tackled IO at all • Haven't looked at PLL setup • Haven't figured out how globals are routed @globals1.jpg
Simuation ═════════ • Model how I'll synthesize once I have all the encoding • Initially is memory wasteful - need to use FPGA bit space instead of allocated structures Goals ═════ • Support talking about multiple wires at once BUS ( wire bus -- bus' ) UNBUS ( bus' -- wire bus ) • Support allocating registers 8 REGISTER ( bus ) • Support multi-bit logic and arithmetic x y + z REG! • Support clocks and enabling ready ENABLE ! clk1 CLOCK ! x y + z REG! variable zero variable one -1 one ! variable luts variable ffs variable clock zero clock ! variable enable one enable ! struct lut cell field ->value cell field ->next cell field ->i0 cell field ->i1 cell field ->i2 cell field ->i3 cell field ->table struct ff cell field ->value cell field ->next cell field ->in cell field ->enable cell field ->clock cell field ->old-clock : LUT4 { i0 i1 i2 i3 tbl -- o } here 0 , luts @ , i0 , i1 , i2 , i3 , tbl , dup luts ! ; : lut-vals { l -- v } l ->i0 @ @ 0<> 1 and l ->i1 @ @ 0<> 2 and or l ->i2 @ @ 0<> 4 and or l ->i3 @ @ 0<> 8 and or ; : tick-lut { l -- } l ->table @ l lut-vals rshift 1 and 0<> l ->value ! ; : tick-luts luts @ begin dup while dup tick-lut ->next @ repeat drop ; : FFL ( -- o ) here 0 , ffs @ , zero , enable @ , clock @ , 0 , dup ffs ! ; : FF! ( v ff -- ) ->in ! ; : tick-ff { f -- } f ->clock @ @ 0<> f ->old-clock @ 0= and if f ->enable @ @ if f ->in @ @ 0<> f ->value ! then then f ->clock @ @ f ->old-clock ! ; : tick-ffs ffs @ begin dup while dup tick-ff ->next @ repeat drop ; : tick tick-luts tick-ffs ; : ticks ( n -- ) 0 ?do tick loop ; : XOR1 ( a b -- o ) zero zero $6666 LUT4 ; : OR1 ( a b -- o ) zero zero $eeee LUT4 ; : AND1 ( a b -- o ) zero zero $8888 LUT4 ; : INVERT1 ( a b -- o ) zero zero zero $aaaa LUT4 ; : HA ( x y -- so co ) 2dup AND1 >r XOR1 r> ; : FA ( x y z -- so co ) HA >r HA r> OR1 ; : BUS ( v bus -- bus ) here >r , , r> ; : UNBUS ( bus -- v bus ) dup cell+ @ swap @ ; : nBUS ( v* n -- bus ) 0 swap 0 ?do BUS loop ; : nUNBUS ( bus -- v* ) begin dup while UNBUS repeat drop ; : BUS. ( bus -- ) begin dup while UNBUS >r @ . r> repeat drop ; : BUS! ( n bus -- ) begin dup while UNBUS >r over 1 and 0<> swap ! 2/ r> repeat 2drop ; : INVERT ( a -- a' ) dup 0= if exit then UNBUS >r INVERT1 r> recurse BUS ; : AND ( a b -- c ) dup 0= if nip exit then UNBUS >r >r UNBUS r> swap >r AND1 r> r> recurse BUS ; : OR ( a b -- c ) dup 0= if nip exit then UNBUS >r >r UNBUS r> swap >r OR1 r> r> recurse BUS ; : XOR ( a b -- c ) dup 0= if nip exit then UNBUS >r >r UNBUS r> swap >r XOR1 r> r> recurse BUS ; : +c ( a b ci -- c ) >r dup 0= if 2drop r> 0 BUS exit then r> -rot UNBUS >r >r UNBUS r> swap >r FA r> r> rot recurse BUS ; : + ( a b -- c ) zero +c ; : REGISTER ( n -- bus ) 0 swap 0 ?do FFL swap BUS loop ; : REG! ( v a -- ) dup 0= if 2drop exit then UNBUS >r >r UNBUS r> swap >r FF! r> r> recurse ; DEMO Mapping to FPGA CRAM ════════════════════ • Sentinels for: No-connection, globals • Instead of allocating LUTs + FFs, encode in FPGA space: bit + x * 8 + y * WIDTH * 8 - CRAM has: Truth Table, 2 layer input route - For simulation keep bit for each LUT and FF, one bit to track enable state per PLB • Handle carry as a special case adder structure? • How to use the LUT in front of a FF? Waste a LUT per FF? What's Next? ════════════ • Bite the bullet and allocate paths greedily • Bootstrap with known good IO pins • Implement the required checksum + write order • Lots of debugging... QUESTIONS❓ 🙏 Thank you!