This is a very simple Arduino sketch and Python 3 client to program SPI flash chips. It's probably not very nice or tolerant, but it does at least have error correction and fast verification.
The requirements are pySerial and clint. Both modules can be installed with pip:
python3 -m pip install pyserial clint
- Program the Arduino with sketch
- Connect the SPI flash chip as described
- Run python client on PC to talk to programmer
Connect the chip as follows, assuming you have an 3.3V 8-pin SSOP Flash chip. You will need an Arduino running at 3.3V logic. See 3.3V Conversion to convert your Arduino to 3.3V.
Or use one of the following devices running at 3.3V:
Chip pin | Arduino pin |
1 /SS | 10 |
2 MISO | 12 |
3 /WP | +3.3V |
4 GND | GND |
5 MOSI | 11 |
6 SCK | 13 |
7 /HOLD | +3.3V |
8 VDD | +3.3V |
# Listing serial ports
> python3 spi_flash_programmer_client.py ports
0: COM15
1: /dev/ttyS1
2: /dev/cu.usbserial
Done
# Read flash
> python3 spi_flash_programmer_client.py \
> -d COM1 -l 4096 -f dump.bin read
Connected to 'SPI Flash programmer v1.0'
....
# Write flash (sectors are erased automatically)
> python3 spi_flash_programmer_client.py \
> -d /dev/ttyS1 -l 4096 -f dump.bin write
Connected to 'SPI Flash programmer v1.0'
....
# Verify flash
> python3 spi_flash_programmer_client.py \
> -d /dev/cu.usbserial -l 4096 -f dump.bin verify
Connected to 'SPI Flash programmer v1.0'
....
# Erase flash
> python3 spi_flash_programmer_client.py \
> -d COM1 -l 4096 erase
Connected to 'SPI Flash programmer v1.0'
[########### ] 383/1024 - 00:01:13
# Set IO Pin value
# Example: IO pin 0x2, set to LOW
> python3 spi_flash_programmer_client.py \
> -d COM1 --io 0x2 --value 0x0 set-output
# Override ChipSelect pin
# Example: use IO pin 13/0xd
> python3 spi_flash_programmer_client.py \
> -d COM1 --io 0xd set-cs-io
# Help text
> python3 spi_flash_programmer_client.py -h
usage: spi_flash_programmer_client.py [-h] [-d DEVICE] [-f FILENAME]
[-l LENGTH] [--rate BAUD_RATE]
[--flash-offset FLASH_OFFSET]
[--file-offset FILE_OFFSET] [--pad PAD]
[--debug {off,normal,verbose}] [--io IO]
[--value VALUE]
{ports,write,read,verify,erase,enable-protection,disable-protection,check-protection,status-register,id-register,set-cs-io,set-output}
Interface with an Arduino-based SPI flash programmer
positional arguments:
{ports,write,read,verify,erase,enable-protection,disable-protection,check-protection,status-register,id-register,set-cs-io,set-output}
command to execute
optional arguments:
-h, --help show this help message and exit
-d DEVICE serial port to communicate with
-f FILENAME file to read from / write to
-l LENGTH length to read/write in bytes, use -1 to write entire
file
--rate BAUD_RATE baud-rate of serial connection
--flash-offset FLASH_OFFSET
offset for flash read/write in bytes
--file-offset FILE_OFFSET
offset for file read/write in bytes
--pad PAD pad value if file is not algined with SECTOR_SIZE
--debug {off,normal,verbose}
enable debug output
--io IO IO pin used for set-cs-io and set-output
--value VALUE value used for set-output
- Try reducing the serial speed from 115200 to 57600. You'll have to edit the value in both the .ino and the .py.
- Play with the SPCR setting in the .ino according to the datasheet.
License [CC0][http://creativecommons.org/publicdomain/zero/1.0/]
To the extent possible under law, the authors below have waived all copyright and related or neighboring rights to spi-flash-programmer.
- Leonardo Goncalves
- Nicholas FitzRoy-Dale, United Kingdom
- Tobias Faller, Germany
I used this to write a 16MB flash chip for the wr703n router running OpenWRT. Recent versions of OpenWRT detect the larger Flash and automatically use it, so you don't need to do any patching. U-Boot still thinks the chip is 4MB large, but Linux doesn't seem to care. So all you need to do is copy the image and write the ART (wireless firmware) partition to the right spot, which is right at the end of Flash.
I guess if you do a system upgrade which puts the kernel image somewhere after the first 4MB you might be in trouble, so upgrade u-boot before doing that.
-
Connect the original chip and dump it:
python3 spi_flash_programmer_client.py -s 4096 -f wr703n.orig.bin read
-
Connect the new chip and write it:
python3 spi_flash_programmer_client.py -s 4096 -f wr703n.orig.bin write
-
Verify the write.
python3 spi_flash_programmer_client.py -s 4096 -f wr703n.orig.bin verify
-
Write the ART partition to the final 64k of the chip (the magic numbers are 16M-64K and 4M-64K respectively).
python3 spi_flash_programmer_client.py -f wr703n.orig.bin --flash-offset 16711680 --file-offset 4128768 write
-
Verify the ART partition.
python3 spi_flash_programmer_client.py -f wr703n.orig.bin --flash-offset 16711680 --file-offset 4128768 verify
-
Solder the new chip in.
If you try this, let me know!
This example uses the OLIMEXINO-32U4 to flash a Olimex iCE40HX8K-EVB. The steps should also work with a iCE40HX1K-EVB.
The board is connected using the UEXT connector.
# Set iCE40-CRESET LOW - PIN 0x2
> python3 spi_flash_programmer_client.py -d COM1 --io 0x2 --value 0x0 set-output
# Set CS/SS to PIN 13/0xd
> python3 spi_flash_programmer_client.py -d COM1 --io 0xd set-cs-io
# power cycle the EVB, check if ID register is readable
> python3 spi_flash_programmer_client.py -d COM1 id-register
# program the bitmap
> python3 spi_flash_programmer_client.py -d COM1 -l -1 --pad 0xff -f toplevel_bitmap.bin write