Loading...
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
"""
IOCSR header file generator

Process the hiof file from Quartus and generate iocsr header
usable by U-Boot.

Copyright (C) 2022 Intel Corporation <www.intel.com>

Author: Lee, Kah Jing <kah.jing.lee@intel.com>
"""
import os
import struct
import streamer

class IOCSRGrokker(object):
    """ Decode the .hiof file and produce some C source code
    """
    IOCSR_ROOT_FILENAME = 'iocsr_config'
    IOCSR_SENTINEL = '__SOCFPGA_IOCSR_CONFIG_H__'
    IOCSR_FILE_EXTENSION_MAX_LEN = 6
    PTAG_HPS_IOCSR_INFO = 39
    PTAG_HPS_IOCSR = 40
    PTAG_DEVICE_NAME = 2
    PTAG_TERMINATION = 8

    def __init__(self, deviceFamily, inputDir, outputDir, hiofSrcFileName):
        """ IOCSRGrokker Initialization """
        self.deviceFamily = deviceFamily
        self.inputDir = inputDir
        self.outputDir = outputDir
        self.hiofInFileName = hiofSrcFileName
        self.iocsrFileName =  self.IOCSR_ROOT_FILENAME
        self.headerOut = None
        self.sourceOut = None
        self.createFilesFromHIOF()

    @staticmethod
    def byteArrayToStr(bytes):
        """ Convert a list of bytes into a string
        """
        # We don't like nulls
        bytes = bytes.replace('\x00', '')
        s = ''
        for b in bytes:
            s += b
        return s

    @staticmethod
    def getLengthData(bytes):
        """
        @param: bytes is a chunk of bytes that we need to decode
        There will be a ptag that we may care about.
        If we care about it, we will get the length of the chunk
        that the ptag cares about.
        @rtype: a pair, length of chunk and the chunk itself
        @return: length of the ptag chunk we care about
        @return: data chunk that ptag indicates we need to decode
        """
        blockSize = len(bytes)
        i = 0
        bitlength = 0
        length = 0
        data = []

        while i < blockSize:
            byte = struct.unpack('B', bytes[i:i+1])[0]
            i += 1

            if byte == 1:
                bitlength = struct.unpack('I', bytes[i:i+4])[0]
                i += 4
            elif byte == 2:
                length = struct.unpack('I', bytes[i:i+4])[0]
                i += 4

            elif byte == 5:
                j = 0
                while i < blockSize:
                    data.append(struct.unpack('I', bytes[i:i+4])[0])
                    i += 4
                    j += 1

            else:
                i += 4

        return (bitlength, data)


    def verifyRead(self, tagWeRead, tagWeExpected):
        """ verify the hiof value with tag expected """
        if tagWeRead != tagWeExpected:
            print ("***Error: Expected ptag of %02d, but got %02d" % (tagWeExpected, tagWeRead))

    def createFilesFromHIOF(self):
        """ read the hiof file to create iocsr_config.h """
        self.hiofStream = streamer.Streamer(self.inputDir + os.sep + self.hiofInFileName, 'rb')
        self.iocsrHeaderStream = streamer.Streamer(self.outputDir + os.sep + self.iocsrFileName + '.h', 'w')
        self.hiofStream.open()
        self.iocsrHeaderStream.open()
        self.iocsrHeaderStream.writeLicenseHeader()
        self.iocsrHeaderStream.write('/*\n * Altera SoCFPGA IOCSR configuration\n */\n\n')
        ret = self.iocsrHeaderStream.writeSentinelStart(IOCSRGrokker.IOCSR_SENTINEL)
        if ret == -1:
            print("Empty header written. Exiting.")

        # Read the file extension (typically .hiof)
        # and the file version
        self.fileExtension = self.hiofStream.readBytesAsString(IOCSRGrokker.IOCSR_FILE_EXTENSION_MAX_LEN)
        self.fileVersion = self.hiofStream.readUnsignedInt()

        # Now read the ptags
        # Device name is first
        self.programmerTag = self.hiofStream.readUnsignedShort()
        self.verifyRead(self.programmerTag, self.PTAG_DEVICE_NAME)
        self.deviceNameLength = self.hiofStream.readUnsignedInt()
        self.deviceName = self.hiofStream.readBytesAsString(self.deviceNameLength)

        # Basic information of the HIOF files
        # This is not used by the preloader generator, but we read it and ignore the
        # contents.
        programmerTag = self.hiofStream.readUnsignedShort()
        self.verifyRead(programmerTag, self.PTAG_HPS_IOCSR_INFO)
        basicHPSIOCSRInfoLength = self.hiofStream.readUnsignedInt()
        self.hiofStream.read(basicHPSIOCSRInfoLength)

        # Actual content of IOCSR information
        self.programmerTag1 = self.hiofStream.readUnsignedShort()
        self.verifyRead(self.programmerTag1, self.PTAG_HPS_IOCSR)
        self.HPSIOCSRLength1 = self.hiofStream.readUnsignedInt()
        self.HPSIOCSRBytes1 = self.hiofStream.read(self.HPSIOCSRLength1)
        self.HPSIOCSRDataLength1, self.HPSIOCSRData1 = IOCSRGrokker.getLengthData(self.HPSIOCSRBytes1)

        # Actual content of IOCSR information
        self.programmerTag2 = self.hiofStream.readUnsignedShort()
        self.verifyRead(self.programmerTag2, self.PTAG_HPS_IOCSR)
        self.HPSIOCSRLength2 = self.hiofStream.readUnsignedInt()
        self.HPSIOCSRBytes2 = self.hiofStream.read(self.HPSIOCSRLength2)
        self.HPSIOCSRDataLength2, self.HPSIOCSRData2 = IOCSRGrokker.getLengthData(self.HPSIOCSRBytes2)

        # Actual content of IOCSR information
        self.programmerTag3 = self.hiofStream.readUnsignedShort()
        self.verifyRead(self.programmerTag3, self.PTAG_HPS_IOCSR)
        self.HPSIOCSRLength3 = self.hiofStream.readUnsignedInt()
        self.HPSIOCSRBytes3 = self.hiofStream.read(self.HPSIOCSRLength3)
        self.HPSIOCSRDataLength3, self.HPSIOCSRData3 = IOCSRGrokker.getLengthData(self.HPSIOCSRBytes3)

        # Actual content of IOCSR information
        self.programmerTag4 = self.hiofStream.readUnsignedShort()
        self.verifyRead(self.programmerTag4, self.PTAG_HPS_IOCSR)
        self.HPSIOCSRLength4 = self.hiofStream.readUnsignedInt()
        self.HPSIOCSRBytes4 = self.hiofStream.read(self.HPSIOCSRLength4)
        self.HPSIOCSRDataLength4, self.HPSIOCSRData4 = IOCSRGrokker.getLengthData(self.HPSIOCSRBytes4)

        # Now we should see the end of the hiof input
        programmerTag = self.hiofStream.readUnsignedShort()
        if 8 != programmerTag:
            print ("I didn't find the end of the .hiof file when I expected to!")

        self.iocsrHeaderStream.write('#define CFG_HPS_IOCSR_SCANCHAIN0_LENGTH\t' +\
                       str(self.HPSIOCSRDataLength1) + '\n')
        self.iocsrHeaderStream.write('#define CFG_HPS_IOCSR_SCANCHAIN1_LENGTH\t' +\
                       str(self.HPSIOCSRDataLength2) + '\n')
        self.iocsrHeaderStream.write('#define CFG_HPS_IOCSR_SCANCHAIN2_LENGTH\t' +\
                       str(self.HPSIOCSRDataLength3) + '\n')
        self.iocsrHeaderStream.write('#define CFG_HPS_IOCSR_SCANCHAIN3_LENGTH\t' +\
                       str(self.HPSIOCSRDataLength4) + '\n')

        self.iocsrHeaderStream.write("\n")

        self.iocsrHeaderStream.write('const unsigned long iocsr_scan_chain0_table[] = {\n')
        for value in self.HPSIOCSRData1:
            hv = '0x%08X' % (value)
            self.iocsrHeaderStream.write('\t' + hv + ',\n')
        self.iocsrHeaderStream.write('};\n')
        self.iocsrHeaderStream.write('\n')

        self.iocsrHeaderStream.write('const unsigned long iocsr_scan_chain1_table[] = {\n')
        for value in self.HPSIOCSRData2:
            hv = '0x%08X' % (value)
            self.iocsrHeaderStream.write('\t' + hv + ',\n')
        self.iocsrHeaderStream.write('};\n')
        self.iocsrHeaderStream.write('\n')

        self.iocsrHeaderStream.write('const unsigned long iocsr_scan_chain2_table[] = {\n')
        for value in self.HPSIOCSRData3:
            hv = '0x%08X' % (value)
            self.iocsrHeaderStream.write('\t' + hv + ',\n')
        self.iocsrHeaderStream.write('};\n')
        self.iocsrHeaderStream.write('\n')

        self.iocsrHeaderStream.write('const unsigned long iocsr_scan_chain3_table[] = {\n')
        for value in self.HPSIOCSRData4:
            hv = '0x%08X' % (value)
            self.iocsrHeaderStream.write('\t' + hv + ',\n')
        self.iocsrHeaderStream.write('};\n')
        self.iocsrHeaderStream.write('\n\n')

        ret = self.iocsrHeaderStream.writeSentinelEnd(IOCSRGrokker.IOCSR_SENTINEL)
        if ret == -1:
            print("Empty header written. Exiting.")

        self.iocsrHeaderStream.close()