| 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 |