-
-
Save gregdavill/4f9f536757966171ef974f98348bbacb to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3 | |
import sys | |
import textwrap | |
# Very basic bitstream to SVF converter, tested with the ULX3S WiFi interface | |
flash_page_size = 256 | |
erase_block_size = 64*1024 | |
def bitreverse(x): | |
y = 0 | |
for i in range(8): | |
if (x >> (7 - i)) & 1 == 1: | |
y |= (1 << i) | |
return y | |
with open(sys.argv[1], 'rb') as bitf: | |
bs = bitf.read() | |
# Autodetect IDCODE from bitstream | |
idcode_cmd = bytes([0xE2, 0x00, 0x00, 0x00]) | |
idcode = None | |
for i in range(len(bs) - 4): | |
if bs[i:i+4] == idcode_cmd: | |
idcode = bs[i+4] << 24 | |
idcode |= bs[i+5] << 16 | |
idcode |= bs[i+6] << 8 | |
idcode |= bs[i+7] | |
break | |
if idcode is None: | |
print("Failed to find IDCODE in bitstream, check bitstream is valid") | |
sys.exit(1) | |
print("IDCODE in bitstream is 0x%08x" % idcode) | |
bitf.seek(0) | |
address = 0 | |
last_page = -1 | |
with open(sys.argv[2], 'w') as svf: | |
print(""" | |
STATE RESET; | |
HDR 0; | |
HIR 0; | |
TDR 0; | |
TIR 0; | |
ENDDR DRPAUSE; | |
ENDIR IRPAUSE; | |
STATE IDLE; | |
""", file=svf) | |
print(""" | |
SIR 8 TDI (E0); | |
SDR 32 TDI (00000000) | |
TDO ({:08X}) | |
MASK (FFFFFFFF); | |
""".format(idcode), file=svf) | |
print(""" | |
SIR 8 TDI (1C); | |
SDR 510 TDI (3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF | |
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); | |
// Enter Programming mode | |
SIR 8 TDI (C6); | |
SDR 8 TDI (00); | |
RUNTEST IDLE 2 TCK 1.00E-02 SEC; | |
// Erase | |
SIR 8 TDI (0E); | |
SDR 8 TDI (01); | |
RUNTEST IDLE 2 TCK 2.0E-1 SEC; | |
// Read STATUS | |
SIR 8 TDI (3C); | |
SDR 32 TDI (00000000) | |
TDO (00000000) | |
MASK (0000B000); | |
// Exit Programming mode | |
SIR 8 TDI (26); | |
RUNTEST IDLE 2 TCK 1.00E-02 SEC; | |
// BYPASS | |
SIR 8 TDI (FF); | |
STATE IDLE; | |
RUNTEST 32 TCK; | |
RUNTEST 2.00E-2 SEC; | |
// Enter SPI mode | |
SIR 8 TDI (3A); | |
SDR 16 TDI (68FE); | |
STATE IDLE; | |
RUNTEST 32 TCK; | |
RUNTEST 2.00E-2 SEC; | |
// SPI IO | |
SDR 8 TDI (D5); | |
RUNTEST 2.00E-0 SEC; | |
// CONFIRM FLASH ID | |
SDR 32 TDI (000000F9) | |
TDO (A8FFFFFF) | |
MASK (FF000000); | |
""", file=svf) | |
while True: | |
if((address // 0x10000) != last_page): | |
last_page = (address // 0x10000) | |
print("""SDR 8 TDI (60); | |
""", file=svf) | |
address_flipped = [bitreverse(x) for x in [0xd8,int(address // 0x10000),0x00,0x00]] | |
hex_address= ["{:02X}".format(x) for x in reversed(address_flipped)] | |
print("\n".join(textwrap.wrap("SDR {} TDI ({});".format(8*len(hex_address), "".join(hex_address)), 100)), file=svf) | |
print("""RUNTEST 3.00 SEC; | |
""", file=svf) | |
chunk = bitf.read(flash_page_size) | |
if not chunk: | |
break | |
# Convert chunk to bit-reversed hex | |
br_chunk = [bitreverse(x) for x in bytes([0x20, int(address / 0x10000 % 0x100),int(address / 0x100 % 0x100),int(address % 0x100)]) + chunk] | |
address += len(chunk) | |
hex_chunk = ["{:02X}".format(x) for x in reversed(br_chunk)] | |
print(""" | |
SDR 8 TDI (60); | |
""", file=svf) | |
print("\n".join(textwrap.wrap("SDR {} TDI ({});".format(8*len(br_chunk), "".join(hex_chunk)), 100)), file=svf) | |
print(""" | |
RUNTEST 2.50E-2 SEC; | |
""", file=svf) | |
print(""" | |
// BYPASS | |
SIR 8 TDI (FF); | |
STATE IDLE; | |
RUNTEST 32 TCK; | |
RUNTEST 2.00E-2 SEC; | |
STATE RESET; | |
""", file=svf) |
If you're in a rush and need a solution that works. You could try installing Diamond and using their in-built programmer.
With regards to the code above I'm not exactly sure why it's not working for you. Do you hove the part number of the FLASH chip used on the ECP5 eval board? It might be that the flash page size is different? Or the time waited for the erase cycle is not long enough.
A known issue with this code (and using SVF for programming FLASH) is that we need to wait the maximum flash erase and write times, as it's impossible to read back the status bits to check when these actions complete.
the flash chip is the MX25L12833FMI-10G
I'm running the whole toolchain on ARM Linux. Diamond won't run on that unfortunately.
You might want to try with 0x20 here https://gist.github.com/gregdavill/4f9f536757966171ef974f98348bbacb#file-background_spi_test-py-L121 set to 0x02 (which seems to be the page program command for your flash)
Whoa! that worked.
So the changes are:
// CONFIRM FLASH ID
SDR 32 TDI (000000F9)
TDO (A8FFFFFF)
MASK (FF000000);
changing to :
// CONFIRM FLASH ID
SDR 32 TDI (000000F9)
TDO (18FFFFFF)
MASK (FF000000);
and:
# Convert chunk to bit-reversed hex
br_chunk = [bitreverse(x) for x in bytes([0x20, int(address / 0x10000 % 0x100),int(address / 0x100 % 0x100),int(address % 0x100)]) + chunk]
change to:
# Convert chunk to bit-reversed hex
br_chunk = [bitreverse(x) for x in bytes([0x02, int(address / 0x10000 % 0x100),int(address / 0x100 % 0x100),int(address % 0x100)]) + chunk]
Then the svf file created by the script can be used to flash the Lattice ECP5 evaluation board.
background_spi_test.py design.bit progfile.svf
openocd -f /usr/share/trellis/misc/openocd/ecp5-evn.cfg -c "transport select jtag; init; svf progfile.svf; exit"
Thanks!!
Is this tool still actual or is it possible to enable SPI flash programming directly from Project Trellis tools now?
This tool will still work.
If you're using an FT232H based programmer or equivalent you can check out this: https://github.com/gregdavill/ecpprog issues/pull requests welcome
ecpprog is awesome and works flawlessly on the ecp5_evn board. I can't believe I read your post about ecpprog on Twitter and then forgot about it. 🤦♂️
That's great! I don't own a ecp5_evn, so I've not tested on that yet. Good to know it works! :D
Hmmm didn't work for me with Winbond 25Q32JVSIQ flash on the Colorlight 5A-75B V8.0
First changed the ID check to
// CONFIRM FLASH ID
SDR 32 TDI (000000F9)
TDO (68FFFFFF)
MASK (FF000000);
No idea if 68 is correct, I can't make sense of what this table is trying to say. But it makes it past the check...
and then also used 0x02 as the page program command, which is the one I need as far as I can tell from the datasheet.
But yea, didn't write anything to flash as far as I can tell.
I have tried the script with the Lattice ECP5 evaluation board.
background_spi_test.py design.bit progfile.svf
openocd -f /usr/share/trellis/misc/openocd/ecp5-evn.cfg -c "transport select jtag; init; svf progfile.svf; exit"
it gives an error that the flash id is not found.
in your script there is:
// CONFIRM FLASH ID
SDR 32 TDI (000000F9)
TDO (A8FFFFFF)
MASK (FF000000);
when changing that to :
// CONFIRM FLASH ID
SDR 32 TDI (000000F9)
TDO (18FFFFFF)
MASK (FF000000);
it continues (takes very long) but the design does not load after a power cycle.
Any ideas what might be wrong or how to solve it?
when uploading the same design with prjtrellis tools it works but that does not support write to flash.