1
0
mirror of https://github.com/YosysHQ/nextpnr.git synced 2026-01-11 23:53:21 +00:00

Gowin. GW5A chips. Implement the DCS primitive. (#1558)

The GW5A series is interesting—in this particular primitive, the inputs
have been renamed from CLKx to CLKINx. Everything else remains the same,
including functionality.

As an output, we will store in the chip database which prefix the DCS
inputs have.

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
YRabbit 2025-09-23 20:42:33 +10:00 committed by GitHub
parent 1742d09edb
commit 22041ed5df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 34 additions and 15 deletions

View File

@ -150,7 +150,7 @@ struct GowinGlobalRouter
bool src_valid = ((!src_is_spine) && src_type.in(id_GLOBAL_CLK, id_IO_O, id_PLL_O, id_HCLK)) || bool src_valid = ((!src_is_spine) && src_type.in(id_GLOBAL_CLK, id_IO_O, id_PLL_O, id_HCLK)) ||
src_name.in(id_SPINE6, id_SPINE7, id_SPINE14, id_SPINE15, id_SPINE22, id_SPINE23, id_SPINE30, src_name.in(id_SPINE6, id_SPINE7, id_SPINE14, id_SPINE15, id_SPINE22, id_SPINE23, id_SPINE30,
id_SPINE31); id_SPINE31);
bool dst_valid = dst_type.in(id_GLOBAL_CLK, id_TILE_CLK, id_PLL_I, id_IO_I, id_HCLK); bool dst_valid = dst_type.in(id_GLOBAL_CLK, id_TILE_CLK, id_PLL_I, id_PLL_O, id_IO_I, id_HCLK);
bool res = (src_valid && dst_valid) || (src_valid && is_local(dst_type)) || (is_local(src_type) && dst_valid); bool res = (src_valid && dst_valid) || (src_valid && is_local(dst_type)) || (is_local(src_type) && dst_valid);
if (ctx->debug && false /*&& res*/) { if (ctx->debug && false /*&& res*/) {
@ -207,6 +207,7 @@ struct GowinGlobalRouter
if (!pip_filter(pip, src)) { if (!pip_filter(pip, src)) {
continue; continue;
} }
// Add to the queue // Add to the queue
visit.push(prev); visit.push(prev);
backtrace[prev] = pip; backtrace[prev] = pip;
@ -399,6 +400,8 @@ struct GowinGlobalRouter
void route_dcs_net(NetInfo *net) void route_dcs_net(NetInfo *net)
{ {
IdString dcs_clock_input_prefix = gwu.get_dcs_prefix();
const char *dcs_clock_input_prefix_str = dcs_clock_input_prefix.c_str(ctx);
// Since CLKOUT is responsible for only one quadrant, we will do // Since CLKOUT is responsible for only one quadrant, we will do
// routing not from it, but from any CLK0-3 input actually connected to // routing not from it, but from any CLK0-3 input actually connected to
// the clock source. // the clock source.
@ -406,7 +409,7 @@ struct GowinGlobalRouter
NetInfo *net_before_dcs; NetInfo *net_before_dcs;
PortRef driver; PortRef driver;
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
net_before_dcs = dcs_ci->getPort(ctx->idf("CLK%d", i)); net_before_dcs = dcs_ci->getPort(ctx->idf("%s%d", dcs_clock_input_prefix_str, i));
if (net_before_dcs == nullptr) { if (net_before_dcs == nullptr) {
continue; continue;
} }
@ -449,7 +452,8 @@ struct GowinGlobalRouter
} }
WireId dst = ctx->getPipDstWire(pip); WireId dst = ctx->getPipDstWire(pip);
IdString dst_name = ctx->getWireName(dst)[1]; IdString dst_name = ctx->getWireName(dst)[1];
if (dst_name.str(ctx).rfind("PCLK", 0) == 0 || dst_name.str(ctx).rfind("LWSPINE", 0) == 0) { if (dst_name.str(ctx).rfind("PCLK", 0) == 0 || dst_name.str(ctx).rfind("LWSPINE", 0) == 0 ||
dst_name.str(ctx).rfind("PLL") == 0) {
// step over dummy pip // step over dummy pip
for (PipId next_pip : ctx->getPipsDownhill(dst)) { for (PipId next_pip : ctx->getPipsDownhill(dst)) {
if (ctx->getBoundPipNet(next_pip) != nullptr) { if (ctx->getBoundPipNet(next_pip) != nullptr) {
@ -498,7 +502,7 @@ struct GowinGlobalRouter
// The input networks must bs same for all hardware dcs. // The input networks must bs same for all hardware dcs.
dcs_ci->copyPortTo(id_SELFORCE, hw_dcs, id_SELFORCE); dcs_ci->copyPortTo(id_SELFORCE, hw_dcs, id_SELFORCE);
dcs_ci->copyPortBusTo(id_CLK, 0, false, hw_dcs, id_CLK, 0, false, 4); dcs_ci->copyPortBusTo(dcs_clock_input_prefix, 0, false, hw_dcs, dcs_clock_input_prefix, 0, false, 4);
dcs_ci->copyPortBusTo(id_CLKSEL, 0, true, hw_dcs, id_CLKSEL, 0, false, 4); dcs_ci->copyPortBusTo(id_CLKSEL, 0, true, hw_dcs, id_CLKSEL, 0, false, 4);
} }
@ -507,7 +511,7 @@ struct GowinGlobalRouter
dcs_ci->disconnectPort(id_CLKOUT); dcs_ci->disconnectPort(id_CLKOUT);
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
dcs_ci->disconnectPort(ctx->idf("CLKSEL[%d]", i)); dcs_ci->disconnectPort(ctx->idf("CLKSEL[%d]", i));
dcs_ci->disconnectPort(ctx->idf("CLK%d", i)); dcs_ci->disconnectPort(ctx->idf("%s%d", dcs_clock_input_prefix_str, i));
} }
log_info(" '%s' net was routed.\n", ctx->nameOf(net)); log_info(" '%s' net was routed.\n", ctx->nameOf(net));
ctx->cells.erase(dcs_ci->name); ctx->cells.erase(dcs_ci->name);
@ -1281,7 +1285,7 @@ struct GowinGlobalRouter
} }
if (route_clk_net(ni) == NOT_ROUTED) { if (route_clk_net(ni) == NOT_ROUTED) {
if (ctx->verbose) { if (ctx->verbose) {
log_info(" try to route as a segmented network.\n"); log_info(" will try to route it as a segmented network.\n");
} }
seg_nets.push_back(net_name); seg_nets.push_back(net_name);
} }

View File

@ -184,6 +184,7 @@ NPNR_PACKED_STRUCT(struct Extra_package_data_POD { RelSlice<Constraint_POD> cst;
NPNR_PACKED_STRUCT(struct Extra_chip_data_POD { NPNR_PACKED_STRUCT(struct Extra_chip_data_POD {
int32_t chip_flags; int32_t chip_flags;
IdString dcs_prefix;
Bottom_io_POD bottom_io; Bottom_io_POD bottom_io;
RelSlice<IdString> diff_io_types; RelSlice<IdString> diff_io_types;
RelSlice<Spine_bel_POD> dqce_bels; RelSlice<Spine_bel_POD> dqce_bels;

View File

@ -266,7 +266,8 @@ class Segment(BBAStruct):
class ChipExtraData(BBAStruct): class ChipExtraData(BBAStruct):
strs: StringPool strs: StringPool
flags: int flags: int
bottom_io: BottomIO dcs_prefix: IdString = field(default = None)
bottom_io: BottomIO = field(default = None)
diff_io_types: list[IdString] = field(default_factory = list) diff_io_types: list[IdString] = field(default_factory = list)
dqce_bels: list[SpineBel] = field(default_factory = list) dqce_bels: list[SpineBel] = field(default_factory = list)
dcs_bels: list[SpineBel] = field(default_factory = list) dcs_bels: list[SpineBel] = field(default_factory = list)
@ -274,6 +275,9 @@ class ChipExtraData(BBAStruct):
io_dlldly_bels: list[IoBel] = field(default_factory = list) io_dlldly_bels: list[IoBel] = field(default_factory = list)
segments: list[Segment] = field(default_factory = list) segments: list[Segment] = field(default_factory = list)
def set_dcs_prefix(self, prefix: str):
self.dcs_prefix = self.strs.id(prefix)
def create_bottom_io(self): def create_bottom_io(self):
self.bottom_io = BottomIO() self.bottom_io = BottomIO()
@ -334,6 +338,7 @@ class ChipExtraData(BBAStruct):
def serialise(self, context: str, bba: BBAWriter): def serialise(self, context: str, bba: BBAWriter):
bba.u32(self.flags) bba.u32(self.flags)
bba.u32(self.dcs_prefix.index)
self.bottom_io.serialise(f"{context}_bottom_io", bba) self.bottom_io.serialise(f"{context}_bottom_io", bba)
bba.slice(f"{context}_diff_io_types", len(self.diff_io_types)) bba.slice(f"{context}_diff_io_types", len(self.diff_io_types))
bba.slice(f"{context}_dqce_bels", len(self.dqce_bels)) bba.slice(f"{context}_dqce_bels", len(self.dqce_bels))
@ -662,6 +667,9 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int):
for idx in range(2): for idx in range(2):
if idx not in desc: if idx not in desc:
continue continue
dcs_prefix = 'CLK'
if hasattr(db, "dcs_prefix"):
dcs_prefix = db.dcs_prefix
bel_z = DCS_Z + idx bel_z = DCS_Z + idx
bel = tt.create_bel(f"DCS{idx}", "DCS", bel_z) bel = tt.create_bel(f"DCS{idx}", "DCS", bel_z)
wire = desc[idx]['clkout'] wire = desc[idx]['clkout']
@ -672,7 +680,7 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int):
for clk_idx, wire in enumerate(desc[idx]['clk']): for clk_idx, wire in enumerate(desc[idx]['clk']):
if not tt.has_wire(wire): if not tt.has_wire(wire):
tt.create_wire(wire, "GLOBAL_CLK") tt.create_wire(wire, "GLOBAL_CLK")
tt.add_bel_pin(bel, f"CLK{clk_idx}", wire, PinType.INPUT) tt.add_bel_pin(bel, f"{dcs_prefix}{clk_idx}", wire, PinType.INPUT)
# This is a fake PIP that allows routing “through” this # This is a fake PIP that allows routing “through” this
# primitive from the CLK input to the CLKOUT output. # primitive from the CLK input to the CLKOUT output.
tt.create_pip(wire, clkout_wire) tt.create_pip(wire, clkout_wire)
@ -1413,7 +1421,11 @@ def create_packages(chip: Chip, db: chipdb):
# Extra chip data # Extra chip data
def create_extra_data(chip: Chip, db: chipdb, chip_flags: int): def create_extra_data(chip: Chip, db: chipdb, chip_flags: int):
chip.extra_data = ChipExtraData(chip.strs, chip_flags, None) chip.extra_data = ChipExtraData(chip.strs, chip_flags)
if hasattr(db, "dcs_prefix"):
chip.extra_data.set_dcs_prefix(db.dcs_prefix)
else:
chip.extra_data.set_dcs_prefix("CLK")
chip.extra_data.create_bottom_io() chip.extra_data.create_bottom_io()
for net_a, net_b in db.bottom_io[2]: for net_a, net_b in db.bottom_io[2]:
chip.extra_data.add_bottom_io_cnd(net_a, net_b) chip.extra_data.add_bottom_io_cnd(net_a, net_b)

View File

@ -290,6 +290,12 @@ BelId GowinUtils::get_dhcen_bel(WireId hclkin_wire, IdString &side)
return BelId(); return BelId();
} }
IdString GowinUtils::get_dcs_prefix(void)
{
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
return extra->dcs_prefix;
}
bool GowinUtils::is_simple_io_bel(BelId bel) bool GowinUtils::is_simple_io_bel(BelId bel)
{ {
return chip_bel_info(ctx->chip_info, bel).flags & BelFlags::FLAG_SIMPLE_IO; return chip_bel_info(ctx->chip_info, bel).flags & BelFlags::FLAG_SIMPLE_IO;

View File

@ -49,6 +49,7 @@ struct GowinUtils
BelId get_dcs_bel(IdString spine_name); BelId get_dcs_bel(IdString spine_name);
BelId get_dhcen_bel(WireId hclkin_wire, IdString &side); BelId get_dhcen_bel(WireId hclkin_wire, IdString &side);
BelId get_dlldly_bel(BelId io_bel); BelId get_dlldly_bel(BelId io_bel);
IdString get_dcs_prefix(void);
// Segments // Segments
int get_segments_count(void) const; int get_segments_count(void) const;

View File

@ -4141,12 +4141,7 @@ struct GowinPacker
if (dcs_bel != BelId()) { if (dcs_bel != BelId()) {
IdString dcs_name = ctx->idf("$PACKER_DCS_SPINE%d", 8 * (i % 4) + 6 + (i >> 2)); IdString dcs_name = ctx->idf("$PACKER_DCS_SPINE%d", 8 * (i % 4) + 6 + (i >> 2));
CellInfo *dcs = ctx->createCell(dcs_name, id_DCS); CellInfo *dcs = ctx->createCell(dcs_name, id_DCS);
dcs->addInput(id_SELFORCE); ctx->copyBelPorts(dcs_name, dcs_bel);
for (int j = 0; j < 4; ++j) {
dcs->addInput(ctx->idf("CLK%d", j));
dcs->addInput(ctx->idf("CLKSEL%d", j));
}
dcs->addOutput(id_CLKOUT);
ctx->bindBel(dcs_bel, dcs, STRENGTH_LOCKED); ctx->bindBel(dcs_bel, dcs, STRENGTH_LOCKED);
} }
} }