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:
YRabbit 2023-07-22 10:01:35 +10:00 committed by myrtle
parent 6eeac1cabf
commit 49f8620ac9
6 changed files with 134 additions and 23 deletions

View File

@ -235,6 +235,9 @@ class TileType(BBAStruct):
def has_wire(self, wire: str): def has_wire(self, wire: str):
# Check if a wire has already been created # Check if a wire has already been created
return self.strs.id(wire) in self._wire2idx 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): def serialise_lists(self, context: str, bba: BBAWriter):
# list children of members # list children of members
for i, bel in enumerate(self.bels): for i, bel in enumerate(self.bels):

View File

@ -1059,6 +1059,7 @@ X(router)
// misc // misc
X(GOWIN_GND) X(GOWIN_GND)
X(GOWIN_VCC) X(GOWIN_VCC)
X(PLL)
// wire types // wire types
X(GLOBAL_CLK) X(GLOBAL_CLK)
@ -1075,6 +1076,8 @@ X(MUX_OUT)
X(MUX_SEL) X(MUX_SEL)
X(ALU_CIN) X(ALU_CIN)
X(ALU_COUT) X(ALU_COUT)
X(PLL_O)
X(PLL_I)
// fake dff inputs // fake dff inputs
X(XD0) X(XD0)

View File

@ -43,14 +43,21 @@ struct GowinGlobalRouter
// allow io->global, global->global and global->tile clock // allow io->global, global->global and global->tile clock
bool global_pip_filter(PipId pip) const 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 src_type = ctx->getWireType(ctx->getPipSrcWire(pip));
IdString dst_type = ctx->getWireType(ctx->getPipDstWire(pip)); IdString dst_type = ctx->getWireType(ctx->getPipDstWire(pip));
bool src_valid = src_type.in(id_GLOBAL_CLK, id_IO_O); 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); bool dst_valid = dst_type.in(id_GLOBAL_CLK, id_TILE_CLK, id_PLL_I, id_IO_I);
return src_valid && dst_valid;
*/ if (ctx->debug) {
return true; 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; } bool is_relaxed_sink(const PortRef &sink) const { return false; }
@ -151,11 +158,20 @@ struct GowinGlobalRouter
void route_clk_net(NetInfo *net) void route_clk_net(NetInfo *net)
{ {
for (auto usr : net->users.enumerate()) bool routed = false;
backwards_bfs_route(net, usr.index, 1000000, true, for (auto usr : net->users.enumerate()) {
[&](PipId pip) { return (is_relaxed_sink(usr.value) || global_pip_filter(pip)); }); 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)); log_info(" routed net '%s' using global resources\n", ctx->nameOf(net));
} }
}
bool driver_is_clksrc(const PortRef &driver) bool driver_is_clksrc(const PortRef &driver)
{ {
@ -166,10 +182,24 @@ struct GowinGlobalRouter
IdStringList pin_func = gwu.get_pin_funcs(driver.cell->bel); IdStringList pin_func = gwu.get_pin_funcs(driver.cell->bel);
for (size_t i = 0; i < pin_func.size(); ++i) { for (size_t i = 0; i < pin_func.size(); ++i) {
if (pin_func[i].str(ctx).rfind("GCLKT", 0) == 0) { 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; 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; return false;
} }

View File

@ -39,6 +39,8 @@ enum
ALU0_Z = 30, // :35, 6 ALU ALU0_Z = 30, // :35, 6 ALU
RAMW_Z = 36, // RAM16SDP4 RAMW_Z = 36, // RAM16SDP4
PLL_Z = 275,
GSR_Z = 276,
VCC_Z = 277, VCC_Z = 277,
VSS_Z = 278 VSS_Z = 278
}; };

View File

@ -22,6 +22,7 @@ MUX27_Z = 29
ALU0_Z = 30 # : 35, 6 ALUs ALU0_Z = 30 # : 35, 6 ALUs
RAMW_Z = 36 # RAM16SDP4 RAMW_Z = 36 # RAM16SDP4
PLL_Z = 275
GSR_Z = 276 GSR_Z = 276
VCC_Z = 277 VCC_Z = 277
GND_Z = 278 GND_Z = 278
@ -121,8 +122,11 @@ def create_nodes(chip: Chip, db: chipdb):
global_nodes.setdefault('VCC', []).append(NodeWire(x, y, 'VCC')) global_nodes.setdefault('VCC', []).append(NodeWire(x, y, 'VCC'))
# add nodes from the apicula db # 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: 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) new_node = NodeWire(x, y, wire)
gl_nodes = global_nodes.setdefault(node_name, []) gl_nodes = global_nodes.setdefault(node_name, [])
if new_node not in gl_nodes: if new_node not in gl_nodes:
@ -139,9 +143,7 @@ def create_nodes(chip: Chip, db: chipdb):
# coordinates. # coordinates.
def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int): def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int):
def get_wire_type(name): def get_wire_type(name):
if name.startswith('GB') or name.startswith('GT'): if name in {'XD0', 'XD1', 'XD2', 'XD3', 'XD4', 'XD5',}:
return "GLOBAL_CLK"
elif name in {'XD0', 'XD1', 'XD2', 'XD3', 'XD4', 'XD5',}:
return "X0" return "X0"
return "" 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)) tt.create_wire(dst, get_wire_type(dst))
for src in srcs.keys(): for src in srcs.keys():
if not tt.has_wire(src): 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) tt.create_pip(src, dst)
# clock wires # clock wires
for dst, srcs in db.grid[y][x].clock_pips.items(): 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): 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"DI[{i}]", f"{lut_inputs[i]}5", PinType.INPUT)
tt.add_bel_pin(ff, f"WAD[{i}]", f"{lut_inputs[i]}4", 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 # for now we connect it only to A0, the others will be connected
# directly during packing. RAD[1...3] - similarly. # 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"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"DO[{i}]", f"F{i}", PinType.OUTPUT)
tt.add_bel_pin(ff, f"CLK", "CLK2", PinType.INPUT) 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"CE", "CE2", PinType.INPUT)
tt.add_bel_pin(ff, f"WRE", "LSR2", PinType.INPUT) tt.add_bel_pin(ff, f"WRE", "LSR2", PinType.INPUT)
return (ttyp, tt) 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... # pinouts, packages...
_tbrlre = re.compile(r"IO([TBRL])(\d+)(\w)") _tbrlre = re.compile(r"IO([TBRL])(\d+)(\w)")
def create_packages(chip: Chip, db: chipdb): 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 # these differences (in case it turns out later that there is a slightly
# different routing or something like that). # different routing or something like that).
logic_tiletypes = {12, 13, 14, 15, 16} 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} ssram_tiletypes = {17, 18, 19}
gsr_tiletypes = {1} gsr_tiletypes = {1}
pll_tiletypes = {86, 87, 42, 45}
# Setup tile grid # Setup tile grid
for x in range(X): for x in range(X):
for y in range(Y): for y in range(Y):
@ -440,6 +481,10 @@ def main():
ttyp, _ = create_io_tiletype(ch, db, x, y, ttyp) ttyp, _ = create_io_tiletype(ch, db, x, y, ttyp)
created_tiletypes.add(ttyp) created_tiletypes.add(ttyp)
ch.set_tile_type(x, y, f"IO_{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: else:
ttyp, _ = create_null_tiletype(ch, db, x, y, ttyp) ttyp, _ = create_null_tiletype(ch, db, x, y, ttyp)
created_tiletypes.add(ttyp) created_tiletypes.add(ttyp)

View File

@ -505,17 +505,18 @@ struct GowinPacker
} }
} }
// Pack global set-reset // ===================================
// Global set/reset
// ===================================
void pack_gsr(void) void pack_gsr(void)
{ {
log_info("Packing GSR..\n"); log_info("Packing GSR..\n");
bool user_gsr = false; bool user_gsr = false;
for (auto &cell : ctx->cells) { for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get(); auto &ci = *cell.second;
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) {
if (ci->type == id_GSR) {
user_gsr = true; user_gsr = true;
break; 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) void run(void)
{ {
pack_iobs(); pack_iobs();
@ -549,6 +576,7 @@ struct GowinPacker
pack_wideluts(); pack_wideluts();
pack_alus(); pack_alus();
constrain_lutffs(); constrain_lutffs();
pack_pll();
pack_ram16sdp4(); pack_ram16sdp4();
} }
}; };