diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index 2588a3e8..3012d0d3 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -1345,6 +1345,22 @@ X(HCLK_OUT0) X(HCLK_OUT1) X(HCLK_OUT2) X(HCLK_OUT3) +X(HCLK00) +X(HCLK01) +X(HCLK02) +X(HCLK03) +X(HCLK10) +X(HCLK11) +X(HCLK12) +X(HCLK13) +X(HCLK20) +X(HCLK21) +X(HCLK22) +X(HCLK23) +X(HCLK30) +X(HCLK31) +X(HCLK32) +X(HCLK33) // BUFG, clock buffers stuff X(CLKSEL) diff --git a/himbaechel/uarch/gowin/gowin.cc b/himbaechel/uarch/gowin/gowin.cc index 0753a607..40121ff3 100644 --- a/himbaechel/uarch/gowin/gowin.cc +++ b/himbaechel/uarch/gowin/gowin.cc @@ -110,6 +110,7 @@ struct GowinImpl : HimbaechelAPI // Place explicityl constrained or implicitly constrained (by IOLOGIC) CLKDIV and CLKDIV2 cells // to avoid routing conflicts and maximize utilization void place_constrained_hclk_cells(); + void place_5a_hclks(void); // bel placement validation bool slice_valid(int x, int y, int z) const; @@ -333,6 +334,179 @@ void GowinImpl::adjust_dsp_pin_mapping(void) } } +// GW5A HCLK +// Each serializer/deserializer can use one of several HCLK wire from a block +// (hclk_idx), but only from the specific block assigned to that +// serializer/deserializer. +// Each HCLK line can be routed through a CLKDIV2 primitive, which halves the signal frequency. +// +// Thus, if the SERDES uses CLKDIV2, the latter must be placed in the specific +// HCLK block corresponding to the SERDES (since the SERDES is part of the I/O, +// and we do not allow unconstrained I/O, the placement of the SERDES is always +// known). +// +// Since users typically do not track which pins belong to which HCLK block, +// situations may arise where a single CLKDIV2 divider in the design serves as +// the signal source for SERDES located in different HCLK blocks, making it +// impossible to connect them. Alternatively, some SERDES in one block may +// receive the signal directly, while others receive it from the CLKDIV2. +// +// Here, we solve this by duplicating the CLKDIV2 cells and placing them +// exactly where they serve specific HCLK blocks. +// +// Information regarding which IO corresponds to a specific HCLK, as well as +// the placement of CLKDIV2 for that specific HCLK, is retrieved from the +// chip's database. +// +void GowinImpl::place_5a_hclks(void) +{ + std::vector> new_cells; + // Cloned CLKDIV2 cells + dict clkdiv2_clones; + // Which users need to be reconnected, and to where. + std::vector> users_to_reconect; + // CLKDIV2 allocator + dict> free_clkdiv2; + + // SERDES can use any wire from the HCLK block; here, we select a wire from + // the unused ones and return the CLKDIV2 location that serves that wire. + auto alloc_clkdiv2 = [&](int hclk_idx, CellInfo *ci) -> Loc { + // first allocation for hclk_idx + if (!free_clkdiv2.count(hclk_idx)) { + std::vector locs; + gwu.get_clkdiv2_locs(hclk_idx, locs); + free_clkdiv2[hclk_idx] = locs; + } + + if (!free_clkdiv2.at(hclk_idx).size()) { + log_error("Can't place %s CLKDIV2.\n", ctx->nameOf(ci)); + } + Loc loc = free_clkdiv2.at(hclk_idx).back(); + free_clkdiv2.at(hclk_idx).pop_back(); + return loc; + }; + + for (auto &cell : ctx->cells) { + auto ci = cell.second.get(); + + // The CLKDIV2s described in the design + if (is_clkdiv2(ci)) { + NetInfo *hclk_net = ci->getPort(id_CLKOUT); + if (!hclk_net || !ci->getPort(id_HCLKIN)) { + continue; + } + if (ctx->debug) { + log_info(" CLKDIV2 cell:%s, HCLKIN:%s, CLKOUT:%s\n", ctx->nameOf(ci), + ctx->nameOf(ci->getPort(id_HCLKIN)), ctx->nameOf(hclk_net)); + } + + clkdiv2_clones.clear(); + users_to_reconect.clear(); + int cur_clkdiv2_hclk_idx = -1; + + // SERDES only + for (auto user : hclk_net->users) { + if (!is_iologico(user.cell) && !is_iologici(user.cell)) { + continue; + } + // checking users' hclk index + NPNR_ASSERT(user.cell->bel != BelId()); + Loc user_loc = ctx->getBelLocation(user.cell->bel); + int hclk_idx = gwu.get_hclk_for_io(user_loc); + if (hclk_idx == -1) { + log_error("%s can't use HCLK with %s.\n", ctx->nameOf(user.cell), ctx->nameOf(ci)); + } + + if (cur_clkdiv2_hclk_idx == -1) { + // Place CLKDIV2 + cur_clkdiv2_hclk_idx = hclk_idx; + BelId bel = ctx->getBelByLocation(alloc_clkdiv2(hclk_idx, ci)); + ctx->bindBel(bel, ci, PlaceStrength::STRENGTH_LOCKED); + if (ctx->debug) { + log_info(" @%s\n", ctx->nameOfBel(bel)); + } + if (ctx->debug) { + log_info(" hclk:%d - %s %s\n", hclk_idx, ctx->nameOfBel(user.cell->bel), + ctx->nameOf(user.cell)); + } + continue; + } + + // If the SERDES is in the current HCLK block, it remains there + if (cur_clkdiv2_hclk_idx == hclk_idx) { + if (ctx->debug) { + log_info(" hclk:%d - %s %s\n", hclk_idx, ctx->nameOfBel(user.cell->bel), + ctx->nameOf(user.cell)); + } + continue; + } + + // Since the SERDES is located in a different HCLK block, we + // need to create a copy of CLKDIV2 and save the SERDES + // for later connection to the copy. + CellInfo *new_clkdiv2; + if (clkdiv2_clones.count(hclk_idx)) { + // already have clone + new_clkdiv2 = clkdiv2_clones.at(hclk_idx); + } else { + // create clone + new_cells.push_back(gwu.create_cell(gwu.create_aux_name(ci->name, hclk_idx), id_CLKDIV2)); + new_clkdiv2 = new_cells.back().get(); + new_clkdiv2->addInput(id_HCLKIN); + new_clkdiv2->addOutput(id_CLKOUT); + clkdiv2_clones[hclk_idx] = new_clkdiv2; + if (ctx->debug) { + log_info(" create clone for hclk:%d - %s\n", hclk_idx, ctx->nameOf(new_clkdiv2)); + } + } + users_to_reconect.push_back(std::make_pair(user, new_clkdiv2)); + } + // move the SERDES by connecting it to a copy of CLKDIV2 + for (auto us_ci : users_to_reconect) { + PortRef user = us_ci.first; + CellInfo *new_clkdiv2 = us_ci.second; + if (ctx->debug) { + log_info(" reconnect %s.%s to %s\n", ctx->nameOf(user.cell), user.port.c_str(ctx), + ctx->nameOf(new_clkdiv2)); + } + // input is same + if (!new_clkdiv2->getPort(id_HCLKIN)) { + ci->copyPortTo(id_HCLKIN, new_clkdiv2, id_HCLKIN); + } + // move user + user.cell->disconnectPort(user.port); + new_clkdiv2->connectPorts(id_CLKOUT, user.cell, user.port); + } + } + } + + // Place new CLKDIV2 + for (auto &ncell : new_cells) { + CellInfo *ci = ncell.get(); + NetInfo *hclk_net = ci->getPort(id_CLKOUT); + if (ctx->debug) { + log_info(" CLKDIV2 cell:%s, HCLKIN:%s, CLKOUT:%s\n", ctx->nameOf(ci), ctx->nameOf(ci->getPort(id_HCLKIN)), + ctx->nameOf(hclk_net)); + } + + // Any user can be used to determine hclk_idx because we connected them + // to copies of CLKDIV2 based precisely on the fact that hclk_idx is + // the same + PortRef &user = *hclk_net->users.begin(); + Loc user_loc = ctx->getBelLocation(user.cell->bel); + int hclk_idx = gwu.get_hclk_for_io(user_loc); + + BelId bel = ctx->getBelByLocation(alloc_clkdiv2(hclk_idx, ci)); + ctx->bindBel(bel, ci, PlaceStrength::STRENGTH_LOCKED); + if (ctx->debug) { + log_info(" @%s\n", ctx->nameOfBel(bel)); + log_info(" hclk:%d - %s %s\n", hclk_idx, ctx->nameOfBel(user.cell->bel), ctx->nameOf(user.cell)); + } + + ctx->cells[ncell->name] = std::move(ncell); + } +} + /* Each HCLK section can serve one of three purposes: 1. A simple routing path to IOLOGIC FCLK or PLL inputs @@ -348,6 +522,11 @@ void GowinImpl::adjust_dsp_pin_mapping(void) void GowinImpl::place_constrained_hclk_cells() { log_info("Running custom HCLK placer...\n"); + if (gwu.has_5A_HCLK()) { + place_5a_hclks(); + return; + } + std::map constrained_clkdivs; std::map>> bel_cell_map; std::vector> alias_cells; @@ -620,6 +799,7 @@ void GowinImpl::place_constrained_hclk_cells() void GowinImpl::prePlace() { place_constrained_hclk_cells(); + ctx->assignArchInfo(); assign_cell_info(); fast_logic_cell.reset(ctx->getGridDimX(), ctx->getGridDimY()); for (auto bel : ctx->getBels()) { @@ -632,7 +812,6 @@ void GowinImpl::prePlace() void GowinImpl::postPlace() { - gwu.has_SP32(); if (ctx->debug) { log_info("================== Final Placement ===================\n"); for (auto &cell : ctx->cells) { @@ -673,11 +852,21 @@ void GowinImpl::postRoute() visited_hclk_users.insert(user.cell->name); PipId up_pip = h_net->wires.at(ctx->getNetinfoSinkWire(h_net, user, 0)).pip; IdString up_wire_name = ctx->getWireName(ctx->getPipSrcWire(up_pip))[1]; - if (up_wire_name.in(id_HCLK_OUT0, id_HCLK_OUT1, id_HCLK_OUT2, id_HCLK_OUT3)) { - user.cell->setAttr(id_IOLOGIC_FCLK, Property(up_wire_name.str(ctx))); - if (ctx->debug) { - log_info("set IOLOGIC_FCLK to %s\n", up_wire_name.c_str(ctx)); + if (!gwu.has_5A_HCLK()) { + if (up_wire_name.in(id_HCLK_OUT0, id_HCLK_OUT1, id_HCLK_OUT2, id_HCLK_OUT3)) { + user.cell->setAttr(id_IOLOGIC_FCLK, Property(up_wire_name.str(ctx))); + if (ctx->debug) { + log_info("set IOLOGIC_FCLK to %s\n", up_wire_name.c_str(ctx)); + } } + } else if (up_wire_name.in(id_HCLK00, id_HCLK10, id_HCLK20, id_HCLK30)) { + user.cell->setAttr(id_IOLOGIC_FCLK, Property("HCLK_OUT0")); + } else if (up_wire_name.in(id_HCLK01, id_HCLK11, id_HCLK21, id_HCLK31)) { + user.cell->setAttr(id_IOLOGIC_FCLK, Property("HCLK_OUT1")); + } else if (up_wire_name.in(id_HCLK02, id_HCLK12, id_HCLK22, id_HCLK32)) { + user.cell->setAttr(id_IOLOGIC_FCLK, Property("HCLK_OUT2")); + } else if (up_wire_name.in(id_HCLK03, id_HCLK13, id_HCLK23, id_HCLK33)) { + user.cell->setAttr(id_IOLOGIC_FCLK, Property("HCLK_OUT3")); } if (ctx->debug) { log_info("HCLK user cell:%s, port:%s, wire:%s, pip:%s, up wire:%s\n", diff --git a/himbaechel/uarch/gowin/gowin.h b/himbaechel/uarch/gowin/gowin.h index 8dd13a5b..efbabad1 100644 --- a/himbaechel/uarch/gowin/gowin.h +++ b/himbaechel/uarch/gowin/gowin.h @@ -199,6 +199,19 @@ NPNR_PACKED_STRUCT(struct Constraint_POD { int32_t iostd; }); +NPNR_PACKED_STRUCT(struct Io2Hclk_POD { + int16_t x; + int16_t y; + int32_t hclk_idx; +}); + +NPNR_PACKED_STRUCT(struct HclkDiv2_POD { + int16_t hclk_idx; + int16_t x; + int16_t y; + int16_t z; +}); + NPNR_PACKED_STRUCT(struct Extra_package_data_POD { RelSlice cst; }); NPNR_PACKED_STRUCT(struct Extra_chip_data_POD { @@ -215,6 +228,8 @@ NPNR_PACKED_STRUCT(struct Extra_chip_data_POD { RelSlice segments; RelSlice spine_select_wires_top; RelSlice spine_select_wires_bottom; + RelSlice io_to_hclk; + RelSlice hclk_div2; // chip flags static constexpr int32_t HAS_SP32 = 1; @@ -233,6 +248,7 @@ NPNR_PACKED_STRUCT(struct Extra_chip_data_POD { static constexpr int32_t HAS_I2CCFG = 8192; static constexpr int32_t HAS_5A_DSP = 16384; static constexpr int32_t NEED_BSRAM_DP_CE_FIX = 32768; + static constexpr int32_t HAS_5A_HCLK = 65536; }); } // namespace diff --git a/himbaechel/uarch/gowin/gowin_arch_gen.py b/himbaechel/uarch/gowin/gowin_arch_gen.py index 3678c8fa..edb7e5ce 100644 --- a/himbaechel/uarch/gowin/gowin_arch_gen.py +++ b/himbaechel/uarch/gowin/gowin_arch_gen.py @@ -35,6 +35,7 @@ CHIP_NEED_CFGPINS_INVERSION = 0x1000 CHIP_HAS_I2CCFG = 0x2000 CHIP_HAS_5A_DSP = 0x4000 CHIP_NEED_BSRAM_DP_CE_FIX = 0x8000 +CHIP_HAS_5A_HCLK = 0x10000 # Tile flags TILE_I3C_CAPABLE_IO = 0x1 @@ -292,6 +293,35 @@ class SpineSelectWire(BBAStruct): bba.u32(self.wire.index) bba.u32(self.vcc_gnd) +@dataclass +class Io2Hclk(BBAStruct): + x: int + y: int + hclk_idx: int + + def serialise_lists(self, context: str, bba: BBAWriter): + pass + def serialise(self, context: str, bba: BBAWriter): + bba.u16(self.x) + bba.u16(self.y) + bba.u32(self.hclk_idx) + +@dataclass +class HclkDiv2(BBAStruct): + hclk_idx: int + # CLKDIV2 location + x: int + y: int + z: int + + def serialise_lists(self, context: str, bba: BBAWriter): + pass + def serialise(self, context: str, bba: BBAWriter): + bba.u16(self.hclk_idx) + bba.u16(self.x) + bba.u16(self.y) + bba.u16(self.z) + @dataclass class ChipExtraData(BBAStruct): strs: StringPool @@ -308,6 +338,8 @@ class ChipExtraData(BBAStruct): 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) + io_to_hclk: list[Io2Hclk] = field(default_factory = list) + hclk_div2: list[HclkDiv2] = field(default_factory = list) def set_dcs_prefix(self, prefix: str): self.dcs_prefix = self.strs.id(prefix) @@ -354,6 +386,12 @@ class ChipExtraData(BBAStruct): 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 add_io2hclk(self, hclk_idx: int, x: int, y: int): + self.io_to_hclk.append(Io2Hclk(x, y, hclk_idx)) + + def add_hclkdiv2(self, hclk_idx: int, x: int, y: int, z: int): + self.hclk_div2.append(HclkDiv2(hclk_idx, x, y, z)) + 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): @@ -382,6 +420,12 @@ class ChipExtraData(BBAStruct): 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) + bba.label(f"{context}_io_to_hclk") + for i, t in enumerate(self.io_to_hclk): + t.serialise(f"{context}_io_to_hclk{i}", bba) + bba.label(f"{context}_hclk_div2") + for i, t in enumerate(self.hclk_div2): + t.serialise(f"{context}_hclk_div2{i}", bba) def serialise(self, context: str, bba: BBAWriter): bba.u32(self.flags) @@ -397,6 +441,8 @@ class ChipExtraData(BBAStruct): 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)) + bba.slice(f"{context}_io_to_hclk", len(self.io_to_hclk)) + bba.slice(f"{context}_hclk_div2", len(self.hclk_div2)) @dataclass class PackageExtraData(BBAStruct): @@ -859,6 +905,18 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int): tt.create_wire('VCC', 'VCC', const_value = 'VCC') gnd = tt.create_bel('VCC', 'VCC', z = VCC_Z) tt.add_bel_pin(gnd, "V", "VCC", PinType.OUTPUT) + elif func == 'clkdiv2': + for i, pins in desc['bels'].items(): + clkdiv2 = tt.create_bel(f"CLKDIV2_{i}", "CLKDIV2", z = CLKDIV2_0_Z + i) + for pin, wire in pins['outputs'].items(): + tt.create_wire(wire, "HCLK") + tt.add_bel_pin(clkdiv2, pin, wire, PinType.OUTPUT) + for pin, wire in pins['inputs'].items(): + if pin == 'RESETN': + tt.create_wire(wire, "") + else: + tt.create_wire(wire, "HCLK") + tt.add_bel_pin(clkdiv2, pin, wire, PinType.INPUT) def set_wire_flags(tt: TileType, tdesc: TypeDesc): @@ -1617,6 +1675,14 @@ def create_extra_data(chip: Chip, db: chipdb, chip_flags: int): 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) + # create HCLK<->IO and HCLK<->CLKDIV2 + if hasattr(db, "io2hclk"): + for hclk_idx, ios in db.io2hclk.items(): + for row_col in ios: + chip.extra_data.add_io2hclk(hclk_idx, row_col[1], row_col[0]) + for hclk_idx, div2 in db.hclk_div2.items(): + for row_col_idx in div2: + chip.extra_data.add_hclkdiv2(hclk_idx, row_col_idx[1], row_col_idx[0], row_col_idx[2] + CLKDIV2_0_Z) def create_timing_info(chip: Chip, db: chipdb.Device): def group_to_timingvalue(group): @@ -1847,6 +1913,8 @@ def main(): chip_flags |= CHIP_HAS_I2CCFG; if "HAS_5A_DSP" in db.chip_flags: chip_flags |= CHIP_HAS_5A_DSP; + if "HAS_5A_HCLK" in db.chip_flags: + chip_flags |= CHIP_HAS_5A_HCLK; X = db.cols; Y = db.rows; diff --git a/himbaechel/uarch/gowin/gowin_utils.cc b/himbaechel/uarch/gowin/gowin_utils.cc index 5b675c61..3a7f4038 100644 --- a/himbaechel/uarch/gowin/gowin_utils.cc +++ b/himbaechel/uarch/gowin/gowin_utils.cc @@ -368,7 +368,7 @@ IdString GowinUtils::get_bottom_io_wire_b_net(int8_t condition) return IdString(extra->bottom_io.conditions[condition].wire_b_net); } -bool GowinUtils::has_BANDGAP(void) +bool GowinUtils::has_BANDGAP(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_BANDGAP; @@ -425,60 +425,66 @@ bool GowinUtils::has_CIN_MUX(void) const return extra->chip_flags & Extra_chip_data_POD::HAS_CIN_MUX; } -bool GowinUtils::has_SP32(void) +bool GowinUtils::has_SP32(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_SP32; } -bool GowinUtils::need_SP_fix(void) +bool GowinUtils::need_SP_fix(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_SP_FIX; } -bool GowinUtils::need_SDP_fix(void) +bool GowinUtils::need_SDP_fix(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_SDP_FIX; } -bool GowinUtils::need_BSRAM_OUTREG_fix(void) +bool GowinUtils::need_BSRAM_OUTREG_fix(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_BSRAM_OUTREG_FIX; } -bool GowinUtils::need_BSRAM_DP_CE_fix(void) +bool GowinUtils::need_BSRAM_DP_CE_fix(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_BSRAM_DP_CE_FIX; } -bool GowinUtils::need_BSRAM_RESET_fix(void) +bool GowinUtils::need_BSRAM_RESET_fix(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_BSRAM_RESET_FIX; } -bool GowinUtils::need_BLKSEL_fix(void) +bool GowinUtils::need_BLKSEL_fix(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_BLKSEL_FIX; } -bool GowinUtils::has_PLL_HCLK(void) +bool GowinUtils::has_PLL_HCLK(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_PLL_HCLK; } -bool GowinUtils::has_CLKDIV_HCLK(void) +bool GowinUtils::has_CLKDIV_HCLK(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_CLKDIV_HCLK; } +bool GowinUtils::has_5A_HCLK(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_5A_HCLK; +} + IdString GowinUtils::create_aux_name(IdString main_name, int idx, const char *str_suffix) { return idx ? ctx->idf("%s%s%d", main_name.c_str(ctx), str_suffix, idx) @@ -634,6 +640,29 @@ CellInfo *GowinUtils::dsp_bus_dst(const CellInfo *ci, const char *bus_prefix, in return connected_to_cell; } +int GowinUtils::get_hclk_for_io(Loc io_loc) const +{ + const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); + for (auto &io_hclk : extra->io_to_hclk) { + if (io_hclk.x == io_loc.x && io_hclk.y == io_loc.y) { + return io_hclk.hclk_idx; + } + } + return -1; +} + +// Location of clkdiv2 for HCLK +void GowinUtils::get_clkdiv2_locs(int hclk_idx, std::vector &locs) +{ + const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); + locs.clear(); + for (auto &hclk_div2 : extra->hclk_div2) { + if (hclk_div2.hclk_idx == hclk_idx) { + locs.push_back(Loc(hclk_div2.x, hclk_div2.y, hclk_div2.z)); + } + } +} + // Use the upper CLKDIV as the id for a hclk section IdStringList GowinUtils::get_hclk_id(BelId hclk_bel) const { diff --git a/himbaechel/uarch/gowin/gowin_utils.h b/himbaechel/uarch/gowin/gowin_utils.h index 6e960855..f6a98a43 100644 --- a/himbaechel/uarch/gowin/gowin_utils.h +++ b/himbaechel/uarch/gowin/gowin_utils.h @@ -106,18 +106,19 @@ struct GowinUtils bool get_spine_select_wire(WireId spine, std::vector> &); // BSRAM - bool has_SP32(void); - bool need_SP_fix(void); - bool need_SDP_fix(void); - bool need_BSRAM_OUTREG_fix(void); - bool need_BSRAM_DP_CE_fix(void); - bool need_BSRAM_RESET_fix(void); - bool need_BLKSEL_fix(void); - bool has_PLL_HCLK(void); - bool has_CLKDIV_HCLK(void); + bool has_SP32(void) const; + bool need_SP_fix(void) const; + bool need_SDP_fix(void) const; + bool need_BSRAM_OUTREG_fix(void) const; + bool need_BSRAM_DP_CE_fix(void) const; + bool need_BSRAM_RESET_fix(void) const; + bool need_BLKSEL_fix(void) const; + bool has_PLL_HCLK(void) const; + bool has_CLKDIV_HCLK(void) const; + bool has_5A_HCLK(void) const; // Power saving - bool has_BANDGAP(void); + bool has_BANDGAP(void) const; // Pin function configuration via wires bool has_PINCFG(void) const; @@ -192,6 +193,8 @@ struct GowinUtils std::unique_ptr create_cell(IdString name, IdString type); // HCLK + void get_clkdiv2_locs(int hclk_idx, std::vector &locs); + int get_hclk_for_io(Loc io_loc) const; BelId get_clkdiv_for_clkdiv2(BelId clkdiv2_bel) const; BelId get_other_hclk_clkdiv2(BelId clkdiv2_bel) const; BelId get_other_hclk_clkdiv(BelId clkdiv_bel) const; diff --git a/himbaechel/uarch/gowin/pack_iologic.cc b/himbaechel/uarch/gowin/pack_iologic.cc index 38759523..80428e79 100644 --- a/himbaechel/uarch/gowin/pack_iologic.cc +++ b/himbaechel/uarch/gowin/pack_iologic.cc @@ -203,7 +203,7 @@ void GowinPacker::pack_single_output_iol(CellInfo &ci, std::vector &ne out_mode = "EMPTY"; break; case ID_OVIDEO: - out_mode = "VIDEORX"; + out_mode = "VIDEOTX"; break; case ID_OSER10: out_mode = "ODDRX5";