
When multiplying 36 bits by 36 bits using four 18x18 multipliers, the sign bits of the higher 18-bit parts of the multipliers were correctly switched, but what was incorrect was leaving the sign bits of the lower parts of the multipliers uninitialized. They now connect to VSS. Addresses https://github.com/YosysHQ/apicula/issues/242 Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
1064 lines
42 KiB
Python
1064 lines
42 KiB
Python
from os import path
|
|
import sys
|
|
|
|
import importlib.resources
|
|
import pickle
|
|
import gzip
|
|
import re
|
|
import argparse
|
|
|
|
sys.path.append(path.join(path.dirname(__file__), "../.."))
|
|
from himbaechel_dbgen.chip import *
|
|
from apycula import chipdb
|
|
|
|
# Bel flags
|
|
BEL_FLAG_SIMPLE_IO = 0x100
|
|
|
|
# Chip flags
|
|
CHIP_HAS_SP32 = 0x1
|
|
|
|
# Z of the bels
|
|
# sync with C++ part!
|
|
LUT0_Z = 0 # z(DFFx) = z(LUTx) + 1
|
|
LUT7_Z = 14
|
|
MUX20_Z = 16
|
|
MUX21_Z = 18
|
|
MUX23_Z = 22
|
|
MUX27_Z = 29
|
|
ALU0_Z = 30 # : 35, 6 ALUs
|
|
RAMW_Z = 36 # RAM16SDP4
|
|
|
|
IOBA_Z = 50
|
|
IOBB_Z = 51
|
|
|
|
IOLOGICA_Z = 70
|
|
IDES16_Z = 74
|
|
OSER16_Z = 75
|
|
|
|
BUFG_Z = 76 # : 81 reserve just in case
|
|
BSRAM_Z = 100
|
|
|
|
OSC_Z = 274
|
|
PLL_Z = 275
|
|
GSR_Z = 276
|
|
VCC_Z = 277
|
|
GND_Z = 278
|
|
|
|
DSP_Z = 509
|
|
|
|
DSP_0_Z = 511 # DSP macro 0
|
|
PADD18_0_0_Z = 512
|
|
PADD9_0_0_Z = 512 + 1
|
|
PADD9_0_1_Z = 512 + 2
|
|
PADD18_0_1_Z = 516
|
|
PADD9_0_2_Z = 516 + 1
|
|
PADD9_0_3_Z = 516 + 2
|
|
|
|
MULT18X18_0_0_Z = 520
|
|
MULT9X9_0_0_Z = 520 + 1
|
|
MULT9X9_0_1_Z = 520 + 2
|
|
MULT18X18_0_1_Z = 524
|
|
MULT9X9_0_2_Z = 524 + 1
|
|
MULT9X9_0_3_Z = 524 + 2
|
|
|
|
ALU54D_0_Z = 524 + 3
|
|
MULTALU18X18_0_Z = 528
|
|
MULTALU36X18_0_Z = 528 + 1
|
|
MULTADDALU18X18_0_Z = 528 + 2
|
|
|
|
MULT36X36_Z = 528 + 3
|
|
|
|
DSP_1_Z = 543 # DSP macro 1
|
|
PADD18_1_0_Z = 544
|
|
PADD9_1_0_Z = 544 + 1
|
|
PADD9_1_1_Z = 544 + 2
|
|
PADD18_1_1_Z = 548
|
|
PADD9_1_2_Z = 548 + 1
|
|
PADD9_1_3_Z = 548 + 2
|
|
|
|
MULT18X18_1_0_Z = 552
|
|
MULT9X9_1_0_Z = 552 + 1
|
|
MULT9X9_1_1_Z = 552 + 2
|
|
MULT18X18_1_1_Z = 556
|
|
MULT9X9_1_2_Z = 556 + 1
|
|
MULT9X9_1_3_Z = 556 + 2
|
|
|
|
ALU54D_1_Z = 556 + 3
|
|
MULTALU18X18_1_Z = 560
|
|
MULTALU36X18_1_Z = 560 + 1
|
|
MULTADDALU18X18_1_Z = 560 + 2
|
|
|
|
# =======================================
|
|
# Chipdb additional info
|
|
# =======================================
|
|
@dataclass
|
|
class TileExtraData(BBAStruct):
|
|
tile_class: IdString # The general functionality of the slightly different tiles,
|
|
# let's say the behavior of LUT+DFF in the tiles are completely identical,
|
|
# but one of them also contains clock-wire switches,
|
|
# then we assign them to the same LOGIC class.
|
|
io16_x_off: int = 0 # OSER16/IDES16 offsets to the aux cell
|
|
io16_y_off: int = 0
|
|
|
|
def serialise_lists(self, context: str, bba: BBAWriter):
|
|
pass
|
|
def serialise(self, context: str, bba: BBAWriter):
|
|
bba.u32(self.tile_class.index)
|
|
bba.u16(self.io16_x_off)
|
|
bba.u16(self.io16_y_off)
|
|
|
|
@dataclass
|
|
class BottomIOCnd(BBAStruct):
|
|
wire_a_net: IdString
|
|
wire_b_net: IdString
|
|
|
|
def serialise_lists(self, context: str, bba: BBAWriter):
|
|
pass
|
|
def serialise(self, context: str, bba: BBAWriter):
|
|
bba.u32(self.wire_a_net.index)
|
|
bba.u32(self.wire_b_net.index)
|
|
|
|
@dataclass
|
|
class BottomIO(BBAStruct):
|
|
conditions: list[BottomIOCnd] = field(default_factory = list)
|
|
|
|
def serialise_lists(self, context: str, bba: BBAWriter):
|
|
bba.label(f"{context}_conditions")
|
|
for i, cnd in enumerate(self.conditions):
|
|
cnd.serialise(f"{context}_cnd{i}", bba)
|
|
|
|
def serialise(self, context: str, bba: BBAWriter):
|
|
bba.slice(f"{context}_conditions", len(self.conditions))
|
|
|
|
@dataclass
|
|
class ChipExtraData(BBAStruct):
|
|
strs: StringPool
|
|
flags: int
|
|
bottom_io: BottomIO
|
|
diff_io_types: list[IdString] = field(default_factory = list)
|
|
|
|
def create_bottom_io(self):
|
|
self.bottom_io = BottomIO()
|
|
|
|
def add_bottom_io_cnd(self, net_a: str, net_b: str):
|
|
self.bottom_io.conditions.append(BottomIOCnd(self.strs.id(net_a), self.strs.id(net_b)))
|
|
|
|
def add_diff_io_type(self, diff_type: str):
|
|
self.diff_io_types.append(self.strs.id(diff_type))
|
|
|
|
def serialise_lists(self, context: str, bba: BBAWriter):
|
|
self.bottom_io.serialise_lists(f"{context}_bottom_io", bba)
|
|
bba.label(f"{context}_diff_io_types")
|
|
for i, diff_io_type in enumerate(self.diff_io_types):
|
|
bba.u32(diff_io_type.index)
|
|
|
|
def serialise(self, context: str, bba: BBAWriter):
|
|
bba.u32(self.flags)
|
|
self.bottom_io.serialise(f"{context}_bottom_io", bba)
|
|
bba.slice(f"{context}_diff_io_types", len(self.diff_io_types))
|
|
|
|
@dataclass
|
|
class PadExtraData(BBAStruct):
|
|
# Which PLL does this pad belong to.
|
|
pll_tile: IdString
|
|
pll_bel: IdString
|
|
pll_type: IdString
|
|
|
|
def serialise_lists(self, context: str, bba: BBAWriter):
|
|
pass
|
|
def serialise(self, context: str, bba: BBAWriter):
|
|
bba.u32(self.pll_tile.index)
|
|
bba.u32(self.pll_bel.index)
|
|
bba.u32(self.pll_type.index)
|
|
|
|
# Unique features of the tiletype
|
|
class TypeDesc:
|
|
def __init__(self, dups, tiletype = '', extra_func = None, sfx = 0):
|
|
self.tiletype = tiletype
|
|
self.extra_func = extra_func
|
|
self.dups = dups
|
|
self.sfx = sfx
|
|
created_tiletypes = {}
|
|
|
|
# u-turn at the rim
|
|
uturnlut = {'N': 'S', 'S': 'N', 'E': 'W', 'W': 'E'}
|
|
def uturn(db: chipdb, x: int, y: int, wire: str):
|
|
m = re.match(r"([NESW])([128]\d)(\d)", wire)
|
|
if m:
|
|
direction, num, segment = m.groups()
|
|
# wires wrap around the edges
|
|
# assumes 0-based indexes
|
|
if y < 0:
|
|
y = -1 - y
|
|
direction = uturnlut[direction]
|
|
if x < 0:
|
|
x = -1 - x
|
|
direction = uturnlut[direction]
|
|
if y > db.rows - 1:
|
|
y = 2 * db.rows - 1 - y
|
|
direction = uturnlut[direction]
|
|
if x > db.cols - 1:
|
|
x = 2 * db.cols - 1 - x
|
|
direction = uturnlut[direction]
|
|
wire = f'{direction}{num}{segment}'
|
|
return (x, y, wire)
|
|
|
|
def create_nodes(chip: Chip, db: chipdb):
|
|
# : (x, y)
|
|
dirs = { 'N': (0, -1), 'S': (0, 1), 'W': (-1, 0), 'E': (1, 0) }
|
|
X = db.cols
|
|
Y = db.rows
|
|
global_nodes = {}
|
|
for y in range(Y):
|
|
for x in range(X):
|
|
nodes = []
|
|
tt = chip.tile_type_at(x, y)
|
|
extra_tile_data = tt.extra_data
|
|
# SN and EW
|
|
for i in [1, 2]:
|
|
nodes.append([NodeWire(x, y, f'SN{i}0'),
|
|
NodeWire(*uturn(db, x, y - 1, f'N1{i}1')),
|
|
NodeWire(*uturn(db, x, y + 1, f'S1{i}1'))])
|
|
nodes.append([NodeWire(x, y, f'EW{i}0'),
|
|
NodeWire(*uturn(db, x - 1, y, f'W1{i}1')),
|
|
NodeWire(*uturn(db, x + 1, y, f'E1{i}1'))])
|
|
for d, offs in dirs.items():
|
|
# 1-hop
|
|
for i in [0, 3]:
|
|
nodes.append([NodeWire(x, y, f'{d}1{i}0'),
|
|
NodeWire(*uturn(db, x + offs[0], y + offs[1], f'{d}1{i}1'))])
|
|
# 2-hop
|
|
for i in range(8):
|
|
nodes.append([NodeWire(x, y, f'{d}2{i}0'),
|
|
NodeWire(*uturn(db, x + offs[0], y + offs[1], f'{d}2{i}1')),
|
|
NodeWire(*uturn(db, x + offs[0] * 2, y + offs[1] * 2, f'{d}2{i}2'))])
|
|
# 4-hop
|
|
for i in range(4):
|
|
nodes.append([NodeWire(x, y, f'{d}8{i}0'),
|
|
NodeWire(*uturn(db, x + offs[0] * 4, y + offs[1] * 4, f'{d}8{i}4')),
|
|
NodeWire(*uturn(db, x + offs[0] * 8, y + offs[1] * 8, f'{d}8{i}8'))])
|
|
# I0 for MUX2_LUT8
|
|
if (x < X - 1 and extra_tile_data.tile_class == chip.strs.id('LOGIC')
|
|
and chip.tile_type_at(x + 1, y).extra_data.tile_class == chip.strs.id('LOGIC')):
|
|
nodes.append([NodeWire(x, y, 'OF30'),
|
|
NodeWire(x + 1, y, 'OF3')])
|
|
|
|
# ALU
|
|
if extra_tile_data.tile_class == chip.strs.id('LOGIC'):
|
|
# local carry chain
|
|
for i in range(5):
|
|
nodes.append([NodeWire(x, y, f'COUT{i}'),
|
|
NodeWire(x, y, f'CIN{i + 1}')]);
|
|
# gobal carry chain
|
|
if x > 1 and chip.tile_type_at(x - 1, y).extra_data.tile_class == chip.strs.id('LOGIC'):
|
|
nodes.append([NodeWire(x, y, f'CIN0'),
|
|
NodeWire(x - 1, y, f'COUT5')])
|
|
|
|
for node in nodes:
|
|
chip.add_node(node)
|
|
|
|
# VCC and VSS sources in the all tiles
|
|
global_nodes.setdefault('GND', []).append(NodeWire(x, y, 'VSS'))
|
|
global_nodes.setdefault('VCC', []).append(NodeWire(x, y, 'VCC'))
|
|
|
|
# add nodes from the apicula db
|
|
for node_name, node_hdr in db.nodes.items():
|
|
wire_type, node = node_hdr
|
|
if len(node) < 2:
|
|
continue
|
|
for y, x, wire in node:
|
|
if wire_type:
|
|
if not chip.tile_type_at(x, y).has_wire(wire):
|
|
chip.tile_type_at(x, y).create_wire(wire, wire_type)
|
|
else:
|
|
chip.tile_type_at(x, y).set_wire_type(wire, wire_type)
|
|
new_node = NodeWire(x, y, wire)
|
|
gl_nodes = global_nodes.setdefault(node_name, [])
|
|
if new_node not in gl_nodes:
|
|
gl_nodes.append(NodeWire(x, y, wire))
|
|
|
|
for name, node in global_nodes.items():
|
|
chip.add_node(node)
|
|
|
|
def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int):
|
|
def get_wire_type(name):
|
|
if name in {'XD0', 'XD1', 'XD2', 'XD3', 'XD4', 'XD5',}:
|
|
return "X0"
|
|
return ""
|
|
|
|
for dst, srcs in db.grid[y][x].pips.items():
|
|
if not tt.has_wire(dst):
|
|
tt.create_wire(dst, get_wire_type(dst))
|
|
for src in srcs.keys():
|
|
if not tt.has_wire(src):
|
|
tt.create_wire(src, get_wire_type(src))
|
|
tt.create_pip(src, dst)
|
|
|
|
# clock wires
|
|
for dst, srcs in db.grid[y][x].pure_clock_pips.items():
|
|
if not tt.has_wire(dst):
|
|
tt.create_wire(dst, "GLOBAL_CLK")
|
|
for src in srcs.keys():
|
|
if not tt.has_wire(src):
|
|
tt.create_wire(src, "GLOBAL_CLK")
|
|
tt.create_pip(src, dst)
|
|
|
|
def create_hclk_switch_matrix(tt: TileType, db: chipdb, x: int, y: int):
|
|
if (y, x) not in db.hclk_pips:
|
|
return
|
|
# hclk wires
|
|
for dst, srcs in db.hclk_pips[y, x].items():
|
|
if not tt.has_wire(dst):
|
|
tt.create_wire(dst, "HCLK")
|
|
for src in srcs.keys():
|
|
if not tt.has_wire(src):
|
|
tt.create_wire(src, "HCLK")
|
|
tt.create_pip(src, dst)
|
|
|
|
def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int):
|
|
if (y, x) not in db.extra_func:
|
|
return
|
|
for func, desc in db.extra_func[(y, x)].items():
|
|
if func == 'osc':
|
|
osc_type = desc['type']
|
|
portmap = db.grid[y][x].bels[osc_type].portmap
|
|
for port, wire in portmap.items():
|
|
tt.create_wire(wire, port)
|
|
bel = tt.create_bel(osc_type, osc_type, z = OSC_Z)
|
|
for port, wire in portmap.items():
|
|
if 'OUT' in port:
|
|
tt.add_bel_pin(bel, port, wire, PinType.OUTPUT)
|
|
else:
|
|
tt.add_bel_pin(bel, port, wire, PinType.INPUT)
|
|
elif func == 'gsr':
|
|
wire = desc['wire']
|
|
tt.create_wire(wire, "GSRI")
|
|
bel = tt.create_bel("GSR", "GSR", z = GSR_Z)
|
|
tt.add_bel_pin(bel, "GSRI", wire, PinType.INPUT)
|
|
if func == 'io16':
|
|
role = desc['role']
|
|
if role == 'MAIN':
|
|
y_off, x_off = desc['pair']
|
|
tt.extra_data.io16_x_off = x_off
|
|
tt.extra_data.io16_y_off = y_off
|
|
|
|
for io_type, z in {('IDES16', IDES16_Z), ('OSER16', OSER16_Z)}:
|
|
bel = tt.create_bel(io_type, io_type, z = z)
|
|
portmap = db.grid[y][x].bels[io_type].portmap
|
|
for port, wire in portmap.items():
|
|
if port == 'FCLK': # XXX compatibility
|
|
wire = 'FCLKA'
|
|
if not tt.has_wire(wire):
|
|
if port in {'CLK', 'PCLK'}:
|
|
tt.create_wire(wire, "TILE_CLK")
|
|
else:
|
|
tt.create_wire(wire, "IOL_PORT")
|
|
if 'OUT' in port:
|
|
tt.add_bel_pin(bel, port, wire, PinType.OUTPUT)
|
|
else:
|
|
tt.add_bel_pin(bel, port, wire, PinType.INPUT)
|
|
if func == 'buf':
|
|
for buf_type, wires in desc.items():
|
|
for i, wire in enumerate(wires):
|
|
if not tt.has_wire(wire):
|
|
tt.create_wire(wire, "TILE_CLK")
|
|
wire_out = f'{buf_type}{i}_O'
|
|
tt.create_wire(wire_out, "TILE_CLK")
|
|
# XXX make Z from buf_type
|
|
bel = tt.create_bel(f'{buf_type}{i}', buf_type, z = BUFG_Z + i)
|
|
bel.flags = BEL_FLAG_GLOBAL
|
|
tt.add_bel_pin(bel, "I", wire, PinType.INPUT)
|
|
tt.add_bel_pin(bel, "O", wire_out, PinType.OUTPUT)
|
|
|
|
def create_tiletype(create_func, chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
|
has_extra_func = (y, x) in db.extra_func
|
|
|
|
# (found, TypeDesc)
|
|
def find_or_make_dup():
|
|
for d in created_tiletypes[ttyp].dups:
|
|
if has_extra_func and d.extra_func == db.extra_func[(y, x)]:
|
|
return (True, d)
|
|
elif not has_extra_func and not d.extra_func:
|
|
return (True, d)
|
|
sfx = len(created_tiletypes[ttyp].dups) + 1
|
|
if has_extra_func:
|
|
tdesc = TypeDesc(extra_func = db.extra_func[(y, x)], sfx = sfx, dups = [])
|
|
else:
|
|
tdesc = TypeDesc(sfx = sfx, dups = [])
|
|
created_tiletypes[ttyp].dups.append(tdesc)
|
|
return (False, tdesc)
|
|
|
|
old_type = False
|
|
if ttyp not in created_tiletypes:
|
|
# new type
|
|
if has_extra_func:
|
|
tdesc = TypeDesc(extra_func = db.extra_func[(y, x)], dups = [])
|
|
else:
|
|
tdesc = TypeDesc(dups = [])
|
|
created_tiletypes.update({ttyp: tdesc})
|
|
else:
|
|
# find similar
|
|
if has_extra_func:
|
|
if created_tiletypes[ttyp].extra_func == db.extra_func[(y, x)]:
|
|
tdesc = created_tiletypes[ttyp]
|
|
old_type = True
|
|
else:
|
|
old_type, tdesc = find_or_make_dup()
|
|
elif not created_tiletypes[ttyp].extra_func:
|
|
tdesc = created_tiletypes[ttyp]
|
|
old_type = True
|
|
else:
|
|
old_type, tdesc = find_or_make_dup()
|
|
|
|
if old_type:
|
|
chip.set_tile_type(x, y, tdesc.tiletype)
|
|
return
|
|
|
|
tt = create_func(chip, db, x, y, ttyp, tdesc)
|
|
|
|
create_extra_funcs(tt, db, x, y)
|
|
create_hclk_switch_matrix(tt, db, x, y)
|
|
create_switch_matrix(tt, db, x, y)
|
|
chip.set_tile_type(x, y, tdesc.tiletype)
|
|
|
|
def add_port_wire(tt, bel, portmap, name, wire_type, port_type, pin_name = None):
|
|
wire = portmap[name]
|
|
if not tt.has_wire(wire):
|
|
if name.startswith('CLK'):
|
|
tt.create_wire(wire, "TILE_CLK")
|
|
else:
|
|
tt.create_wire(wire, wire_type)
|
|
if pin_name:
|
|
tt.add_bel_pin(bel, pin_name, wire, port_type)
|
|
else:
|
|
tt.add_bel_pin(bel, name, wire, port_type)
|
|
|
|
def create_null_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc: TypeDesc):
|
|
typename = "NULL"
|
|
tiletype = f"{typename}_{ttyp}"
|
|
if tdesc.sfx != 0:
|
|
tiletype += f"_{tdesc.sfx}"
|
|
tt = chip.create_tile_type(tiletype)
|
|
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
|
tdesc.tiletype = tiletype
|
|
return tt
|
|
|
|
# responsible nodes, there will be IO banks, configuration, etc.
|
|
def create_corner_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc: TypeDesc):
|
|
typename = "CORNER"
|
|
tiletype = f"{typename}_{ttyp}"
|
|
if tdesc.sfx != 0:
|
|
tiletype += f"_{tdesc.sfx}"
|
|
tt = chip.create_tile_type(tiletype)
|
|
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
|
|
|
if x == 0 and y == 0:
|
|
# GND is the logic low level generator
|
|
tt.create_wire('VSS', 'GND')
|
|
gnd = tt.create_bel('GND', 'GND', z = GND_Z)
|
|
tt.add_bel_pin(gnd, "G", "VSS", PinType.OUTPUT)
|
|
# VCC is the logic high level generator
|
|
tt.create_wire('VCC', 'VCC')
|
|
gnd = tt.create_bel('VCC', 'VCC', z = VCC_Z)
|
|
tt.add_bel_pin(gnd, "V", "VCC", PinType.OUTPUT)
|
|
|
|
tdesc.tiletype = tiletype
|
|
return tt
|
|
|
|
|
|
# IO
|
|
def create_io_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc: TypeDesc):
|
|
typename = "IO"
|
|
tiletype = f"{typename}_{ttyp}"
|
|
if tdesc.sfx != 0:
|
|
tiletype += f"_{tdesc.sfx}"
|
|
tt = chip.create_tile_type(tiletype)
|
|
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
|
|
|
simple_io = y in db.simplio_rows and chip.name in {'GW1N-1', 'GW1NZ-1', 'GW1N-4'}
|
|
if simple_io:
|
|
rng = 10
|
|
else:
|
|
rng = 2
|
|
for i in range(rng):
|
|
name = 'IOB' + 'ABCDEFGHIJ'[i]
|
|
# XXX some IOBs excluded from generic chipdb for some reason
|
|
if name not in db.grid[y][x].bels:
|
|
continue
|
|
# wires
|
|
portmap = db.grid[y][x].bels[name].portmap
|
|
tt.create_wire(portmap['I'], "IO_I")
|
|
tt.create_wire(portmap['O'], "IO_O")
|
|
tt.create_wire(portmap['OE'], "IO_OE")
|
|
# bels
|
|
io = tt.create_bel(name, "IOB", z = IOBA_Z + i)
|
|
if simple_io and chip.name in {'GW1N-1'}:
|
|
io.flags |= BEL_FLAG_SIMPLE_IO
|
|
tt.add_bel_pin(io, "I", portmap['I'], PinType.INPUT)
|
|
tt.add_bel_pin(io, "OEN", portmap['OE'], PinType.INPUT)
|
|
tt.add_bel_pin(io, "O", portmap['O'], PinType.OUTPUT)
|
|
# bottom io
|
|
if 'BOTTOM_IO_PORT_A' in portmap:
|
|
if not tt.has_wire(portmap['BOTTOM_IO_PORT_A']):
|
|
tt.create_wire(portmap['BOTTOM_IO_PORT_A'], "IO_I")
|
|
tt.create_wire(portmap['BOTTOM_IO_PORT_B'], "IO_I")
|
|
tt.add_bel_pin(io, "BOTTOM_IO_PORT_A", portmap['BOTTOM_IO_PORT_A'], PinType.INPUT)
|
|
tt.add_bel_pin(io, "BOTTOM_IO_PORT_B", portmap['BOTTOM_IO_PORT_B'], PinType.INPUT)
|
|
# create IOLOGIC bels if any
|
|
for idx, name in {(IOLOGICA_Z, 'IOLOGICA'), (IOLOGICA_Z + 1, 'IOLOGICB')}:
|
|
if name not in db.grid[y][x].bels:
|
|
continue
|
|
for off, io_type in {(0, 'O'), (2, 'I')}:
|
|
iol = tt.create_bel(f"{name}{io_type}", f"IOLOGIC{io_type}", z = idx + off)
|
|
for port, wire in db.grid[y][x].bels[name].portmap.items():
|
|
if port == 'FCLK': # XXX compatibility
|
|
wire = f'FCLK{name[-1]}'
|
|
if not tt.has_wire(wire):
|
|
if port in {'CLK', 'PCLK'}:
|
|
tt.create_wire(wire, "TILE_CLK")
|
|
else:
|
|
tt.create_wire(wire, "IOL_PORT")
|
|
if port in {'Q', 'Q0', 'Q1', 'Q2', 'Q3', 'Q4', 'Q5', 'Q6', 'Q7', 'Q8', 'Q9', 'DF', 'LAG', 'LEAD'}:
|
|
tt.add_bel_pin(iol, port, wire, PinType.OUTPUT)
|
|
else:
|
|
tt.add_bel_pin(iol, port, wire, PinType.INPUT)
|
|
tdesc.tiletype = tiletype
|
|
return tt
|
|
|
|
# logic: luts, dffs, alu etc
|
|
def create_logic_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc: TypeDesc):
|
|
typename = "LOGIC"
|
|
tiletype = f"{typename}_{ttyp}"
|
|
if tdesc.sfx != 0:
|
|
tiletype += f"_{tdesc.sfx}"
|
|
tt = chip.create_tile_type(tiletype)
|
|
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
|
|
|
lut_inputs = ['A', 'B', 'C', 'D']
|
|
# setup LUT wires
|
|
for i in range(8):
|
|
for inp_name in lut_inputs:
|
|
tt.create_wire(f"{inp_name}{i}", "LUT_INPUT")
|
|
tt.create_wire(f"F{i}", "LUT_OUT")
|
|
# experimental. the wire is false - it is assumed that DFF is always
|
|
# connected to the LUT's output F{i}, but we can place primitives
|
|
# arbitrarily and create a pass-through LUT afterwards.
|
|
# just out of curiosity
|
|
tt.create_wire(f"XD{i}", "FF_INPUT")
|
|
tt.create_wire(f"Q{i}", "FF_OUT")
|
|
# setup DFF wires
|
|
for j in range(3):
|
|
tt.create_wire(f"CLK{j}", "TILE_CLK")
|
|
tt.create_wire(f"LSR{j}", "TILE_LSR")
|
|
tt.create_wire(f"CE{j}", "TILE_CE")
|
|
# setup MUX2 wires
|
|
for j in range(8):
|
|
tt.create_wire(f"OF{j}", "MUX_OUT")
|
|
tt.create_wire(f"SEL{j}", "MUX_SEL")
|
|
tt.create_wire("OF30", "MUX_OUT")
|
|
# setup ALU wires
|
|
for j in range(6):
|
|
tt.create_wire(f"CIN{j}", "ALU_CIN")
|
|
tt.create_wire(f"COUT{j}", "ALU_COUT")
|
|
|
|
# create logic cells
|
|
for i in range(8):
|
|
# LUT
|
|
lut = tt.create_bel(f"LUT{i}", "LUT4", z = (i * 2 + 0))
|
|
for j, inp_name in enumerate(lut_inputs):
|
|
tt.add_bel_pin(lut, f"I{j}", f"{inp_name}{i}", PinType.INPUT)
|
|
tt.add_bel_pin(lut, "F", f"F{i}", PinType.OUTPUT)
|
|
if i < 6:
|
|
# FF data can come from LUT output, but we pretend that we can use
|
|
# any LUT input
|
|
tt.create_pip(f"F{i}", f"XD{i}")
|
|
for inp_name in lut_inputs:
|
|
tt.create_pip(f"{inp_name}{i}", f"XD{i}")
|
|
# FF
|
|
ff = tt.create_bel(f"DFF{i}", "DFF", z =(i * 2 + 1))
|
|
tt.add_bel_pin(ff, "D", f"XD{i}", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "CLK", f"CLK{i // 2}", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "Q", f"Q{i}", PinType.OUTPUT)
|
|
tt.add_bel_pin(ff, "SET", f"LSR{i // 2}", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "RESET", f"LSR{i // 2}", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "PRESET", f"LSR{i // 2}", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "CLEAR", f"LSR{i // 2}", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "CE", f"CE{i // 2}", PinType.INPUT)
|
|
|
|
# ALU
|
|
ff = tt.create_bel(f"ALU{i}", "ALU", z = i + ALU0_Z)
|
|
tt.add_bel_pin(ff, "SUM", f"F{i}", PinType.OUTPUT)
|
|
tt.add_bel_pin(ff, "COUT", f"COUT{i}", PinType.OUTPUT)
|
|
tt.add_bel_pin(ff, "CIN", f"CIN{i}", PinType.INPUT)
|
|
# pinout for the ADDSUB ALU mode
|
|
tt.add_bel_pin(ff, "I0", f"A{i}", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "I1", f"B{i}", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "I2", f"C{i}", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "I3", f"D{i}", PinType.INPUT)
|
|
|
|
# wide luts
|
|
for i in range(4):
|
|
ff = tt.create_bel(f"MUX{i * 2}", "MUX2_LUT5", z = MUX20_Z + i * 4)
|
|
tt.add_bel_pin(ff, "I0", f"F{i * 2}", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "I1", f"F{i * 2 + 1}", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "O", f"OF{i * 2}", PinType.OUTPUT)
|
|
tt.add_bel_pin(ff, "S0", f"SEL{i * 2}", PinType.INPUT)
|
|
for i in range(2):
|
|
ff = tt.create_bel(f"MUX{i * 4 + 1}", "MUX2_LUT6", z = MUX21_Z + i * 8)
|
|
tt.add_bel_pin(ff, "I0", f"OF{i * 4 + 2}", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "I1", f"OF{i * 4}", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "O", f"OF{i * 4 + 1}", PinType.OUTPUT)
|
|
tt.add_bel_pin(ff, "S0", f"SEL{i * 4 + 1}", PinType.INPUT)
|
|
ff = tt.create_bel(f"MUX3", "MUX2_LUT7", z = MUX23_Z)
|
|
tt.add_bel_pin(ff, "I0", f"OF5", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "I1", f"OF1", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "O", f"OF3", PinType.OUTPUT)
|
|
tt.add_bel_pin(ff, "S0", f"SEL3", PinType.INPUT)
|
|
ff = tt.create_bel(f"MUX7", "MUX2_LUT8", z = MUX27_Z)
|
|
tt.add_bel_pin(ff, "I0", f"OF30", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "I1", f"OF3", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "O", f"OF7", PinType.OUTPUT)
|
|
tt.add_bel_pin(ff, "S0", f"SEL7", PinType.INPUT)
|
|
|
|
tdesc.tiletype = tiletype
|
|
return tt
|
|
|
|
def create_ssram_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc: TypeDesc):
|
|
# SSRAM is LUT based, so it's logic-like
|
|
tt = create_logic_tiletype(chip, db, x, y, ttyp, tdesc)
|
|
|
|
lut_inputs = ['A', 'B', 'C', 'D']
|
|
ff = tt.create_bel(f"RAM16SDP4", "RAM16SDP4", z = RAMW_Z)
|
|
for i in range(4):
|
|
tt.add_bel_pin(ff, f"DI[{i}]", f"{lut_inputs[i]}5", PinType.INPUT)
|
|
tt.add_bel_pin(ff, f"WAD[{i}]", f"{lut_inputs[i]}4", PinType.INPUT)
|
|
# RAD[0] is assumed to be connected to A3, A2, A1 and A0. But
|
|
# for now we connect it only to A0, the others will be connected
|
|
# directly during packing. RAD[1...3] - similarly.
|
|
tt.add_bel_pin(ff, f"RAD[{i}]", f"{lut_inputs[i]}0", PinType.INPUT)
|
|
tt.add_bel_pin(ff, f"DO[{i}]", f"F{i}", PinType.OUTPUT)
|
|
|
|
tt.add_bel_pin(ff, "CLK", "CLK2", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "CE", "CE2", PinType.INPUT)
|
|
tt.add_bel_pin(ff, "WRE", "LSR2", PinType.INPUT)
|
|
return tt
|
|
|
|
# BSRAM
|
|
_bsram_inputs = {'CLK', 'OCE', 'CE', 'RESET', 'WRE'}
|
|
def create_bsram_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc: TypeDesc):
|
|
typename = "BSRAM"
|
|
tiletype = f"{typename}_{ttyp}"
|
|
if tdesc.sfx != 0:
|
|
tiletype += f"_{tdesc.sfx}"
|
|
tt = chip.create_tile_type(tiletype)
|
|
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
|
|
|
portmap = db.grid[y][x].bels['BSRAM'].portmap
|
|
bsram = tt.create_bel("BSRAM", "BSRAM", z = BSRAM_Z)
|
|
|
|
|
|
for sfx in {'', 'A', 'B'}:
|
|
for inp in _bsram_inputs:
|
|
add_port_wire(tt, bsram, portmap, f"{inp}{sfx}", "BSRAM_I", PinType.INPUT)
|
|
for idx in range(3):
|
|
add_port_wire(tt, bsram, portmap, f"BLKSEL{sfx}{idx}", "BSRAM_I", PinType.INPUT)
|
|
for idx in range(14):
|
|
add_port_wire(tt, bsram, portmap, f"AD{sfx}{idx}", "BSRAM_I", PinType.INPUT)
|
|
for idx in range(18):
|
|
add_port_wire(tt, bsram, portmap, f"DI{sfx}{idx}", "BSRAM_I", PinType.INPUT)
|
|
add_port_wire(tt, bsram, portmap, f"DO{sfx}{idx}", "BSRAM_O", PinType.OUTPUT)
|
|
if not sfx:
|
|
for idx in range(18, 36):
|
|
add_port_wire(tt, bsram, portmap, f"DI{idx}", "BSRAM_I", PinType.INPUT)
|
|
add_port_wire(tt, bsram, portmap, f"DO{idx}", "BSRAM_O", PinType.OUTPUT)
|
|
|
|
tdesc.tiletype = tiletype
|
|
return tt
|
|
|
|
# DSP
|
|
_mult_inputs = {'ASEL', 'BSEL', 'ASIGN', 'BSIGN'}
|
|
def create_dsp_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc: TypeDesc):
|
|
typename = "DSP"
|
|
tiletype = f"{typename}_{ttyp}"
|
|
if tdesc.sfx != 0:
|
|
tiletype += f"_{tdesc.sfx}"
|
|
tt = chip.create_tile_type(tiletype)
|
|
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
|
|
|
# create big DSP
|
|
belname = f'DSP'
|
|
portmap = db.grid[y][x].bels[belname].portmap
|
|
dsp = tt.create_bel(belname, "DSP", DSP_Z)
|
|
dsp.flags = BEL_FLAG_HIDDEN
|
|
|
|
# create DSP macros
|
|
for idx in range(2):
|
|
belname = f'DSP{idx}'
|
|
portmap = db.grid[y][x].bels[belname].portmap
|
|
dsp = tt.create_bel(belname, "DSP", eval(f'DSP_{idx}_Z'))
|
|
dsp.flags = BEL_FLAG_HIDDEN
|
|
|
|
# create pre-adders
|
|
for mac, idx in [(mac, idx) for mac in range(2) for idx in range(4)]:
|
|
belname = f'PADD9{mac}{idx}'
|
|
portmap = db.grid[y][x].bels[belname].portmap
|
|
dsp = tt.create_bel(belname, "PADD9", eval(f'PADD9_{mac}_{idx}_Z'))
|
|
|
|
add_port_wire(tt, dsp, portmap, "ADDSUB", "DSP_I", PinType.INPUT)
|
|
for sfx in {'A', 'B'}:
|
|
for inp in range(9):
|
|
add_port_wire(tt, dsp, portmap, f"{sfx}{inp}", "DSP_I", PinType.INPUT)
|
|
for inp in range(9):
|
|
add_port_wire(tt, dsp, portmap, f"C{inp}", "DSP_I", PinType.INPUT)
|
|
for inp in range(4):
|
|
add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, "ASEL", "DSP_I", PinType.INPUT)
|
|
for outp in range(9):
|
|
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
|
|
|
for mac, idx in [(mac, idx) for mac in range(2) for idx in range(2)]:
|
|
belname = f'PADD18{mac}{idx}'
|
|
portmap = db.grid[y][x].bels[belname].portmap
|
|
dsp = tt.create_bel(belname, "PADD18", eval(f'PADD18_{mac}_{idx}_Z'))
|
|
|
|
add_port_wire(tt, dsp, portmap, "ADDSUB", "DSP_I", PinType.INPUT)
|
|
for sfx in {'A', 'B'}:
|
|
for inp in range(18):
|
|
add_port_wire(tt, dsp, portmap, f"{sfx}{inp}", "DSP_I", PinType.INPUT)
|
|
for inp in range(18):
|
|
add_port_wire(tt, dsp, portmap, f"C{inp}", "DSP_I", PinType.INPUT)
|
|
for inp in range(4):
|
|
add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, "ASEL", "DSP_I", PinType.INPUT)
|
|
for outp in range(18):
|
|
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
|
|
|
# create multipliers
|
|
# mult 9x9
|
|
for mac, idx in [(mac, idx) for mac in range(2) for idx in range(4)]:
|
|
belname = f'MULT9X9{mac}{idx}'
|
|
portmap = db.grid[y][x].bels[belname].portmap
|
|
dsp = tt.create_bel(belname, "MULT9X9", eval(f'MULT9X9_{mac}_{idx}_Z'))
|
|
|
|
for sfx in {'A', 'B'}:
|
|
for inp in range(9):
|
|
add_port_wire(tt, dsp, portmap, f"{sfx}{inp}", "DSP_I", PinType.INPUT)
|
|
for inp in _mult_inputs:
|
|
add_port_wire(tt, dsp, portmap, inp, "DSP_I", PinType.INPUT)
|
|
for inp in range(4):
|
|
add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT)
|
|
for outp in range(18):
|
|
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
|
|
|
# mult 18x18
|
|
for mac, idx in [(mac, idx) for mac in range(2) for idx in range(2)]:
|
|
belname = f'MULT18X18{mac}{idx}'
|
|
portmap = db.grid[y][x].bels[belname].portmap
|
|
dsp = tt.create_bel(belname, "MULT18X18", eval(f'MULT18X18_{mac}_{idx}_Z'))
|
|
|
|
for sfx in {'A', 'B'}:
|
|
for inp in range(18):
|
|
add_port_wire(tt, dsp, portmap, f"{sfx}{inp}", "DSP_I", PinType.INPUT)
|
|
for inp in _mult_inputs:
|
|
add_port_wire(tt, dsp, portmap, inp, "DSP_I", PinType.INPUT)
|
|
for inp in range(4):
|
|
add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT)
|
|
for outp in range(36):
|
|
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
|
|
|
# mult 36x36
|
|
belname = 'MULT36X36'
|
|
portmap = db.grid[y][x].bels[belname].portmap
|
|
dsp = tt.create_bel(belname, "MULT36X36", MULT36X36_Z)
|
|
|
|
# LSB 18x18 multipliers sign ports must be zero
|
|
add_port_wire(tt, dsp, db.grid[y][x].bels['MULT18X1800'].portmap, 'ASIGN', "DSP_I", PinType.INPUT, 'ZERO_ASIGN0')
|
|
add_port_wire(tt, dsp, db.grid[y][x].bels['MULT18X1800'].portmap, 'BSIGN', "DSP_I", PinType.INPUT, 'ZERO_BSIGN0')
|
|
add_port_wire(tt, dsp, db.grid[y][x].bels['MULT18X1801'].portmap, 'BSIGN', "DSP_I", PinType.INPUT, 'ZERO_BSIGN1')
|
|
add_port_wire(tt, dsp, db.grid[y][x].bels['MULT18X1810'].portmap, 'ASIGN', "DSP_I", PinType.INPUT, 'ZERO_ASIGN1')
|
|
for i in range(2):
|
|
for sfx in {'A', 'B'}:
|
|
for inp in range(36):
|
|
add_port_wire(tt, dsp, portmap, f"{sfx}{inp}{i}", "DSP_I", PinType.INPUT)
|
|
for inp in {'ASIGN', 'BSIGN'}:
|
|
add_port_wire(tt, dsp, portmap, f"{inp}{i}", "DSP_I", PinType.INPUT)
|
|
for inp in range(4):
|
|
add_port_wire(tt, dsp, portmap, f"CE{inp}{i}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"CLK{inp}{i}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"RESET{inp}{i}", "DSP_I", PinType.INPUT)
|
|
for outp in range(72):
|
|
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
|
|
|
# create alus
|
|
for mac in range(2):
|
|
belname = f'ALU54D{mac}'
|
|
portmap = db.grid[y][x].bels[belname].portmap
|
|
dsp = tt.create_bel(belname, "ALU54D", eval(f'ALU54D_{mac}_Z'))
|
|
|
|
for sfx in {'A', 'B'}:
|
|
for inp in range(54):
|
|
add_port_wire(tt, dsp, portmap, f"{sfx}{inp}", "DSP_I", PinType.INPUT)
|
|
for inp in {'ASIGN', 'BSIGN'}:
|
|
add_port_wire(tt, dsp, portmap, inp, "DSP_I", PinType.INPUT)
|
|
for inp in range(4):
|
|
add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT)
|
|
if inp < 2:
|
|
add_port_wire(tt, dsp, portmap, f"ACCLOAD{inp}", "DSP_I", PinType.INPUT)
|
|
for outp in range(54):
|
|
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
|
|
|
# create multalus
|
|
# MULTALU18X18
|
|
for mac in range(2):
|
|
belname = f'MULTALU18X18{mac}'
|
|
portmap = db.grid[y][x].bels[belname].portmap
|
|
dsp = tt.create_bel(belname, "MULTALU18X18", eval(f'MULTALU18X18_{mac}_Z'))
|
|
|
|
for i in range(2):
|
|
for sfx in {'ASIGN', 'BSIGN'}:
|
|
add_port_wire(tt, dsp, portmap, f"{sfx}{i}", "DSP_I", PinType.INPUT)
|
|
for sfx in {'A', 'B'}:
|
|
for inp in range(18):
|
|
add_port_wire(tt, dsp, portmap, f"{sfx}{inp}{i}", "DSP_I", PinType.INPUT)
|
|
for sfx in {'C', 'D'}:
|
|
for inp in range(54):
|
|
add_port_wire(tt, dsp, portmap, f"{sfx}{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, "DSIGN", "DSP_I", PinType.INPUT)
|
|
for inp in range(4):
|
|
add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT)
|
|
if inp < 2:
|
|
add_port_wire(tt, dsp, portmap, f"ACCLOAD{inp}", "DSP_I", PinType.INPUT)
|
|
for outp in range(54):
|
|
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
|
|
|
# MULTALU36X18
|
|
for mac in range(2):
|
|
belname = f'MULTALU36X18{mac}'
|
|
portmap = db.grid[y][x].bels[belname].portmap
|
|
dsp = tt.create_bel(belname, "MULTALU36X18", eval(f'MULTALU36X18_{mac}_Z'))
|
|
|
|
for i in range(2):
|
|
for sfx in {'ASIGN', 'BSIGN'}:
|
|
add_port_wire(tt, dsp, portmap, f"{sfx}{i}", "DSP_I", PinType.INPUT)
|
|
for inp in range(18):
|
|
add_port_wire(tt, dsp, portmap, f"A{inp}{i}", "DSP_I", PinType.INPUT)
|
|
for inp in range(7):
|
|
add_port_wire(tt, dsp, portmap, f"ALUSEL{inp}", "DSP_I", PinType.INPUT)
|
|
for inp in range(36):
|
|
add_port_wire(tt, dsp, portmap, f"B{inp}", "DSP_I", PinType.INPUT)
|
|
for inp in range(54):
|
|
add_port_wire(tt, dsp, portmap, f"C{inp}", "DSP_I", PinType.INPUT)
|
|
for inp in range(4):
|
|
add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT)
|
|
for outp in range(54):
|
|
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
|
|
|
# MULTADDALU18X18
|
|
for mac in range(2):
|
|
belname = f'MULTADDALU18X18{mac}'
|
|
portmap = db.grid[y][x].bels[belname].portmap
|
|
dsp = tt.create_bel(belname, "MULTADDALU18X18", eval(f'MULTADDALU18X18_{mac}_Z'))
|
|
|
|
for i in range(2):
|
|
for sfx in {'ASIGN', 'BSIGN', 'ASEL', 'BSEL'}:
|
|
add_port_wire(tt, dsp, portmap, f"{sfx}{i}", "DSP_I", PinType.INPUT)
|
|
for inp in range(18):
|
|
add_port_wire(tt, dsp, portmap, f"A{inp}{i}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"B{inp}{i}", "DSP_I", PinType.INPUT)
|
|
for inp in range(7):
|
|
add_port_wire(tt, dsp, portmap, f"ALUSEL{inp}", "DSP_I", PinType.INPUT)
|
|
for inp in range(54):
|
|
add_port_wire(tt, dsp, portmap, f"C{inp}", "DSP_I", PinType.INPUT)
|
|
for inp in range(4):
|
|
add_port_wire(tt, dsp, portmap, f"CE{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"CLK{inp}", "DSP_I", PinType.INPUT)
|
|
add_port_wire(tt, dsp, portmap, f"RESET{inp}", "DSP_I", PinType.INPUT)
|
|
for outp in range(54):
|
|
add_port_wire(tt, dsp, portmap, f"DOUT{outp}", "DSP_O", PinType.OUTPUT)
|
|
|
|
tdesc.tiletype = tiletype
|
|
return tt
|
|
|
|
# PLL main tile
|
|
_pll_inputs = {'CLKFB', 'FBDSEL0', 'FBDSEL1', 'FBDSEL2', 'FBDSEL3',
|
|
'FBDSEL4', 'FBDSEL5', 'IDSEL0', 'IDSEL1', 'IDSEL2', 'IDSEL3',
|
|
'IDSEL4', 'IDSEL5', 'ODSEL0', 'ODSEL1', 'ODSEL2', 'ODSEL3',
|
|
'ODSEL4', 'ODSEL5', 'RESET', 'RESET_P', 'PSDA0', 'PSDA1',
|
|
'PSDA2', 'PSDA3', 'DUTYDA0', 'DUTYDA1', 'DUTYDA2', 'DUTYDA3',
|
|
'FDLY0', 'FDLY1', 'FDLY2', 'FDLY3', 'CLKIN', 'VREN'}
|
|
_pll_outputs = {'CLKOUT', 'LOCK', 'CLKOUTP', 'CLKOUTD', 'CLKOUTD3'}
|
|
def create_pll_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc: TypeDesc):
|
|
typename = "PLL"
|
|
tiletype = f"{typename}_{ttyp}"
|
|
if tdesc.sfx != 0:
|
|
tiletype += f"_{tdesc.sfx}"
|
|
|
|
# disabled PLLs
|
|
if tdesc.extra_func and 'disabled' in tdesc.extra_func and 'PLL' in tdesc.extra_func['disabled']:
|
|
tiletype += '_disabled'
|
|
tt = chip.create_tile_type(tiletype)
|
|
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
|
tdesc.tiletype = tiletype
|
|
return tt
|
|
tt = chip.create_tile_type(tiletype)
|
|
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
|
|
|
|
|
# wires
|
|
if chip.name == 'GW1NS-4':
|
|
pll_name = 'PLLVR'
|
|
bel_type = 'PLLVR'
|
|
else:
|
|
pll_name = 'RPLLA'
|
|
bel_type = 'rPLL'
|
|
portmap = db.grid[y][x].bels[pll_name].portmap
|
|
pll = tt.create_bel("PLL", bel_type, z = PLL_Z)
|
|
pll.flags = BEL_FLAG_GLOBAL
|
|
for pin, wire in portmap.items():
|
|
if pin in _pll_inputs:
|
|
tt.create_wire(wire, "PLL_I")
|
|
tt.add_bel_pin(pll, pin, wire, PinType.INPUT)
|
|
else:
|
|
assert pin in _pll_outputs, f"Unknown PLL pin {pin}"
|
|
tt.create_wire(wire, "PLL_O")
|
|
tt.add_bel_pin(pll, pin, wire, PinType.OUTPUT)
|
|
tdesc.tiletype = tiletype
|
|
return tt
|
|
|
|
# add Pll's bel to the pad
|
|
def add_pll(chip: Chip, db: chipdb, pad: PadInfo, ioloc: str):
|
|
try:
|
|
if ioloc in db.pad_pll:
|
|
row, col, ttyp, bel_name = db.pad_pll[ioloc]
|
|
pad.extra_data = PadExtraData(chip.strs.id(f'X{col}Y{row}'), chip.strs.id(bel_name), chip.strs.id(ttyp))
|
|
except:
|
|
return
|
|
|
|
# pinouts, packages...
|
|
_tbrlre = re.compile(r"IO([TBRL])(\d+)(\w)")
|
|
def create_packages(chip: Chip, db: chipdb):
|
|
def ioloc_to_tile_bel(ioloc):
|
|
side, num, bel_idx = _tbrlre.match(ioloc).groups()
|
|
if side == 'T':
|
|
row = 0
|
|
col = int(num) - 1
|
|
elif side == 'B':
|
|
row = db.rows - 1
|
|
col = int(num) - 1
|
|
elif side == 'L':
|
|
row = int(num) - 1
|
|
col = 0
|
|
elif side == 'R':
|
|
row = int(num) - 1
|
|
col = db.cols - 1
|
|
return (f'X{col}Y{row}', f'IOB{bel_idx}')
|
|
|
|
created_pkgs = set()
|
|
for partno_spd, partdata in db.packages.items():
|
|
pkgname, variant, spd = partdata
|
|
partno = partno_spd.removesuffix(spd) # drop SPEED like 'C7/I6'
|
|
if partno in created_pkgs:
|
|
continue
|
|
created_pkgs.add(partno)
|
|
pkg = chip.create_package(partno)
|
|
for pinno, pininfo in db.pinout[variant][pkgname].items():
|
|
io_loc, cfgs = pininfo
|
|
tile, bel = ioloc_to_tile_bel(io_loc)
|
|
pad_func = ""
|
|
for cfg in cfgs:
|
|
pad_func += cfg + "/"
|
|
pad_func = pad_func.rstrip('/')
|
|
bank = int(db.pin_bank[io_loc])
|
|
pad = pkg.create_pad(pinno, tile, bel, pad_func, bank)
|
|
# add PLL if any is connected
|
|
add_pll(chip, db, pad, io_loc)
|
|
|
|
# Extra chip data
|
|
def create_extra_data(chip: Chip, db: chipdb, chip_flags: int):
|
|
chip.extra_data = ChipExtraData(chip.strs, chip_flags, None)
|
|
chip.extra_data.create_bottom_io()
|
|
for net_a, net_b in db.bottom_io[2]:
|
|
chip.extra_data.add_bottom_io_cnd(net_a, net_b)
|
|
for diff_type in db.diff_io_types:
|
|
chip.extra_data.add_diff_io_type(diff_type)
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='Make Gowin BBA')
|
|
parser.add_argument('-d', '--device', required=True)
|
|
parser.add_argument('-o', '--output', default="out.bba")
|
|
|
|
args = parser.parse_args()
|
|
|
|
device = args.device
|
|
with gzip.open(importlib.resources.files("apycula").joinpath(f"{device}.pickle"), 'rb') as f:
|
|
db = pickle.load(f)
|
|
|
|
chip_flags = 0;
|
|
if device not in {"GW1NS-4", "GW1N-9"}:
|
|
chip_flags &= CHIP_HAS_SP32;
|
|
|
|
X = db.cols;
|
|
Y = db.rows;
|
|
|
|
ch = Chip("gowin", device, X, Y)
|
|
|
|
# Init constant ids
|
|
ch.strs.read_constids(path.join(path.dirname(__file__), "constids.inc"))
|
|
|
|
# packages from parntnumbers
|
|
create_packages(ch, db)
|
|
|
|
# The manufacturer distinguishes by externally identical tiles, so keep
|
|
# these differences (in case it turns out later that there is a slightly
|
|
# different routing or something like that).
|
|
logic_tiletypes = db.tile_types['C']
|
|
io_tiletypes = db.tile_types['I']
|
|
ssram_tiletypes = db.tile_types['M']
|
|
pll_tiletypes = db.tile_types['P']
|
|
bsram_tiletypes = db.tile_types.get('B', set())
|
|
dsp_tiletypes = db.tile_types.get('D', set())
|
|
|
|
# Setup tile grid
|
|
for x in range(X):
|
|
for y in range(Y):
|
|
ttyp = db.grid[y][x].ttyp
|
|
if (x == 0 or x == X - 1) and (y == 0 or y == Y - 1):
|
|
assert ttyp not in created_tiletypes, "Duplication of corner types"
|
|
create_tiletype(create_corner_tiletype, ch, db, x, y, ttyp)
|
|
continue
|
|
elif ttyp in logic_tiletypes:
|
|
create_tiletype(create_logic_tiletype, ch, db, x, y, ttyp)
|
|
elif ttyp in ssram_tiletypes:
|
|
create_tiletype(create_ssram_tiletype, ch, db, x, y, ttyp)
|
|
elif ttyp in io_tiletypes:
|
|
create_tiletype(create_io_tiletype, ch, db, x, y, ttyp)
|
|
elif ttyp in pll_tiletypes:
|
|
create_tiletype(create_pll_tiletype, ch, db, x, y, ttyp)
|
|
elif ttyp in bsram_tiletypes:
|
|
create_tiletype(create_bsram_tiletype, ch, db, x, y, ttyp)
|
|
elif ttyp in dsp_tiletypes:
|
|
create_tiletype(create_dsp_tiletype, ch, db, x, y, ttyp)
|
|
else:
|
|
create_tiletype(create_null_tiletype, ch, db, x, y, ttyp)
|
|
|
|
# Create nodes between tiles
|
|
create_nodes(ch, db)
|
|
create_extra_data(ch, db, chip_flags)
|
|
ch.write_bba(args.output)
|
|
if __name__ == '__main__':
|
|
main()
|