mirror of https://github.com/openXC7/prjxray.git
Working bits2fasm using prjxray and fasm library.
Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
This commit is contained in:
parent
6dd9626cfc
commit
41d9ede26a
|
|
@ -0,0 +1,7 @@
|
|||
set -ex
|
||||
|
||||
BIT_IN=$1
|
||||
BITS=$(tempfile --suffix .bits)
|
||||
|
||||
${XRAY_BITREAD} -F $XRAY_ROI_FRAMES -o ${BITS} -z -y ${BIT_IN}
|
||||
${XRAY_DIR}/utils/bits2fasm.py ${BITS}
|
||||
|
|
@ -27,7 +27,7 @@ echo "Design .fasm: $fasm_in"
|
|||
echo "Harness .bit: $bit_in"
|
||||
echo "Out .bit: $bit_out"
|
||||
|
||||
${XRAY_DIR}/tools/fasm2frame.py $fasm_in roi_partial.frm
|
||||
${XRAY_DIR}/utils/fasm2frames.py --sparse $fasm_in roi_partial.frm
|
||||
|
||||
${XRAY_TOOLS_DIR}/xc7patch \
|
||||
--part_name ${XRAY_PART} \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
# Break frames into WORD_SIZE bit words.
|
||||
WORD_SIZE_BITS = 32
|
||||
|
||||
def load_bitdata(f):
|
||||
""" Read bit file and return bitdata map.
|
||||
|
||||
bitdata is a map of of two sets.
|
||||
The map key is the frame address.
|
||||
The first sets are the word columns that have any bits set.
|
||||
Word columsn are WORD_SIZE_BITS wide.
|
||||
The second sets are bit index within the frame and word if it is set.
|
||||
"""
|
||||
bitdata = dict()
|
||||
|
||||
for line in f:
|
||||
line = line.split("_")
|
||||
frame = int(line[1], 16)
|
||||
wordidx = int(line[2], 10)
|
||||
bitidx = int(line[3], 10)
|
||||
|
||||
if frame not in bitdata:
|
||||
bitdata[frame] = set(), set()
|
||||
|
||||
bitdata[frame][0].add(wordidx)
|
||||
bitdata[frame][1].add(wordidx*WORD_SIZE_BITS + bitidx)
|
||||
|
||||
return bitdata
|
||||
|
|
@ -2,6 +2,7 @@ import os.path
|
|||
import json
|
||||
from prjxray import grid
|
||||
from prjxray import tile
|
||||
from prjxray import tile_segbits
|
||||
from prjxray import site_type
|
||||
from prjxray import connections
|
||||
|
||||
|
|
@ -37,7 +38,9 @@ class Database(object):
|
|||
self.tile_types = None
|
||||
|
||||
self.tile_types = {}
|
||||
self.tile_segbits = {}
|
||||
self.site_types = {}
|
||||
|
||||
for f in os.listdir(self.db_root):
|
||||
if f.endswith('.json') and f.startswith('tile_type_'):
|
||||
tile_type = f[len('tile_type_'):-len('.json')].lower()
|
||||
|
|
@ -47,6 +50,11 @@ class Database(object):
|
|||
if not os.path.isfile(segbits):
|
||||
segbits = None
|
||||
|
||||
ppips = os.path.join(
|
||||
self.db_root, 'ppips_{}.db'.format(tile_type))
|
||||
if not os.path.isfile(ppips):
|
||||
ppips = None
|
||||
|
||||
mask = os.path.join(
|
||||
self.db_root, 'mask_{}.db'.format(tile_type))
|
||||
if not os.path.isfile(mask):
|
||||
|
|
@ -60,6 +68,7 @@ class Database(object):
|
|||
|
||||
self.tile_types[tile_type.upper()] = tile.TileDbs(
|
||||
segbits=segbits,
|
||||
ppips=ppips,
|
||||
mask=mask,
|
||||
tile_type=tile_type_file,
|
||||
)
|
||||
|
|
@ -124,3 +133,10 @@ class Database(object):
|
|||
site_type_data = json.load(f)
|
||||
|
||||
return site_type.SiteType(site_type_data)
|
||||
|
||||
def get_tile_segbits(self, tile_type):
|
||||
if tile_type not in self.tile_segbits:
|
||||
self.tile_segbits[tile_type] = tile_segbits.TileSegbits(
|
||||
self.tile_types[tile_type.upper()])
|
||||
|
||||
return self.tile_segbits[tile_type]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,140 @@
|
|||
import fasm
|
||||
from prjxray import bitstream
|
||||
from prjxray import grid
|
||||
|
||||
class FasmLookupError(Exception):
|
||||
pass
|
||||
|
||||
class FasmInconsistentBits(Exception):
|
||||
pass
|
||||
|
||||
# How many 32-bit words for frame in a 7-series bitstream?
|
||||
FRAME_WORD_COUNT = 101
|
||||
|
||||
def init_frame_at_address(frames, addr):
|
||||
'''Set given frame to 0 if not initialized '''
|
||||
if not addr in frames:
|
||||
frames[addr] = [0 for _i in range(FRAME_WORD_COUNT)]
|
||||
|
||||
class FasmAssembler(object):
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
self.grid = db.grid()
|
||||
|
||||
self.frames = {}
|
||||
self.frames_line = {}
|
||||
|
||||
def get_frames(self, sparse=False):
|
||||
if not sparse:
|
||||
frames = self.frames_init()
|
||||
else:
|
||||
frames = {}
|
||||
|
||||
for (frame_addr, word_addr, bit_index), is_set in self.frames.items():
|
||||
init_frame_at_address(frames, frame_addr)
|
||||
|
||||
if is_set:
|
||||
frames[frame_addr][word_addr] |= 1 << bit_index
|
||||
|
||||
return frames
|
||||
|
||||
def frames_init(self):
|
||||
'''Set all frames to 0'''
|
||||
frames = {}
|
||||
|
||||
for bits_info in self.grid.iter_all_frames():
|
||||
for coli in range(bits_info.bits.frames):
|
||||
init_frame_at_address(frames, bits_info.bits.base_address + coli)
|
||||
|
||||
return frames
|
||||
|
||||
def frame_set(self, frame_addr, word_addr, bit_index, line):
|
||||
'''Set given bit in given frame address and word'''
|
||||
assert bit_index is not None
|
||||
|
||||
key = (frame_addr, word_addr, bit_index)
|
||||
if key in self.frames:
|
||||
if self.frames[key] != 1:
|
||||
raise FasmInconsistentBits(
|
||||
'FASM line "{}" wanted to set bit {} but was cleared by FASM line "{}"'.format(
|
||||
line, key, self.frames_line[key],
|
||||
))
|
||||
return
|
||||
|
||||
self.frames[key] = 1
|
||||
self.frames_line[key] = line
|
||||
|
||||
def frame_clear(self, frame_addr, word_addr, bit_index, line):
|
||||
'''Set given bit in given frame address and word'''
|
||||
assert bit_index is not None
|
||||
|
||||
key = (frame_addr, word_addr, bit_index)
|
||||
if key in self.frames:
|
||||
if self.frames[key] != 0:
|
||||
raise FasmInconsistentBits(
|
||||
'FASM line "{}" wanted to clear bit {} but was set by FASM line "{}"'.format(
|
||||
line, key, self.frames_line[key],
|
||||
))
|
||||
return
|
||||
|
||||
self.frames[key] = 0
|
||||
self.frames_line[key] = line
|
||||
|
||||
def enable_feature(self, tile, feature, address, line):
|
||||
gridinfo = self.grid.gridinfo_at_tilename(tile)
|
||||
|
||||
# TODO: How to determine if the feature targets BLOCK_RAM segment type?
|
||||
bits = gridinfo.bits[grid.SegmentType.CLB_IO_CLK]
|
||||
|
||||
seg_baseaddr = bits.base_address
|
||||
seg_word_base = bits.offset
|
||||
|
||||
def update_segbit(bit):
|
||||
'''Set or clear a single bit in a segment at the given word column and word bit position'''
|
||||
# Now we have the word column and word bit index
|
||||
# Combine with the segments relative frame position to fully get the position
|
||||
frame_addr = seg_baseaddr + bit.word_column
|
||||
# 2 words per segment
|
||||
word_addr = seg_word_base + bit.word_bit // bitstream.WORD_SIZE_BITS
|
||||
bit_index = bit.word_bit % bitstream.WORD_SIZE_BITS
|
||||
if bit.isset:
|
||||
self.frame_set(frame_addr, word_addr, bit_index, line)
|
||||
else:
|
||||
self.frame_clear(frame_addr, word_addr, bit_index, line)
|
||||
|
||||
segbits = self.db.get_tile_segbits(gridinfo.tile_type)
|
||||
|
||||
db_k = '%s.%s' % (gridinfo.tile_type, feature)
|
||||
|
||||
try:
|
||||
for bit in segbits.feature_to_bits(db_k, address):
|
||||
update_segbit(bit)
|
||||
except KeyError:
|
||||
raise FasmLookupError(
|
||||
"Segment DB %s, key %s not found from line '%s'" %
|
||||
(gridinfo.tile_type, db_k, line))
|
||||
|
||||
def parse_fasm_filename(self, filename):
|
||||
for line in fasm.parse_fasm_filename(filename):
|
||||
if not line.set_feature:
|
||||
continue
|
||||
|
||||
line_strs = tuple(fasm.fasm_line_to_string(line))
|
||||
assert len(line_strs) == 1
|
||||
line_str = line_strs[0]
|
||||
|
||||
parts = line.set_feature.feature.split('.')
|
||||
tile = parts[0]
|
||||
feature = '.'.join(parts[1:])
|
||||
|
||||
# canonical_features flattens multibit feature enables to only
|
||||
# single bit features, which is what enable_feature expects.
|
||||
#
|
||||
# canonical_features also filters out features that are not enabled,
|
||||
# which are no-ops.
|
||||
for flat_set_feature in fasm.canonical_features(line.set_feature):
|
||||
address = 0
|
||||
if flat_set_feature.start is not None:
|
||||
address = flat_set_feature.start
|
||||
|
||||
self.enable_feature(tile, feature, address, line_str)
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
import re
|
||||
import fasm
|
||||
from prjxray import bitstream
|
||||
|
||||
def mk_fasm(tile_name, feature):
|
||||
""" Convert matches tile and feature to FasmLine tuple. """
|
||||
# Seperate addressing of multi-bit features:
|
||||
# TILE.ALUT[0] -> ('TILE', 'ALUT', '0')
|
||||
# TILE.ALUT.SMALL -> ('TILE', 'ALUT.SMALL', None)
|
||||
m = re.match(r'([A-Za-z0-9_]+).([^\[]+)(\[[0-9]+\])?', feature)
|
||||
tag_post = m.group(2)
|
||||
address = None
|
||||
if m.group(3) is not None:
|
||||
address = int(m.group(3)[1:-1])
|
||||
|
||||
feature = '{}.{}'.format(tile_name, tag_post)
|
||||
|
||||
return fasm.FasmLine(
|
||||
set_feature=fasm.SetFasmFeature(
|
||||
feature=feature,
|
||||
start=address,
|
||||
end=None,
|
||||
value=1,
|
||||
value_format=None,
|
||||
),
|
||||
annotations=None,
|
||||
comment=None)
|
||||
|
||||
class FasmDisassembler(object):
|
||||
""" Given a Project X-ray data, outputs FasmLine tuples for bits set. """
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
self.grid = self.db.grid()
|
||||
self.segment_map = self.grid.get_segment_map()
|
||||
self.decode_warnings = set()
|
||||
|
||||
def find_features_in_tile(self, tile_name, bits, bitdata, verbose=False):
|
||||
gridinfo = self.grid.gridinfo_at_tilename(tile_name)
|
||||
|
||||
try:
|
||||
tile_segbits = self.db.get_tile_segbits(gridinfo.tile_type)
|
||||
except KeyError as e:
|
||||
if not verbose:
|
||||
return
|
||||
|
||||
if gridinfo.tile_type in self.decode_warnings:
|
||||
return
|
||||
|
||||
comment = " WARNING: failed to load DB for tile type {}".format(
|
||||
gridinfo.tile_type)
|
||||
yield fasm.FasmLine(
|
||||
set_feature=None,
|
||||
annotations=None,
|
||||
comment=comment,
|
||||
)
|
||||
yield fasm.FasmLine(
|
||||
set_feature=None,
|
||||
annotations=[
|
||||
fasm.Annotation('missing_segbits', gridinfo.tile_type),
|
||||
fasm.Annotation('exception', str(e)),
|
||||
],
|
||||
comment=None,
|
||||
)
|
||||
|
||||
self.decode_warnings.add(gridinfo.tile_type)
|
||||
return
|
||||
|
||||
for ones_matched, feature in tile_segbits.match_bitdata(
|
||||
bits, bitdata):
|
||||
for frame, bit in ones_matched:
|
||||
bitdata[frame][1].remove(bit)
|
||||
|
||||
yield mk_fasm(tile_name=tile_name, feature=feature)
|
||||
|
||||
def find_features_in_bitstream(self, bitdata, verbose=False):
|
||||
frames = set(bitdata.keys())
|
||||
tiles_checked = set()
|
||||
|
||||
while len(frames) > 0:
|
||||
frame = frames.pop()
|
||||
|
||||
# Skip frames that were emptied in a previous iteration.
|
||||
if not bitdata[frame]:
|
||||
continue
|
||||
|
||||
# Iterate over all tiles that use this frame.
|
||||
for bits_info in self.segment_map.segment_info_for_frame(frame):
|
||||
# Don't examine a tile twice
|
||||
if bits_info.tile in tiles_checked:
|
||||
continue
|
||||
|
||||
# Check if this frame has any data for the relevant tile.
|
||||
any_column = False
|
||||
for word_idx in range(bits_info.bits.words):
|
||||
if word_idx + bits_info.bits.offset in bitdata[frame][0]:
|
||||
any_column = True
|
||||
break
|
||||
|
||||
if not any_column:
|
||||
continue
|
||||
|
||||
tiles_checked.add(bits_info.tile)
|
||||
|
||||
for fasm_line in self.find_features_in_tile(
|
||||
bits_info.tile,
|
||||
bits_info.bits,
|
||||
bitdata,
|
||||
verbose=verbose):
|
||||
yield fasm_line
|
||||
|
||||
if len(bitdata[frame]) > 0 and verbose:
|
||||
# Some bits were not decoded, add warning and annotations to
|
||||
# FASM.
|
||||
yield fasm.FasmLine(
|
||||
set_feature=None,
|
||||
annotations=None,
|
||||
comment=" In frame 0x{:08x} {} bits were not converted.".format(
|
||||
frame, len(bitdata[frame]),
|
||||
))
|
||||
|
||||
for bit in bitdata[frame][1]:
|
||||
wordidx = bit // bitstream.WORD_SIZE_BITS
|
||||
bitidx = bit % bitstream.WORD_SIZE_BITS
|
||||
annotation = fasm.Annotation('unknown_bit',
|
||||
'{:08x}_{}_{}'.format(frame, wordidx, bitidx))
|
||||
yield fasm.FasmLine(
|
||||
set_feature=None,
|
||||
annotations=[annotation],
|
||||
comment=None,
|
||||
)
|
||||
|
|
@ -1,8 +1,18 @@
|
|||
from collections import namedtuple
|
||||
import enum
|
||||
from prjxray import segment_map
|
||||
|
||||
class SegmentType(enum.Enum):
|
||||
# Segments describing CLB features, interconnect, clocks and IOs.
|
||||
CLB_IO_CLK = 'CLB_IO_CLK'
|
||||
|
||||
# Segments describing block RAM initialization.
|
||||
BLOCK_RAM = 'BLOCK_RAM'
|
||||
|
||||
GridLoc = namedtuple('GridLoc', 'grid_x grid_y')
|
||||
GridInfo = namedtuple('GridInfo', 'segment sites tile_type in_roi')
|
||||
|
||||
GridInfo = namedtuple('GridInfo', 'segment bits sites tile_type in_roi')
|
||||
Bits = namedtuple('Bits', 'base_address frames offset words')
|
||||
BitsInfo = namedtuple('BitsInfo', 'segment_type tile bits')
|
||||
|
||||
class Grid(object):
|
||||
""" Object that represents grid for a given database.
|
||||
|
|
@ -15,6 +25,14 @@ class Grid(object):
|
|||
self.tilegrid = tilegrid
|
||||
self.loc = {}
|
||||
self.tileinfo = {}
|
||||
# Map of segment name to tiles in that segment
|
||||
self.segments = {}
|
||||
|
||||
# Map of (base_address, segment type) -> segment name
|
||||
self.base_addresses = {}
|
||||
|
||||
# Map of base_address -> (segment type, segment name)
|
||||
self.base_addresses = {}
|
||||
|
||||
for tile in self.tilegrid:
|
||||
tileinfo = self.tilegrid[tile]
|
||||
|
|
@ -27,8 +45,28 @@ class Grid(object):
|
|||
else:
|
||||
in_roi = True
|
||||
|
||||
bits = {}
|
||||
|
||||
if 'segment' in tileinfo:
|
||||
if tileinfo['segment'] not in self.segments:
|
||||
self.segments[tileinfo['segment']] = []
|
||||
|
||||
self.segments[tileinfo['segment']].append(tile)
|
||||
|
||||
if 'bits' in tileinfo:
|
||||
for k in tileinfo['bits']:
|
||||
segment_type = SegmentType(k)
|
||||
base_address = int(tileinfo['bits'][k]['baseaddr'], 0)
|
||||
bits[segment_type] = Bits(
|
||||
base_address=base_address,
|
||||
frames=tileinfo['bits'][k]['frames'],
|
||||
offset=tileinfo['bits'][k]['offset'],
|
||||
words=tileinfo['bits'][k]['words'],
|
||||
)
|
||||
|
||||
self.tileinfo[tile] = GridInfo(
|
||||
segment=tileinfo['segment'] if 'segment' in tileinfo else None,
|
||||
bits=bits,
|
||||
sites=tileinfo['sites'],
|
||||
tile_type=tileinfo['type'],
|
||||
in_roi=in_roi,
|
||||
|
|
@ -64,3 +102,15 @@ class Grid(object):
|
|||
|
||||
def gridinfo_at_tilename(self, tilename):
|
||||
return self.tileinfo[tilename]
|
||||
|
||||
def iter_all_frames(self):
|
||||
for tile, tileinfo in self.tileinfo.items():
|
||||
for segment_type, bits in tileinfo.bits.items():
|
||||
yield BitsInfo(
|
||||
segment_type=segment_type,
|
||||
tile=tile,
|
||||
bits=bits,
|
||||
)
|
||||
|
||||
def get_segment_map(self):
|
||||
return segment_map.SegmentMap(self)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
import json
|
||||
|
||||
class Roi(object):
|
||||
def __init__(self, tilegrid_file, x1, x2, y1, y2):
|
||||
self.tilegrid_file = tilegrid_file
|
||||
self.tilegrid = None
|
||||
self.x1 = x1
|
||||
self.x2 = x2
|
||||
self.y1 = y1
|
||||
self.y2 = y2
|
||||
|
||||
def tile_in_roi(self, tilej):
|
||||
x = int(tilej['grid_x'])
|
||||
y = int(tilej['grid_y'])
|
||||
return self.x1 <= x and x <= self.x2 and self.y1 <= y and y <= self.y2
|
||||
|
||||
def read_tilegrid(self):
|
||||
if not self.tilegrid:
|
||||
with open(self.tilegrid_file) as f:
|
||||
self.tilegrid = json.load(f)
|
||||
|
||||
|
||||
def gen_tiles(self, tile_types=None):
|
||||
'''
|
||||
tile_types: list of tile types to keep, or None for all
|
||||
'''
|
||||
|
||||
self.read_tilegrid()
|
||||
|
||||
for tile_name, tilej in self.tilegrid.items():
|
||||
if self.tile_in_roi(tilej) and (tile_types is None
|
||||
or tilej['type'] in tile_types):
|
||||
yield (tile_name, tilej)
|
||||
|
||||
|
||||
def gen_sites(self, site_types=None):
|
||||
'''
|
||||
site_types: list of site types to keep, or None for all
|
||||
'''
|
||||
|
||||
self.read_tilegrid()
|
||||
|
||||
for tile_name, tilej in self.tilegrid.items():
|
||||
if not self.tile_in_roi(tilej):
|
||||
continue
|
||||
|
||||
for site_name, site_type in tilej['sites'].items():
|
||||
if site_types is None or site_type in site_types:
|
||||
yield (tile_name, site_name, site_type)
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
from intervaltree import IntervalTree, Interval
|
||||
from prjxray import bitstream
|
||||
|
||||
class SegmentMap(object):
|
||||
def __init__(self, grid):
|
||||
self.segment_tree = IntervalTree()
|
||||
|
||||
for bits_info in grid.iter_all_frames():
|
||||
self.segment_tree.add(Interval(
|
||||
begin=bits_info.bits.base_address,
|
||||
end=bits_info.bits.base_address+bits_info.bits.frames,
|
||||
data=bits_info,
|
||||
))
|
||||
|
||||
def segment_info_for_frame(self, frame):
|
||||
""" Return all bits info that match frame address. """
|
||||
for frame in self.segment_tree[frame]:
|
||||
yield frame.data
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
from collections import namedtuple
|
||||
import json
|
||||
from prjxray import lib
|
||||
|
||||
""" Database files available for a tile """
|
||||
TileDbs = namedtuple('TileDbs', 'segbits mask tile_type')
|
||||
TileDbs = namedtuple('TileDbs', 'segbits ppips mask tile_type')
|
||||
|
||||
Pip = namedtuple(
|
||||
'Pip', 'name net_to net_from can_invert is_directional is_pseudo')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,136 @@
|
|||
from collections import namedtuple
|
||||
from prjxray import bitstream
|
||||
import enum
|
||||
|
||||
class PsuedoPipType(enum.Enum):
|
||||
ALWAYS = 'always'
|
||||
DEFAULT = 'default'
|
||||
HINT = 'hint'
|
||||
|
||||
def read_ppips(f):
|
||||
ppips = {}
|
||||
|
||||
for l in f:
|
||||
l = l.strip()
|
||||
if not l:
|
||||
continue
|
||||
|
||||
feature, ppip_type = l.split(' ')
|
||||
|
||||
ppips[feature] = PsuedoPipType(ppip_type)
|
||||
|
||||
return ppips
|
||||
|
||||
Bit = namedtuple('Bit', 'word_column word_bit isset')
|
||||
|
||||
def parsebit(val):
|
||||
'''Return "!012_23" => (12, 23, False)'''
|
||||
isset = True
|
||||
# Default is 0. Skip explicit call outs
|
||||
if val[0] == '!':
|
||||
isset = False
|
||||
val = val[1:]
|
||||
# 28_05 => 28, 05
|
||||
seg_word_column, word_bit_n = val.split('_')
|
||||
|
||||
return Bit(
|
||||
word_column=int(seg_word_column),
|
||||
word_bit=int(word_bit_n),
|
||||
isset=isset,
|
||||
)
|
||||
|
||||
def read_segbits(f):
|
||||
segbits = {}
|
||||
|
||||
for l in f:
|
||||
# CLBLM_L.SLICEL_X1.ALUT.INIT[10] 29_14
|
||||
l = l.strip()
|
||||
|
||||
if not l:
|
||||
continue
|
||||
|
||||
parts = l.split(' ')
|
||||
|
||||
assert len(parts) > 1
|
||||
|
||||
segbits[parts[0]] = [parsebit(val) for val in parts[1:]]
|
||||
|
||||
return segbits
|
||||
|
||||
class TileSegbits(object):
|
||||
def __init__(self, tile_db):
|
||||
self.segbits = {}
|
||||
self.ppips = {}
|
||||
self.feature_addresses = {}
|
||||
|
||||
if tile_db.ppips is not None:
|
||||
with open(tile_db.ppips) as f:
|
||||
self.ppips = read_ppips(f)
|
||||
|
||||
if tile_db.segbits is not None:
|
||||
with open(tile_db.segbits) as f:
|
||||
self.segbits = read_segbits(f)
|
||||
|
||||
for feature in self.segbits:
|
||||
sidx = feature.rfind('[')
|
||||
eidx = feature.rfind(']')
|
||||
|
||||
if sidx != -1:
|
||||
assert eidx != -1
|
||||
|
||||
base_feature = feature[:sidx]
|
||||
|
||||
if base_feature not in self.feature_addresses:
|
||||
self.feature_addresses[base_feature] = {}
|
||||
|
||||
self.feature_addresses[base_feature][int(feature[sidx+1:eidx])] = feature
|
||||
|
||||
|
||||
def match_bitdata(self, bits, bitdata):
|
||||
""" Return matching features for tile bits data (grid.Bits) and bitdata.
|
||||
|
||||
See bitstream.load_bitdata for details on bitdata structure.
|
||||
|
||||
"""
|
||||
|
||||
for feature, segbit in self.segbits.items():
|
||||
match = True
|
||||
for query_bit in segbit:
|
||||
frame = bits.base_address + query_bit.word_column
|
||||
bitidx = bits.offset*bitstream.WORD_SIZE_BITS + query_bit.word_bit
|
||||
|
||||
if frame not in bitdata:
|
||||
match = not query_bit.isset
|
||||
if match:
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
found_bit = bitidx in bitdata[frame][1]
|
||||
match = found_bit == query_bit.isset
|
||||
|
||||
if not match:
|
||||
break
|
||||
|
||||
if not match:
|
||||
continue
|
||||
|
||||
def inner():
|
||||
for query_bit in segbit:
|
||||
if query_bit.isset:
|
||||
frame = bits.base_address + query_bit.word_column
|
||||
bitidx = bits.offset*bitstream.WORD_SIZE_BITS + query_bit.word_bit
|
||||
yield (frame, bitidx)
|
||||
|
||||
yield (tuple(inner()), feature)
|
||||
|
||||
def feature_to_bits(self, feature, address=0):
|
||||
if feature in self.ppips:
|
||||
return
|
||||
|
||||
if address == 0 and feature in self.segbits:
|
||||
for bit in self.segbits[feature]:
|
||||
yield bit
|
||||
else:
|
||||
for bit in self.segbits[self.feature_addresses[feature][address]]:
|
||||
yield bit
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit faed2f5e9915497c3774ec201ced8dc017cc03ae
|
||||
Subproject commit 46eadd95407381752160a62972ec256a97e03f3d
|
||||
|
|
@ -1,310 +1,47 @@
|
|||
#!/usr/bin/env python3
|
||||
'''
|
||||
Take raw .bits files and decode them to higher level functionality
|
||||
This output is intended for debugging and not directly related to FASM
|
||||
However, as of 2018-10-16, the output is being parsed to create FASM,
|
||||
so be mindful when changing output format
|
||||
|
||||
TODO:
|
||||
Take raw .bits files and decode them to FASM.
|
||||
'''
|
||||
|
||||
import sys, os, json, re
|
||||
import os
|
||||
import fasm
|
||||
from prjxray import db
|
||||
from prjxray import fasm_disassembler
|
||||
from prjxray import bitstream
|
||||
|
||||
def run(db_root, bits_file):
|
||||
disassembler = fasm_disassembler.FasmDisassembler(db.Database(db_root))
|
||||
|
||||
class NoDB(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def line(s=''):
|
||||
print(s)
|
||||
|
||||
|
||||
def comment(s):
|
||||
print('# %s' % s)
|
||||
|
||||
|
||||
enumdb = dict()
|
||||
|
||||
|
||||
# TODO: migrate to library
|
||||
def process_db(tile_type, process):
|
||||
if tile_type in ('INT_L', 'INT_R'):
|
||||
# interconnect
|
||||
fn = "%s/%s/segbits_int_%s.db" % (
|
||||
os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"),
|
||||
tile_type[-1].lower())
|
||||
else:
|
||||
# sites
|
||||
fn = "%s/%s/segbits_%s.db" % (
|
||||
os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"),
|
||||
tile_type.lower())
|
||||
|
||||
if not os.path.exists(fn):
|
||||
raise NoDB(tile_type)
|
||||
|
||||
with open(fn, "r") as f:
|
||||
for line in f:
|
||||
process(line)
|
||||
|
||||
|
||||
def get_enums(tile_type):
|
||||
if tile_type in enumdb:
|
||||
return enumdb[tile_type]
|
||||
|
||||
enumdb[tile_type] = {}
|
||||
|
||||
def process(l):
|
||||
# CLBLM_L.SLICEL_X1.ALUT.INIT[10] 29_14
|
||||
parts = l.strip().split()
|
||||
name = parts[0]
|
||||
bit_vals = parts[1:]
|
||||
|
||||
# Assumption
|
||||
# only 1 bit => non-enumerated value
|
||||
enumdb[tile_type][name] = len(bit_vals) != 1
|
||||
|
||||
process_db(tile_type, process)
|
||||
|
||||
return enumdb[tile_type]
|
||||
|
||||
|
||||
def isenum(tilename, tag):
|
||||
return get_enums(tilename)[tag]
|
||||
|
||||
|
||||
# cache
|
||||
segbitsdb = dict()
|
||||
|
||||
|
||||
def get_database(tile_type):
|
||||
if tile_type in segbitsdb:
|
||||
return segbitsdb[tile_type]
|
||||
|
||||
ret = list()
|
||||
|
||||
def process(l):
|
||||
ret.append(l.split())
|
||||
|
||||
process_db(tile_type, process)
|
||||
|
||||
assert len(ret)
|
||||
segbitsdb[tile_type] = ret
|
||||
return ret
|
||||
|
||||
|
||||
def mk_fasm(segj, entry):
|
||||
tile_name = segj['tile_name']
|
||||
|
||||
# ex: CLBLL_L.SLICEL_X0.AFF.DMUX.O6
|
||||
tag = entry[0]
|
||||
m = re.match(r'([A-Za-z0-9_]+)[.](.*)', tag)
|
||||
# tile_type = m.group(1)
|
||||
# the postfix, O6 in the above example
|
||||
tag_post = m.group(2)
|
||||
|
||||
if not isenum(segj['type'], tag):
|
||||
return '%s.%s 1' % (tile_name, tag_post)
|
||||
else:
|
||||
# Make the selection an argument of the configruation
|
||||
m = re.match(r'(.*)[.]([A-Za-z0-9_]+)', tag_post)
|
||||
which = m.group(1)
|
||||
value = m.group(2)
|
||||
return '%s.%s %s' % (tile_name, which, value)
|
||||
|
||||
|
||||
def mk_segbits(seginfo, bitdata):
|
||||
baseframe = int(seginfo["baseaddr"][0], 16)
|
||||
basewordidx = int(seginfo["baseaddr"][1])
|
||||
numframes = int(seginfo["frames"])
|
||||
numwords = int(seginfo["words"])
|
||||
|
||||
segbits = set()
|
||||
for frame in range(baseframe, baseframe + numframes):
|
||||
if frame not in bitdata:
|
||||
continue
|
||||
for wordidx in range(basewordidx, basewordidx + numwords):
|
||||
if wordidx not in bitdata[frame]:
|
||||
continue
|
||||
for bitidx in bitdata[frame][wordidx]:
|
||||
segbits.add(
|
||||
"%02d_%02d" %
|
||||
(frame - baseframe, 32 * (wordidx - basewordidx) + bitidx))
|
||||
return segbits
|
||||
|
||||
|
||||
def tagmatch(entry, segbits):
|
||||
for bit in entry[1:]:
|
||||
if bit[0] != "!" and bit not in segbits:
|
||||
return False
|
||||
if bit[0] == "!" and bit[1:] in segbits:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def tag_matched(entry, segbits):
|
||||
for bit in entry[1:]:
|
||||
if bit[0] != "!":
|
||||
segbits.remove(bit)
|
||||
|
||||
|
||||
decode_warnings = set()
|
||||
|
||||
|
||||
def seg_decode(seginfo, segbits, verbose=False):
|
||||
fasms = set()
|
||||
|
||||
# already failed?
|
||||
if seginfo["type"] in decode_warnings:
|
||||
return fasms
|
||||
|
||||
try:
|
||||
db = get_database(seginfo["type"])
|
||||
except NoDB:
|
||||
verbose and comment(
|
||||
"WARNING: failed to load DB for %s" % seginfo["type"])
|
||||
decode_warnings.add(seginfo["type"])
|
||||
return fasms
|
||||
|
||||
for entry in db:
|
||||
if not tagmatch(entry, segbits):
|
||||
continue
|
||||
tag_matched(entry, segbits)
|
||||
#fasms.add('%s.%s 1' % (seginfo['tile_name'], entry[0]))
|
||||
fasm = mk_fasm(seginfo, entry)
|
||||
fasms.add(fasm)
|
||||
return fasms
|
||||
|
||||
|
||||
def handle_segment(segname, grid, bitdata, verbose=False):
|
||||
|
||||
assert segname
|
||||
|
||||
# only print bitstream tiles
|
||||
if segname not in grid["segments"]:
|
||||
return
|
||||
seginfo = grid["segments"][segname]
|
||||
|
||||
segbits = mk_segbits(seginfo, bitdata)
|
||||
|
||||
fasms = seg_decode(seginfo, segbits, verbose=verbose)
|
||||
|
||||
# Found something to print?
|
||||
if len(segbits) == 0 and len(fasms) == 0:
|
||||
return
|
||||
|
||||
line('')
|
||||
comment("seg %s" % (segname, ))
|
||||
|
||||
for fasm in sorted(fasms):
|
||||
line(fasm)
|
||||
|
||||
if verbose and len(segbits) > 0:
|
||||
comment('%u unknown bits' % len(segbits))
|
||||
for bit in sorted(segbits):
|
||||
comment("bit %s" % bit)
|
||||
|
||||
|
||||
def load_bitdata(bits_file):
|
||||
bitdata = dict()
|
||||
|
||||
with open(bits_file, "r") as f:
|
||||
for line in f:
|
||||
line = line.split("_")
|
||||
frame = int(line[1], 16)
|
||||
wordidx = int(line[2], 10)
|
||||
bitidx = int(line[3], 10)
|
||||
|
||||
if frame not in bitdata:
|
||||
bitdata[frame] = dict()
|
||||
|
||||
if wordidx not in bitdata[frame]:
|
||||
bitdata[frame][wordidx] = set()
|
||||
|
||||
bitdata[frame][wordidx].add(bitidx)
|
||||
return bitdata
|
||||
|
||||
|
||||
def mk_grid():
|
||||
'''Load tilegrid, flattening all blocks into one dictionary'''
|
||||
|
||||
with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"),
|
||||
os.getenv("XRAY_DATABASE")), "r") as f:
|
||||
new_grid = json.load(f)
|
||||
|
||||
# TODO: Migrate to new tilegrid format via library.
|
||||
grid = {'tiles': new_grid, 'segments': {}}
|
||||
|
||||
for tile_name, tile in grid['tiles'].items():
|
||||
bits = tile.get('bits', None)
|
||||
if not bits:
|
||||
continue
|
||||
for block_name, block in bits.items():
|
||||
segname = mksegment(tile_name, block_name)
|
||||
grid['segments'][segname] = {
|
||||
'baseaddr': [
|
||||
block['baseaddr'],
|
||||
block['offset'],
|
||||
],
|
||||
'type': tile['type'],
|
||||
'frames': block['frames'],
|
||||
'words': block['words'],
|
||||
'tile_name': tile_name,
|
||||
'block_name': block_name,
|
||||
}
|
||||
return grid
|
||||
|
||||
|
||||
def mksegment(tile_name, block_name):
|
||||
'''Create a segment name'''
|
||||
return '%s:%s' % (tile_name, block_name)
|
||||
|
||||
|
||||
def tile_segnames(grid):
|
||||
ret = []
|
||||
for tile_name, tile in grid['tiles'].items():
|
||||
for block_name in tile['bits'].keys():
|
||||
ret.append(mksegment(tile_name, block_name))
|
||||
return ret
|
||||
|
||||
|
||||
def run(bits_file, segnames, verbose=False):
|
||||
grid = mk_grid()
|
||||
|
||||
bitdata = load_bitdata(bits_file)
|
||||
|
||||
# Default: print all
|
||||
if segnames:
|
||||
for i, segname in enumerate(segnames):
|
||||
# Default to common tile config area if tile given without explicit block
|
||||
if ':' not in segname:
|
||||
segnames[i] = mksegment(segname, 'CLB_IO_CLK')
|
||||
else:
|
||||
segnames = sorted(tile_segnames(grid))
|
||||
|
||||
comment('Segments: %u' % len(segnames))
|
||||
|
||||
# XXX: previously this was sorted by address, not name
|
||||
# revisit?
|
||||
for segname in segnames:
|
||||
handle_segment(segname, grid, bitdata, verbose=verbose)
|
||||
with open(bits_file) as f:
|
||||
bitdata = bitstream.load_bitdata(f)
|
||||
|
||||
for fasm_line in disassembler.find_features_in_bitstream(bitdata):
|
||||
for line in fasm.fasm_line_to_string(fasm_line):
|
||||
print(line)
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
# XXX: tool still works, but not well
|
||||
# need to eliminate segments entirely
|
||||
parser = argparse.ArgumentParser(
|
||||
description='XXX: does not print all data?')
|
||||
description='Convert 7-series bits file to FASM.')
|
||||
|
||||
database_dir = os.getenv("XRAY_DATABASE_DIR")
|
||||
database = os.getenv("XRAY_DATABASE")
|
||||
db_root_kwargs = {}
|
||||
if database_dir is None or database is None:
|
||||
db_root_kwargs['required'] = True
|
||||
else:
|
||||
db_root_kwargs['required'] = False
|
||||
db_root_kwargs['default'] = os.path.join(database_dir, database)
|
||||
|
||||
parser.add_argument('--verbose', action='store_true', help='')
|
||||
parser.add_argument('bits_file', help='')
|
||||
parser.add_argument(
|
||||
'segnames', nargs='*', help='List of tile or tile:block to print')
|
||||
'--db_root', help="Database root.", **db_root_kwargs)
|
||||
parser.add_argument('bits_file', help='')
|
||||
parser.add_argument('verbose', help='Print lines for unknown tiles and bits',
|
||||
action='store_true')
|
||||
args = parser.parse_args()
|
||||
|
||||
run(args.bits_file, args.segnames, verbose=args.verbose)
|
||||
|
||||
run(args.db_root, args.bits_file)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -1,97 +1,11 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import print_function
|
||||
from prjxray import fasm_assembler
|
||||
from prjxray import db
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
import collections
|
||||
import fasm
|
||||
|
||||
def parsebit(val):
|
||||
'''Return "!012_23" => (12, 23, False)'''
|
||||
isset = True
|
||||
# Default is 0. Skip explicit call outs
|
||||
if val[0] == '!':
|
||||
isset = False
|
||||
val = val[1:]
|
||||
# 28_05 => 28, 05
|
||||
seg_word_column, word_bit_n = val.split('_')
|
||||
return int(seg_word_column), int(word_bit_n), isset
|
||||
|
||||
# TODO: migrate to library
|
||||
def process_db(tile_type, process):
|
||||
fns = [
|
||||
# sites
|
||||
"%s/%s/segbits_%s.db" % (
|
||||
os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"),
|
||||
tile_type.lower()),
|
||||
# interconnect
|
||||
"%s/%s/segbits_int_%s.db" % (
|
||||
os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"),
|
||||
tile_type[-1].lower()),
|
||||
]
|
||||
|
||||
for fn in fns:
|
||||
if os.path.exists(fn):
|
||||
with open(fn, "r") as f:
|
||||
for line in f:
|
||||
process(line)
|
||||
|
||||
|
||||
'''
|
||||
Loosely based on segprint function
|
||||
Maybe better to return as two distinct dictionaries?
|
||||
|
||||
{
|
||||
'tile.meh': {
|
||||
'O5': [(11, 2, False), (12, 2, True)],
|
||||
'O6': [(11, 2, True), (12, 2, False)],
|
||||
},
|
||||
}
|
||||
'''
|
||||
segbitsdb = dict()
|
||||
|
||||
|
||||
def get_database(tile_type):
|
||||
if tile_type in segbitsdb:
|
||||
return segbitsdb[tile_type]
|
||||
|
||||
segbitsdb[tile_type] = {}
|
||||
|
||||
def process(l):
|
||||
# CLBLM_L.SLICEL_X1.ALUT.INIT[10] 29_14
|
||||
parts = l.strip().split()
|
||||
name = parts[0]
|
||||
bit_vals = parts[1:]
|
||||
|
||||
# Assumption
|
||||
# only 1 bit => non-enumerated value
|
||||
if len(bit_vals) == 1:
|
||||
seg_word_column, word_bit_n, isset = parsebit(bit_vals[0])
|
||||
if not isset:
|
||||
raise Exception(
|
||||
"Expect single bit DB entries to be set, got %s" % l)
|
||||
# Treat like an enumerated value with keys 0 or 1
|
||||
assert name not in segbitsdb[tile_type]
|
||||
segbitsdb[tile_type][name] = {
|
||||
'0': [(seg_word_column, word_bit_n, 0)],
|
||||
'1': [(seg_word_column, word_bit_n, 1)],
|
||||
}
|
||||
else:
|
||||
# An enumerated value
|
||||
# Split the base name and selected key
|
||||
m = re.match(r'(.+)[.](.+)', name)
|
||||
namepart = m.group(1)
|
||||
key = m.group(2)
|
||||
|
||||
# May or may not be the first key encountered
|
||||
bits_map = segbitsdb[tile_type].setdefault(namepart, {})
|
||||
bits_map[key] = [parsebit(x) for x in bit_vals]
|
||||
|
||||
process_db(tile_type, process)
|
||||
|
||||
return segbitsdb[tile_type]
|
||||
|
||||
import os.path
|
||||
|
||||
def dump_frames_verbose(frames):
|
||||
print()
|
||||
|
|
@ -102,7 +16,6 @@ def dump_frames_verbose(frames):
|
|||
'0x%08X ' % addr + ', '.join(['0x%08X' % w for w in words]) +
|
||||
'...')
|
||||
|
||||
|
||||
def dump_frames_sparse(frames):
|
||||
print()
|
||||
print("Frames: %d" % len(frames))
|
||||
|
|
@ -121,7 +34,6 @@ def dump_frames_sparse(frames):
|
|||
if w:
|
||||
print(' % 3d: 0x%08X' % (i, w))
|
||||
|
||||
|
||||
def dump_frm(f, frames):
|
||||
'''Write a .frm file given a list of frames, each containing a list of 101 32 bit words'''
|
||||
for addr in sorted(frames.keys()):
|
||||
|
|
@ -129,214 +41,40 @@ def dump_frm(f, frames):
|
|||
f.write(
|
||||
'0x%08X ' % addr + ','.join(['0x%08X' % w for w in words]) + '\n')
|
||||
|
||||
|
||||
def mksegment(tile_name, block_name):
|
||||
'''Create a segment name'''
|
||||
return '%s:%s' % (tile_name, block_name)
|
||||
|
||||
|
||||
def mk_grid():
|
||||
'''Load tilegrid, flattening all blocks into one dictionary'''
|
||||
|
||||
with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"),
|
||||
os.getenv("XRAY_DATABASE")), "r") as f:
|
||||
new_grid = json.load(f)
|
||||
|
||||
# TODO: Migrate to new tilegrid format via library.
|
||||
grid = {'tiles': new_grid, 'segments': {}}
|
||||
|
||||
for tile_name, tile in grid['tiles'].items():
|
||||
for block_name, block in tile['bits'].items():
|
||||
segname = mksegment(tile_name, block_name)
|
||||
grid['segments'][segname] = {
|
||||
'baseaddr': [
|
||||
block['baseaddr'],
|
||||
block['offset'],
|
||||
],
|
||||
'type': tile['type'],
|
||||
'frames': block['frames'],
|
||||
'words': block['words'],
|
||||
'tile_name': tile_name,
|
||||
'block_name': block_name,
|
||||
}
|
||||
return grid
|
||||
|
||||
|
||||
def frame_init(frames, addr):
|
||||
'''Set given frame to 0'''
|
||||
if not addr in frames:
|
||||
frames[addr] = [0 for _i in range(101)]
|
||||
|
||||
|
||||
def frames_init(frames, grid):
|
||||
'''Set all frames to 0'''
|
||||
for segj in grid['segments'].values():
|
||||
seg_baseaddr, seg_word_base = segj['baseaddr']
|
||||
seg_baseaddr = int(seg_baseaddr, 0)
|
||||
for coli in range(segj['frames']):
|
||||
frame_init(frames, seg_baseaddr + coli)
|
||||
|
||||
|
||||
def frame_set(frames, frame_addr, word_addr, bit_index):
|
||||
'''Set given bit in given frame address and word'''
|
||||
frames[frame_addr][word_addr] |= 1 << bit_index
|
||||
|
||||
|
||||
def frame_clear(frames, frame_addr, word_addr, bit_index):
|
||||
'''Set given bit in given frame address and word'''
|
||||
frames[frame_addr][word_addr] &= 0xFFFFFFFF ^ (1 << bit_index)
|
||||
|
||||
|
||||
def parse_line(l):
|
||||
# Comment
|
||||
# Remove all text including and after #
|
||||
i = l.rfind('#')
|
||||
if i >= 0:
|
||||
l = l[0:i]
|
||||
l = l.strip()
|
||||
|
||||
# Ignore blank lines
|
||||
if not l:
|
||||
return
|
||||
|
||||
# tile.site.stuff value
|
||||
# INT_L_X10Y102.CENTER_INTER_L.IMUX_L1 EE2END0
|
||||
# Optional value
|
||||
m = re.match(r'([a-zA-Z0-9_]+)[.]([a-zA-Z0-9_.\[\]]+)([ ](.+))?', l)
|
||||
if not m:
|
||||
raise FASMSyntaxError("Bad line: %s" % l)
|
||||
tile = m.group(1)
|
||||
name = m.group(2)
|
||||
value = m.group(4)
|
||||
|
||||
return tile, name, value
|
||||
|
||||
|
||||
def check_duplicate(used_names, tile, name, l, line_number):
|
||||
'''Throw an exception if a conflicting FASM directive was given'''
|
||||
used_name = (tile, name)
|
||||
old_line_number = used_names.get(used_name, None)
|
||||
if old_line_number:
|
||||
raise FASMSyntaxError(
|
||||
"Duplicate name lines %d and %d, second line: %s" %
|
||||
(old_line_number, line_number, l))
|
||||
used_names[used_name] = line_number
|
||||
|
||||
|
||||
def update_segbit(
|
||||
frames, seg_word_column, word_bit_n, isset, seg_baseaddr,
|
||||
seg_word_base):
|
||||
'''Set or clear a single bit in a segment at the given word column and word bit position'''
|
||||
# Now we have the word column and word bit index
|
||||
# Combine with the segments relative frame position to fully get the position
|
||||
frame_addr = seg_baseaddr + seg_word_column
|
||||
# 2 words per segment
|
||||
word_addr = seg_word_base + word_bit_n // 32
|
||||
bit_index = word_bit_n % 32
|
||||
if isset:
|
||||
frame_set(frames, frame_addr, word_addr, bit_index)
|
||||
else:
|
||||
frame_clear(frames, frame_addr, word_addr, bit_index)
|
||||
|
||||
|
||||
def default_value(db_vals, name):
|
||||
# If its binary, allow omitted value default to 1
|
||||
if tuple(sorted(db_vals.keys())) == ('0', '1'):
|
||||
return '1'
|
||||
else:
|
||||
raise FASMSyntaxError(
|
||||
"Enumerable entry %s must have explicit value" % name)
|
||||
|
||||
|
||||
def process_line(line_number, l, grid, frames, used_names):
|
||||
parsed = parse_line(l)
|
||||
# empty line
|
||||
if not parsed:
|
||||
return
|
||||
tile_name, name, value = parsed
|
||||
check_duplicate(used_names, tile_name, name, l, line_number)
|
||||
|
||||
tilej = grid['tiles'][tile_name]
|
||||
for block_name, block in tilej['bits'].items():
|
||||
segname = mksegment(tile_name, block_name)
|
||||
|
||||
segj = grid['segments'][segname]
|
||||
seg_baseaddr, seg_word_base = segj['baseaddr']
|
||||
seg_baseaddr = int(seg_baseaddr, 0)
|
||||
|
||||
# Ensure that all frames exist for this segment
|
||||
# FIXME: type dependent
|
||||
for coli in range(segj['frames']):
|
||||
frame_init(frames, seg_baseaddr + coli)
|
||||
|
||||
# Now lets look up the bits we need frames for
|
||||
segdb = get_database(segj['type'])
|
||||
|
||||
db_k = '%s.%s' % (tilej['type'], name)
|
||||
try:
|
||||
db_vals = segdb[db_k]
|
||||
except KeyError:
|
||||
raise FASMSyntaxError(
|
||||
"Segment DB %s, key %s not found from line '%s'" %
|
||||
(segj['type'], db_k, l)) from None
|
||||
|
||||
if not value:
|
||||
value = default_value(db_vals, name)
|
||||
|
||||
# Get the specific entry we need
|
||||
try:
|
||||
db_vals = db_vals[value]
|
||||
except KeyError:
|
||||
raise FASMSyntaxError(
|
||||
"Invalid entry %s. Valid entries are %s" %
|
||||
(value, db_vals.keys()))
|
||||
|
||||
for seg_word_column, word_bit_n, isset in db_vals:
|
||||
update_segbit(
|
||||
frames, seg_word_column, word_bit_n, isset, seg_baseaddr,
|
||||
seg_word_base)
|
||||
|
||||
|
||||
def run(f_in, f_out, sparse=False, debug=False):
|
||||
# address to array of 101 32 bit words
|
||||
frames = {}
|
||||
# Directives we've seen so far
|
||||
# Complain if there is a duplicate
|
||||
# Contains line number of last entry
|
||||
used_names = {}
|
||||
|
||||
grid = mk_grid()
|
||||
|
||||
if not sparse:
|
||||
# Initiaize bitstream to 0
|
||||
frames_init(frames, grid)
|
||||
|
||||
for line_number, l in enumerate(f_in, 1):
|
||||
process_line(line_number, l, grid, frames, used_names)
|
||||
def run(db_root, filename_in, f_out, sparse=False, debug=False):
|
||||
assembler = fasm_assembler.FasmAssembler(db.Database(db_root))
|
||||
assembler.parse_fasm_filename(filename_in)
|
||||
frames = assembler.get_frames(sparse=sparse)
|
||||
|
||||
if debug:
|
||||
#dump_frames_verbose(frames)
|
||||
dump_frames_sparse(frames)
|
||||
|
||||
dump_frm(f_out, frames)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Convert FPGA configuration description ("FPGA assembly") into binary frame equivalent'
|
||||
)
|
||||
|
||||
database_dir = os.getenv("XRAY_DATABASE_DIR")
|
||||
database = os.getenv("XRAY_DATABASE")
|
||||
db_root_kwargs = {}
|
||||
if database_dir is None or database is None:
|
||||
db_root_kwargs['required'] = True
|
||||
else:
|
||||
db_root_kwargs['required'] = False
|
||||
db_root_kwargs['default'] = os.path.join(database_dir, database)
|
||||
|
||||
parser.add_argument(
|
||||
'--db_root', help="Database root.", **db_root_kwargs)
|
||||
parser.add_argument(
|
||||
'--sparse', action='store_true', help="Don't zero fill all frames")
|
||||
parser.add_argument(
|
||||
'--debug', action='store_true', help="Print debug dump")
|
||||
parser.add_argument(
|
||||
'fn_in',
|
||||
default='/dev/stdin',
|
||||
nargs='?',
|
||||
help='Input FPGA assembly (.fasm) file')
|
||||
parser.add_argument(
|
||||
'fn_out',
|
||||
|
|
@ -346,7 +84,12 @@ if __name__ == '__main__':
|
|||
|
||||
args = parser.parse_args()
|
||||
run(
|
||||
open(args.fn_in, 'r'),
|
||||
open(args.fn_out, 'w'),
|
||||
db_root=args.db_root,
|
||||
filename_in=args.fn_in,
|
||||
f_out=open(args.fn_out, 'w'),
|
||||
sparse=args.sparse,
|
||||
debug=args.debug)
|
||||
debug=args.debug
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import print_function
|
||||
import os.path
|
||||
import fasm
|
||||
from prjxray import db
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=
|
||||
'Convert FASM to pip list'
|
||||
)
|
||||
|
||||
database_dir = os.getenv("XRAY_DATABASE_DIR")
|
||||
database = os.getenv("XRAY_DATABASE")
|
||||
db_root_kwargs = {}
|
||||
if database_dir is None or database is None:
|
||||
db_root_kwargs['required'] = True
|
||||
else:
|
||||
db_root_kwargs['required'] = False
|
||||
db_root_kwargs['default'] = os.path.join(database_dir, database)
|
||||
|
||||
parser.add_argument(
|
||||
'--db_root', help="Database root.", **db_root_kwargs)
|
||||
parser.add_argument(
|
||||
'fn_in',
|
||||
help='Input FPGA assembly (.fasm) file')
|
||||
|
||||
args = parser.parse_args()
|
||||
database = db.Database(args.db_root)
|
||||
grid = database.grid()
|
||||
|
||||
def inner():
|
||||
for line in fasm.parse_fasm_filename(args.fn_in):
|
||||
if not line.set_feature:
|
||||
continue
|
||||
|
||||
parts = line.set_feature.feature.split('.')
|
||||
tile = parts[0]
|
||||
gridinfo = grid.gridinfo_at_tilename(tile)
|
||||
|
||||
tile_type = database.get_tile_type(gridinfo.tile_type)
|
||||
|
||||
for pip in tile_type.pips:
|
||||
if pip.net_from == parts[2] and pip.net_to == parts[1]:
|
||||
yield '{}/{}.{}'.format(tile, gridinfo.tile_type, pip.name)
|
||||
|
||||
print('highlight_objects [concat {}]'.format(' '.join('[get_pips {}]'.format(pip) for pip in inner())))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Reference in New Issue