-
Notifications
You must be signed in to change notification settings - Fork 224
Adding support for new devices
These instructions were originally posted to reddit here and here but moved here so others could contribute.
I've been asked what steps are necessary to add support for other chips (such as iCE40 UltraLite) to Project IceStorm. I've offered to give some guidance. I might as well do it in form of posts here, so everyone can read along.
icefuzz/icecube.sh
is a shell script that runs the Lattice iCEcube tools to synthesize a given design. The design must be a single Verilog file and the top module must be named "top". For example:
$ cat demo.v
module top(input A, B, C, D, output X, Y, Z);
assign X = |{A, B, C, D};
assign Y = &{A, B, C, D};
assign Z = ^{A, B, C, D};
endmodule
$ ICEDEV=ul1k-cm36a bash icefuzz/icecube.sh demo.v
I've already added support for ICEDEV=ul1k-cm36a
and ICEDEV=ul1k-swg16
to the script some time ago. It currently support this two iCE40 UltraLite devices as well as all HX/LP 1K, 4K, and 8K devices.
The first step is adding support for all chips (and package variations) that you want to add support for to this script. There is a big "case" statement for all supported $ICEDEV values, setting $iCEPACKAGE and $iCE40DEV. Then there is another "case" statement for all $iCE40DEV values setting the variables $icetech, $libfile, and $devfile.
The easiest way to figure out what values to use for this variables is running the iCEcube GUI, synthesizing for the new device you want to add support for (select LSE as synthesis tool), and for a device that already is supported by IceStorm, and simply comparing the log messages for this two cases.
When the target device is not yet supported by icepack/iceunpack, you will get the following error message at the end of the icecube.sh output:
+ icefuzz/../icepack/iceunpack demo.bin demo.asc
Error: Failed to detect chip type.
This is OK. Simply ignore it for now. Adding support to icepack/iceunpack for the new chip will be part 2 of this series.
Running icecube.sh on demo.v will produce the following output files:
File | Description |
---|---|
demo.tmp/ | Working directory for synthesis |
demo.bin | Generated iCE40 bit-stream |
demo.asc | IceStorm ASCII version of bit-stream (when iceunpack support is available) |
demo.glb | Debug output written by the iCEcube "bitmap" tool |
demo.psb | Placement constraint file generated by iCEcube tools |
demo.rpt | Timing report generated by iCEcube "sbtimer" tool |
demo.sdf | Timing edges (SDF format) generated by iCEcube "sbtimer" tool |
demo.vsb | Timing netlist generated by iCEcube "sbtimer" tool |
The most interesting file for us to look at is "demo.glb", the debug output generated by Lattice's "bitmap" tool. It contains blocks of text like this one:
LogicTile_8_10
(11 6) (323 182) (323 182) routing T_6_11.sp4_h_r_11 <X> T_6_11.sp4_v_t_40
(13 6) (325 182) (325 182) routing T_6_11.sp4_h_r_11 <X> T_6_11.sp4_v_t_40
(12 7) (324 183) (324 183) routing T_6_11.sp4_h_r_11 <X> T_6_11.sp4_v_t_40
This tells us that bits (11 6), (13 6), and (12 7) are set in the LogicTile 8 10. Note that the bit (11 6) corresponds to the bit B6[11] in IceStorm notation.
The 2nd pair of numbers (I don't know why this is included twice) are the coordinates of that same bit, but not relative to the tile but to the start of the CRAM bank the tile is in. The rest of the line is a brief description of what the bit does.
If you haven't already you should carefully read the stuff in the "Where is the Documentation?" section on http://www.clifford.at/icestorm/, and the documents linked from there. You should also browse the "Bit Docs" for LOGIC and IO tiles a little bit to get a better understanding and feeling for what the individual bits in the iCE40 tiles do.
Now we can use Lattice iCEcube easily and in a script-based fashion to generate bitstreams and additional output files for a given verilog source file. Now we will be using the information in this .glb file to add support for new devices to icepack/iceunpack.
The next step is adding support for the new part to icepack/iceunpack. First you should make sure you have carefully read the bitstream file format documentation on the IceStorm website.
Running iceunpack demo.bin demo.asc
on a binary bit-stream file for the new chip will likely produce Error: Failed to detect chip type.
now. It's time to start hacking on icepack.cc
to add support for the new chip type. Chip types are detected based on the CRAM size. Run the iceunpack
command again, but this time with the -v
option (use -vv
for increased verbosity). This time the command will (among other things) print the size of the CRAM segments.
Go through icepack.cc
and add a new device type to the existing ones ("1k" and "8k", searching for those strings in icepack.cc
will get you to all the relevant places). Start by adding detection for your chip (see the end of FpgaConfig::read_bits
). For some of the information (like size of the chip in rows and columns of tiles) you might need to consult the device datasheet or the floorplanner view of the iCEcube GUI. Compare the values for already supported devices with their datasheet values and iCEcube floorplanner view if it is hard to make sense of the parameters.
Note that FpgaConfig::chip_cols()
describes the left half of the device. The right half is a mirrored image of the left one. 18, 54, and 42 are the CRAM widths of IO Tiles, LOGIC tiles and RAM tiles respectively.
As soon as iceunpack is able to at least load the binary bit-stream it can come handy to use the various options that write netpbm images as output files. I'd recommend to first familiarize yourself with those options and the images they create using bit-streams for already supported devices, and then use them to analyze the files for the new chip type. (The colorful checkerboard-like image on the IceStorm doc page for the bit-stream format was created using this options.)
Above I briefly discussed the .glb files
created by icecube.sh
. The script icefuzz/glbcheck.py
can be used to compare a .glb
dump file with an ASCII file generated by iceunpack
. This should help a lot with debugging problems with icepack.cc
.
Verification is key! Make sure to set up a small set of examples and write tiny scripts that help you track the mismatches between the generated .asc
and .glb
using glbcheck.py
. Usually when I get stuck in a reverse engineering project, as soon as I have implemented a way to measure my progress, I quickly start making progress again.
Luckily, the icefuzz
directory contains a number of python scripts which can generate sample Verilog. You can run these auto-generated files through iCEcube by running make
. This defaults to the 1k device but you can add additional devices to the Makefile
and fuzzconfig.py
file. (Note, @tannewt is changing the way to select the device to make ICEDEVICE=5k
for example.)
In fuzzconfig.py
you'll need to provide a list of pins in pins
, the number of ram blocks in num_ramb40
and the list of global buffer pins in gpins
. Global buffer pins are those that can be used to provide high drive signals through the whole array for signals such as clock and reset. The list of pins is most easily found in the Package view within iCEcube2. This view also marks pisn as GBIN which go into gpins
. The number of ram blocks can be counted off of the Floor Planner view in iCEcube2.