Getting Started with FPGAs

- 8 mins read

https://github.com/minsun-ss/learning-fpgas

As part of W1'25 Recurse batch, my resolution for this batch was to do something interesting with FPGAs and maybe eventually commit something more substantial to Tiny Tapeout in March 2026. It made sense to start working with nandland’s Getting Started with FPGAs book, and while I was happy to do this exploration exercise with the nandland go board, I was pretty reluctant to work with the iceCube2 tooling since it seemed unwieldy (buggy on Linux and didn’t really want to work in Windows for this batch). Fortunately for all of us, there is a lot of open source tooling around iCE40 boards that is (relatively) easy to set up once you understand all the moving parts.

Some note: I did this all on Debian 13 (Trixie). I eventually plan to replicate the workflow on my Mac laptop (M2), but think most of this workflow is agnostic (mostly) to OS; in Windows, I do think maybe setting up the iceCube2 workflow is honestly the easiest way to go if you do not intend to use WSL 2.

iCE40 FPGA Boards

I am assuming here that you are using an iCE40 board of some sort for working through the project. For a complete beginner (I put myself squarely in this camp), the nandland go board is quite nice to start with the projects as you don’t need to stare at schematics to figure out pins (it’s provided for you in the book) and it’s just one less thing to worry about at the beginning, especially since we are removing the scaffolding of using the iceCube2 design software. If you do go with iceCube2, though, the license requires emailing Lattice and waiting; this took about a week for me.

If you use the nandland go board, you can find schematics on nandland; a copy of this pdf is also contained in the repo under /docs.

Installing the tooling

The suggested tools to install, in no particular order, are:

Synthesis, Place and Route, Programming:

Simulation, Test benches, Waveforms:

There is a lot of overlap between the instructions for all of the above, some of which are detailed below. If you start with reading Project Icestorm docs first, you’ll see that Project IceStorm suggests you install a lot of things already: IceStorm tools, arachne-pnr, next-pnr, yosys. Of the four, you only need three; ignore arachne-pnr, as it’s been fully superceded by nextpnr.

With that said, a lot of stuff online still uses arachne-pnr in their makefiles and you might need to fiddle with it, so it’s good to know that it was commonly used. For the case of iCE40 boards, though, there’s virtually no difference between nextpnr and arachne except for nextpnr’s use of json format rather than blif; that requires subbing out the -blif output flag in yosys to -json and that’s pretty much it; everything else can remain the same.

The last thing to note is that some of these tools do not necessarily need to be installed separately: if you decide to use yosys via the suggested oss-cad-suite set of binaries then you do not, in fact, need to build anything at all; all of the above tools are part of the oss-cad-suite already in the /bin:

reginleif:/opt/oss-cad-suite  $ cat VERSION
20251113

@reginleif:/opt/oss-cad-suite/bin  $ ls
aigand           cvc4            icebox_maps         smvtoaig
aigbmc           cvc5            icebox_stat         soltostim
aigcexmin        dfu-prefix      icebox_vlog         suprove
aigdd            dfu-suffix      icebram             surfer
aigdep           dfu-util        icefunprog          surver
aigflip          dot             icemulti            tabbyadm
aigfuzz          ecpbram         icepack             tabbypip
aiginfo          ecpdap          icepll              tabbypy3
aigjoin          ecpmulti        iceprog             tinyfpgab
aigmiter         ecppack         iceprogduino        tinyprog
aigmove          ecppll          icesprog            twinwave
aignm            ecpprog         icetime             vcd2fst
aigor            ecpunpack       iceunpack           vcd2lxt
aigreset         eqy             imctk-eqy-engine    vcd2lxt2
aigselect        evcd2vcd        iverilog            vcd2vzt
aigsim           find_libpython  iverilog-vpi        verilator
aigsplit         fst2vcd         lsftdi              verilator_bin
aigstrip         fstminer        lsusb               verilator_bin_dbg
aigtoaig         fujprog         lxt2miner           verilator_coverage
aigtoblif        ghdl            lxt2vcd             verilator_coverage_bin_dbg
aigtobtor        ghdl1-llvm      mcy                 verilator_gantt
aigtocnf         ghwdump         mcy-dash            verilator_profcfunc
aigtodot         gmpack          mcy-gui             vvp
aigtosmv         gmunpack        nextpnr-ecp5        vzt2vcd
aiguncomment     gowin_pack      nextpnr-generic     vztminer
aigunconstraint  gowin_pll       nextpnr-himbaechel  wrapstim
aigunor          gowin_unpack    nextpnr-ice40       xdot
aigunroll        gtkwave         nextpnr-machxo2     xml2stems
amaranth-rpc     hex2bin         nextpnr-nexus       yices
andtoaig         hex2dump        openFPGALoader      yices-sat
avy              hexdiff         openocd             yices-smt
avybmc           hexinfo         pono                yices-smt2
bin2hex          hexmerge        prjoxide            yosys
bitwuzla         icebox_asc2hlc  pyserial-miniterm   yosys-abc
black-iceprog    icebox_chipdb   pyserial-ports      yosys-config
bliftoaig        icebox_colbuf   rtlbrowse           yosys-filterlib
boolector        icebox_diff     sby                 yosys-smtbmc
btormc           icebox_explain  sby-gui             yosys-witness
btorsim          icebox_hlc2asc  scy                 z3
cocotb-config    icebox_html     shmidcat

There is a shell script in the oss-cad-suite that you can activate that will give you access to those tools. Pretty easy, eh? Wish I had figured this out after building and installing all of the above. But my loss is your time savings! From here I’ll presume that you’ll be using oss-cad-suite, although in the repo I do have some notes on the builds for the above tools if you decide to build them separately. I wrote a very simple script to stuff into my .bashrc to activate the env and dumped the oss cad suite into my /opt. Maybe there’s a better way to source virtual env shells, but I’ve never found a great way for doing it in a simple way.

fpgashell() {
    source /opt/oss-cad-suite/environment
}

With that you’re almost ready to go, although there’s one other thing you should set up, although it’s optional since it’s dependent on the board.

In Project Icestorm’s docs, they suggest creating a 53-lattice-ftdi.rules file to put in your /etc/udev/rules.d; this allows you to simply call iceprog to flash your fpga without requiring sudo. A copy of this .rules is in the /tools folder in the repo. You can verify that the vendor id and product id in the rules file matches your nandland device by calling lsusb (there’s probably better ways of doing this but I found this the easiest):

@reginleif:/opt/oss-cad-suite/bin  $ lsusb
Bus 007 Device 016: ID 0403:6010 Future Technology Devices International, Ltd FT2232C/D/H Dual UART/FIFO IC
Bus 005 Device 097: ID 20b7:9db1 Qi Hardware Glasgow Debug Tool

0403 is the vendor id and 6010 is the product id of the nandland go board. I’ve included the similar info list for the Glasgow Interface Explorer, which is another board that also uses a iCE40 FPGA for comparison.

Running the workflow

For the book, the workflow is similar to how it’s presented in the book: verilog/pcf/sbc/module names are the same per project. The makefile included in the repo will build any project stored inside /src and output the results in the /build folder:

make build PROJ=projectname

/src should contain each program in its own self contained folder; see the repo for structure. This was mostly just to ease having multiple projects to work through in a single repo. To program your FPGA once you finish building, you can call the ship command and it’ll program the fpga with your specified *.bin file as long as the FPGA is plugged in.

make ship PROJ=projectname

If you do not use the nandland go board, then you will need to figure out what iCE40 device and package you need to use for synthesis. You’ll have to refer to the datasheets for your device and then cross reference it against Lattice datasheets. A copy of Lattice’s datasheet is included in the repo for you to check against.

Once you know both, replace the value in the DEVICE and PACKAGE parameters for your board in the top of the Makefile.

For the nandland go board the device is hx1k and the package is vq100 (these are set already as defaults in the makefile); you can actually verify this using the go board constraint pcf provided by nandland:

# iCEcube PCF
# Version:            2014.12.27052
# File Generated:     Apr 27 2015 09:46:33
# Family & Device:    iCE40HX1K
# Package:            VQ100

I’ve also included a copy of this .pcf file under the /src folder for convenience.

Differences Between Book & Using Open Source tooling

The repo, under /docs, contains a document that goes through, chapter by chapter, the major differences between using the iceCube2 suite of tools vs open source tooling. Where possible; I’ve provided roughly equivalent substitute commands to achieve parity and describe to some detail some of the differences, including what you need to modify in the book code to make work for OSS tooling. For the most part, it seems to be very little; e.g., for simulation I would suggest adding the timescale direction at the top of your files to make GTKwave produce the correct resolution waveforms for you out of the box, but that’s about it so far.

Additional Reading

A list of additional materials I read that I found interesting.

To be continued!

I intend to cover some of the things I’ve learned in additional posts:

  • formal verification is on my to do list!
  • covering some of the other tools in the oss-cad-suite. Almost every step of the fpga process, e.g., synthesis, placement/routing, timing, etc, seems to have at least 2 tools to compare against in the suite; it is worth investigating.