gowin: Himbaechel. Implement PLLs
- The global router is modified to work out the routing of PLL outputs and inputs; - Added API function to change wire type after its creation - there was a need to unify all wires included in the node at the stage of node creation, when all wires have already been created. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
parent
6eeac1cabf
commit
49f8620ac9
@ -235,6 +235,9 @@ class TileType(BBAStruct):
|
||||
def has_wire(self, wire: str):
|
||||
# Check if a wire has already been created
|
||||
return self.strs.id(wire) in self._wire2idx
|
||||
def set_wire_type(self, wire: str, type: str):
|
||||
# wire type change
|
||||
self.wires[self._wire2idx[self.strs.id(wire)]].wire_type = self.strs.id(type)
|
||||
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||
# list children of members
|
||||
for i, bel in enumerate(self.bels):
|
||||
|
@ -1059,6 +1059,7 @@ X(router)
|
||||
// misc
|
||||
X(GOWIN_GND)
|
||||
X(GOWIN_VCC)
|
||||
X(PLL)
|
||||
|
||||
// wire types
|
||||
X(GLOBAL_CLK)
|
||||
@ -1075,6 +1076,8 @@ X(MUX_OUT)
|
||||
X(MUX_SEL)
|
||||
X(ALU_CIN)
|
||||
X(ALU_COUT)
|
||||
X(PLL_O)
|
||||
X(PLL_I)
|
||||
|
||||
// fake dff inputs
|
||||
X(XD0)
|
||||
|
@ -43,14 +43,21 @@ struct GowinGlobalRouter
|
||||
// allow io->global, global->global and global->tile clock
|
||||
bool global_pip_filter(PipId pip) const
|
||||
{
|
||||
/*
|
||||
auto is_local = [&](IdString wire_type) {
|
||||
return !wire_type.in(id_GLOBAL_CLK, id_IO_O, id_IO_I, id_PLL_O, id_PLL_I, id_TILE_CLK);
|
||||
};
|
||||
|
||||
IdString src_type = ctx->getWireType(ctx->getPipSrcWire(pip));
|
||||
IdString dst_type = ctx->getWireType(ctx->getPipDstWire(pip));
|
||||
bool src_valid = src_type.in(id_GLOBAL_CLK, id_IO_O);
|
||||
bool dst_valid = dst_type.in(id_GLOBAL_CLK, id_TILE_CLK);
|
||||
return src_valid && dst_valid;
|
||||
*/
|
||||
return true;
|
||||
bool src_valid = src_type.in(id_GLOBAL_CLK, id_IO_O, id_PLL_O);
|
||||
bool dst_valid = dst_type.in(id_GLOBAL_CLK, id_TILE_CLK, id_PLL_I, id_IO_I);
|
||||
|
||||
if (ctx->debug) {
|
||||
log_info("%s <- %s [%s <- %s]\n", ctx->getWireName(ctx->getPipDstWire(pip)).str(ctx).c_str(),
|
||||
ctx->getWireName(ctx->getPipSrcWire(pip)).str(ctx).c_str(), dst_type.c_str(ctx),
|
||||
src_type.c_str(ctx));
|
||||
}
|
||||
return (src_valid && dst_valid) || (src_valid && is_local(dst_type)) || (is_local(src_type) && dst_valid);
|
||||
}
|
||||
|
||||
bool is_relaxed_sink(const PortRef &sink) const { return false; }
|
||||
@ -151,10 +158,19 @@ struct GowinGlobalRouter
|
||||
|
||||
void route_clk_net(NetInfo *net)
|
||||
{
|
||||
for (auto usr : net->users.enumerate())
|
||||
backwards_bfs_route(net, usr.index, 1000000, true,
|
||||
[&](PipId pip) { return (is_relaxed_sink(usr.value) || global_pip_filter(pip)); });
|
||||
log_info(" routed net '%s' using global resources\n", ctx->nameOf(net));
|
||||
bool routed = false;
|
||||
for (auto usr : net->users.enumerate()) {
|
||||
routed = backwards_bfs_route(net, usr.index, 1000000, true, [&](PipId pip) {
|
||||
return (is_relaxed_sink(usr.value) || global_pip_filter(pip));
|
||||
});
|
||||
if (!routed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (routed) {
|
||||
log_info(" routed net '%s' using global resources\n", ctx->nameOf(net));
|
||||
}
|
||||
}
|
||||
|
||||
bool driver_is_clksrc(const PortRef &driver)
|
||||
@ -166,10 +182,24 @@ struct GowinGlobalRouter
|
||||
IdStringList pin_func = gwu.get_pin_funcs(driver.cell->bel);
|
||||
for (size_t i = 0; i < pin_func.size(); ++i) {
|
||||
if (pin_func[i].str(ctx).rfind("GCLKT", 0) == 0) {
|
||||
if (ctx->debug) {
|
||||
log_info("Clock pin:%s:%s\n", ctx->getBelName(driver.cell->bel).str(ctx).c_str(),
|
||||
pin_func[i].c_str(ctx));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// PLL outputs
|
||||
if (driver.cell->type.in(id_rPLL, id_PLLVR)) {
|
||||
if (driver.port.in(id_CLKOUT, id_CLKOUTD, id_CLKOUTD3, id_CLKOUTP)) {
|
||||
if (ctx->debug) {
|
||||
log_info("PLL out:%s:%s\n", ctx->getBelName(driver.cell->bel).str(ctx).c_str(),
|
||||
driver.port.c_str(ctx));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,8 @@ enum
|
||||
ALU0_Z = 30, // :35, 6 ALU
|
||||
RAMW_Z = 36, // RAM16SDP4
|
||||
|
||||
PLL_Z = 275,
|
||||
GSR_Z = 276,
|
||||
VCC_Z = 277,
|
||||
VSS_Z = 278
|
||||
};
|
||||
|
@ -22,6 +22,7 @@ MUX27_Z = 29
|
||||
ALU0_Z = 30 # : 35, 6 ALUs
|
||||
RAMW_Z = 36 # RAM16SDP4
|
||||
|
||||
PLL_Z = 275
|
||||
GSR_Z = 276
|
||||
VCC_Z = 277
|
||||
GND_Z = 278
|
||||
@ -121,8 +122,11 @@ def create_nodes(chip: Chip, db: chipdb):
|
||||
global_nodes.setdefault('VCC', []).append(NodeWire(x, y, 'VCC'))
|
||||
|
||||
# add nodes from the apicula db
|
||||
for node_name, node in db.nodes.items():
|
||||
for node_name, node_hdr in db.nodes.items():
|
||||
wire_type, node = node_hdr
|
||||
for y, x, wire in node:
|
||||
if wire_type:
|
||||
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:
|
||||
@ -139,9 +143,7 @@ def create_nodes(chip: Chip, db: chipdb):
|
||||
# coordinates.
|
||||
def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int):
|
||||
def get_wire_type(name):
|
||||
if name.startswith('GB') or name.startswith('GT'):
|
||||
return "GLOBAL_CLK"
|
||||
elif name in {'XD0', 'XD1', 'XD2', 'XD3', 'XD4', 'XD5',}:
|
||||
if name in {'XD0', 'XD1', 'XD2', 'XD3', 'XD4', 'XD5',}:
|
||||
return "X0"
|
||||
return ""
|
||||
|
||||
@ -150,7 +152,7 @@ def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int):
|
||||
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(dst))
|
||||
tt.create_wire(src, get_wire_type(src))
|
||||
tt.create_pip(src, dst)
|
||||
# clock wires
|
||||
for dst, srcs in db.grid[y][x].clock_pips.items():
|
||||
@ -336,18 +338,56 @@ def create_ssram_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
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 RAD[0] is assumed to be connected to A3, A2, A1 and A0. But
|
||||
# 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, f"CLK", "CLK2", PinType.INPUT)
|
||||
tt.add_bel_pin(ff, f"CE", "CE2", PinType.INPUT)
|
||||
tt.add_bel_pin(ff, f"WRE", "LSR2", PinType.INPUT)
|
||||
return (ttyp, 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'}
|
||||
_pll_outputs = {'CLKOUT', 'LOCK', 'CLKOUTP', 'CLKOUTD', 'CLKOUTD3'}
|
||||
def create_pll_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
if ttyp in created_tiletypes:
|
||||
return ttyp, None
|
||||
typename = "PLL"
|
||||
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
||||
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)
|
||||
# Not sure how this will affect routing - PLLs are fixed and their outputs
|
||||
# will be handled by a dedicated router
|
||||
#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)
|
||||
|
||||
create_switch_matrix(tt, db, x, y)
|
||||
return (ttyp, tt)
|
||||
|
||||
# pinouts, packages...
|
||||
_tbrlre = re.compile(r"IO([TBRL])(\d+)(\w)")
|
||||
def create_packages(chip: Chip, db: chipdb):
|
||||
@ -411,9 +451,10 @@ def main():
|
||||
# these differences (in case it turns out later that there is a slightly
|
||||
# different routing or something like that).
|
||||
logic_tiletypes = {12, 13, 14, 15, 16}
|
||||
io_tiletypes = {52, 53, 55, 58, 59, 64, 65, 66}
|
||||
io_tiletypes = {52, 53, 54, 55, 58, 59, 64, 65, 66}
|
||||
ssram_tiletypes = {17, 18, 19}
|
||||
gsr_tiletypes = {1}
|
||||
pll_tiletypes = {86, 87, 42, 45}
|
||||
# Setup tile grid
|
||||
for x in range(X):
|
||||
for y in range(Y):
|
||||
@ -440,6 +481,10 @@ def main():
|
||||
ttyp, _ = create_io_tiletype(ch, db, x, y, ttyp)
|
||||
created_tiletypes.add(ttyp)
|
||||
ch.set_tile_type(x, y, f"IO_{ttyp}")
|
||||
elif ttyp in pll_tiletypes:
|
||||
ttyp, _ = create_pll_tiletype(ch, db, x, y, ttyp)
|
||||
created_tiletypes.add(ttyp)
|
||||
ch.set_tile_type(x, y, f"PLL_{ttyp}")
|
||||
else:
|
||||
ttyp, _ = create_null_tiletype(ch, db, x, y, ttyp)
|
||||
created_tiletypes.add(ttyp)
|
||||
|
@ -505,17 +505,18 @@ struct GowinPacker
|
||||
}
|
||||
}
|
||||
|
||||
// Pack global set-reset
|
||||
// ===================================
|
||||
// Global set/reset
|
||||
// ===================================
|
||||
void pack_gsr(void)
|
||||
{
|
||||
log_info("Packing GSR..\n");
|
||||
|
||||
bool user_gsr = false;
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo *ci = cell.second.get();
|
||||
if (ctx->verbose)
|
||||
log_info("cell '%s' is of type '%s'\n", ctx->nameOf(ci), ci->type.c_str(ctx));
|
||||
if (ci->type == id_GSR) {
|
||||
auto &ci = *cell.second;
|
||||
|
||||
if (ci.type == id_GSR) {
|
||||
user_gsr = true;
|
||||
break;
|
||||
}
|
||||
@ -541,6 +542,32 @@ struct GowinPacker
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// PLL
|
||||
// ===================================
|
||||
void pack_pll(void)
|
||||
{
|
||||
log_info("Packing PLL..\n");
|
||||
|
||||
for (auto &cell : ctx->cells) {
|
||||
auto &ci = *cell.second;
|
||||
|
||||
if (ci.type == id_rPLL) {
|
||||
// pin renaming for compatibility
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
ci.renamePort(ctx->idf("FBDSEL[%d]", i), ctx->idf("FBDSEL%d", i));
|
||||
ci.renamePort(ctx->idf("IDSEL[%d]", i), ctx->idf("IDSEL%d", i));
|
||||
ci.renamePort(ctx->idf("ODSEL[%d]", i), ctx->idf("ODSEL%d", i));
|
||||
if (i < 4) {
|
||||
ci.renamePort(ctx->idf("PSDA[%d]", i), ctx->idf("PSDA%d", i));
|
||||
ci.renamePort(ctx->idf("DUTYDA[%d]", i), ctx->idf("DUTYDA%d", i));
|
||||
ci.renamePort(ctx->idf("FDLY[%d]", i), ctx->idf("FDLY%d", i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run(void)
|
||||
{
|
||||
pack_iobs();
|
||||
@ -549,6 +576,7 @@ struct GowinPacker
|
||||
pack_wideluts();
|
||||
pack_alus();
|
||||
constrain_lutffs();
|
||||
pack_pll();
|
||||
pack_ram16sdp4();
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user