diff --git a/himbaechel/uarch/gowin/CMakeLists.txt b/himbaechel/uarch/gowin/CMakeLists.txt index cb27b235..ee0638b5 100644 --- a/himbaechel/uarch/gowin/CMakeLists.txt +++ b/himbaechel/uarch/gowin/CMakeLists.txt @@ -20,7 +20,7 @@ add_nextpnr_himbaechel_microarchitecture(${uarch} CORE_SOURCES ${SOURCES} ) -set(ALL_HIMBAECHEL_GOWIN_DEVICES GW1N-1 GW1NZ-1 GW1N-4 GW1N-9 GW1N-9C GW1NS-4 GW2A-18 GW2A-18C GW5A-25A) +set(ALL_HIMBAECHEL_GOWIN_DEVICES GW1N-1 GW1NZ-1 GW1N-4 GW1N-9 GW1N-9C GW1NS-4 GW2A-18 GW2A-18C GW5A-25A GW5AST-138C) set(HIMBAECHEL_GOWIN_DEVICES ${ALL_HIMBAECHEL_GOWIN_DEVICES} CACHE STRING "Include support for these Gowin devices (available: ${ALL_HIMBAECHEL_GOWIN_DEVICES})") if (HIMBAECHEL_GOWIN_DEVICES STREQUAL "all") diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index b781f37c..4a59355e 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -1237,6 +1237,7 @@ X(FB_C) X(BOTTOM_IO_PORT_A) X(BOTTOM_IO_PORT_B) X(IOLOGIC_DUMMY) +X(SPINE_SELECT) // User Flash X(INUSEN) diff --git a/himbaechel/uarch/gowin/globals.cc b/himbaechel/uarch/gowin/globals.cc index ee90b5d9..64c230d1 100644 --- a/himbaechel/uarch/gowin/globals.cc +++ b/himbaechel/uarch/gowin/globals.cc @@ -1187,6 +1187,76 @@ struct GowinGlobalRouter } } + // Enable clocked spines by connecting magic wires to VCC/GND if necessary. + void enable_spines(void) + { + if (ctx->verbose) { + log_info("Check for spine select wires.\n"); + } + + NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get(); + NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get(); + + std::unique_ptr top_ci = gwu.create_cell(ctx->id("spine_select$top"), id_SPINE_SELECT); + top_ci->pseudo_cell = std::make_unique(Loc(0, 0, 0)); + std::unique_ptr bottom_ci = gwu.create_cell(ctx->id("spine_select$bottom"), id_SPINE_SELECT); + bottom_ci->pseudo_cell = std::make_unique(Loc(0, 0, 0)); + + pool seen_spines; + dict top_connections; + dict bottom_connections; + + for (auto &net : ctx->nets) { + const NetInfo *ni = net.second.get(); + for (auto &wire : ni->wires) { + WireId spine = wire.first; + IdString spine_name = ctx->getWireName(spine)[1]; + if (spine_name.str(ctx).rfind("SPINE", 0) == 0 && seen_spines.count(spine) == 0) { + seen_spines.insert(spine); + std::vector> wires; + if (gwu.get_spine_select_wire(spine, wires)) { + int sfx = 0; // To activate a single spine, it may be necessary to connect an unknown number of + // wires. + CellInfo *select_cell = top_ci.get(); + auto connections = &top_connections; + if (gwu.wire_in_bottom_half(spine)) { + select_cell = bottom_ci.get(); + connections = &bottom_connections; + } + + for (auto gate : wires) { + IdString port_name = ctx->idf("%s.%d", spine_name.c_str(ctx), sfx); + + select_cell->addInput(port_name); + + RegionPlug *rp = dynamic_cast(select_cell->pseudo_cell.get()); + rp->port_wires[port_name] = gate.first; + (*connections)[port_name] = gate.second; + ++sfx; + if (ctx->verbose) { + log_info(" %s->%s\n", port_name.c_str(ctx), ctx->nameOfWire(gate.first)); + } + } + } + } + } + } + + // realy connect nets + if (!top_connections.empty()) { + for (auto conn : top_connections) { + top_ci->connectPort(conn.first, conn.second ? vcc_net : vss_net); + } + ctx->cells[top_ci->name] = std::move(top_ci); + } + if (!bottom_connections.empty()) { + for (auto conn : bottom_connections) { + bottom_ci->connectPort(conn.first, conn.second ? vcc_net : vss_net); + } + ctx->cells[bottom_ci->name] = std::move(bottom_ci); + } + } + // Route all void run(void) { @@ -1295,6 +1365,14 @@ struct GowinGlobalRouter if (gwu.get_segments_count() != 0) { route_segmented(seg_nets); } + + // In some GW5 series chips, in addition to the mechanism for + // enabling/disabling individual clock spines using fuses, which is + // invisible to nextpnr, it is necessary to enable them by connecting + // some ports of the mysterious MUX to VSS/GND. + if (gwu.has_spine_enable_nets()) { + enable_spines(); + } } }; diff --git a/himbaechel/uarch/gowin/gowin.cc b/himbaechel/uarch/gowin/gowin.cc index b4615a05..9b2c369e 100644 --- a/himbaechel/uarch/gowin/gowin.cc +++ b/himbaechel/uarch/gowin/gowin.cc @@ -143,7 +143,8 @@ void GowinImpl::init_database(Arch *arch) std::regex devicere = std::regex("GW5A(T|ST)?-LV(25|60|138)[A-Z]*.*"); std::smatch match; if (std::regex_match(args.device, match, devicere)) { - family = stringf("GW5A%s-%sA", match[1].str().c_str(), match[2].str().c_str()); + family = stringf("GW5A%s-%s%s", match[1].str().c_str(), match[2].str().c_str(), + match[2].str() == "25" ? "A" : "C"); } else { std::regex devicere = std::regex("GW1N([SZ]?)[A-Z]*-(LV|UV|UX)([0-9])(C?).*"); std::smatch match; @@ -1012,6 +1013,12 @@ bool GowinImpl::slice_valid(int x, int y, int z) const ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, 5 * 2 + 1)))) { return false; } + if (gwu.has_DFF67()) { + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, 6 * 2 + 1))) || + ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, 7 * 2 + 1)))) { + return false; + } + } // ALU/LUTs in slices 4, 5, 6, 7 are not allowed for (int i = 4; i < 8; ++i) { if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, i * 2)))) { diff --git a/himbaechel/uarch/gowin/gowin.h b/himbaechel/uarch/gowin/gowin.h index 3aff35b6..7496015a 100644 --- a/himbaechel/uarch/gowin/gowin.h +++ b/himbaechel/uarch/gowin/gowin.h @@ -176,6 +176,14 @@ NPNR_PACKED_STRUCT(struct Segment_POD { RelSlice bottom_gate_wire; }); +NPNR_PACKED_STRUCT(struct SpineSelectWire_POD { + uint32_t spine; + int16_t x; + int16_t y; + uint32_t wire; + uint32_t vcc_gnd; +}); + NPNR_PACKED_STRUCT(struct Constraint_POD { int32_t net; int32_t row; @@ -196,6 +204,9 @@ NPNR_PACKED_STRUCT(struct Extra_chip_data_POD { RelSlice dhcen_bels; RelSlice io_dlldly_bels; RelSlice segments; + RelSlice spine_select_wires_top; + RelSlice spine_select_wires_bottom; + // chip flags static constexpr int32_t HAS_SP32 = 1; static constexpr int32_t NEED_SP_FIX = 2; @@ -209,6 +220,8 @@ NPNR_PACKED_STRUCT(struct Extra_chip_data_POD { static constexpr int32_t HAS_CIN_MUX = 512; static constexpr int32_t NEED_BSRAM_RESET_FIX = 1024; static constexpr int32_t NEED_SDP_FIX = 2048; + static constexpr int32_t NEED_CFGPINS_INVERSION = 4096; + static constexpr int32_t HAS_I2CCFG = 8192; }); } // namespace diff --git a/himbaechel/uarch/gowin/gowin_arch_gen.py b/himbaechel/uarch/gowin/gowin_arch_gen.py index 6a9668af..4cb8c095 100644 --- a/himbaechel/uarch/gowin/gowin_arch_gen.py +++ b/himbaechel/uarch/gowin/gowin_arch_gen.py @@ -15,21 +15,24 @@ from apycula import chipdb BEL_FLAG_SIMPLE_IO = 0x100 # Wire flags -WIRE_FLAG_CLOCK_GATE = 0x1 +WIRE_FLAG_CLOCK_GATE = 0x1 +WIRE_FLAG_BOTTOM_HALF = 0x2 # the wire is located in the bottom half of the chip # Chip flags -CHIP_HAS_SP32 = 0x1 -CHIP_NEED_SP_FIX = 0x2 -CHIP_NEED_BSRAM_OUTREG_FIX = 0x4 -CHIP_NEED_BLKSEL_FIX = 0x8 -CHIP_HAS_BANDGAP = 0x10 -CHIP_HAS_PLL_HCLK = 0x20 -CHIP_HAS_CLKDIV_HCLK = 0x40 -CHIP_HAS_PINCFG = 0x80 -CHIP_HAS_DFF67 = 0x100 -CHIP_HAS_CIN_MUX = 0x200 -CHIP_NEED_BSRAM_RESET_FIX = 0x400 -CHIP_NEED_SDP_FIX = 0x800 +CHIP_HAS_SP32 = 0x1 +CHIP_NEED_SP_FIX = 0x2 +CHIP_NEED_BSRAM_OUTREG_FIX = 0x4 +CHIP_NEED_BLKSEL_FIX = 0x8 +CHIP_HAS_BANDGAP = 0x10 +CHIP_HAS_PLL_HCLK = 0x20 +CHIP_HAS_CLKDIV_HCLK = 0x40 +CHIP_HAS_PINCFG = 0x80 +CHIP_HAS_DFF67 = 0x100 +CHIP_HAS_CIN_MUX = 0x200 +CHIP_NEED_BSRAM_RESET_FIX = 0x400 +CHIP_NEED_SDP_FIX = 0x800 +CHIP_NEED_CFGPINS_INVERSION = 0x1000 +CHIP_HAS_I2CCFG = 0x2000 # Tile flags TILE_I3C_CAPABLE_IO = 0x1 @@ -265,6 +268,23 @@ class Segment(BBAStruct): bba.slice(f"{context}_top_gate_wire", len(self.top_gate_wire)) bba.slice(f"{context}_bottom_gate_wire", len(self.bottom_gate_wire)) +@dataclass +class SpineSelectWire(BBAStruct): + spine: IdString + x: int + y: int + wire: IdString + vcc_gnd: int + + def serialise_lists(self, context: str, bba: BBAWriter): + pass + def serialise(self, context: str, bba: BBAWriter): + bba.u32(self.spine.index) + bba.u16(self.x) + bba.u16(self.y) + bba.u32(self.wire.index) + bba.u32(self.vcc_gnd) + @dataclass class ChipExtraData(BBAStruct): strs: StringPool @@ -277,6 +297,8 @@ class ChipExtraData(BBAStruct): dhcen_bels: list[WireBel] = field(default_factory = list) io_dlldly_bels: list[IoBel] = field(default_factory = list) segments: list[Segment] = field(default_factory = list) + spine_select_wires_top: list[SpineSelectWire] = field(default_factory = list) + spine_select_wires_bottom: list[SpineSelectWire] = field(default_factory = list) def set_dcs_prefix(self, prefix: str): self.dcs_prefix = self.strs.id(prefix) @@ -301,6 +323,7 @@ class ChipExtraData(BBAStruct): def add_io_dlldly_bel(self, io: str, dlldly: str): self.io_dlldly_bels.append(IoBel(self.strs.id(io), self.strs.id(dlldly))) + def add_segment(self, x: int, seg_idx: int, min_x: int, min_y: int, max_x: int, max_y: int, top_row: int, bottom_row: int, top_wire: str, bottom_wire: str, top_gate_wire: list, bottom_gate_wire: list): new_seg = Segment(x, seg_idx, min_x, min_y, max_x, max_y, top_row, bottom_row, @@ -316,6 +339,12 @@ class ChipExtraData(BBAStruct): new_seg.bottom_gate_wire.append(self.strs.id('')) self.segments.append(new_seg) + def add_spine_select_wire_top(self, spine: str, x: int, y: int, wire: str, vcc_gnd: int): + self.spine_select_wires_top.append(SpineSelectWire(self.strs.id(spine), x, y, self.strs.id(wire), vcc_gnd)) + + def add_spine_select_wire_bottom(self, spine: str, x: int, y: int, wire: str, vcc_gnd: int): + self.spine_select_wires_bottom.append(SpineSelectWire(self.strs.id(spine), x, y, self.strs.id(wire), vcc_gnd)) + def serialise_lists(self, context: str, bba: BBAWriter): self.bottom_io.serialise_lists(f"{context}_bottom_io", bba) for i, t in enumerate(self.segments): @@ -338,6 +367,12 @@ class ChipExtraData(BBAStruct): bba.label(f"{context}_segments") for i, t in enumerate(self.segments): t.serialise(f"{context}_segment{i}", bba) + bba.label(f"{context}_spine_select_wires_top") + for i, t in enumerate(self.spine_select_wires_top): + t.serialise(f"{context}_spine_select_wire_top{i}", bba) + bba.label(f"{context}_spine_select_wires_bottom") + for i, t in enumerate(self.spine_select_wires_bottom): + t.serialise(f"{context}_spine_select_wire_bottom{i}", bba) def serialise(self, context: str, bba: BBAWriter): bba.u32(self.flags) @@ -349,6 +384,8 @@ class ChipExtraData(BBAStruct): bba.slice(f"{context}_dhcen_bels", len(self.dhcen_bels)) bba.slice(f"{context}_io_dlldly_bels", len(self.io_dlldly_bels)) bba.slice(f"{context}_segments", len(self.segments)) + bba.slice(f"{context}_spine_select_wires_top", len(self.spine_select_wires_top)) + bba.slice(f"{context}_spine_select_wires_bottom", len(self.spine_select_wires_bottom)) @dataclass class PackageExtraData(BBAStruct): @@ -527,12 +564,17 @@ def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int): tt.create_pip(src, dst, get_tm_class(db, src)) # clock wires + # always mark clock wires with location flag for dst, srcs in db.grid[y][x].clock_pips.items(): if not tt.has_wire(dst): - tt.create_wire(dst, "GLOBAL_CLK") + wire = tt.create_wire(dst, "GLOBAL_CLK") + if hasattr(db, "last_top_row") and y > db.last_top_row: + wire.flags |= WIRE_FLAG_BOTTOM_HALF for src in srcs.keys(): if not tt.has_wire(src): - tt.create_wire(src, "GLOBAL_CLK") + wire = tt.create_wire(src, "GLOBAL_CLK") + if hasattr(db, "last_top_row") and y > db.last_top_row: + wire.flags |= WIRE_FLAG_BOTTOM_HALF src_tm_class = get_tm_class(db, src) tt.create_pip(src, dst, src_tm_class) @@ -1466,6 +1508,17 @@ def create_extra_data(chip: Chip, db: chipdb, chip_flags: int): node.append(NodeWire(col, row, f'LB{idx}1')) chip.add_node(node) chip.add_node(lt_node) + # create spine select wires + if hasattr(db, "spine_select_wires"): + if 'top' in db.spine_select_wires: + for spine, wire_desc in db.spine_select_wires['top'].items(): + for y, x, wire, vcc_gnd in wire_desc: + chip.extra_data.add_spine_select_wire_top(spine, x, y, wire, vcc_gnd) + + if 'bottom' in db.spine_select_wires: + for spine, wire_desc in db.spine_select_wires['bottom'].items(): + for y, x, wire, vcc_gnd in wire_desc: + chip.extra_data.add_spine_select_wire_bottom(spine, x, y, wire, vcc_gnd) def create_timing_info(chip: Chip, db: chipdb.Device): def group_to_timingvalue(group): @@ -1636,6 +1689,10 @@ def main(): chip_flags |= CHIP_NEED_BSRAM_RESET_FIX; if "NEED_SDP_FIX" in db.chip_flags: chip_flags |= CHIP_NEED_SDP_FIX; + if "NEED_CFGPINS_INVERSION" in db.chip_flags: + chip_flags |= CHIP_NEED_CFGPINS_INVERSION; + if "CHIP_HAS_I2CCFG" in db.chip_flags: + chip_flags |= CHIP_HAS_I2CCFG; X = db.cols; Y = db.rows; diff --git a/himbaechel/uarch/gowin/gowin_utils.cc b/himbaechel/uarch/gowin/gowin_utils.cc index aa7a1be0..390c553f 100644 --- a/himbaechel/uarch/gowin/gowin_utils.cc +++ b/himbaechel/uarch/gowin/gowin_utils.cc @@ -350,18 +350,51 @@ bool GowinUtils::has_BANDGAP(void) return extra->chip_flags & Extra_chip_data_POD::HAS_BANDGAP; } -bool GowinUtils::has_PINCFG(void) +bool GowinUtils::has_PINCFG(void) const { const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); return extra->chip_flags & Extra_chip_data_POD::HAS_PINCFG; } +bool GowinUtils::need_CFGPINS_INVERSION(void) const +{ + const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); + return extra->chip_flags & Extra_chip_data_POD::NEED_CFGPINS_INVERSION; +} + +bool GowinUtils::has_I2CCFG(void) const +{ + const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); + return extra->chip_flags & Extra_chip_data_POD::HAS_I2CCFG; +} + bool GowinUtils::has_DFF67(void) const { const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); return extra->chip_flags & Extra_chip_data_POD::HAS_DFF67; } +bool GowinUtils::has_spine_enable_nets(void) const +{ + const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); + return extra->spine_select_wires_top.ssize() || extra->spine_select_wires_bottom.ssize(); +} + +bool GowinUtils::get_spine_select_wire(WireId spine, std::vector> &wires) +{ + const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); + + wires.clear(); + for (auto &rec : wire_in_bottom_half(spine) ? extra->spine_select_wires_bottom : extra->spine_select_wires_top) { + if (IdString(rec.spine) == ctx->getWireName(spine)[1]) { + IdString tile = ctx->idf("X%dY%d", rec.x, rec.y); + IdStringList name = IdStringList::concat(tile, IdString(rec.wire)); + wires.push_back(std::make_pair(ctx->getWireByName(name), rec.vcc_gnd)); + } + } + return !wires.empty(); +} + bool GowinUtils::has_CIN_MUX(void) const { const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); diff --git a/himbaechel/uarch/gowin/gowin_utils.h b/himbaechel/uarch/gowin/gowin_utils.h index 2b6fbf82..4255b8e3 100644 --- a/himbaechel/uarch/gowin/gowin_utils.h +++ b/himbaechel/uarch/gowin/gowin_utils.h @@ -14,7 +14,8 @@ static constexpr uint32_t FLAG_SIMPLE_IO = 0x100; namespace WireFlags { static constexpr uint32_t FLAG_CLOCK_GATE = 0x1; -} +static constexpr uint32_t FLAG_BOTTOM_HALF = 0x2; +} // namespace WireFlags struct GowinUtils { @@ -96,6 +97,12 @@ struct GowinUtils return chip_wire_info(ctx->chip_info, wire).flags & WireFlags::FLAG_CLOCK_GATE; } + bool wire_in_bottom_half(WireId wire) const + { + return chip_wire_info(ctx->chip_info, wire).flags & WireFlags::FLAG_BOTTOM_HALF; + } + bool get_spine_select_wire(WireId spine, std::vector> &); + // BSRAM bool has_SP32(void); bool need_SP_fix(void); @@ -110,7 +117,9 @@ struct GowinUtils bool has_BANDGAP(void); // Pin function configuration via wires - bool has_PINCFG(void); + bool has_PINCFG(void) const; + bool need_CFGPINS_INVERSION(void) const; + bool has_I2CCFG(void) const; // Logic cell structure bool has_DFF67(void) const; @@ -118,6 +127,9 @@ struct GowinUtils // ALU bool has_CIN_MUX(void) const; + // Clock MUX + bool has_spine_enable_nets(void) const; + // DSP inline int get_dsp_18_z(int z) const { return z & (~3); } inline int get_dsp_9_idx(int z) const { return z & 3; } diff --git a/himbaechel/uarch/gowin/pack.cc b/himbaechel/uarch/gowin/pack.cc index 730f297e..47b7c26e 100644 --- a/himbaechel/uarch/gowin/pack.cc +++ b/himbaechel/uarch/gowin/pack.cc @@ -3959,10 +3959,15 @@ struct GowinPacker auto pincfg_cell = std::make_unique(ctx, id_PINCFG, id_PINCFG); - for (int i = 0; i < 5; ++i) { + const int pin_cnt = gwu.has_I2CCFG() ? 5 : 4; + for (int i = 0; i < pin_cnt; ++i) { IdString port = ctx->idf("UNK%d_VCC", i); pincfg_cell->addInput(port); - pincfg_cell->connectPort(port, ctx->nets.at(ctx->id("$PACKER_VCC")).get()); + if (i && gwu.need_CFGPINS_INVERSION()) { + pincfg_cell->connectPort(port, ctx->nets.at(ctx->id("$PACKER_GND")).get()); + } else { + pincfg_cell->connectPort(port, ctx->nets.at(ctx->id("$PACKER_VCC")).get()); + } } const ArchArgs &args = ctx->args; @@ -3975,12 +3980,14 @@ struct GowinPacker pincfg_cell->connectPort(id_SSPI, ctx->nets.at(ctx->id("$PACKER_GND")).get()); } - pincfg_cell->addInput(id_I2C); - if (args.options.count("i2c_as_gpio")) { - pincfg_cell->connectPort(id_I2C, ctx->nets.at(ctx->id("$PACKER_VCC")).get()); - pincfg_cell->setParam(id_I2C, 1); - } else { - pincfg_cell->connectPort(id_I2C, ctx->nets.at(ctx->id("$PACKER_GND")).get()); + if (gwu.has_I2CCFG()) { + pincfg_cell->addInput(id_I2C); + if (args.options.count("i2c_as_gpio")) { + pincfg_cell->connectPort(id_I2C, ctx->nets.at(ctx->id("$PACKER_VCC")).get()); + pincfg_cell->setParam(id_I2C, 1); + } else { + pincfg_cell->connectPort(id_I2C, ctx->nets.at(ctx->id("$PACKER_GND")).get()); + } } ctx->cells[pincfg_cell->name] = std::move(pincfg_cell); }