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):
|
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):
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
@ -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)
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user