IOB_SING solution via segbit aliases.

Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
This commit is contained in:
Keith Rothman 2019-03-01 08:12:36 -08:00
parent ca45656e5f
commit 1307e11397
9 changed files with 272 additions and 54 deletions

View File

@ -245,6 +245,78 @@ def propagate_rebuf(database, tiles_by_grid):
database[rebuf_above]['bits']['CLB_IO_CLK']['words'] = 4
def propagate_IOB_SING(database, tiles_by_grid):
""" The IOB_SING are half tiles at top and bottom of every IO column.
Unlike most tiles, they do not behave consistently. The tile at the top
of the column is the bottom half of a full IOB, and the tile at the bottom
of the column is the top half of a full IOB. For this reason, explicit
bit aliasing is used to map the full IOB bits into the two halves, and a
mapping is provided for the site naming.
"""
seen_iobs = set()
for tile in database:
if tile in seen_iobs:
continue
if database[tile]["type"] not in ["LIOB33", "RIOB33"]:
continue
while True:
prev_tile = tile
tile = tiles_by_grid[(
database[tile]['grid_x'], database[tile]['grid_y'] + 1)]
if '_SING' in database[tile]['type']:
break
bottom_tile = tile
seen_iobs.add(bottom_tile)
bits = database[prev_tile]['bits']['CLB_IO_CLK']
while True:
tile = tiles_by_grid[(
database[tile]['grid_x'], database[tile]['grid_y'] - 1)]
seen_iobs.add(tile)
if '_SING' in database[tile]['type']:
break
if 'CLB_IO_CLK' in database[tile]['bits']:
assert bits['baseaddr'] == database[tile]['bits'][
'CLB_IO_CLK']['baseaddr']
assert bits['frames'] == database[tile]['bits']['CLB_IO_CLK'][
'frames']
assert bits['words'] == database[tile]['bits']['CLB_IO_CLK'][
'words']
top_tile = tile
database[top_tile]['bits']['CLB_IO_CLK'] = copy.deepcopy(bits)
database[top_tile]['bits']['CLB_IO_CLK']['words'] = 2
database[top_tile]['bits']['CLB_IO_CLK']['offset'] = 99
database[top_tile]['bits']['CLB_IO_CLK']['alias'] = {
'type': database[prev_tile]['type'],
'start_offset': 0,
'sites': {
'IOB33_Y0': 'IOB33_Y1',
}
}
database[bottom_tile]['bits']['CLB_IO_CLK'] = copy.deepcopy(bits)
database[bottom_tile]['bits']['CLB_IO_CLK']['words'] = 2
database[bottom_tile]['bits']['CLB_IO_CLK']['offset'] = 0
database[bottom_tile]['bits']['CLB_IO_CLK']['alias'] = {
'type': database[prev_tile]['type'],
'start_offset': 2,
'sites': {
'IOB33_Y0': 'IOB33_Y0',
}
}
def run(json_in_fn, json_out_fn, verbose=False):
# Load input files
database = json.load(open(json_in_fn, "r"))
@ -253,6 +325,7 @@ def run(json_in_fn, json_out_fn, verbose=False):
propagate_INT_lr_bits(database, tiles_by_grid, verbose=verbose)
propagate_INT_bits_in_column(database, tiles_by_grid)
propagate_rebuf(database, tiles_by_grid)
propagate_IOB_SING(database, tiles_by_grid)
# Save
xjson.pprint(open(json_out_fn, "w"), database)

View File

@ -114,7 +114,7 @@ class Database(object):
def grid(self):
""" Return Grid object for database. """
self._read_tilegrid()
return grid.Grid(self.tilegrid)
return grid.Grid(self, self.tilegrid)
def _read_tile_types(self):
for tile_type, db in self.tile_types.items():

View File

@ -98,26 +98,18 @@ class FasmAssembler(object):
def enable_feature(self, tile, feature, address, line):
gridinfo = self.grid.gridinfo_at_tilename(tile)
def update_segbit(block_type, bit):
def update_segbit(bit):
'''Set or clear a single bit in a segment at the given word column and word bit position'''
bits = gridinfo.bits[block_type]
seg_baseaddr = bits.base_address
seg_word_base = bits.offset
# 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
frame_addr = bit.word_column
word_addr = 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)
segbits = self.grid.get_tile_segbits_at_tilename(tile)
self.seen_tile.add(tile)
@ -126,9 +118,10 @@ class FasmAssembler(object):
any_bits = set()
try:
for block_type, bit in segbits.feature_to_bits(db_k, address):
for block_type, bit in segbits.feature_to_bits(gridinfo.bits, db_k,
address):
any_bits.add(block_type)
update_segbit(block_type, bit)
update_segbit(bit)
except KeyError:
raise FasmLookupError(
"Segment DB %s, key %s not found from line '%s'" %

View File

@ -48,7 +48,7 @@ class FasmDisassembler(object):
gridinfo = self.grid.gridinfo_at_tilename(tile_name)
try:
tile_segbits = self.db.get_tile_segbits(gridinfo.tile_type)
tile_segbits = self.grid.get_tile_segbits_at_tilename(tile_name)
except KeyError as e:
if not verbose:
return
@ -163,3 +163,19 @@ class FasmDisassembler(object):
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

View File

@ -1,20 +1,6 @@
from collections import namedtuple
import enum
from prjxray import segment_map
class BlockType(enum.Enum):
# Frames describing CLB features, interconnect, clocks and IOs.
CLB_IO_CLK = 'CLB_IO_CLK'
# Frames describing block RAM initialization.
BLOCK_RAM = 'BLOCK_RAM'
GridLoc = namedtuple('GridLoc', 'grid_x grid_y')
GridInfo = namedtuple('GridInfo', 'bits sites tile_type')
Bits = namedtuple('Bits', 'base_address frames offset words')
BitsInfo = namedtuple('BitsInfo', 'block_type tile bits')
from prjxray.grid_types import BlockType, GridLoc, GridInfo, BitAlias, Bits, BitsInfo
from prjxray.tile_segbits_alias import TileSegbitsAlias
class Grid(object):
@ -24,7 +10,8 @@ class Grid(object):
of segment offsets for particular grid locations and their tile types.
"""
def __init__(self, tilegrid):
def __init__(self, db, tilegrid):
self.db = db
self.tilegrid = tilegrid
self.loc = {}
self.tileinfo = {}
@ -41,11 +28,22 @@ class Grid(object):
for k in tileinfo['bits']:
segment_type = BlockType(k)
base_address = int(tileinfo['bits'][k]['baseaddr'], 0)
alias = None
if 'alias' in tileinfo['bits'][k]:
alias = BitAlias(
tile_type=tileinfo['bits'][k]['alias']['type'],
start_offset=tileinfo['bits'][k]['alias']
['start_offset'],
sites=tileinfo['bits'][k]['alias']['sites'],
)
bits[segment_type] = Bits(
base_address=base_address,
frames=tileinfo['bits'][k]['frames'],
offset=tileinfo['bits'][k]['offset'],
words=tileinfo['bits'][k]['words'],
alias=alias,
)
self.tileinfo[tile] = GridInfo(
@ -103,3 +101,17 @@ class Grid(object):
tile_type = gridinfo.tile_type
return (tile_type, loc.grid_x, -loc.grid_y)
def get_tile_segbits_at_tilename(self, tilename):
gridinfo = self.gridinfo_at_tilename(tilename)
# Check to see if alias is present
any_alias = False
for block_type, bits in gridinfo.bits.items():
if bits.alias is not None:
any_alias = True
if any_alias:
return TileSegbitsAlias(self.db, gridinfo.tile_type, gridinfo.bits)
else:
return self.db.get_tile_segbits(gridinfo.tile_type)

17
prjxray/grid_types.py Normal file
View File

@ -0,0 +1,17 @@
from collections import namedtuple
import enum
class BlockType(enum.Enum):
# Frames describing CLB features, interconnect, clocks and IOs.
CLB_IO_CLK = 'CLB_IO_CLK'
# Frames describing block RAM initialization.
BLOCK_RAM = 'BLOCK_RAM'
GridLoc = namedtuple('GridLoc', 'grid_x grid_y')
GridInfo = namedtuple('GridInfo', 'bits sites tile_type')
BitAlias = namedtuple('BitAlias', 'tile_type start_offset sites')
Bits = namedtuple('Bits', 'base_address frames offset words alias')
BitsInfo = namedtuple('BitsInfo', 'block_type tile bits')

View File

@ -99,7 +99,7 @@ class TileSegbits(object):
self.feature_addresses[base_feature][int(
feature[sidx + 1:eidx])] = (block_type, feature)
def match_bitdata(self, block_type, bits, bitdata):
def match_bitdata(self, block_type, bits, bitdata, match_filter=None):
""" Return matching features for tile bits data (grid.Bits) and bitdata.
See bitstream.load_bitdata for details on bitdata structure.
@ -111,7 +111,13 @@ class TileSegbits(object):
for feature, segbit in self.segbits[block_type].items():
match = True
skip = False
for query_bit in segbit:
if match_filter is not None and not match_filter(block_type,
query_bit):
skip = True
break
frame = bits.base_address + query_bit.word_column
bitidx = bits.offset * bitstream.WORD_SIZE_BITS + query_bit.word_bit
@ -128,7 +134,7 @@ class TileSegbits(object):
if not match:
break
if not match:
if not match or skip:
continue
def inner():
@ -140,16 +146,26 @@ class TileSegbits(object):
yield (tuple(inner()), feature)
def feature_to_bits(self, feature, address=0):
def map_bit_to_frame(self, block_type, bits, bit):
""" Convert bit from segbit to frame data. """
return Bit(
word_column=bits.base_address + bit.word_column,
word_bit=bits.offset * bitstream.WORD_SIZE_BITS + bit.word_bit,
isset=bit.isset,
)
def feature_to_bits(self, bits_map, feature, address=0):
if feature in self.ppips:
return
for block_type in self.segbits:
if address == 0 and feature in self.segbits[block_type]:
for bit in self.segbits[block_type][feature]:
yield block_type, bit
yield block_type, self.map_bit_to_frame(
block_type, bits_map[block_type], bit)
return
block_type, feature = self.feature_addresses[feature][address]
for bit in self.segbits[block_type][feature]:
yield block_type, bit
yield block_type, self.map_bit_to_frame(
block_type, bits_map[block_type], bit)

View File

@ -0,0 +1,106 @@
""" TileSegbitsAlias provides an alias from one tile type to another.
TileSegbitsAlias performs severals functions to achieve the alias:
- Remaps tile type from the original tile type to the alias tile type
- Offsets the bits from the original to the alias type
- Renames sites from the original to the alias type
- Filters bits outside of the alias.
"""
from prjxray import bitstream
from prjxray.grid_types import Bits
class TileSegbitsAlias(object):
def __init__(self, db, tile_type, bits_map):
# Name of tile_type that is using the alias
self.tile_type = tile_type
# Name of aliased tile_type
self.alias_tile_type = None
# BlockType -> BitAlias map
self.alias = {}
self.bits_map = bits_map
# BlockType -> aliased Bits map
self.alias_bits_map = {}
# aliased site name to site name map
self.sites_rev_map = {}
for block_type in bits_map:
self.alias[block_type] = bits_map[block_type].alias
self.alias_bits_map[block_type] = Bits(
base_address=bits_map[block_type].base_address,
frames=bits_map[block_type].frames,
offset=bits_map[block_type].offset -
self.alias[block_type].start_offset,
words=bits_map[block_type].words,
alias=None,
)
if self.alias_tile_type is None:
self.alias_tile_type = self.alias[block_type].tile_type
else:
assert self.alias_tile_type == self.alias[block_type].tile_type
self.sites_rev_map[block_type] = {}
for site, alias_site in self.alias[block_type].sites.items():
assert alias_site not in self.sites_rev_map[block_type]
self.sites_rev_map[block_type][alias_site] = site
self.tile_segbits = db.get_tile_segbits(self.alias_tile_type)
def map_feature_to_segbits(self, feature):
""" Map from the output feature name to the aliased feature name. """
parts = feature.split('.')
assert parts[0] == self.tile_type
parts[0] = self.alias_tile_type
for block_type in self.alias:
if len(parts) > 1 and parts[1] in self.alias[block_type].sites:
parts[1] = self.alias[block_type].sites[parts[1]]
return '.'.join(parts)
def map_feature_from_segbits(self, feature):
""" Map from the aliases feature name to the output feature name. """
parts = feature.split('.')
assert parts[0] == self.alias_tile_type
parts[0] = self.tile_type
for block_type in self.alias:
if len(parts) > 1 and parts[1] in self.sites_rev_map[block_type]:
parts[1] = self.sites_rev_map[block_type][parts[1]]
return '.'.join(parts)
def match_filter(self, block_type, query_bit):
word = query_bit.word_bit // bitstream.WORD_SIZE_BITS
real_word = word - self.alias[block_type].start_offset
if real_word < 0 or real_word >= self.bits_map[block_type].words:
return False
return True
def match_bitdata(self, block_type, bits, bitdata):
alias_bits = self.alias_bits_map[block_type]
for bits_found, alias_feature in self.tile_segbits.match_bitdata(
block_type, alias_bits, bitdata,
match_filter=self.match_filter):
feature = self.map_feature_from_segbits(alias_feature)
yield (bits_found, feature)
def feature_to_bits(self, bits_map, feature, address=0):
alias_feature = self.map_feature_to_segbits(feature)
for block_type, bit in self.tile_segbits.feature_to_bits(
self.alias_bits_map, alias_feature, address):
yield block_type, bit

View File

@ -35,24 +35,9 @@ def bits_to_fasm(db_root, bits_file, verbose, canonical):
with open(bits_file) as f:
bitdata = bitstream.load_bitdata(f)
def is_zero_feature(feature):
parts = feature.split('.')
tile = parts[0]
gridinfo = grid.gridinfo_at_tilename(tile)
feature = '.'.join(parts[1:])
db_k = '%s.%s' % (gridinfo.tile_type, feature)
segbits = db.get_tile_segbits(gridinfo.tile_type)
any_bits = False
for block_type, bit in segbits.feature_to_bits(db_k):
if bit.isset:
any_bits = True
return not any_bits
model = fasm.output.merge_and_sort(
disassembler.find_features_in_bitstream(bitdata, verbose=verbose),
zero_function=is_zero_feature,
zero_function=disassembler.is_zero_feature,
sort_key=grid.tile_key,
)