pulp-runtime/bin/plp_flash_stimuli.py
Luca Valente 277d7ac330 Add support for running from flash
* Add possibility to supply additional vsim flags with run target
* Now the user can supply the bootmode=<spi|flash|fast_debug|jtag>
variable with the run target to choose the desired boot mode.
2021-05-05 17:39:24 +02:00

469 lines
16 KiB
Python
Executable file

#!/usr/bin/env python3
#
# Copyright (C) 2015 ETH Zurich and University of Bologna
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.
#
# Authors: Germain Haugou (germain.haugou@gmail.com)
#
#file extracted from pulp sdk : https://github.com/pulp-platform/pulp-sdk.git
#to be used in marsellus project
import os
import struct
from elftools.elf.elffile import ELFFile
import subprocess
def dumpByteToSlm(file, addr, value):
file.write("@%08X %02X\n" % (addr, value))
def dumpShortToSlm(file, addr, value):
file.write("@%08X %04X\n" % (addr, value))
def dumpWordToSlm(file, addr, value):
file.write("@%08X %08X\n" % (addr, value))
def dumpLongToSlm(file, addr, value):
file.write("@%08X %016X\n" % (addr, value))
def dump_word( filetoprint, addr, data_s):
for i in xrange(0,4,1):
filetoprint.write("@%08X %s\n" % ( addr+i, data_s[i*2:(i+1)*2] ))
return 4
class Comp(object):
def __init__(self, dirpath, name):
self.path = os.path.join(dirpath, name)
self.name = name
self.size = os.path.getsize(self.path)
def dump(self):
print (self.name)
print ('Path: ' + self.path)
print ('Flash addr: ' + str(self.flashAddr))
print ('Size: ' + str(self.size))
print ('')
class BinarySegment(object):
def __init__(self, base, data):
self.base = base
self.data = data
self.size = len(data)
class Binary(object):
def __init__(self, elf=None):
self.segments = []
if elf != None:
with open(elf, 'rb') as file:
elffile = ELFFile(file)
self.entry = elffile['e_entry']
for segment in elffile.iter_segments():
if segment['p_type'] == 'PT_LOAD':
self.segments.append(BinarySegment(segment['p_paddr'], segment.data()))
class FlashImage(object):
def __init__(self, raw=None, stimuli=None, verbose=True, archi=None, encrypt=False, aesKey=None, aesIv=None, flashType='spi', qpi=True):
self.bootBinary = None
self.raw = raw
self.stimuli = stimuli
self.compList = []
self.buff = []
self.flashOffset = 0
if flashType == 'hyper': self.blockSize = 1024
else: self.blockSize = 4096
self.bootaddr = 0x1c000000
self.verbose = verbose
self.archi = archi
self.encrypt = encrypt
self.aesKey = aesKey
self.aesIv = aesIv
self.flashType = flashType
self.qpi = qpi
def appendBootBinary(self, elf=None):
self.bootBinary = Binary(elf=elf)
def appendComponent(self, dirname, name):
self.compList.append(Comp(dirname, name))
def __roundToNextBlock(self):
nextOffset = (int)((self.flashOffset + self.blockSize - 1) / self.blockSize) * self.blockSize
padding = nextOffset - self.flashOffset
self.flashOffset = nextOffset
for i in range(0, padding):
self.buff.append(0)
def __padBlock(self, size):
padsize = (int)((size + self.blockSize - 1) / self.blockSize) * self.blockSize - size
self.flashOffset += padsize
for i in range(0, padsize):
self.buff.append(0)
def __pad(self, padsize, buff=None):
if buff is None:
self.flashOffset += padsize
for i in range(0, padsize):
self.buff.append(0)
else:
for i in range(0, padsize):
buff = self.__appendByte(0, buff=buff)
return buff
def __appendInt(self, value, newBlock=False, buff=None):
if buff is None:
#if newBlock: self.__roundToNextBlock()
self.buff += struct.pack("I", value)
self.flashOffset += 4
else:
buff += struct.pack("I", value)
return buff
def __appendLongInt(self, value, newBlock=False, buff=None):
if buff is None:
#if newBlock: self.__roundToNextBlock()
self.buff += struct.pack("Q", value)
self.flashOffset += 8
else:
buff += struct.pack("Q", value)
return buff
def __appendByte(self, value, newBlock=False, buff=None):
if buff is None:
#if newBlock: self.__roundToNextBlock()
self.buff += struct.pack("B", value)
self.flashOffset += 1
else:
buff += struct.pack("B", value)
return buff
def get_crc(self, buff):
crc = 0xffffffff
for data in buff:
crc = crc ^ data
for i in range(7, -1, -1):
if crc & 1 == 1:
mask = 0xffffffff
else:
mask = 0
crc2 = crc >> 1
crc = (crc >> 1) ^ (0xEDB88320 & mask)
return (crc ^ 0xffffffff)
def __appendBuffer(self, buffer, newBlock=False, pad=False, encrypt=False, padToOffset=None):
#if newBlock: self.__roundToNextBlock()
if self.encrypt:
cmd = 'aes_encode %s %s' % (self.aesKey, self.aesIv)
crc = self.get_crc(buffer)
buffer += struct.pack("I", crc)
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
out, err = p.communicate(buffer)
if p.returncode != 0:
raise Exception('Error when executing aes_encore to encrypt binary, probably this tool is not available')
buffer = out
if padToOffset != None:
self.__pad(padToOffset - self.flashOffset)
self.buff += buffer
self.flashOffset += len(buffer)
if pad:
self.__padBlock(len(buffer))
def __dumpToBuff(self):
self.__dumpBootBinaryToBuff()
self.__dumpCompsToBuff()
def __dumpFlashHeader_v1(self):
if self.verbose:
print ("Generating boot binary")
index = 0
print ("Merging following segments:")
for segment in self.bootBinary.segments:
if self.verbose: print (" Area %d: base: 0x%x, size: 0x%x" % (index, segment.base, segment.size))
index = index + 1
l1Area = BinarySegment(0x10000000, [])
l2Area = BinarySegment(0x1c000000, [])
self.bootBinary.mergedSegments = [l2Area, l1Area]
for segment in self.bootBinary.segments:
if segment.base < 0x1c000000: area = l1Area
else: area = l2Area
if segment.base > area.base + area.size:
diff = segment.base - (area.base + area.size)
area.size += diff
for i in range(0, diff):
area.data.append(0)
area.data += segment.data
area.size += segment.size
# First compute areas flash information
flashOffset = 0
flashOffset += 4*4*2
print ("Merged into:")
index = 0
for segment in self.bootBinary.mergedSegments:
segment.nbBlocks = int((segment.size + self.blockSize - 1) / self.blockSize)
segment.offset = flashOffset
flashOffset += segment.nbBlocks * self.blockSize
if self.verbose: print (" Area %d: offset: 0x%x, base: 0x%x, size: 0x%x, nbBlocks: %d" % (index, segment.offset, segment.base, segment.size, segment.nbBlocks))
index += 1
# Then write the header containing memory areas declaration
self.__appendInt(l2Area.offset)
self.__appendInt(l2Area.base)
self.__appendInt(l2Area.size)
self.__appendInt(l2Area.nbBlocks)
self.__appendInt(l1Area.offset)
self.__appendInt(l1Area.base)
self.__appendInt(l1Area.size)
self.__appendInt(l1Area.nbBlocks)
# Finally write the data
for area in self.bootBinary.mergedSegments:
self.__appendBuffer(area.data, pad=True, encrypt=self.encrypt)
def __dumpFlashHeader_v2(self):
if self.verbose:
print ("Generating boot binary")
print (" Nb areas: %d" % len(self.bootBinary.segments))
# First compute areas flash information
flashOffset = 0
flashOffset += 4 + 4 + 4 + 4 + 16 * 4 * len(self.bootBinary.segments)
crc_offset = flashOffset
flashOffset += 4
flashOffset = (flashOffset + self.blockSize - 1) & ~(self.blockSize - 1)
if self.encrypt:
for segment in self.bootBinary.segments:
segment.size += 4
index = 0
for segment in self.bootBinary.segments:
segment.nbBlocks = int((segment.size + self.blockSize - 1) / self.blockSize)
segment.offset = flashOffset
flashOffset += segment.nbBlocks * self.blockSize
if self.verbose: print (" Area %d: offset: 0x%x, base: 0x%x, size: 0x%x, nbBlocks: %d" % (index, segment.offset, segment.base, segment.size, segment.nbBlocks))
index += 1
# Then write the header containing memory areas declaration
flashOffset = (flashOffset + 7) & ~7
self.fsOffset = flashOffset
header_buff = bytes([])
header_buff = self.__appendInt(flashOffset, buff=header_buff)
header_buff = self.__appendInt(len(self.bootBinary.segments), buff=header_buff)
#self.__appendInt(self.bootBinary.entry)
header_buff = self.__appendInt(self.bootBinary.entry, buff=header_buff)
header_buff = self.__appendInt(self.bootaddr, buff=header_buff)
for area in self.bootBinary.segments:
header_buff = self.__appendInt(area.offset, buff=header_buff)
header_buff = self.__appendInt(area.base, buff=header_buff)
header_buff = self.__appendInt(area.size, buff=header_buff)
header_buff = self.__appendInt(area.nbBlocks, buff=header_buff)
header_buff = self.__pad(crc_offset - self.flashOffset - len(header_buff), buff=header_buff)
crc = self.get_crc(header_buff)
header_buff = self.__appendInt(crc, buff=header_buff)
self.__appendBuffer(header_buff, encrypt=self.encrypt)
# Finally write the data
for area in self.bootBinary.segments:
self.__appendBuffer(area.data, padToOffset=area.offset, encrypt=self.encrypt)
self.__pad(self.fsOffset - self.flashOffset)
def __dumpBootBinaryToBuff(self):
if self.bootBinary != None:
if self.archi == 'vivosoc2' or self.archi == 'fulmine':
self.__dumpFlashHeader_v1()
else:
self.__dumpFlashHeader_v2()
else:
# In case no boot binary is there, we must have at least the first word telling where starts the next descriptor
self.__appendLongInt(8)
def __dumpCompsToBuff(self):
#
# Flash address computation
#
if self.verbose: print ('Generating files (header offset: 0x%x)' % self.flashOffset)
flashAddr = self.flashOffset
headerSize = 0
# Compute the header size
headerSize += 12 # Header size and number of components
for comp in self.compList:
headerSize += 12 # Flash address, size and path length
headerSize += len(comp.name)+1 # Path
flashAddr += headerSize
# Now set the flash address for each component
for comp in self.compList:
comp.flashAddr = (flashAddr + 3) & ~3
if self. verbose:
print (' Adding component (name: %s, flashOffset: 0x%x)' % (comp.name, comp.flashAddr))
flashAddr = comp.flashAddr + comp.size
# Now create the raw image as a byte array
# First header size
self.__appendLongInt(headerSize)
# Number of components
self.__appendInt(len(self.compList))
# Then for each component
for comp in self.compList:
# The flash address
self.__appendInt(comp.flashAddr)
# Binary size
self.__appendInt(comp.size)
# The path length
self.__appendInt(len(comp.name)+1)
# And the path
self.__appendBuffer(comp.name.encode('utf-8'))
self.__appendByte(0)
# Then dump all components
for comp in self.compList:
with open(comp.path, 'rb') as file:
self.__appendBuffer(file.read(), padToOffset=comp.flashAddr)
def generate(self):
self.__dumpToBuff()
if self.raw != None:
try:
os.makedirs(os.path.dirname(self.raw))
except:
pass
with open(self.raw, 'wb') as file:
file.write(bytes(self.buff))
if self.stimuli != None:
try:
os.makedirs(os.path.dirname(self.stimuli))
except:
pass
with open(self.stimuli, 'w') as file:
if self.flashType == 'mram':
last_bytes = len(self.buff) & 0x7
for i in range(0, 8 - last_bytes):
self.__appendByte(0)
for i in range(0, len(self.buff)>>3):
value = (self.buff[i*8+7] << 56) + (self.buff[i*8+6] << 48) + (self.buff[i*8+5] << 40) + (self.buff[i*8+4] << 32) + (self.buff[i*8+3] << 24) + (self.buff[i*8+2] << 16) + (self.buff[i*8+1] << 8) + self.buff[i*8]
dumpLongToSlm(file, i, value)
elif self.flashType == 'hyper':
if len(self.buff) & 1 != 0:
self.__appendByte(0)
for i in range(0, len(self.buff)>>1):
value = (self.buff[i*2+1] << 8) + self.buff[i*2]
dumpShortToSlm(file, i, value)
elif self.archi == 'vivosoc2' or self.archi == 'fulmine':
if len(self.buff) % 4 != 0:
for i in range(0, 4 - (len(self.buff)%4)):
self.buff.append(0)
for i in range(0, len(self.buff), 4):
dumpByteToSlm(file, i, self.buff[i+3])
dumpByteToSlm(file, i+1, self.buff[i+2])
dumpByteToSlm(file, i+2, self.buff[i+1])
dumpByteToSlm(file, i+3, self.buff[i+0])
else:
for i in range(0, len(self.buff)):
dumpByteToSlm(file, i, self.buff[i])
def genFlashImage(slmStim=None, raw_stim=None, bootBinary=None, comps=[], verbose=False, archi=None, encrypt=False, aesKey=None, aesIv=None, flashType='spi', qpi=True):
print('gen flash image')
saved_args = locals()
print("saved_args is", saved_args)
if bootBinary != None or len(comps) != 0:
if slmStim != None or raw_stim is not None:
compsList = ''
romBoot = ''
if bootBinary != None:
romBoot = ' --flash-boot-binary=%s' % bootBinary
for comp in comps:
compsList += ' --comp=%s' % comp
if slmStim is not None:
cmd = "plp_mkflash %s %s --stimuli=%s --flash-type=%s" % (romBoot, compsList, slmStim, flashType)
else:
cmd = "plp_mkflash %s %s --raw=%s --flash-type=%s" % (romBoot, compsList, raw_stim, flashType)
if qpi: cmd+= ' --qpi'
if encrypt: cmd += ' --encrypt --aes-key=%s --aes-iv=%s' % (aesKey, aesIv)
if verbose: cmd += ' --verbose'
if archi != None: cmd += ' --archi=%s' % archi
if not verbose:
print ('Building flash stimuli with command:')
print (cmd)
if os.system(cmd) != 0:
raise Exception('Error while generating flash image')
return 0