Skip navigation

Arachne-pnr by Cotton Seed (who also uses pseudonyms cseed and mian2zi3) is an open-source FPGA placement and routing tool for Lattice iCE40 FPGAs.  It's a companion to the open-source Project IceStorm by Clifford Wolf and Mathias Lasser, which has reverse-engineered the iCE40 bitstream.

The usual design flow for IceStorm is to synthesize Verilog source code using Clifford Wolf's Yosys to produce a netlist in the form of a Berkeley Logic Interchange Format (BLIF) file.  Arachne-pnr performs physical placement and routing of the BLIF netlist and produces a text file for IceStorm's icepack tool.  Icepack in turn produces a binary bitstream that can be downloaded to an iCE40 FPGA or SPI Flash memory using IceStorm's iceprog tool.  Here's a typical command sequence:

$ yosys -p "synth_ice40 -blif rot.blif" rot.v

$ arachne-pnr -d 1k -p rot.pcf rot.blif -o rot.txt

$ icepack rot.txt rot.bin

$ [sudo] iceprog rot.bin

I'm going to add IceStorm as a synthesis target for my XXICC (21st Century Co-design) project.  Since XXICC already has rudimentary synthesis, I will skip the Yosys step and go directly to arachne-pnr and IceStorm.  This means I need to produce BLIF files from scratch, which isn't described in the arachne-pnr documentation as far as I can tell.  However, BLIF itself is documented and it's fairly easy to figure out how arachne-pnr uses BLIF from arachne-pnr's example files and others produced by Yosys.  This 'blog documents my experience so others can benefit.  I will be adding to it as I learn more.  This content is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License so others can share it.


Arachne-pnr's source code and documentation are at GitHib: cseed/arachne-pnr


Here is my original IceStorm discussion here at element14: Project IceStorm: fully open-source FPGA tools for Lattice iCE40 and my IceStorm notes: John Beetem's IceStorm Notes

IceStorm uses a Lattice iCEstick as a development board.  It's available for US$21 in the USA:Lattice Semiconductor: ICE40HX1K-STICK-EVN iCEstick Evaluation Kit.

Here are instructions for installing IceStorm and its companion tools: Projet IceStorm: le FPGA libéré! [the FPGA set free!].  The instructions are a combination of French and GNU/Linux.  The IceStorm steps are out of date, since IceStorm is now at GitHub.  The arachne-pnr steps are accurate:

1.  Install IceStorm first.  Arachne-pnr needs IceStorm's textual chip database files which it converts to a faster binary form as part of the make process.  For IceStorm installation instructions, see John Beetem's IceStorm Notes

2.  You can install arachne-pnr anywhere, but the standard place is in "/opt".  You'll probably need to change permissions on "/opt" so you can write to it.

$ cd /opt/


3.  Clone arachne-pnr from GitHub:

$ git clone


4.  Compile arachne-pnr.  It's in C++11, so your GCC will need to support it (GCC 4.8.1 or later).

$ cd arachne-pnr

$ make

$ sudo make install

That's it!  Arachne-pnr is ready to go.  There are sample files in /opt/arachne-pnr/examples/rot and /opt/arachne-pnr/tests.

Like many open-source projects, arachne-pnr doesn't have a lot of documentation.  GitHub is your best starting point.  The arachne-pnr program prints usage and options with the "-h" (help) option:

$ arachne-pnr -h

The usual command for arachne-pnr looks like this:

$ arachne-pnr -d 1k -p rot.pcf rot.blif -o rot.txt


"-d 1k" is the target device, in this case the iCE40 HX1K with 1280 logic cells.  You can also specify "-d 8k" for the HX8K with 7,680 logic cells.  If you leave out "-d", you get 1k by default.  It's the FPGA on the Lattice iCEstick.

"-p rot.pcf" is the physical constraint file (PCF).  It specifies the pinout, i.e., how your top-level signals attach to pins, and has lines like this:

set_io a 78

set_io b 87

set_io LED1 99

HX1K pin numbers assume the TQ144 FPGA used by the Lattice iCEstick.  You can specify a different package with the "-P" option.

If present, "rot.blif" is the input BLIF file.  If absent, I think arachne-pnr reads from standard input.


If present, "-o rot.txt" is the output text file for icepack.  If absent, I think arachne-pnr writes to standard output.

Here are some other useful options:

Like many physical design tools, arachne-pnr uses simulated annealing (SA) for placement.  SA is a pseudo-random algorithm and requires a seed for the pseudo-random number generator.  As of 8 August 2015, arachne-pnr uses 1 as the default seed or you can use the "-r" option to get a random seed, which arachne-pnr prints out.  Earlier arachne-pnr releases always generated a random seed, which meant that each time you ran it you got different results even with the same BLIF and PCF.  You can specify a different fixed seed with the "-s" option.

As we will see shortly, arachne-pnr may pack multiple components -- e.g., a look-up table (LUT) and a flip-flop -- into a single iCE40 logic cell.  The "-B" option creates a post-pack BLIF file to show you what arachne-pnr did.  There's also a "-V" option that creates a post-pack netlist as Verilog.

BLIF and the Silicon Blue Library

BLIF is a general-purpose logic format and I believe it was originally created for specifying logic functions for logic minimization tools.  As such, if you look at the BLIF document you will see logic truth tables and "don't cares" -- all that good stuff you learned about in logic design class.  Remember Karnaugh Maps?

Arachne-pnr doesn't need any of that Boolean stuff.  It assumes all the logic minimization was already done by Yosys or another synthesis tool and logic functions have already been combined into LUTs.  The "gates" used by arachne-pnr are the iCE primitive blocks in Lattice's iCE Technology Library document.  This document is rather hard to find by browsing at the Lattice site: it's hiding at the iCEcube2 Design Software page.  Scroll to the bottom, find "Software Downloads & Documentation", and click "Technical Briefs".  Click on iCE 2015-04 (or whatever) Technology Library.  You'll also need the iCE40 LP/HX Family Data Sheet, especially the Architecture Overview section.

The most important iCE primitives are SB_LUT4 (4-input LUT), SB_CARRY (fast carry logic), SB_DFFxx (D flip-flops with various clock enable and set/reset options) and SB_IO (I/O block with all the options).  "SB" stands for Silicon Blue Technologies, the company that created the iCE40 FPGA and was bought by Lattice Semiconductor.

Here is a diagram showing how SB_LUT4, SB_CARRY, and SB_DFFxx combine to make an iCE40 Logic Cell (LC):


SB_LUT4 has four inputs I0-I3 and output O, which can be the logic cell output O and/or the input to a D flip-flop.  SB_DFFxx is a DFF with various clock-enable and set/reset options, which are listed in the iCE Technology Library document.  For example, SB_DFF has EN=1 and no set/reset, SB_DFFR has asynchronous reset, SB_DFFSS has synchronous set, and SB_DFFESR has a clock-enable input and synchronous reset.  There are also versions with inverted clocks.  LC output O is either the LUT output or the DFF state Q, selected by a configurable multiplexer.

Logic cells are stacked into Programmable Logic Blocks (PLBs), each of which has 8 LCs.  The EN and SR signals and clock polarity must be the same for all LCs in a PLB.  (SR options may be different for each LC.)

Each LC also has carry logic for building high-speed adder, subtracters, comparators, binary counters, etc.  SB_CARRY is the majority function, i.e., the carry-out function of a full adder.  SB_LUT4 calculates the sum output of a full adder and similar functions, setting the configurable mux for LUT input I3 to be the carry in (CI) from the LC below this one.  CI may also be 0 or 1 if this is the lowest LC in a PLB.  The carry out (CO) from SB_CARRY goes to the LC above this one.

Arachne-pnr tries to combine SB_LUT4, SB_CARRY, and SB_DFFxx whenever possible.  In some cases, the logic cannot be combined and arachne-pnr uses SB_LUT4 as a "pass-through" for carry in/out or a DFF input.  Combining SB_LUT4 and SB_CARRY requires the SB_CARRY I0, I1, and CI input signals to be the same as the SB_LUT4 I1, I2, and I3 inputs.

Here's a BLIF example from a binary counter.  Given the current state p5 of the counter,  it calculates the next state np5:


.gate SB_LUT4  I0=clr I1=$0 I2=p5 I3=pc5 O=np5  # np5 = next p5

.param LUT_INIT 0000010101010000

.gate SB_DFF  C=clk D=np5 Q=p5

.gate SB_CARRY CI=pc5 I0=$0 I1=p5 CO=pc6        # pc6 = carry to p6

SB_LUT4 calculates np5 given p5 and carry-in pc5 from the next lower bit of the counter.  np5 is clocked into SB_DFF to update p5 at the next rising edge of clk.  SB_CARRY calculates carry-out pc6 for the next higher bit of the counter.  Note that SB_CARRY's I0, I1, and CI inputs match SB_LUT4's I1, I2, and I3.  SB_LUT4 I0 is set to signal clr which synchronously resets the counter, and I1 is not used so I set it to the constant 0.  (Yosys uses $true and $false for 1 and 0, but BLIF lets you define them to be something else.)

SB_LUT4 has a LUT_INIT parameter that specifies the binary truth table for the LUT.  Each bit i (numbered from LSb = 0) is the LUT value if I[3:0] = i.  If I0 = clr = 1, the LUT output np5 is 0.  If I0 = 0, the LUT output is I2⊕I3 = p5⊕pc5.

Arachne-pnr combines SB_LUT4, SB_DFFxx, and SB_CARRY into an ICESTORM_LC, which includes parameters for DFF and carry chain options.  You can see how it did this using the "-B" option.  I think arachne-pnr understands ICESTORM_LC blocks in its BLIF input if you want to combine LUTs, DFFs, and carry logic yourself before running arachne-pnr.

Finally, let's take a quick look at SB_IO, the I/O block.  The iCE40 I/O block has many options including built-in flip-flops for DDR, tri-state output enable, and pull-up resistor.  See the iCE Technology Library document for details.  If your design has simple inputs and outputs, you can let arachne-pnr generate SB_IO blocks automatically.  However, if you want to use some options like pull-up you may need to include an SB_IO explicitly.

Here's the BLIF for an input pin "en" that has a pull-up:

.gate SB_IO PACKAGE_PIN=en D_IN_0=en.d0

.param PINTYPE  000001000001

.param PULLUP 1

This SB_IO has two of its pins connected: PACKAGE_PIN is the external pin "en" and D_IN_0 is the internal data-in signal, which may be latched or registered.  There's also a D_IN_1 registered on a falling clock edge for DDR.  I've named the internal signal "en.d0" and connect it to internal logic elsewhere in the BLIF file.

The PIN_TYPE parameter specifies whether SB_IO input is registered, latched, or combinational, and whether the SB_IO output is disabled, combinational, registered, inverted, DDR, etc.  The PULLUP parameter enables the I/O pin's pull-up resistor.

OK, that's all for now.  I'll add more as needed.