#!
# -*- 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()