Litex System on Colorlight Board. ECP5 FPGA runs Risc-V Core and custom Hardware
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

145 lines
4.2 KiB

#!/usr/bin/env python3
import sys
import textwrap
import datetime
# Very basic bitstream to SVF converter, tested with the ULX3S WiFi interface
def bitreverse(x):
y = 0
for i in range(8):
if (x >> (7 - i)) & 1 == 1:
y |= (1 << i)
return y
def convertBitToFlashFile(bitFile, FlashFile, flash_page_size = 256, address = 0, erase_block_size = 64*1024):
with open(bitFile, '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)
last_page = -1
with open(FlashFile, 'w') as svf:
now = datetime.datetime.now()
print(f"""// generated {now:%Y-%m-%d %H:%M}
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 (CMD 0xAB release Power Down???)
SDR 8 TDI (D5);
RUNTEST 2.00E-0 SEC;
// CONFIRM FLASH ID (CMD 0x9f JEDEC Flash Id -> Size must be 0x16)
SDR 32 TDI (000000F9)
TDO (68FFFFFF)
MASK (FF000000);
// CMD 0x06 Write Enable + 0x01 Write 0x00 to Status Register
SDR 8 TDI(60);
SDR 16 TDI(0080);
RUNTEST 1.00E-0 SEC;
""", file=svf)
while True:
if((address // 0x10000) != last_page):
last_page = (address // 0x10000)
print(f"// CMD 0x06 Write Enable + 0xd8 Erase 64K at 0x{address:x}", file=svf)
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;\n", file=svf)
chunk = bitf.read(flash_page_size)
if not chunk:
break
# Convert chunk to bit-reversed hex
print(f"// CMD 0x06 Write Enable + 0x02 Write Chunk to 0x{address:x}", file=svf)
br_chunk = [bitreverse(x) for x in bytes([0x02, 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;\n", file=svf)
print("""
// BYPASS
SIR 8 TDI (FF);
//REFRESH
SIR 8 TDI(79);
SDR 24 TDI(000000);
STATE IDLE;
RUNTEST 32 TCK;
RUNTEST 2.00E-2 SEC;
STATE RESET;
""", file=svf)
if __name__ == "__main__":
convertBitToFlashFile(sys.argv[1], sys.argv[2])