Commit 0b84e995 authored by Theodor-Adrian Stana's avatar Theodor-Adrian Stana

sw: Updated multiboot.py

The script now uses the same library files used by mass-multiboot.py, but is a
bit more interactive to the user than mass-multiboot.py.

mass-multiboot.py is intended to be used from the server directly to update
cards in a crate.

multiboot.py is intended to be used locally by a user, either to update a single
card, or to debug which gateware versions are loaded on the card's flash.
parent ae9af129
......@@ -46,376 +46,168 @@
# TODO: -
#===============================================================================
import os
import sys
sys.path.append("../ei2c")
import time
from ei2c import *
import ei2cdefine
from ei2cexcept import *
from functools import partial
#===============================================================================
MB_BASE = 0x100
MB_CR_OFS = 0x00
MB_SR_OFS = 0x04
MB_GBBAR_OFS = 0x08
MB_MBBAR_OFS = 0x0C
MB_FAR_OFS = 0x10
#===============================================================================
#===============================================================================
slot = 0
#===============================================================================
# This function handles the OR-ing together of the data bytes and the control
# byte in FAR.
#
# Formatting of data bytes in the FAR register should be done outside this
# function.
def spi_transfer(nbytes, cs, dat):
retval = 0
wval = []
# control byte, to be shifted left by 24
ctrl = ((cs << 3) | 0x4) | (nbytes-1)
# Use appropriate command by type
#
# write - we send up to three data bytes in one FAR register write
# writemregs - we send up to 24 data bytes in eight FAR register writes
if isinstance(dat,int):
testelma.write(slot, MB_BASE+MB_FAR_OFS, (ctrl << 24) | dat)
else:
for i in xrange(len(dat)):
wval.append((ctrl << 24) | dat[i])
testelma.writemregs(slot, MB_BASE+MB_FAR_OFS, wval)
# Read the data and prepare the return value
while (retval & (1 << 28) == 0):
retval = testelma.read(slot, MB_BASE+MB_FAR_OFS)
return retval & 0xFFFFFF
# This function is used to write data bytes to flash. It calls spi_transfer
# and sends the commands to the flash chip. You can follow the logic of this
# function by looking at the sequence of the write command in [1]
def flash_write(addr, dat):
# write enable cmd
spi_transfer(1,1,0x06)
spi_transfer(1,0,0)
# write page cmd
spi_transfer(1,1,0x02)
# send address in reverse order
addr = ((addr & 0xff0000) >> 16) | (((addr & 0xff00) >> 8) << 8) | ((addr & 0xff) << 16)
spi_transfer(3,1,addr)
# Now, massage the data array into 3-byte commands sent into 32-bit register,
# in batches of eight registers (writemregs command).
i = 0
l = len(dat)
while (l):
wval = []
# If we have more than 24 bytes left in the data array, we can use the full
# writemregs
if (int(l/24)):
for j in xrange(0, 8):
wval.append((dat[(i+2)+3*j] << 16) | (dat[(i+1)+3*j] << 8) | dat[i+3*j])
spi_transfer(3, 1, wval)
i += 24;
l -= 24;
# When we're left to fewer than 24 bytes in the array, try to use one more
# writemregs with as many regs as possible
elif (int(l/3)):
for j in xrange(0, l/3):
wval.append((dat[(i+2)+3*j] << 16) | (dat[(i+1)+3*j] << 8) | dat[i+3*j])
spi_transfer(3,1,wval);
i += 3 * int(l/3)
l -= 3 * int(l/3)
# When we're down to less than three bytes, just use simple one-byte xfers
elif (l):
for j in xrange(0, l):
spi_transfer(1,1,dat[i])
i += 1
l -= 1
spi_transfer(1,0,0)
# This function is used to read a number of data bytes from the flash memory
# It returns the values of the three consecutive flash registers packed into
# one 24-bit data value in little-endian order
def flash_read(addr, nrbytes):
ret = []
spi_transfer(1,1,0x0b)
# send address in reverse order
addr = ((addr & 0xff0000) >> 16) | (((addr & 0xff00) >> 8) << 8) | ((addr & 0xff) << 16)
spi_transfer(3,1,addr)
spi_transfer(1,1,0)
# Read bytes in groups of three
for i in range(nrbytes):
ret.append(spi_transfer(1,1,0))
spi_transfer(1,0,0)
return ret
# Send a sector erase command
def flash_serase(addr):
spi_transfer(1,1,0x06)
spi_transfer(1,0,0)
spi_transfer(1,1,0xD8)
# send address in reverse order
addr = ((addr & 0xff0000) >> 16) | (((addr & 0xff00) >> 8) << 8) | ((addr & 0xff) << 16)
spi_transfer(3,1,addr)
spi_transfer(1,0,0)
# Send a block erase command
def flash_berase():
spi_transfer(1,1,0x06)
spi_transfer(1,0,0)
spi_transfer(1,1,0xc7)
spi_transfer(1,0,0)
# Read status register command
def flash_rsr():
spi_transfer(1,1,0x05)
ret = spi_transfer(1,1,0)
spi_transfer(1,0,0)
return ret
#=============================================================================#
#
# Read from flash
#
def _read(addr):
print "Reading 256 bytes from 0x%06X" % addr
tr0 = time.time()
rd = flash_read(addr,256)
tr1 = time.time()
rd = "".join(["%02X" % b for b in rd])
print rd
#
# Write to flash
#
def _write(addr):
# Ookay, now we start to write the flash
print "Writing bitstream..."
# Prepare time arrays, to calculate average time each operation takes
tdat = []
twr = []
twa = []
te = []
tp = []
# Ask for and open bitstream file
fname = raw_input("Input file name: ")
f = open(fname,'rb')
tw0 = time.time()
# Each line in the input file contains the data for one page
for dat in iter(partial(f.read, 256), ''):
dat = [int(d.encode("hex"), 16) for d in dat]
print "%x" % addr
# Erase on sector boundary
if not (addr % 0x10000):
print 'erase'
t1 = time.time()
flash_serase(addr)
while (flash_rsr() & 0x01):
pass
t2 = time.time()
te.append(t2-t1)
t3 = time.time()
flash_write(addr, dat)
t4 = time.time()
while (flash_rsr() & 0x01):
pass
t5 = time.time()
addr += 256
twr.append(t4-t3)
twa.append(t5-t4)
tp.append(t5-t3)
tw1 = time.time()
# Close file handle
f.close()
# Phew! We're ready, so calculate the mean time it took for each process
print "DONE!"
print "erase time: %2.6f" % float(sum(te)/len(te))
print "write time: %2.6f" % float(sum(twr)/len(twr)) #(t4-t3)
print "wait time: %2.6f" % float(sum(twa)/len(twa)) #(t5-t4)
print "page write time: %2.6f" % float(sum(tp)/len(tp))
print "total write time: %2.6f" % float(tw1-tw0)
#
# Start IPROG sequence
#
def _iprog(addr):
print "Issuing IPROG command..."
testelma.write(slot, MB_BASE+MB_GBBAR_OFS, 0x44 | (0x0b << 24))
testelma.write(slot, MB_BASE+MB_MBBAR_OFS, addr | (0x0b << 24))
testelma.write(slot, MB_BASE+MB_CR_OFS, 0x10000)
try:
testelma.write(slot, MB_BASE+MB_CR_OFS, 0x20000)
except NAckError:
pass
# Set timeout...
t0 = time.time()
t1 = t0 + 20
# and wait for the FPGA to gracefully respond, or die trying
while (1):
try:
if (time.time() >= t1):
print "Timeout, IPROG unsuccessful!"
break
if ((testelma.read(slot, 0x4) & 0xF0) == 0x00):
print "IPROG unsuccessful, fallback to Golden bitstream occured!"
break
else:
print "IPROG successful!"
break
except NAckError:
continue
def _rdcfgreg():
print "Press 'q' to end config reg readout"
while 1:
try:
reg = raw_input('Address (hex): ')
reg = int(reg, 16)
if (reg < 0x00) or (reg > 0x22):
raise ValueError
testelma.write(slot, MB_BASE+MB_CR_OFS, (1 << 6) | reg)
val = testelma.read(slot, MB_BASE+MB_SR_OFS)
if (val & (1 << 16)):
print "REG(0x%02X) = 0x%04X" % (reg, val & 0xFFFF)
else:
print "CFGREGIMG invalid!"
except ValueError:
if (reg == 'q'):
break
print "Please input a hex value in the range [0x00, 0x22] or 'q' to quit"
#=============================================================================#
sys.path.append("lib-multiboot")
from xil_multiboot import *
# Return the list of gateware files at the requested path; each board should
# have a folder under "path" with its specific name. Files under this folder
# with the board's name should be bitstream files only.
def get_gw_list(path):
path = path+'/'
return [path+f for f in os.listdir(path) if not os.path.isdir(f)]
# MAIN "FUNCTION"
if __name__ == "__main__":
# Get the IP, user and password for the ELMA crate from ei2cdefine.py
ip = ei2cdefine.HNAME
user = ei2cdefine.USER
pwd = ei2cdefine.PWD
testelma = EI2C(ip, user, pwd)
testelma.open()
# Ask for slot number
while 1:
try:
slot = raw_input("Slot no.: ")
slot = int(slot)
break
except TypeError as e:
print("Please input a decimal slot number.")
except SlotError as e:
print(e.strerror)
except KeyboardInterrupt:
sys.exit();
except:
print("Unexpected error: ", sys.exc_info()[0])
# For blocking boads:
# The PULSETEST bitstream has the MultiBoot module starting at address 0x300
# Gateware versions < 3.0, or golden <0.2 have it starting at address 0x040
boardid = testelma.read(slot,0x00)
gwvers = testelma.read(slot, 0x04) & 0xff
if (boardid == 0x54424c4f):
if (gwvers == 0xff):
MB_BASE = 0x300
elif ((gwvers > 0x10) and (gwvers < 0x23)) or \
(((gwvers & 0xf0) == 0x00) and (gwvers < 0x02)):
MB_BASE = 0x040
# Read config reg, if user wants to
while 1:
rdcfgreg = raw_input("Read config reg? (y/n) ")
if (rdcfgreg == 'y'):
_rdcfgreg()
break
elif (rdcfgreg == 'n'):
break
# Ask else to do (read/write/iprog)
while 1:
rd = raw_input("Read first page? (y/n) ")
if (rd == 'y') or (rd == 'n'):
break
while 1:
wr = raw_input("Write to flash? (y/n) ")
# Get the IP, user and password for the ELMA crate from ei2cdefine.py
ip = ei2cdefine.HNAME
user = ei2cdefine.USER
pwd = ei2cdefine.PWD
elma = EI2C(ip, user, pwd)
elma.open()
# Ask for slot number
while 1:
try:
slot = raw_input("Slot no.: ")
slot = int(slot)
break
except TypeError as e:
print("Please input a decimal slot number.")
except SlotError as e:
print(e.strerror)
except ValueError as e :
print ("%s is not a valid integer." % e.args[0].split(": ")[1])
except KeyboardInterrupt:
sys.exit();
except:
print("Unexpected error: ", sys.exc_info()[0])
# For blocking boads:
# The PULSETEST bitstream has the MultiBoot module starting at address 0x300
# Gateware versions < 3.0, or golden <0.2 have it starting at address 0x040
boardid = elma.read(slot,0x00)
gwvers = elma.read(slot, 0x04) & 0xff
baseaddr = 0x100
if (boardid == 0x54424c4f):
if (gwvers == 0xff):
baseaddr = 0x300
elif ((gwvers > 0x10) and (gwvers < 0x23)) or \
(((gwvers & 0xf0) == 0x00) and (gwvers < 0x02)):
baseaddr = 0x040
mb = XilMultiboot(ELMA_I2C_MULTIBOOT, elma, slot, baseaddr, "")
# Read config reg, if user wants to
while 1:
rdcfgreg = raw_input("Read config reg? (y/n) ")
if (rdcfgreg == 'y'):
mb.rdcfgreg()
break
elif (rdcfgreg == 'n'):
break
# Ask what else to do (read/write/iprog)
while 1:
rd = raw_input("Read from flash? (y/n) ")
if (rd == 'y'):
print("This might take a VERY long time if you read a lot of bytes...")
while 1:
try:
rdsa = raw_input("Start address: 0x")
rdsa = int(rdsa, 16)
rdea = raw_input("End address: 0x")
rdea = int(rdea, 16)
break
except ValueError as e:
print "Please input a hexadecimal address!"
except KeyboardInterrupt:
sys.exit()
except:
print "Unexpected error: ", sys.exc_info()[0]
break
elif (rd == 'n'):
break
while 1:
wr = raw_input("Write to flash? (y/n) ")
if (wr == 'y'):
while 1:
wr = raw_input("Are you sure? (y/n) ")
if (wr == 'y'):
# Get gateware list from current folder
print("Which gateware would you like to program?")
gw_list = sorted(get_gw_list("bin"))
for (i, gw) in enumerate(gw_list):
print(" %2d -> %s" % (i, os.path.basename(gw)))
if (len(gw_list) == 0):
print("No bitstreams file found in binaries folder, cannot perform write...")
wr = 'n'
break
start = False
while (not start):
is_valid = False
while(not is_valid):
try :
print("")
choice = int(raw_input("Enter your choice : "))
if choice < len(gw_list):
is_valid = 1
else:
print("%d is not in range 0-%d"%(choice, len(gw_list)-1))
except ValueError as e :
print ("%s is not a valid integer." % e.args[0].split(": ")[1])
gw_path = gw_list[choice]
print("Selected gateware: %s" % os.path.basename(gw_path))
inp = raw_input("Is this okay? (y/n) ");
if (inp.lower() == 'y'):
start = True
print("")
# Set bitstream filename to selected bitstream
mb.set_bitstream_file(gw_list[choice])
break
elif (wr == 'n'):
break
break
elif (wr == 'n'):
break
while 1:
iprog = raw_input("Issue IPROG? (y/n) ")
if (iprog == 'y') or (iprog == 'n'):
break
# Ask for multiboot address on write or IPROG
if (wr == 'y') or (iprog == 'y'):
while 1:
try:
multiboot_addr = raw_input("MultiBoot bitstream address: 0x")
multiboot_addr = int(multiboot_addr, 16)
break
except ValueError as e:
print "Please input a hexadecimal address!"
except KeyboardInterrupt:
sys.exit()
except:
print "Unexpected error: ", sys.exc_info()[0]
# Now do something based on user selection
if (rd == 'y'):
mb.read(rdsa, rdea)
if (wr == 'y'):
while 1:
wr = raw_input("Are you sure? (y/n) ")
if (wr == 'y') or (wr == 'n'):
break
break
elif (wr == 'n'):
break
while 1:
iprog = raw_input("Issue IPROG? (y/n) ")
if (iprog == 'y') or (iprog == 'n'):
break
# Ask for multiboot address
while 1:
try:
multiboot_addr = raw_input("MultiBoot bitstream address: 0x")
multiboot_addr = int(multiboot_addr, 16)
break
except ValueError as e:
print "Please input a hexadecimal address!"
except KeyboardInterrupt:
sys.exit()
except:
print "Unexpected error: ", sys.exc_info()[0]
# Now do something based on user selection
# read from flash
if (rd == 'y'):
_read(multiboot_addr)
# write to flash
if (wr == 'y'):
_write(multiboot_addr)
# Finally, issue the reprogramming (IPROG) command
if (iprog == 'y'):
_iprog(multiboot_addr)
mb.write(multiboot_addr)
if (iprog == 'y'):
mb.iprog(multiboot_addr)
# Close I2C connection
testelma.close()
# Close I2C connection
elma.close()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment