mirror of
https://github.com/saymrwulf/pulp-runtime.git
synced 2026-05-14 20:48:09 +00:00
* 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.
469 lines
16 KiB
Python
Executable file
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
|