BramMan のソースコード

#!
# -*- coding: utf-8 -*-
# Copyright (C) 2015 TOPS SYSTEMS
### @file BramMan.py
### @brief Bram Manupilator
###
### BITファイルの中のBRAMデータを操作するサポートを
### するためのツール。実際の操作にはData2mem必要。
###
### Contact: izumida@topscom.co.jp
###
### @author: M.Izumida
### @date: June 10, 2015
###
#  Update: v01r03 supports DM MEM file
#  Update: v01r04 supports Ultra Scale
#
# Written for Python 2.7 (NOT FOR 3.x)
#=======================================================================
# インポート宣言
from __future__ import division
import sys
import re
import argparse
import os
from datetime import datetime
#=======================================================================
# バージョン文字列
versionSTR = "BramMan.py v01r04 Tops Systems (pgm by mpi)"
#=======================================================================
# 共通サブルーチン
#-------------------------------------------------------------------
def errPrint(mes):
    """errPrint.

    エラー出力へのメッセージ表示
    後の始末はその後で別に書くこと
    """
    sys.stderr.write(mes)
    sys.stderr.write('\n')

#-------------------------------------------------------------------
def stdExceptionHandler(mes):
    """standard Exception Handler.

    エラーメッセージを送出し、デバッグのための情報を出力する
    """
    errPrint("Exception Captured: " + str(mes))
    errPrint("Type:" + str(sys.exc_info()[0]))
    errPrint("Value:" + str(sys.exc_info()[1]))

#-------------------------------------------------------------------
def tryIntParse(st, dval, rdx=10):
    """try Parse string to Integer

    文字列stをパースして整数化できれば値を返す、できなければデフォルト値dvalを返す
    :param str st: 変換対象文字列
    :param str dval: 変換不能時のデフォルト値
    :param int rdx: 基数、指定しないと10進
    :return: 整数に変換した値
    :rtype: int
    """
    try:
        work = int(st, rdx)
    except:
        return dval
    return work

#-------------------------------------------------------------------
def findCount(pat, sss):
    """find counter.
    
    パターンのマッチ回数を列挙するクラス
    :param str pat: 検索するパターン
    :param str sss: 検索対象文字列
    :return: マッチ回数
    :rtype: int
    """
    cnt = 0
    pos = 0
    while pos >= 0:
        pos = sss.find(pat, pos)
        if pos >= 0:
            cnt += 1
            pos += 1
        if pos < 0:
            break
    return cnt

#-------------------------------------------------------------------
def str2dic(sss):
    """string to dictionary.
    
    [key1=val1, ...] 型の文字列を辞書にして返すクラス
    型にはまらない文字列は、key=desc, val=文字列として辞書にする
    :param str sss: 変換対象文字列
    :return: 辞書
    :rtype: dictionary
    """
    work = sss.strip()
    result = dict()
    if work.startswith("[") and work.endswith("]"):
        work = work[1:len(work)-1]
        plis = work.split(",")
        for pp in plis:
            kv = pp.split("=")
            if len(kv) != 2:
                continue
            ky = kv[0].strip()
            vl = kv[1].strip()
            result[ky]=vl
    else:
        result['desc']=work
    return result

#-------------------------------------------------------------------
def nameAndParams(sss):
    """string to name anad parameter.
    
    Name Parameter型の文字列をリストにして返すクラス
    型にはまらない場合はNoneを返す
    :param str sss: 変換対象文字列
    :return: [name, param]リストまたはNone
    """
    mx1 = re.match('(\S+)', sss)
    if mx1:
        mx2 = re.match('(\S+)\s+([\S].*)', sss)
        if mx2:
            return [mx2.group(1), mx2.group(2)]
        else:
            return [mx1.group(1), ""]
    else:
        return None

#=======================================================================
# 解析オプション クラス
class Options:
    """Options Class.

    グローバルオプションを保持するクラス
    """
    pass

#=======================================================================
# DMPファイルリーダクラス
[ドキュメント]class dmpReader: """DMP File Reader Class. DMPファイルを読み取ってBRAM部分をチェックするクラス """ #-------------------------------------------------------------------- def __init__(self, fnam): """DMP File Reader Constructor コンストラクタ """ self.logFname = fnam # self.errorMessage = "" self.errorFlag = False #高速化のためマッチパターンをコンパイルしておく self.p0match = re.compile('BRAM data\, Column ([0-9]+)\, Row ([0-9]+)\. (.*)') self.p1match = re.compile(' ([0-9a-fA-F]{8})\: ([0-9a-fA-F]{2})') # self.nonZero = False self.currBram = "" self.lNumber = 1 self.state = 0 # self.nBRAM = 0 self.nzBRAM = 0 self.nGhost = 0 # self.eOpt = False self.inst = False self.nzOpt = False # self.bramDic = dict() self.bramDataDic = dict() self.bramParityDic = dict() self.bramDataLis = [] self.bramParityLis = [] #デバッグ self.debug = False self.verbose = False #--------------------------------------------------------------------
[ドキュメント] def read(self): """read method ファイルから行をリードして処理するメソッド """ try: with open(self.logFname, 'r') as f: for line in f: if self.rLine(line): break self.lNumber += 1 except: stdExceptionHandler("ERROR: in file reading = " + self.logFname) return False return True
#--------------------------------------------------------------------
[ドキュメント] def rLine(self, lin): """Read Line method 読み取った1行を処理するメソッド エラーならTrueを返す そうでない場合はFalseを返す state 0 ... BRAM発見のみ 1 ... BRAM発見、次の空行みつける 2 ... 最初の空行発見、ボディ処理 3 ... ボディ後の空行発見、ボディ処理終わり 4 ... BRAM parity発見、次の空行見つける 5 ... パリティ直上の空行発見、次からパリティ処理 >>> 末尾の空行発見、末尾処理、0に戻る """ slin = lin.strip() sLen = len(slin) if self.state == 1: if sLen < 2: self.state = 2 else: self.errorMessage = "ERROR: #1 Blank line expected. @" + str(self.lNumber) + lin self.errorFlag = False errPrint(self.errorMessage) return True return False if self.state == 2: if sLen < 2: self.state = 3 else: #ボディ内処理 p1 = self.p1match.match(lin) if p1: if self.inst: print slin datLin = lin[12:110].strip() datArray = datLin.split(' ') if len(datArray) != 32: self.errorMessage = "ERROR: BRAM data format @" + str(self.lNumber) + lin self.errorFlag = False errPrint(self.errorMessage) return True lineNonZero = False for item in datArray: temp = tryIntParse(item, 0, 16) self.bramDataLis.append(temp) if temp != 0: lineNonZero = True self.nonZero = True if self.nzOpt and lineNonZero: print self.currBram, datLin return False if self.state == 3: if slin.startswith("BRAM parity"): if self.inst: print slin self.state = 4 else: self.errorMessage = "ERROR: #2 BRAM parity line expected. @" + str(self.lNumber) + lin self.errorFlag = False errPrint(self.errorMessage) return True return False if self.state == 4: if sLen < 2: self.state = 5 else: self.errorMessage = "ERROR: #3 Blank line expected. @" + str(self.lNumber) + lin self.errorFlag = False errPrint(self.errorMessage) return True return False if self.state == 5: if sLen < 2: #BRAM末処理 if self.currBram in self.bramDic: temp = self.bramDic[self.currBram] temp.append(self.nonZero) self.bramDic[self.currBram] = temp if self.nonZero: self.nzBRAM += 1 else: self.errorMessage = "ERROR: bramDic error @" + str(self.lNumber) + lin self.errorFlag = False errPrint(self.errorMessage) return True self.bramDataDic[self.currBram] = self.bramDataLis self.bramParityDic[self.currBram] = self.bramParityLis self.currBram = "" self.state = 0 self.nonZero = False self.inst = False else: #パリティ内処理 p1 = self.p1match.match(lin) if p1: if self.inst: print slin datLin = lin[12:110].strip() datArray = datLin.split(' ') if len(datArray) != 32: self.errorMessage = "ERROR: BRAM parity format @" + str(self.lNumber) + lin self.errorFlag = False errPrint(self.errorMessage) return True lineNonZero = False for item in datArray: temp = tryIntParse(item, 0, 16) self.bramParityLis.append(temp) if temp != 0: self.nonZero = True lineNonZero = True if self.nzOpt and lineNonZero: print self.currBram, datLin return False # BRAM dataを見つける p0 = self.p0match.match(lin) if p0: col = tryIntParse(p0.group(1), 0) row = tryIntParse(p0.group(2), 0) self.currBram = "{0:03d}_{1:03d}".format(col, row) mes = p0.group(3) if self.currBram in self.bramDic: self.currBram += "_Ghost" mes = "Ghost==> " + mes self.nGhost += 1 self.bramDic[self.currBram] = [col, row, mes] self.bramDataLis = [] self.bramParityLis = [] self.nBRAM += 1 if (mes.startswith('Not used')) or (mes.startswith('Ghost')): self.inst = False elif self.eOpt: self.inst = True print lin else: self.inst = False self.state = 1 return False
#--------------------------------------------------------------------
[ドキュメント] def printBram(self): """list Bram 蓄えたBRAM情報を出力する """ for ky in sorted(self.bramDic.keys()): item = self.bramDic[ky] nz = 'T' if item[3] else '_' print "BRAM COL={0:03d} ROW={1:03d} NZ={2} : {3}".format(item[0], item[1], nz, item[2])
#--------------------------------------------------------------------
[ドキュメント] def conv32sub(self, HHkey, HLkey, LHkey, LLkey, idx): """convert 32 bit data format 5+9+9+9フォーマットのデータを32ビットに復元して返す """ try: LL8 = self.bramDataDic[LLkey][idx] LH8 = self.bramDataDic[LHkey][idx] HL8 = self.bramDataDic[HLkey][idx] HH5 = self.bramDataDic[HHkey][idx] LLP = (self.bramParityDic[LLkey][idx // 8] >> (idx % 8)) & 0x1 LHP = (self.bramParityDic[LHkey][idx // 8] >> (idx % 8)) & 0x1 HLP = (self.bramParityDic[HLkey][idx // 8] >> (idx % 8)) & 0x1 except: self.errorMessage = "ERROR: Unexpected index (MAYBE GHOST) @" + str(idx) self.errorFlag = False errPrint(self.errorMessage) return 0 return (LL8 | (LLP << 8) | (LH8 << 9) | (LHP << 17) | (HL8 << 18) | (HLP << 26) | (HH5 << 27))
#--------------------------------------------------------------------
[ドキュメント] def dump32bitData(self, keyLis): """dump 32 bit data format keyListに基づいて、4,8,12,16個のBRAM情報を32ビット化して出力する """ nKey = len(keyLis) if not ((nKey == 4) or (nKey == 8) or (nKey == 12) or (nKey ==16)): return False for idx in range(0, 0x1000): print "{0:04X} {1:08X} ".format(idx, self.conv32sub(keyLis[3], keyLis[2], keyLis[1], keyLis[0], idx)), if nKey == 4: print continue print "{0:08X} ".format(self.conv32sub(keyLis[7], keyLis[6], keyLis[5], keyLis[4], idx)), if nKey == 8: print continue print "{0:08X} ".format(self.conv32sub(keyLis[11], keyLis[10], keyLis[9], keyLis[8], idx)), if nKey == 12: print continue print "{0:08X} ".format(self.conv32sub(keyLis[15], keyLis[14], keyLis[13], keyLis[12], idx))
#======================================================================= # HEXファイルリーダライタ クラス
[ドキュメント]class HexFileRWriter: """HexFile loader Class. HEXファイルを読みとって 指定したアドレス範囲内のデータを保持するクラス mem形式での出力も可能 アドレス範囲により、自動的にDM/IM認識 """ #-------------------------------------------------------------------- def __init__(self, Fnam, sadr, eadr): """HEX File Reader/Writer Constructor コンストラクタ """ self.iFname = Fnam self.sadr = sadr self.eadr = eadr self.IM = 0x40000000 self.DM = 0x60000000 self.memMode = 0 # 0:IM 1:DM self.shortAdr = False # self.storage = dict() # self.errorMessage = "" self.errorFlag = False # self.debug = False self.verbose = False # self.ultra = False #--------------------------------------------------------------------
[ドキュメント] def read(self): """Read method HEXファイルを読み込むメソッド """ try: with open(self.iFname, 'r') as f: for line in f: if self.rLine(line): break except: stdExceptionHandler("ERROR: in file reading = " + self.iFname) return False return True
#--------------------------------------------------------------------
[ドキュメント] def setAddr(self): """Set Address method sadrとeadrを変換して使えるようにするメソッド """ self.sadrInt = tryIntParse(self.sadr, 0, 16) if (self.sadrInt == 0) and (self.sadr != '0'): errPrint("ERROR: Suspicious HEX argument =" + self.sadr) sys.exit(1) #print "SADR={0:08X} {1:08X}\n".format(self.sadrInt, (self.sadrInt & 0xF0000000)) if (not self.ultra ) and ((self.sadrInt & 0xF0000000) == self.IM): self.memMode = 0 else: self.memMode = 1 self.eadrInt = tryIntParse(self.eadr, 0, 16) if (self.eadrInt == 0) and (self.eadr != '0'): errPrint("ERROR: Suspicious HEX argument =" + self.eadr) sys.exit(1) if self.sadrInt >= self.eadrInt: errPrint("ERROR: Start Address > End Address : " + self.sadr + ">" + self.eadr) sys.exit(1)
#--------------------------------------------------------------------
[ドキュメント] def rLine(self, lin): """Read Line method 読み取った1行を処理するメソッド """ p0 = re.match('S3([0-9a-fA-F]{2})([0-9a-fA-F]{8})([0-9a-fA-F]*)', lin) if p0: siz = tryIntParse(p0.group(1), 0, 16) if siz < 6: errPrint("WARNING: RECORD TOO SHORT = " + lin) return False adr = tryIntParse(p0.group(2), 0, 16) if self.debug: print "FIND REC SIZ={0} ADDR={1:08X} {2}".format(siz, adr, lin.strip()) if (adr >= self.sadrInt) and (adr < self.eadrInt): for idx in range(0, (siz-5)): tmp = p0.group(3)[idx*2:idx*2+2] self.storage[adr+idx] = tmp return False
#--------------------------------------------------------------------
[ドキュメント] def saveMem(self, fnam): """save mem file IM/DM mem file生成メソッドの入り口 """ if self.memMode == 0: return self.saveMemIM(fnam) else: return self.saveMemDM(fnam)
#--------------------------------------------------------------------
[ドキュメント] def saveMemIM(self, fnam): """save mem file for IM bit 5+9+9+9 width 連続 mem形式で書き出すメソッド """ klis = sorted(self.storage.keys()) topAdr = klis[0] endAdr = klis[-1] + 1 idx =0 try: with open(fnam, 'w') as f: if self.shortAdr: f.write("@{0:04X}\n".format(topAdr & 0xFFFF)) else: f.write("@{0:X}\n".format(topAdr)) for adr in range(topAdr, endAdr): if adr in self.storage: dat = tryIntParse(self.storage[adr], 0, 16) & 0xFF else: dat = 0 if (idx % 4) == 0: LL = dat elif (idx % 4) == 1: LH = dat elif (idx % 4) == 2: HL = dat else: HH = dat D32 = (HH << 24) | (HL << 16) | (LH << 8) | LL f.write("{0:03x}".format(D32 & 0x1FF)) f.write(" ") f.write("{0:03x}".format((D32 >> 9) & 0x1FF)) f.write(" ") f.write("{0:03x}".format((D32 >> 18) & 0x1FF)) f.write(" ") f.write("{0:03x}".format((D32 >> 27) & 0x1F)) f.write(" ") if (idx % 16) == 15: f.write("\n") idx += 1 f.write("\n") except: stdExceptionHandler("ERROR: in file writing = " + fnam) return False return True
#--------------------------------------------------------------------
[ドキュメント] def saveMemDM(self, fnam): """save mem file for DM bit 8+8+8+8 width 連続 mem形式で書き出すメソッド """ klis = sorted(self.storage.keys()) topAdr = klis[0] endAdr = klis[-1] + 1 idx =0 try: with open(fnam, 'w') as f: if self.shortAdr: f.write("@{0:05X}\n".format(topAdr & 0x1FFFF)) else: f.write("@{0:X}\n".format(topAdr)) for adr in range(topAdr, endAdr): if adr in self.storage: dat = tryIntParse(self.storage[adr], 0, 16) & 0xFF else: dat = 0 f.write("{0:02x} ".format(dat)) if (idx % 16) == 15: f.write("\n") idx += 1 f.write("\n") except: stdExceptionHandler("ERROR: in file writing = " + fnam) return False return True
#======================================================================= # BMMファイルリーダクラス
[ドキュメント]class bmmReader: """BMM File Reader Class. BMMファイルを読み取ってPLACED構成からビット幅構成を決める """ #-------------------------------------------------------------------- def __init__(self, fnam): """BMM File Reader Constructor コンストラクタ """ self.logFname = fnam self.lNumber = 1 # self.errorMessage = "" self.errorFlag = False #高速化のためマッチパターンをコンパイルしておく self.p0search = re.compile('PLACED=X([0-9]+)Y([0-9]+)') # self.rcList = [] #デバッグ self.debug = False self.verbose = False #--------------------------------------------------------------------
[ドキュメント] def read(self): """read method ファイルから行をリードして処理するメソッド """ try: with open(self.logFname, 'r') as f: for line in f: if self.rLine(line): break self.lNumber += 1 except: stdExceptionHandler("ERROR: in file reading = " + self.logFname) return False return True
#--------------------------------------------------------------------
[ドキュメント] def rLine(self, lin): """Read Line method 読み取った1行を処理するメソッド エラーならTrueを返す そうでない場合はFalseを返す """ p0 = self.p0search.search(lin) if p0: col = tryIntParse(p0.group(1), 0) row = tryIntParse(p0.group(2), 0) self.rcList.append("{0:03d}_{1:03d}".format(col, row)) return False
#--------------------------------------------------------------------
[ドキュメント] def save(self, fnam): """save BRAM col row list BRAMのインデックスリストをファイルに出力する """ try: with open(fnam, 'w') as f: for item in self.rcList: f.write(item) f.write("\n") except: stdExceptionHandler("ERROR: in file writing = " + fnam) return False return True
#======================================================================= # メインプログラム def main(): """main. メインプログラム """ #----------------------------------------------------------------------- # コマンドラインオプション処理 # parser = argparse.ArgumentParser(description='Bram Manupilator.') parser.add_argument('--DMP', nargs=1, help='Specify DMP file name.') parser.add_argument('--HEX', nargs=1, help='Specify HEX file name.') parser.add_argument('--MEM', nargs=1, help='Specify MEM file name.') parser.add_argument('--BMM', nargs=1, help='Specify BMM file name.') parser.add_argument('--SADR', nargs=1, help='Start Address(HEX).') parser.add_argument('--EADR', nargs=1, help='End Address(HEX).') parser.add_argument('-a', dest='shortAdr', help='Use short address for Mem file.', action='store_true', default=False) parser.add_argument('-L', dest='dump32', help='Dump 32 bit data format.', action='store_true', default=False) parser.add_argument('-n', dest='nzOption', help='Print NZ BRAM line.', action='store_true', default=False) parser.add_argument('-p', dest='pbOption', help='Print BRAM Information.', action='store_true', default=False) parser.add_argument('-e', dest='extOption', help='Extract OCCUPIED BRAM Data.', action='store_true', default=False) parser.add_argument('-u', dest='ultra', help='Ultra Scale mode.', action='store_true', default=False) parser.add_argument('-d', dest='debug', help='Debug mode, print debug information.', action='store_true', default=False) parser.add_argument('-v', dest='verbose', help='Verbose mode.', action='store_true', default=False) parser.add_argument('-V', dest='VERSION', help='Show Version, then exit', action='store_true', default=False) args = parser.parse_args() #----------------------------------------------------------------------- # Version 表示 # print versionSTR if args.VERSION: sys.exit(0) #----------------------------------------------------------------------- # ファイル名処理 # if args.DMP is not None: dmpFname = args.DMP[0] if not os.path.isfile(dmpFname): errPrint('ERROR: DMP FILE, NOT EXIST. ' + dmpFname) sys.exit(1) if args.HEX is not None: hexFname = args.HEX[0] if not os.path.isfile(hexFname): errPrint('ERROR: HEX FILE, NOT EXIST. ' + hexFname) sys.exit(1) if args.BMM is not None: bmmFname = args.BMM[0] if not os.path.isfile(bmmFname): errPrint('ERROR: BMM FILE, NOT EXIST. ' + bmmFname) sys.exit(1) #----------------------------------------------------------------------- # パラメータ処理 if args.SADR is not None: sadr = args.SADR[0] else: sadr = 'N/A' if args.EADR is not None: eadr = args.EADR[0] else: eadr = 'N/A' #----------------------------------------------------------------------- # 実処理 # print "-"*50 # BMMファイルの読み込みとBRAM_RC.LSTファイルの書き出し if args.BMM is not None: bmm = bmmReader(bmmFname) bmm.read() if args.verbose: bmm.save("BRAM_RC.LST") # HEXファイルの読み込みとMEMファイルの書き出し if args.HEX is not None: hexF = HexFileRWriter(hexFname, sadr, eadr) hexF.shortAdr = args.shortAdr hexF.debug = args.debug hexF.verbose = args.verbose hexF.ultra = args.ultra hexF.setAddr() hexF.read() if args.MEM is not None: hexF.saveMem(args.MEM[0]) # DMPファイルの読み込み if args.DMP is not None: dmp = dmpReader(dmpFname) dmp.eOpt = args.extOption dmp.nzOpt = args.nzOption dmp.read() if args.pbOption: dmp.printBram() print "TOTAL # of BRAM : ", dmp.nBRAM print "TOTAL # of NON ZERO BRAM : ", dmp.nzBRAM print "TOTAL # of Ghost BRAM : ", dmp.nGhost if args.dump32 and (args.BMM is not None): dmp.dump32bitData(bmm.rcList) #終了表示 today = datetime.today() print today.strftime("FINISH: %Y/%m/%d %H:%M:%S") #----------------------------------------------------------------------- # 正常終了 # sys.exit(0) #======================================================================= # メインプログラムの起動 if __name__ == "__main__": main()