blob: 337cffd402e6e21af2b2df7761cf2202493fdc6c [file] [log] [blame] [edit]
import re
import fasm
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,
block_type,
bits,
solved_bitdata,
bitdata,
verbose=False):
gridinfo = self.grid.gridinfo_at_tilename(tile_name)
try:
tile_segbits = self.grid.get_tile_segbits_at_tilename(tile_name)
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(
block_type, bits, bitdata):
for frame, bit in ones_matched:
if frame not in solved_bitdata:
solved_bitdata[frame] = set()
solved_bitdata[frame].add(bit)
yield mk_fasm(tile_name=tile_name, feature=feature)
def find_features_in_bitstream(self, bitdata, verbose=False):
solved_bitdata = {}
frames = set(bitdata.keys())
tiles_checked = set()
emitted_features = 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, bits_info.block_type) 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, bits_info.block_type))
for fasm_line in self.find_features_in_tile(
bits_info.tile,
bits_info.block_type,
bits_info.bits,
solved_bitdata,
bitdata,
verbose=verbose):
if fasm_line not in emitted_features:
emitted_features.add(fasm_line)
yield fasm_line
remaining_bits = bitdata[frame][1]
if frame in solved_bitdata:
remaining_bits -= solved_bitdata[frame]
if len(remaining_bits) > 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(remaining_bits),
))
for bit in remaining_bits:
frame_offset = frame % bitstream.FRAME_ALIGNMENT
aligned_frame = frame - frame_offset
wordidx = bit // bitstream.WORD_SIZE_BITS
bitidx = bit % bitstream.WORD_SIZE_BITS
annotations = []
annotations.append(
fasm.Annotation(
'unknown_bit', '{:08x}_{}_{}'.format(
frame, wordidx, bitidx)))
annotations.append(
fasm.Annotation('unknown_segment',
'0x{:08x}'.format(aligned_frame)))
annotations.append(
fasm.Annotation(
'unknown_segbit', '{:02d}_{:02d}'.format(
frame_offset, bit)))
yield fasm.FasmLine(
set_feature=None,
annotations=tuple(annotations),
comment=None,
)
def is_zero_feature(self, feature):
parts = feature.split('.')
tile = parts[0]
gridinfo = self.grid.gridinfo_at_tilename(tile)
feature = '.'.join(parts[1:])
db_k = '%s.%s' % (gridinfo.tile_type, feature)
segbits = self.grid.get_tile_segbits_at_tilename(tile)
any_bits = False
for block_type, bit in segbits.feature_to_bits(gridinfo.bits, db_k):
if bit.isset:
any_bits = True
break
return not any_bits