Yes you heard right, I’ve discovered a set of tools that allows for FPGA development on Mac OS. Here’s list of some things you’ll need.
- A Mac, obviously
- A Lattice FPGA (iCE40 LP/HX 1K/4K/8K)
or a dev board such as icoBoard, iCEstick, etc..
If you decide to purchase an FPGA from Mouser or Digikey, I have some breakout boards for the iCE40HX1K.. - A programmer using an FTDI chip (FT232H)
- The IceStorm Toolchain
- A text editor. I prefer Sublime Text, it has some pretty cool Verilog and VHDL syntax highlighting as well as auto completion packages.
- Some patience
Since step 1 is obviously covered, I’m going to go ahead with Step 2. So I mentioned a programmer in step 2, well its not so much a programmer as it is an interface between USB and SPI, the software that communicates with the FT232H chip is really what’s going to be doing the reading and writing to the SPI Flash memory. So what you’ll need to get here is something like the FT232H breakout board by Adafruit or if you like to tinker and build one yourself, you can get the components from Mouser or Digikey, and purchase one of my bare bones breakout boards.
You can also do a search on Amazon or eBay for a compatible programmer but I can’t guarantee that you’ll have positive results.
So you got your programmer ready? Lets move on to Step 3
You’ll need a little patience with this step because we need to download some software and because its all source code, it needs to compile which can take some time depending on your processor, memory, etc… First, do you have Homebrew installed? because if you don’t, you’ll need to do that first. Installing homebrew is super easy and fast. You’ll need to open up Terminal and copy and paste the following command:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
First, download ice tools from GitHub.
After you’ve downloaded ice tools go ahead and double-click on it so that it uncompressed the files into a folder icetools-master.
From Terminal, you should go to the folder where you downloaded and extracted ice tools.
For me this is as follows:
cd /Users/chris/Downloads/icetools-master
Once you’re in the folder you can run the installation script which goes like this:
./icetools.sh
This will start downloading all of the files required to compile the open source FPGA toolchain which consists of Yosys for synthesizing, Icarus Verilog and Verilator for simulation, Arachne-Pnr for place & Route and IceStorm for Bitstream. This part could take a good 30 min or more depending on your processor.
Note: If the IceStorm Tools which includes (IcePack needed for creating a BIN) does not install, it is most likely because of how the sed command is executed within the Makefile in the icebox folder. If this happens we will need to manually install the IceStorm Tools. So within the icetools-master folder execute the following commands.
cd icestorm cd icebox nano Makefile
This opens the Makefile inside the nano editor. You’ll now need replace the following:
sed -i 's+import iceboxdb+import $(subst -,_,$(PROGRAM_PREFIX))iceboxdb as iceboxdb+g' $(DESTDIR)$(PREFIX)/bin/$(subst -,_,$(PROGRAM_PREFIX))icebox.py sed -i 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_chipdb$(PY_EXE) sed -i 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_diff$(PY_EXE) sed -i 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_diff$(PY_EXE) sed -i 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_explain$(PY_EXE) sed -i 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_explain$(PY_EXE) sed -i 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_asc2hlc$(PY_EXE) sed -i 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_asc2hlc$(PY_EXE) sed -i 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_hlc2asc$(PY_EXE) sed -i 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_hlc2asc$(PY_EXE) sed -i 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_colbuf$(PY_EXE) sed -i 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_colbuf$(PY_EXE) sed -i 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_html$(PY_EXE) sed -i 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_html$(PY_EXE) sed -i 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_maps$(PY_EXE) sed -i 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_maps$(PY_EXE) sed -i 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_vlog$(PY_EXE) sed -i 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_vlog$(PY_EXE) sed -i 's+/usr/local/share/icebox+$(PREFIX)/share/$(PROGRAM_PREFIX)icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_vlog$(PY_EXE) sed -i 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_stat$(PY_EXE) sed -i 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_stat$(PY_EXE)
with this:
sed -i '' 's+import iceboxdb+import $(subst -,_,$(PROGRAM_PREFIX))iceboxdb as iceboxdb+g' $(DESTDIR)$(PREFIX)/bin/$(subst -,_,$(PROGRAM_PREFIX))icebox.py sed -i '' 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_chipdb$(PY_EXE) sed -i '' 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_diff$(PY_EXE) sed -i '' 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_diff$(PY_EXE) sed -i '' 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_explain$(PY_EXE) sed -i '' 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_explain$(PY_EXE) sed -i '' 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_asc2hlc$(PY_EXE) sed -i '' 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_asc2hlc$(PY_EXE) sed -i '' 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_hlc2asc$(PY_EXE) sed -i '' 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_hlc2asc$(PY_EXE) sed -i '' 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_colbuf$(PY_EXE) sed -i '' 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_colbuf$(PY_EXE) sed -i '' 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_html$(PY_EXE) sed -i '' 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_html$(PY_EXE) sed -i '' 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_maps$(PY_EXE) sed -i '' 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_maps$(PY_EXE) sed -i '' 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_vlog$(PY_EXE) sed -i '' 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_vlog$(PY_EXE) sed -i '' 's+/usr/local/share/icebox+$(PREFIX)/share/$(PROGRAM_PREFIX)icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_vlog$(PY_EXE) sed -i '' 's+import icebox+import $(subst -,_,$(PROGRAM_PREFIX))icebox as icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_stat$(PY_EXE) sed -i '' 's+from icebox+from $(subst -,_,$(PROGRAM_PREFIX))icebox+g' $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_PREFIX)icebox_stat$(PY_EXE)
Basically you’re adding an empty string as an additional argument to the parameter.
Before we do anything else, iceprog, which is used to program the SPI flash, sometimes has issues connecting to the FT232H chip, so we unload the default and vendor drivers like so:
sudo kextunload -v -b com.apple.driver.AppleUSBFTDI sudo kextunload -v -b com.FTDI.driver.FTDIUSBSerialDriver
and then reload them like this:
sudo kextload -v -b com.apple.driver.AppleUSBFTDI sudo kextload -v -b com.FTDI.driver.FTDIUSBSerialDriver
Next you’ll want to install the FTDI Library (This information is verbatim from https://www.clifford.at/icestorm/notes_osx.html)
brew install libftdi0
Perfect, you should now have all the tools required to synthesize, place and route, and program. Here’s how you use the tools you just installed. In the following example, we are using the rot example from arachne-pnr (this example targets the iCE40-HX1K-VQ100 package):
Synthesize: (Note: if you have more than one module here you could just append to the command rot.v rot1.v rot2.v)
yosys -q -p "synth_ice40 -blif rot.blif" rot.v
Place-and-Route:
If you’re following along and using the rot example, and you’re building for the iCE40-HX1K-VQ100 package, you’ll need to change the rot.pcf file slightly. PIN 98 is utilized as an IO pin in the Pin configuration file, you’ll need to change this to Pin 100 instead as PIN 98 on the VQ100 package is tied to ground.
arachne-pnr -d 1k -P vq100 -p rot.pcf rot.blif -o rot.asc
Convert to bin:
icepack rot.asc rot.bin
Program:
iceprog rot.bin
Alternatively, you can use the TL866 programmer to write the BIN file using either the ICSP port or the ZIF socket. So there are tons of options for getting the data onto your SPI flash.
A simple timing analysis report can be generated using the ice time utility:
icetime -tmd hx1k rot.asc
Congratulations, you now have a fully functional toolchain for developing FPGA’s with MacOS.
Something else that you might find useful is a SOIC-8 ZIF adapter. I purchased one of these to put on a breadboard and now programming SPI flash is really simple. You can pick these up on Amazon.
You shouldn’t need this but just in case you do, you can download the FTDI driver for MacOS below:
FTDIUSBSerialDriver for Mac OS
References (Thanks to the internet and everyone who provided content on the following sites without which I would have not been able to put this article together)
https://github.com/ddm/icetools
https://learn.adafruit.com/programming-spi-flash-prom-with-an-ft232h-breakout
1 Comment for “FPGA Development Workflow on a Mac”