From abb52f81c2504f637705d4f3734a2e70dd3bc188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miodrag=20Milanovi=C4=87?= Date: Tue, 30 Sep 2025 13:00:02 +0200 Subject: [PATCH] gatemate: cleanup of PLL and BUFG (#1562) * Check SER_CLK more * Use connectPorts * move rewire code * Move data structures * move placement decision for later * cleanups * find working layout * clangformat * Inverted input on ODDR * Fix some tests * Copy clocks for multi die * cleanup * reporting * bugfix * handle PLL special inputs * Fix user globals * Proper DDR per bank and cleanup * Add extra data for die regions and create them * Better forced_die implementation * Copy region to newly generated cells, and update when constrained * Update PLL error messages * Add TODO comment --- himbaechel/uarch/gatemate/ccf.cc | 5 + himbaechel/uarch/gatemate/cells.cc | 15 + himbaechel/uarch/gatemate/constids.inc | 6 + himbaechel/uarch/gatemate/extra_data.h | 10 + himbaechel/uarch/gatemate/gatemate.cc | 25 +- himbaechel/uarch/gatemate/gatemate.h | 6 + himbaechel/uarch/gatemate/gen/arch_gen.py | 38 ++ himbaechel/uarch/gatemate/pack.cc | 17 +- himbaechel/uarch/gatemate/pack.h | 9 +- himbaechel/uarch/gatemate/pack_bram.cc | 7 +- himbaechel/uarch/gatemate/pack_clocking.cc | 481 +++++++++++++++------ himbaechel/uarch/gatemate/pack_cpe.cc | 20 + himbaechel/uarch/gatemate/pack_io.cc | 35 +- himbaechel/uarch/gatemate/pack_mult.cc | 2 + himbaechel/uarch/gatemate/tests/lut.cc | 22 +- himbaechel/uarch/gatemate/tests/testing.cc | 7 + himbaechel/uarch/gatemate/tests/testing.h | 1 + 17 files changed, 533 insertions(+), 173 deletions(-) diff --git a/himbaechel/uarch/gatemate/ccf.cc b/himbaechel/uarch/gatemate/ccf.cc index 0913a6c4..85baca90 100644 --- a/himbaechel/uarch/gatemate/ccf.cc +++ b/himbaechel/uarch/gatemate/ccf.cc @@ -94,6 +94,11 @@ struct GateMateCCFReader count[i]--; } uarch->available_pads.erase(ctx->id(value)); + // Erase aliases as well + if (value == "SER_CLK") + uarch->available_pads.erase(ctx->id("SER_CLK_N")); + if (value == "SER_CLK_N") + uarch->available_pads.erase(ctx->id("SER_CLK")); } else if (name == "SCHMITT_TRIGGER" || name == "PULLUP" || name == "PULLDOWN" || name == "KEEPER" || name == "FF_IBF" || name == "FF_OBF" || name == "LVDS_BOOST" || name == "LVDS_RTERM") { if (value == "1") diff --git a/himbaechel/uarch/gatemate/cells.cc b/himbaechel/uarch/gatemate/cells.cc index 4f2cac56..47fdc933 100644 --- a/himbaechel/uarch/gatemate/cells.cc +++ b/himbaechel/uarch/gatemate/cells.cc @@ -122,6 +122,21 @@ CellInfo *GateMatePacker::create_cell_ptr(IdString type, IdString name) add_port(id_CLOCK2, PORT_IN); add_port(id_CLOCK3, PORT_IN); add_port(id_CLOCK4, PORT_IN); + } else if (type.in(id_CPE_IBUF)) { + add_port(id_Y, PORT_OUT); + add_port(id_I, PORT_IN); + } else if (type.in(id_PLL)) { + add_port(id_CLK_REF, PORT_IN); + add_port(id_USR_CLK_REF, PORT_IN); + add_port(id_USR_SEL_A_B, PORT_IN); + add_port(id_CLK_FEEDBACK, PORT_IN); + add_port(id_CLK0, PORT_OUT); + add_port(id_CLK90, PORT_OUT); + add_port(id_CLK180, PORT_OUT); + add_port(id_CLK270, PORT_OUT); + add_port(id_USR_PLL_LOCKED, PORT_OUT); + add_port(id_USR_PLL_LOCKED_STDY, PORT_OUT); + add_port(id_USR_LOCKED_STDY_RST, PORT_IN); } else { log_error("Trying to create unknown cell type %s\n", type.c_str(ctx)); } diff --git a/himbaechel/uarch/gatemate/constids.inc b/himbaechel/uarch/gatemate/constids.inc index 00542b9e..e82a1620 100644 --- a/himbaechel/uarch/gatemate/constids.inc +++ b/himbaechel/uarch/gatemate/constids.inc @@ -2468,6 +2468,12 @@ X(MULT_INVERT) X(RAM_HALF) X(RAM_HALF_DUMMY) +// Dies +X(1A) +X(1B) +X(2A) +X(2B) + // Timing X(timing_ADDF2x_IN5_8_comb2) X(timing_CIN_C_CINI) diff --git a/himbaechel/uarch/gatemate/extra_data.h b/himbaechel/uarch/gatemate/extra_data.h index a7923036..96e8cc5b 100644 --- a/himbaechel/uarch/gatemate/extra_data.h +++ b/himbaechel/uarch/gatemate/extra_data.h @@ -69,6 +69,16 @@ NPNR_PACKED_STRUCT(struct GateMateTimingExtraDataPOD { NPNR_PACKED_STRUCT(struct GateMateSpeedGradeExtraDataPOD { RelSlice timings; }); +NPNR_PACKED_STRUCT(struct GateMateDieRegionPOD { + int32_t name; + uint16_t x1; + uint16_t y1; + uint16_t x2; + uint16_t y2; +}); + +NPNR_PACKED_STRUCT(struct GateMateChipExtraDataPOD { RelSlice dies; }); + enum MuxFlags { MUX_INVERT = 1, diff --git a/himbaechel/uarch/gatemate/gatemate.cc b/himbaechel/uarch/gatemate/gatemate.cc index 490c4fd9..485e6952 100644 --- a/himbaechel/uarch/gatemate/gatemate.cc +++ b/himbaechel/uarch/gatemate/gatemate.cc @@ -150,6 +150,27 @@ void GateMateImpl::init(Context *ctx) ram_signal_clk.emplace(ctx->idf("DOB[%d]", i + num * 20), num + 2); } } + + const GateMateChipExtraDataPOD *extra = + reinterpret_cast(ctx->chip_info->extra_data.get()); + + const ArchArgs &args = ctx->args; + std::string die_name; + if (args.options.count("force_die")) + die_name = args.options.at("force_die"); + bool found = false; + int index = 0; + for (auto &die : extra->dies) { + IdString name(die.name); + die_to_index[name] = index++; + ctx->createRectangularRegion(name, die.x1, die.y1, die.x2, die.y2); + if (die_name == name.c_str(ctx)) { + found = true; + forced_die = name; + } + } + if (!die_name.empty() && !found) + log_error("Unable to select forced die '%s'.\n", die_name.c_str()); } bool GateMateImpl::isBelLocationValid(BelId bel, bool explain_invalid) const @@ -159,10 +180,6 @@ bool GateMateImpl::isBelLocationValid(BelId bel, bool explain_invalid) const return true; } - // TODO: remove when placemente per die is better handled - if (cell->belStrength != PlaceStrength::STRENGTH_FIXED && tile_extra_data(bel.tile)->die != preferred_die) - return false; - if (getBelBucketForBel(bel) == id_CPE_FF) { Loc loc = ctx->getBelLocation(bel); const CellInfo *adj_half = ctx->getBoundBelCell( diff --git a/himbaechel/uarch/gatemate/gatemate.h b/himbaechel/uarch/gatemate/gatemate.h index acbaf06c..8c61a42b 100644 --- a/himbaechel/uarch/gatemate/gatemate.h +++ b/himbaechel/uarch/gatemate/gatemate.h @@ -90,6 +90,10 @@ struct GateMateImpl : HimbaechelAPI std::vector used_cpes; int fpga_mode; int timing_mode; + std::map global_signals; + std::vector clkin; + std::vector glbout; + std::vector pll; private: bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc, @@ -122,6 +126,8 @@ struct GateMateImpl : HimbaechelAPI std::map> pin_to_constr; std::map timing; dict ram_signal_clk; + IdString forced_die; + dict die_to_index; }; NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/gen/arch_gen.py b/himbaechel/uarch/gatemate/gen/arch_gen.py index e4f1bbc5..f85d6990 100644 --- a/himbaechel/uarch/gatemate/gen/arch_gen.py +++ b/himbaechel/uarch/gatemate/gen/arch_gen.py @@ -161,6 +161,40 @@ class SpeedGradeExtraData(BBAStruct): def serialise(self, context: str, bba: BBAWriter): bba.slice(f"{context}_timings", len(self.timings)) +@dataclass +class DieRegion(BBAStruct): + name: IdString + x1: int = 0 + y1: int = 0 + x2: int = 0 + y2: int = 0 + + def serialise_lists(self, context: str, bba: BBAWriter): + pass + def serialise(self, context: str, bba: BBAWriter): + bba.u32(self.name.index) + bba.u16(self.x1) + bba.u16(self.y1) + bba.u16(self.x2) + bba.u16(self.y2) + +@dataclass +class ChipExtraData(BBAStruct): + dies: list[DieRegion] = field(default_factory = list) + + def add_die(self, name: IdString, x1: int, y1: int, x2:int, y2:int): + item = DieRegion(name,x1,y1,x2,y2) + self.dies.append(item) + + def serialise_lists(self, context: str, bba: BBAWriter): + self.dies.sort(key=lambda p: p.name.index) + bba.label(f"{context}_dies") + for i, t in enumerate(self.dies): + t.serialise(f"{context}_die{i}", bba) + pass + def serialise(self, context: str, bba: BBAWriter): + bba.slice(f"{context}_dies", len(self.dies)) + def convert_timing(tim): return TimingValue(tim.rise.min, tim.rise.max, tim.fall.min, tim.fall.max) @@ -222,6 +256,10 @@ def main(): print("==============================================================================") os._exit(-1) + ch.extra_data = ChipExtraData() + for d in sorted(dev.dies): + ch.extra_data.add_die(ch.strs.id(dev.dies[d].name), dev.dies[d].offset_x, dev.dies[d].offset_y, dev.dies[d].offset_x + die.num_cols() - 1, dev.dies[d].offset_y + die.num_rows()-1) + new_wires = dict() wire_delay = dict() for _,nodes in dev.get_connections(): diff --git a/himbaechel/uarch/gatemate/pack.cc b/himbaechel/uarch/gatemate/pack.cc index 6faee950..354e86d1 100644 --- a/himbaechel/uarch/gatemate/pack.cc +++ b/himbaechel/uarch/gatemate/pack.cc @@ -98,7 +98,7 @@ void GateMatePacker::disconnect_not_used() } } -void GateMatePacker::copy_constraint(NetInfo *in_net, NetInfo *out_net) +void GateMatePacker::copy_constraint(const NetInfo *in_net, NetInfo *out_net) { if (!in_net || !out_net) return; @@ -385,12 +385,14 @@ void GateMateImpl::pack() parse_ccf(args.options.at("ccf")); } + if (forced_die != IdString()) + preferred_die = die_to_index[forced_die]; + GateMatePacker packer(ctx, this); packer.pack_constants(); packer.cleanup(); packer.pack_io(); packer.insert_clocking(); - packer.sort_bufg(); packer.pack_pll(); packer.pack_bufg(); packer.pack_io_sel(); // merge in FF and DDR @@ -400,8 +402,15 @@ void GateMateImpl::pack() packer.pack_mult(); packer.pack_addf(); packer.pack_cpe(); + packer.copy_clocks(); packer.remove_constants(); - packer.remove_clocking(); + + if (forced_die != IdString()) { + for (auto &cell : ctx->cells) { + if (cell.second->belStrength != PlaceStrength::STRENGTH_FIXED) + ctx->constrainCellToRegion(cell.second->name, forced_die); + } + } } void GateMateImpl::repack() @@ -409,6 +418,8 @@ void GateMateImpl::repack() GateMatePacker packer(ctx, this); packer.repack_ram(); packer.repack_cpe(); + packer.reassign_clocks(); + packer.remove_clocking(); } NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/pack.h b/himbaechel/uarch/gatemate/pack.h index b423a17e..65ab9baf 100644 --- a/himbaechel/uarch/gatemate/pack.h +++ b/himbaechel/uarch/gatemate/pack.h @@ -68,6 +68,8 @@ struct GateMatePacker void cleanup(); void repack_cpe(); void repack_ram(); + void reassign_clocks(); + void copy_clocks(); private: void rename_param(CellInfo *cell, IdString name, IdString new_name, int width); @@ -75,6 +77,7 @@ struct GateMatePacker void dff_update_params(); void disconnect_if_gnd(CellInfo *cell, IdString input); void pll_out(CellInfo *cell, IdString origPort, Loc fixed); + void rewire_ram_o(CellInfo *first, IdString port, CellInfo *second); void disconnect_not_used(); void optimize_lut(); @@ -104,12 +107,9 @@ struct GateMatePacker CellInfo *create_cell_ptr(IdString type, IdString name); void flush_cells(); void pack_ram_cell(CellInfo &ci, CellInfo *cell, bool is_split); - void copy_constraint(NetInfo *in_net, NetInfo *out_net); + void copy_constraint(const NetInfo *in_net, NetInfo *out_net); pool packed_cells; - std::map global_signals; - std::vector clkin; - std::vector glbout; Context *ctx; GateMateImpl *uarch; @@ -117,6 +117,7 @@ struct GateMatePacker HimbaechelHelpers h; NetInfo *net_PACKER_VCC; NetInfo *net_PACKER_GND; + NetInfo *net_SER_CLK; int count; std::map count_per_type; }; diff --git a/himbaechel/uarch/gatemate/pack_bram.cc b/himbaechel/uarch/gatemate/pack_bram.cc index 64cc96de..c0fe6172 100644 --- a/himbaechel/uarch/gatemate/pack_bram.cc +++ b/himbaechel/uarch/gatemate/pack_bram.cc @@ -47,10 +47,10 @@ uint8_t GateMatePacker::ram_ctrl_signal(CellInfo *cell, IdString port, bool alt) uint8_t GateMatePacker::ram_clk_signal(CellInfo *cell, IdString port) { NetInfo *clk_net = cell->getPort(port); - if (!global_signals.count(clk_net)) { + if (!uarch->global_signals.count(clk_net)) { return 0b00000000; } else { - int index = global_signals[clk_net]; + int index = uarch->global_signals[clk_net]; uint8_t val = 0; switch (index) { case 0: @@ -348,6 +348,7 @@ void GateMatePacker::pack_ram() cell->constr_y = +8; cell->constr_z = RAM_HALF_L_Z; cell->cluster = ci.cluster; + cell->region = ci.region; cell->params[id_RAM_cfg_ecc_enable] = Property(b_ecc_en << 1 | a_ecc_en, 2); cell->params[id_RAM_cfg_sram_mode] = Property(ram_mode << 1 | split, 2); } @@ -395,7 +396,7 @@ void GateMatePacker::pack_ram() ci.renamePort(ctx->idf("F_ALMOST_EMPTY_OFFSET[%d]", i), ctx->idf("WEA[%d]", i)); // WEA[34:20] = F_ALMOST_FULL_OFFSET ci.disconnectPort(ctx->idf("WEA[%d]", 20 + i)); - ci.renamePort(ctx->idf("F_ALMOST_FULL_OFFSET[%d]", i), ctx->idf("WEA[%d]", 20 + i)); + ci.renamePort(ctx->idf("F_ALMOST_FULL_OFFSET[%d]", i), ctx->idf("WEA[%d]", 20 + i)); } } } diff --git a/himbaechel/uarch/gatemate/pack_clocking.cc b/himbaechel/uarch/gatemate/pack_clocking.cc index dbe5f364..929b98cb 100644 --- a/himbaechel/uarch/gatemate/pack_clocking.cc +++ b/himbaechel/uarch/gatemate/pack_clocking.cc @@ -92,65 +92,99 @@ static int glb_mux_mapping[] = { void GateMatePacker::pack_bufg() { - log_info("Packing BUFGs..\n"); - CellInfo *bufg[4] = {nullptr}; - CellInfo *pll[4] = {nullptr}; + sort_bufg(); - auto update_bufg_port = [&](CellInfo *cell, int port_num, int pll_num) { + log_info("Packing BUFGs..\n"); + auto update_bufg_port = [&](std::vector &bufg, CellInfo *cell, int port_num, int pll_num) { CellInfo *b = net_only_drives(ctx, cell->getPort(ctx->idf("CLK%d", 90 * port_num)), is_bufg, id_I, false); if (b) { if (bufg[port_num] == nullptr) { bufg[port_num] = b; + return true; } else { if (bufg[pll_num] == nullptr) { bufg[pll_num] = b; + return true; } else { - log_error("Unable to place BUFG for PLL.\n"); + return false; } } } + return true; }; - for (auto &cell : ctx->cells) { - CellInfo &ci = *cell.second; - if (!ci.type.in(id_PLL)) + unsigned max_plls = 4; // * uarch->dies; + // Index vector for permutation + std::vector indexes(max_plls); + for (unsigned i = 0; i < max_plls; ++i) + indexes[i] = i; + + std::vector bufg(max_plls, nullptr); + std::vector pll(max_plls, nullptr); + pool used_bufg; + bool valid = true; + do { + valid = true; + std::vector> tmp_bufg(uarch->dies, std::vector(4, nullptr)); + for (unsigned i = 0; i < max_plls; ++i) { + if (indexes[i] < uarch->pll.size()) { + for (int j = 0; j < 4; j++) + valid &= update_bufg_port(tmp_bufg[i >> 2], uarch->pll[indexes[i]], j, i & 3); + } + } + if (valid) { + for (unsigned i = 0; i < max_plls; ++i) { + bufg[i] = tmp_bufg[i >> 2][i & 3]; + if (bufg[i]) + used_bufg.insert(bufg[i]->name); + if (indexes[i] < uarch->pll.size()) + pll[i] = uarch->pll[indexes[i]]; + } + break; + } + } while (std::next_permutation(indexes.begin(), indexes.end())); + if (!valid) + log_error("Unable to place PLLs and BUFGs\n"); + + for (unsigned i = 0; i < max_plls; ++i) { + + int die = i >> 2; + if (!pll[i]) continue; - int index = ci.constr_z - 2; - pll[index] = &ci; - for (int j = 0; j < 4; j++) - update_bufg_port(pll[index], j, index); + CellInfo &ci = *pll[i]; + ci.cluster = ci.name; + ci.constr_abs_z = true; + ci.constr_z = 2 + (i & 3); // Position to a proper Z location + + Loc fixed_loc = uarch->locations[std::make_pair(ctx->idf("PLL%d", i & 3), die)]; + BelId pll_bel = ctx->getBelByLocation(fixed_loc); + ctx->bindBel(pll_bel, &ci, PlaceStrength::STRENGTH_FIXED); + + pll_out(&ci, id_CLK0, fixed_loc); + pll_out(&ci, id_CLK90, fixed_loc); + pll_out(&ci, id_CLK180, fixed_loc); + pll_out(&ci, id_CLK270, fixed_loc); + + move_ram_i_fixed(&ci, id_USR_PLL_LOCKED, fixed_loc); + move_ram_i_fixed(&ci, id_USR_PLL_LOCKED_STDY, fixed_loc); + move_ram_o_fixed(&ci, id_USR_LOCKED_STDY_RST, fixed_loc); + move_ram_o_fixed(&ci, id_USR_CLK_REF, fixed_loc); + move_ram_o_fixed(&ci, id_USR_SEL_A_B, fixed_loc); } for (auto &cell : ctx->cells) { CellInfo &ci = *cell.second; if (!ci.type.in(id_CC_BUFG)) continue; + if (used_bufg.count(cell.second->name)) + continue; NetInfo *in_net = ci.getPort(id_I); - int die = uarch->preferred_die; if (in_net) { - if (in_net->driver.cell) { - if (ctx->getBelBucketForCellType(in_net->driver.cell->type) == id_IOSEL) { - auto pad_info = uarch->bel_to_pad[in_net->driver.cell->bel]; - if (pad_info->flags) { - int index = pad_info->flags - 1; - die = uarch->tile_extra_data(in_net->driver.cell->bel.tile)->die; - if (!clkin[die]->getPort(ctx->idf("CLK%d", index))) { - clkin[die]->connectPort(ctx->idf("CLK%d", index), in_net->driver.cell->getPort(id_Y)); - } - } - } - } else { - // SER_CLK - clkin[die]->connectPort(id_SER_CLK, in_net); - } - - copy_constraint(in_net, ci.getPort(id_O)); - if ((in_net->driver.cell && ctx->getBelBucketForCellType(in_net->driver.cell->type) != id_PLL) || !in_net->driver.cell) { - for (int i = 0; i < 4; i++) { - if (bufg[i] == nullptr) { + for (unsigned i = 0; i < max_plls; ++i) { + if (bufg[i] == nullptr && pll[i] == nullptr) { // PLL must not be used bufg[i] = &ci; break; } @@ -159,24 +193,29 @@ void GateMatePacker::pack_bufg() } } - for (int i = 0; i < 4; i++) { - if (bufg[i]) { - CellInfo &ci = *bufg[i]; - global_signals.emplace(ci.getPort(id_O), i); + for (unsigned j = 0; j < max_plls; ++j) { + if (bufg[j]) { + CellInfo &ci = *bufg[j]; + int i = j & 3; + int die = j >> 2; + uarch->global_signals.emplace(ci.getPort(id_O), j); int glb_mux = 0; NetInfo *in_net = ci.getPort(id_I); - int die = uarch->preferred_die; + copy_constraint(in_net, ci.getPort(id_O)); if (in_net->driver.cell) { bool user_glb = true; if (ctx->getBelBucketForCellType(in_net->driver.cell->type) == id_IOSEL) { auto pad_info = uarch->bel_to_pad[in_net->driver.cell->bel]; if (pad_info->flags) { - die = uarch->tile_extra_data(in_net->driver.cell->bel.tile)->die; - clkin[die]->params[ctx->idf("REF%d", i)] = Property(pad_info->flags - 1, 3); - clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0); - NetInfo *conn = ctx->createNet(ci.name); - clkin[die]->connectPort(ctx->idf("CLK_REF%d", i), conn); - glbout[die]->connectPort(ctx->idf("CLK_REF_OUT%d", i), conn); + uarch->clkin[die]->params[ctx->idf("REF%d", i)] = Property(pad_info->flags - 1, 3); + uarch->clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0); + uarch->clkin[die]->connectPorts(ctx->idf("CLK_REF%d", i), uarch->glbout[die], + ctx->idf("CLK_REF_OUT%d", i)); + int index = pad_info->flags - 1; + if (!uarch->clkin[die]->getPort(ctx->idf("CLK%d", index))) { + uarch->clkin[die]->connectPort(ctx->idf("CLK%d", index), + in_net->driver.cell->getPort(id_Y)); + } user_glb = false; } } @@ -195,49 +234,71 @@ void GateMatePacker::pack_bufg() else log_error("Uknown connecton on BUFG to PLL.\n"); glb_mux = glb_mux_mapping[i * 16 + pll_index * 4 + pll_out]; - ci.movePortTo(id_I, glbout[die], ctx->idf("%s_%d", in_net->driver.port.c_str(ctx), pll_index)); + ci.movePortTo(id_I, uarch->glbout[die], + ctx->idf("%s_%d", in_net->driver.port.c_str(ctx), pll_index)); user_glb = false; } if (user_glb) { - ci.movePortTo(id_I, glbout[die], ctx->idf("USR_GLB%d", i)); - move_ram_o_fixed(glbout[die], ctx->idf("USR_GLB%d", i), ctx->getBelLocation(glbout[die]->bel)); - glbout[die]->params[ctx->idf("USR_GLB%d_EN", i)] = Property(Property::State::S1); + ci.movePortTo(id_I, uarch->glbout[die], ctx->idf("USR_GLB%d", i)); + move_ram_o_fixed(uarch->glbout[die], ctx->idf("USR_GLB%d", i), + ctx->getBelLocation(uarch->glbout[die]->bel)); + uarch->glbout[die]->params[ctx->idf("USR_GLB%d_EN", i)] = Property(Property::State::S1); } } else { // SER_CLK - clkin[die]->params[ctx->idf("REF%d", i)] = Property(0b100, 3); - clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0); - NetInfo *conn = ctx->createNet(ci.name); - clkin[die]->connectPort(ctx->idf("CLK_REF%d", i), conn); - glbout[die]->connectPort(ctx->idf("CLK_REF_OUT%d", i), conn); + uarch->clkin[die]->connectPort(id_SER_CLK, in_net); + uarch->clkin[die]->params[ctx->idf("REF%d", i)] = Property(0b100, 3); + uarch->clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0); + uarch->clkin[die]->connectPorts(ctx->idf("CLK_REF%d", i), uarch->glbout[die], + ctx->idf("CLK_REF_OUT%d", i)); } - ci.movePortTo(id_O, glbout[die], ctx->idf("GLB%d", i)); - glbout[die]->params[ctx->idf("GLB%d_EN", i)] = Property(Property::State::S1); - glbout[die]->params[ctx->idf("GLB%d_CFG", i)] = Property(glb_mux, 3); + ci.movePortTo(id_O, uarch->glbout[die], ctx->idf("GLB%d", i)); + uarch->glbout[die]->params[ctx->idf("GLB%d_EN", i)] = Property(Property::State::S1); + uarch->glbout[die]->params[ctx->idf("GLB%d_CFG", i)] = Property(glb_mux, 3); packed_cells.emplace(ci.name); } } - for (int i = 0; i < 4; i++) { - if (pll[i]) { - NetInfo *feedback_net = pll[i]->getPort(id_CLK_FEEDBACK); - int die = uarch->tile_extra_data(pll[i]->bel.tile)->die; - if (feedback_net) { - if (!global_signals.count(feedback_net)) { - pll[i]->movePortTo(id_CLK_FEEDBACK, glbout[die], ctx->idf("USR_FB%d", i)); - move_ram_o_fixed(glbout[die], ctx->idf("USR_FB%d", i), ctx->getBelLocation(glbout[die]->bel)); - glbout[die]->params[ctx->idf("USR_FB%d_EN", i)] = Property(Property::State::S1); - } else { - int index = global_signals[feedback_net]; - glbout[die]->params[ctx->idf("FB%d_CFG", i)] = Property(index, 2); - pll[i]->disconnectPort(id_CLK_FEEDBACK); - } - NetInfo *conn = - ctx->createNet(ctx->idf("%s_%s", glbout[die]->name.c_str(ctx), feedback_net->name.c_str(ctx))); - pll[i]->connectPort(id_CLK_FEEDBACK, conn); - glbout[die]->connectPort(ctx->idf("CLK_FB%d", i), conn); + for (auto &cell : uarch->pll) { + CellInfo &ci = *cell; + int i = ci.constr_z - 2; + NetInfo *clk = ci.getPort(id_CLK_REF); + int die = uarch->tile_extra_data(ci.bel.tile)->die; + if (clk) { + if (clk->driver.cell) { + auto pad_info = uarch->bel_to_pad[clk->driver.cell->bel]; + uarch->clkin[die]->params[ctx->idf("REF%d", i)] = Property(pad_info->flags - 1, 3); + uarch->clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0); + int index = pad_info->flags - 1; + if (!uarch->clkin[die]->getPort(ctx->idf("CLK%d", index))) + ci.movePortTo(id_CLK_REF, uarch->clkin[die], ctx->idf("CLK%d", index)); + else + ci.disconnectPort(id_CLK_REF); + } else { + // SER_CLK + uarch->clkin[die]->params[ctx->idf("REF%d", i)] = Property(0b100, 3); + uarch->clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0); + ci.movePortTo(id_CLK_REF, uarch->clkin[die], id_SER_CLK); } + uarch->clkin[die]->connectPorts(ctx->idf("CLK_REF%d", i), &ci, id_CLK_REF); + } + + NetInfo *feedback_net = ci.getPort(id_CLK_FEEDBACK); + if (feedback_net) { + if (!uarch->global_signals.count(feedback_net)) { + ci.movePortTo(id_CLK_FEEDBACK, uarch->glbout[die], ctx->idf("USR_FB%d", i)); + move_ram_o_fixed(uarch->glbout[die], ctx->idf("USR_FB%d", i), + ctx->getBelLocation(uarch->glbout[die]->bel)); + uarch->glbout[die]->params[ctx->idf("USR_FB%d_EN", i)] = Property(Property::State::S1); + } else { + int index = uarch->global_signals[feedback_net]; + if ((index >> 2) != die) + log_error("TODO: Feedback signal from another die.\n"); + uarch->glbout[die]->params[ctx->idf("FB%d_CFG", i)] = Property(index, 2); + ci.disconnectPort(id_CLK_FEEDBACK); + } + ci.connectPorts(id_CLK_FEEDBACK, uarch->glbout[die], ctx->idf("CLK_FB%d", i)); } } @@ -268,13 +329,13 @@ void GateMatePacker::insert_clocking() log_info("Insert clocking cells..\n"); for (int i = 0; i < uarch->dies; i++) { Loc fixed_loc = uarch->locations[std::make_pair(id_CLKIN, i)]; - clkin.push_back(create_cell_ptr(id_CLKIN, ctx->idf("CLKIN%d", i))); + uarch->clkin.push_back(create_cell_ptr(id_CLKIN, ctx->idf("CLKIN%d", i))); BelId clkin_bel = ctx->getBelByLocation(fixed_loc); - ctx->bindBel(clkin_bel, clkin.back(), PlaceStrength::STRENGTH_FIXED); - glbout.push_back(create_cell_ptr(id_GLBOUT, ctx->idf("GLBOUT%d", i))); + ctx->bindBel(clkin_bel, uarch->clkin.back(), PlaceStrength::STRENGTH_FIXED); + uarch->glbout.push_back(create_cell_ptr(id_GLBOUT, ctx->idf("GLBOUT%d", i))); fixed_loc = uarch->locations[std::make_pair(id_GLBOUT, i)]; BelId glbout_bel = ctx->getBelByLocation(fixed_loc); - ctx->bindBel(glbout_bel, glbout.back(), PlaceStrength::STRENGTH_FIXED); + ctx->bindBel(glbout_bel, uarch->glbout.back(), PlaceStrength::STRENGTH_FIXED); } } @@ -298,8 +359,8 @@ void GateMatePacker::remove_clocking() } } }; - remove_unused_cells(clkin); - remove_unused_cells(glbout); + remove_unused_cells(uarch->clkin); + remove_unused_cells(uarch->glbout); flush_cells(); } @@ -317,7 +378,6 @@ static const char *timing_mode_to_str(int mode) void GateMatePacker::pack_pll() { - std::vector pll_index(uarch->dies); log_info("Packing PLLs..\n"); for (auto &cell : ctx->cells) { CellInfo &ci = *cell.second; @@ -329,39 +389,18 @@ void GateMatePacker::pack_pll() disconnect_if_gnd(&ci, id_CLK_FEEDBACK); disconnect_if_gnd(&ci, id_USR_LOCKED_STDY_RST); - int die = uarch->preferred_die; - - NetInfo *clk = ci.getPort(id_CLK_REF); - if (clk && clk->driver.cell) { // Only for GPIO pins - if (ctx->getBelBucketForCellType(clk->driver.cell->type) == id_CC_BUFG) { - clk = clk->driver.cell->getPort(id_I); - } - if (ctx->getBelBucketForCellType(clk->driver.cell->type) == id_IOSEL) { - auto pad_info = uarch->bel_to_pad[clk->driver.cell->bel]; - if (pad_info->flags != 0) { - die = uarch->tile_extra_data(clk->driver.cell->bel.tile)->die; - } - } - } - - if (pll_index[die] >= 4) + if (uarch->pll.size() >= (uarch->dies * 4U)) log_error("Used more than available PLLs.\n"); if (ci.getPort(id_CLK_REF) == nullptr && ci.getPort(id_USR_CLK_REF) == nullptr) - log_error("At least one reference clock (CLK_REF or USR_CLK_REF) must be set.\n"); + log_error("At least one reference clock (CLK_REF or USR_CLK_REF) must be set for cell '%s'.\n", + ci.name.c_str(ctx)); if (ci.getPort(id_CLK_REF) != nullptr && ci.getPort(id_USR_CLK_REF) != nullptr) - log_error("CLK_REF and USR_CLK_REF are not allowed to be set in same time.\n"); + log_error("CLK_REF and USR_CLK_REF are not allowed to be set in same time for cell '%s'.\n", + ci.name.c_str(ctx)); - ci.cluster = ci.name; - ci.constr_abs_z = true; - ci.constr_z = 2 + pll_index[die]; // Position to a proper Z location - - Loc fixed_loc = uarch->locations[std::make_pair(ctx->idf("PLL%d", pll_index[die]), die)]; - BelId pll_bel = ctx->getBelByLocation(fixed_loc); - ctx->bindBel(pll_bel, &ci, PlaceStrength::STRENGTH_FIXED); - - clk = ci.getPort(id_CLK_REF); + NetInfo *clk = ci.getPort(id_CLK_REF); delay_t period = ctx->getDelayFromNS(1.0e9 / ctx->setting("target_freq")); if (clk) { if (clk->driver.cell) { @@ -372,45 +411,36 @@ void GateMatePacker::pack_pll() clk = in; } if (ctx->getBelBucketForCellType(clk->driver.cell->type) != id_IOSEL) - log_error("CLK_REF must be driven with GPIO pin.\n"); + log_error("CLK_REF must be driven with GPIO pin for cell '%s'.\n", ci.name.c_str(ctx)); auto pad_info = uarch->bel_to_pad[clk->driver.cell->bel]; if (pad_info->flags == 0) - log_error("CLK_REF must be driven with CLK dedicated pin.\n"); - clkin[die]->params[ctx->idf("REF%d", pll_index[die])] = Property(pad_info->flags - 1, 3); - clkin[die]->params[ctx->idf("REF%d_INV", pll_index[die])] = Property(Property::State::S0); - ci.movePortTo(id_CLK_REF, clkin[die], ctx->idf("CLK%d", pad_info->flags - 1)); + log_error("CLK_REF must be driven with CLK dedicated pin for cell '%s'.\n", ci.name.c_str(ctx)); } else { // SER_CLK - clkin[die]->params[ctx->idf("REF%d", pll_index[die])] = Property(0b100, 3); - clkin[die]->params[ctx->idf("REF%d_INV", pll_index[die])] = Property(Property::State::S0); - ci.movePortTo(id_CLK_REF, clkin[die], id_SER_CLK); + if (clk != net_SER_CLK) + log_error("CLK_REF connected to uknown pin for cell '%s'.\n", ci.name.c_str(ctx)); } if (clk->clkconstr) period = clk->clkconstr->period.minDelay(); - NetInfo *conn = ctx->createNet(ctx->idf("%s_CLK_REF", ci.name.c_str(ctx))); - clkin[die]->connectPort(ctx->idf("CLK_REF%d", pll_index[die]), conn); - ci.connectPort(id_CLK_REF, conn); } clk = ci.getPort(id_USR_CLK_REF); if (clk) { - move_ram_o_fixed(&ci, id_USR_CLK_REF, fixed_loc); ci.params[ctx->id("USR_CLK_REF")] = Property(0b1, 1); + if (clk->driver.cell) { + if (ctx->getBelBucketForCellType(clk->driver.cell->type) == id_CC_BUFG) { + NetInfo *in = clk->driver.cell->getPort(id_I); + ci.disconnectPort(id_USR_CLK_REF); + ci.connectPort(id_USR_CLK_REF, in); + clk = in; + } + } if (clk->clkconstr) period = clk->clkconstr->period.minDelay(); } if (ci.getPort(id_CLK_REF_OUT)) - log_error("Output CLK_REF_OUT cannot be used if PLL is used.\n"); - - pll_out(&ci, id_CLK0, fixed_loc); - pll_out(&ci, id_CLK90, fixed_loc); - pll_out(&ci, id_CLK180, fixed_loc); - pll_out(&ci, id_CLK270, fixed_loc); - - move_ram_i_fixed(&ci, id_USR_PLL_LOCKED, fixed_loc); - move_ram_i_fixed(&ci, id_USR_PLL_LOCKED_STDY, fixed_loc); - move_ram_o_fixed(&ci, id_USR_LOCKED_STDY_RST, fixed_loc); + log_error("Output CLK_REF_OUT cannot be used if PLL '%s' is used.\n", ci.name.c_str(ctx)); double out_clk_max = 0; int clk270_doub = 0; @@ -450,18 +480,18 @@ void GateMatePacker::pack_pll() double ref_clk = double_or_default(ci.params, id_REF_CLK, 0.0); if (ref_clk <= 0 || ref_clk > 125) - log_error("REF_CLK parameter is out of range (0,125.00].\n"); + log_error("REF_CLK parameter is out of range (0,125.00] for '%s'.\n", ci.name.c_str(ctx)); double out_clk = double_or_default(ci.params, id_OUT_CLK, 0.0); if (out_clk <= 0 || out_clk > max_freq) - log_error("OUT_CLK parameter is out of range (0,%.2lf].\n", max_freq); + log_error("OUT_CLK parameter is out of range (0,%.2lf] for '%s'.\n", max_freq, ci.name.c_str(ctx)); if ((ci_const < 1) || (ci_const > 31)) { - log_warning("CI const out of range. Set to default CI = 2\n"); + log_warning("CI const out of range. Set to default CI = 2 for '%s'\n", ci.name.c_str(ctx)); ci_const = 2; } if ((cp_const < 1) || (cp_const > 31)) { - log_warning("CP const out of range. Set to default CP = 4\n"); + log_warning("CP const out of range. Set to default CP = 4 for '%s'\n", ci.name.c_str(ctx)); cp_const = 4; } // PLL_cfg_val_800_1400 PLL values from 11.08.2021 @@ -578,7 +608,6 @@ void GateMatePacker::pack_pll() ci.disconnectPort(id_USR_SEL_A_B); } else { ci.params[ctx->id("USR_SET")] = Property(0b1, 1); - move_ram_o_fixed(&ci, id_USR_SEL_A_B, fixed_loc); } ci.params[ctx->id("LOCK_REQ")] = Property(0b1, 1); ci.unsetParam(id_PLL_CFG_A); @@ -615,7 +644,187 @@ void GateMatePacker::pack_pll() ci.type = id_PLL; - pll_index[die]++; + uarch->pll.push_back(&ci); + } +} + +void GateMatePacker::rewire_ram_o(CellInfo *first, IdString port, CellInfo *second) +{ + NetInfo *net = first->getPort(port); + if (net && net->driver.cell) { + net = net->driver.cell->getPort(id_I); + if (net && net->driver.cell) { + uint8_t val = int_or_default(net->driver.cell->params, id_INIT_L00, 0); + switch (val) { + case LUT_ZERO: + net = net_PACKER_GND; + break; + case LUT_ONE: + net = net_PACKER_VCC; + break; + case LUT_D0: + net = net->driver.cell->getPort(id_IN1); + break; + default: + log_error("Unsupported config, rewire from '%s' port '%s'\n", first->name.c_str(ctx), port.c_str(ctx)); + break; + } + second->connectPort(port, net); + } else { + log_error("Missing cell, rewire from '%s' port '%s'\n", first->name.c_str(ctx), port.c_str(ctx)); + } + } else { + log_error("Missing cell, rewire from '%s' port '%s'\n", first->name.c_str(ctx), port.c_str(ctx)); + } +} + +void GateMatePacker::copy_clocks() +{ + if (uarch->dies == 1) + return; + log_info("Copy clocks..\n"); + + // Save first CLKIN inputs + std::vector clk_iosel(4, nullptr); + bool use_ser_clk = false; + for (int i = 0; i < 4; i++) { + NetInfo *in_net = uarch->clkin[0]->getPort(ctx->idf("CLK%d", i)); + if (in_net) { + if (in_net->driver.cell) + clk_iosel[i] = in_net->driver.cell; + else + use_ser_clk = true; + } + uarch->clkin[0]->disconnectPort(ctx->idf("CLK%d", i)); + } + + for (int new_die = 0; new_die < uarch->dies; new_die++) { + // Reconnect CLKIN and create appropriate GPIO and IOSEL cells + for (int i = 0; i < 4; i++) { + if (clk_iosel[i]) { + CellInfo *iosel = clk_iosel[i]; + auto pad_info = uarch->bel_to_pad[iosel->bel]; + Loc l = uarch->locations[std::make_pair(IdString(pad_info->package_pin), new_die)]; + CellInfo *iosel_new = ctx->getBoundBelCell(ctx->getBelByLocation(l)); + if (!iosel_new) { + iosel_new = create_cell_ptr(iosel->type, ctx->idf("%s$die%d", iosel->name.c_str(ctx), new_die)); + iosel_new->params = iosel->params; + ctx->bindBel(ctx->getBelByLocation(l), iosel_new, PlaceStrength::STRENGTH_FIXED); + + CellInfo *gpio = iosel->getPort(id_GPIO_IN)->driver.cell; + CellInfo *gpio_new = + create_cell_ptr(gpio->type, ctx->idf("%s$die%d", gpio->name.c_str(ctx), new_die)); + gpio_new->params = gpio->params; + ctx->bindBel(ctx->getBelByLocation({l.x, l.y, 0}), gpio_new, PlaceStrength::STRENGTH_FIXED); + + // Duplicate input connection + gpio_new->connectPort(id_I, gpio->getPort(id_I)); + // Connect IOSEL and CPE_IBUF + gpio_new->connectPorts(id_Y, iosel_new, id_GPIO_IN); + } + if (iosel_new->getPort(id_IN1)) + uarch->clkin[new_die]->connectPort(ctx->idf("CLK%d", i), iosel_new->getPort(id_IN1)); + else + iosel_new->connectPorts(id_IN1, uarch->clkin[new_die], ctx->idf("CLK%d", i)); + } + } + if (use_ser_clk) + uarch->clkin[new_die]->connectPort(id_SER_CLK, net_SER_CLK); + + if (new_die != 0) { + // Copy configuration from first die to other dies + uarch->clkin[new_die]->params = uarch->clkin[0]->params; + uarch->glbout[new_die]->params = uarch->glbout[0]->params; + + // Copy PLLs + for (int i = 0; i < 4; i++) { + Loc fixed_loc = uarch->locations[std::make_pair(ctx->idf("PLL%d", i), 0)]; + BelId pll_bel = ctx->getBelByLocation(fixed_loc); + CellInfo *pll = ctx->getBoundBelCell(pll_bel); + if (pll) { + // Create new PLL + CellInfo *pll_new = create_cell_ptr(pll->type, ctx->idf("%s$die%d", pll->name.c_str(ctx), new_die)); + pll_new->params = pll->params; + // Bind to new location + Loc new_loc = uarch->locations[std::make_pair(ctx->idf("PLL%d", i), new_die)]; + BelId bel = ctx->getBelByLocation(new_loc); + ctx->bindBel(bel, pll_new, PlaceStrength::STRENGTH_FIXED); + + if (pll->getPort(id_CLK_REF)) + uarch->clkin[new_die]->connectPorts(ctx->idf("CLK_REF%d", i), pll_new, id_CLK_REF); + + if (pll->getPort(id_CLK0)) + pll_new->connectPorts(id_CLK0, uarch->glbout[new_die], ctx->idf("CLK0_%d", i)); + if (pll->getPort(id_CLK90)) + pll_new->connectPorts(id_CLK90, uarch->glbout[new_die], ctx->idf("CLK90_%d", i)); + if (pll->getPort(id_CLK180)) + pll_new->connectPorts(id_CLK180, uarch->glbout[new_die], ctx->idf("CLK180_%d", i)); + if (pll->getPort(id_CLK270)) + pll_new->connectPorts(id_CLK270, uarch->glbout[new_die], ctx->idf("CLK270_%d", i)); + if (pll->getPort(id_USR_LOCKED_STDY_RST)) + rewire_ram_o(pll, id_USR_LOCKED_STDY_RST, pll_new); + if (pll->getPort(id_USR_CLK_REF)) + rewire_ram_o(pll, id_USR_CLK_REF, pll_new); + if (pll->getPort(id_USR_SEL_A_B)) + rewire_ram_o(pll, id_USR_SEL_A_B, pll_new); + move_ram_o_fixed(pll_new, id_USR_LOCKED_STDY_RST, new_loc); + move_ram_o_fixed(pll_new, id_USR_CLK_REF, new_loc); + move_ram_o_fixed(pll_new, id_USR_SEL_A_B, new_loc); + // TODO: AND outputs of all USR_LOCKED_STDY_RST and use that signal to drive logic + } + } + // Copy GLBOUT inputs + for (int i = 0; i < 4; i++) { + Loc new_loc = uarch->locations[std::make_pair(id_GLBOUT, new_die)]; + // Plain copy of user signals + NetInfo *net = uarch->glbout[0]->getPort(ctx->idf("USR_GLB%d", i)); + if (net) + rewire_ram_o(uarch->glbout[0], ctx->idf("USR_GLB%d", i), uarch->glbout[new_die]); + + net = uarch->glbout[0]->getPort(ctx->idf("USR_FB%d", i)); + if (net) + rewire_ram_o(uarch->glbout[0], ctx->idf("USR_FB%d", i), uarch->glbout[new_die]); + + move_ram_o_fixed(uarch->glbout[new_die], ctx->idf("USR_GLB%d", i), new_loc); + move_ram_o_fixed(uarch->glbout[new_die], ctx->idf("USR_FB%d", i), new_loc); + + if (uarch->glbout[0]->getPort(ctx->idf("CLK_REF_OUT%d", i))) + uarch->clkin[new_die]->connectPorts(ctx->idf("CLK_REF%d", i), uarch->glbout[new_die], + ctx->idf("CLK_REF_OUT%d", i)); + } + } + } +} +void GateMatePacker::reassign_clocks() +{ + if (uarch->dies == 1) + return; + log_info("Reassign clocks..\n"); + + std::vector> new_bufg(uarch->dies, std::vector(4)); + + for (auto &glob : uarch->global_signals) { + const NetInfo *net = glob.first; + int index = glob.second; + int drv_die = uarch->tile_extra_data(net->driver.cell->bel.tile)->die; + auto users = net->users; // make a copy + int count = 0; + for (auto &user : users) { + int cell_die = uarch->tile_extra_data(user.cell->bel.tile)->die; + if (cell_die != drv_die) { + if (!new_bufg[cell_die][index]) { + NetInfo *new_signal = ctx->createNet(ctx->idf("%s$die%d", net->name.c_str(ctx), cell_die)); + new_bufg[cell_die][index] = new_signal; + uarch->glbout[cell_die]->connectPort(ctx->idf("GLB%d", index), new_signal); + copy_constraint(net, new_signal); + } + user.cell->disconnectPort(user.port); + user.cell->connectPort(user.port, new_bufg[cell_die][index]); + count++; + } + } + if (count) + log_info(" reassign %d net '%s' users\n", count, net->name.c_str(ctx)); } } diff --git a/himbaechel/uarch/gatemate/pack_cpe.cc b/himbaechel/uarch/gatemate/pack_cpe.cc index 7ecf0dcc..93f42fab 100644 --- a/himbaechel/uarch/gatemate/pack_cpe.cc +++ b/himbaechel/uarch/gatemate/pack_cpe.cc @@ -173,6 +173,7 @@ void GateMatePacker::pack_cpe() auto merge_dff = [&](CellInfo &ci, CellInfo *dff) { dff->cluster = ci.name; + dff->region = ci.region; dff->constr_abs_z = false; dff->constr_z = +2; ci.cluster = ci.name; @@ -253,6 +254,7 @@ void GateMatePacker::pack_cpe() CellInfo *lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$lower", ci.name.c_str(ctx))); ci.constr_children.push_back(lower); lower->cluster = ci.name; + lower->region = ci.region; lower->constr_abs_z = true; lower->constr_z = CPE_LT_L_Z; lower->params[id_INIT_L20] = Property(LUT_D0, 4); @@ -280,6 +282,7 @@ void GateMatePacker::pack_cpe() for (auto ci : l2t5_list) { CellInfo *upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$upper", ci->name.c_str(ctx))); upper->cluster = ci->name; + upper->region = ci->region; upper->constr_abs_z = true; upper->constr_z = CPE_LT_U_Z; ci->movePortTo(id_I4, upper, id_IN1); @@ -334,6 +337,7 @@ void GateMatePacker::pack_cpe() CellInfo *upper = create_cell_ptr(id_CPE_LT_U, ctx->idf("%s$upper", ci.name.c_str(ctx))); upper->cluster = ci.name; + upper->region = ci.region; upper->constr_abs_z = false; upper->constr_z = -1; upper->params[id_INIT_L10] = Property(select, 4); // Selection bits @@ -365,6 +369,7 @@ void GateMatePacker::pack_cpe() CellInfo &ci = *cell; CellInfo *lt = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$lt", ci.name.c_str(ctx))); lt->cluster = ci.name; + lt->region = ci.region; lt->constr_abs_z = false; lt->constr_z = -2; ci.cluster = ci.name; @@ -515,6 +520,7 @@ void GateMatePacker::pack_addf() CellInfo *dff = net_only_drives(ctx, o, is_dff, id_D, true); if (dff && are_ffs_compatible(dff, other)) { dff->cluster = cell->cluster; + dff->region = cell->region; dff->constr_abs_z = false; dff->constr_z = +2; cell->constr_children.push_back(dff); @@ -534,6 +540,7 @@ void GateMatePacker::pack_addf() CellInfo *ci_upper = create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$ci_upper", root->name.c_str(ctx))); root->constr_children.push_back(ci_upper); ci_upper->cluster = root->name; + ci_upper->region = root->region; ci_upper->constr_abs_z = false; ci_upper->constr_z = -1; ci_upper->constr_y = -1; @@ -541,6 +548,7 @@ void GateMatePacker::pack_addf() CellInfo *ci_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$ci", root->name.c_str(ctx))); root->constr_children.push_back(ci_lower); ci_lower->cluster = root->name; + ci_lower->region = root->region; ci_lower->constr_abs_z = false; ci_lower->constr_y = -1; ci_lower->params[id_INIT_L00] = Property(LUT_ZERO, 4); @@ -551,6 +559,7 @@ void GateMatePacker::pack_addf() ci_cplines->params[id_C_CY1_I] = Property(1, 1); root->constr_children.push_back(ci_cplines); ci_cplines->cluster = root->name; + ci_cplines->region = root->region; ci_cplines->constr_abs_z = true; ci_cplines->constr_y = -1; ci_cplines->constr_z = CPE_CPLINES_Z; @@ -580,6 +589,7 @@ void GateMatePacker::pack_addf() CellInfo *cy = grp.at(i); if (i != 0) { cy->cluster = root->name; + cy->region = root->region; root->constr_children.push_back(cy); cy->constr_abs_z = false; cy->constr_y = +i; @@ -602,6 +612,7 @@ void GateMatePacker::pack_addf() CellInfo *upper = create_cell_ptr(id_CPE_LT_U, ctx->idf("%s$upper", cy->name.c_str(ctx))); upper->cluster = root->name; + upper->region = root->region; root->constr_children.push_back(upper); upper->constr_abs_z = false; upper->constr_y = +i; @@ -625,12 +636,14 @@ void GateMatePacker::pack_addf() break; CellInfo *co_upper = create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$co_upper", cy->name.c_str(ctx))); co_upper->cluster = root->name; + co_upper->region = root->region; root->constr_children.push_back(co_upper); co_upper->constr_abs_z = false; co_upper->constr_z = -1; co_upper->constr_y = +i + 1; CellInfo *co_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$co", cy->name.c_str(ctx))); co_lower->cluster = root->name; + co_lower->region = root->region; root->constr_children.push_back(co_lower); co_lower->constr_abs_z = false; co_lower->constr_y = +i + 1; @@ -678,6 +691,7 @@ void GateMatePacker::pack_constants() h.replace_constants(CellTypePort(id_CPE_L2T4, id_OUT), CellTypePort(id_CPE_L2T4, id_OUT), vcc_params, gnd_params); net_PACKER_VCC = ctx->nets.at(ctx->id("$PACKER_VCC")).get(); net_PACKER_GND = ctx->nets.at(ctx->id("$PACKER_GND")).get(); + net_SER_CLK = nullptr; } void GateMatePacker::remove_constants() @@ -719,6 +733,7 @@ std::pair GateMatePacker::move_ram_i(CellInfo *cell, IdS if (place) { cell->constr_children.push_back(cpe_ramio); cpe_ramio->cluster = cell->cluster; + cpe_ramio->region = cell->region; cpe_ramio->constr_abs_z = false; cpe_ramio->constr_z = PLACE_DB_CONSTR + origPort.index; } else { @@ -730,6 +745,7 @@ std::pair GateMatePacker::move_ram_i(CellInfo *cell, IdS if (place) { cpe_ramio->constr_children.push_back(cpe_half); cpe_half->cluster = cell->cluster; + cpe_half->region = cell->region; cpe_half->constr_abs_z = false; cpe_half->constr_z = -4; } else { @@ -757,6 +773,7 @@ std::pair GateMatePacker::move_ram_o(CellInfo *cell, IdS if (place) { cell->constr_children.push_back(cpe_ramio); cpe_ramio->cluster = cell->cluster; + cpe_ramio->region = cell->region; cpe_ramio->constr_abs_z = false; cpe_ramio->constr_z = PLACE_DB_CONSTR + origPort.index; } else { @@ -767,6 +784,7 @@ std::pair GateMatePacker::move_ram_o(CellInfo *cell, IdS if (place) { cpe_ramio->constr_children.push_back(cpe_half); cpe_half->cluster = cell->cluster; + cpe_half->region = cell->region; cpe_half->constr_abs_z = false; cpe_half->constr_z = -4; } else { @@ -815,6 +833,7 @@ std::pair GateMatePacker::move_ram_io(CellInfo *cell, Id if (place) { cell->constr_children.push_back(cpe_ramio); cpe_ramio->cluster = cell->cluster; + cpe_ramio->region = cell->region; cpe_ramio->constr_abs_z = false; cpe_ramio->constr_z = PLACE_DB_CONSTR + oPort.index; } else { @@ -826,6 +845,7 @@ std::pair GateMatePacker::move_ram_io(CellInfo *cell, Id if (place) { cpe_ramio->constr_children.push_back(cpe_half); cpe_half->cluster = cell->cluster; + cpe_half->region = cell->region; cpe_half->constr_abs_z = false; cpe_half->constr_z = -4; } else { diff --git a/himbaechel/uarch/gatemate/pack_io.cc b/himbaechel/uarch/gatemate/pack_io.cc index 3a12e0d8..31f7eaf8 100644 --- a/himbaechel/uarch/gatemate/pack_io.cc +++ b/himbaechel/uarch/gatemate/pack_io.cc @@ -142,6 +142,7 @@ void GateMatePacker::pack_io() s.cell->disconnectPort(s.port); s.cell->connectPort(s.port, ser_clk); } + net_SER_CLK = ser_clk; ci.disconnectPort(id_I); packed_cells.emplace(ci.name); continue; @@ -302,7 +303,6 @@ void GateMatePacker::pack_io() uarch->available_pads.erase(id); loc = id.c_str(ctx); } - ci.params[id_LOC] = Property(loc); BelId bel; if (uarch->locations.count(std::make_pair(ctx->id(loc), uarch->preferred_die))) @@ -370,12 +370,12 @@ void GateMatePacker::pack_io_sel() } else if (clk_net == net_PACKER_VCC) { cell->disconnectPort(id_CLK); } else { - if (!global_signals.count(clk_net)) { + if (!uarch->global_signals.count(clk_net)) { cell->movePortTo(id_CLK, target, id_OUT4); target->params[id_SEL_OUT_CLOCK] = Property(Property::State::S1); return true; } else { - int index = global_signals[clk_net]; + int index = uarch->global_signals[clk_net]; cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1)); target->params[id_OUT_CLOCK] = Property(index, 2); } @@ -391,11 +391,11 @@ void GateMatePacker::pack_io_sel() } else if (clk_net == net_PACKER_VCC) { cell->disconnectPort(id_CLK); } else { - if (!global_signals.count(clk_net)) { + if (!uarch->global_signals.count(clk_net)) { cell->movePortTo(id_CLK, target, id_OUT4); target->params[id_SEL_IN_CLOCK] = Property(Property::State::S1); } else { - int index = global_signals[clk_net]; + int index = uarch->global_signals[clk_net]; cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1)); target->params[id_IN_CLOCK] = Property(index, 2); } @@ -406,7 +406,7 @@ void GateMatePacker::pack_io_sel() auto merge_ibf = [&](NetInfo *di_net, CellInfo &ci, bool use_custom_clock) -> bool { CellInfo *dff = (*di_net->users.begin()).cell; if (is_gpio_valid_dff(dff)) { - if (!global_signals.count(ci.getPort(id_CLK)) && use_custom_clock) { + if (!uarch->global_signals.count(ci.getPort(id_CLK)) && use_custom_clock) { log_warning("Found DFF %s cell, but not enough CLK signals.\n", dff->name.c_str(ctx)); return false; } @@ -431,7 +431,7 @@ void GateMatePacker::pack_io_sel() auto merge_iddr = [&](NetInfo *di_net, CellInfo &ci, bool use_custom_clock) -> bool { CellInfo *iddr = (*di_net->users.begin()).cell; - if (!global_signals.count(ci.getPort(id_CLK)) && use_custom_clock) { + if (!uarch->global_signals.count(ci.getPort(id_CLK)) && use_custom_clock) { log_warning("Found IDDR %s cell, but not enough CLK signals.\n", iddr->name.c_str(ctx)); return false; } @@ -454,6 +454,9 @@ void GateMatePacker::pack_io_sel() return true; }; + // Invert CPE out for output enable (OUT3) + bool is_inverted[4] = {false, false, true, false}; + for (auto &cell : cells) { CellInfo &ci = *cell; bool ff_obf = bool_or_default(ci.params, id_FF_OBF, 0); @@ -467,8 +470,6 @@ void GateMatePacker::pack_io_sel() } ci.cluster = ci.name; - std::string loc = str_or_default(ci.params, id_LOC, "UNPLACED"); - ci.unsetParam(id_LOC); NetInfo *do_net = ci.getPort(id_A); bool use_custom_clock = false; @@ -503,6 +504,16 @@ void GateMatePacker::pack_io_sel() } } bool oddr_merged = false; + if (do_net->driver.cell && do_net->driver.cell->type == id_CC_LUT1 && do_net->users.entries() == 1) { + NetInfo *net = do_net->driver.cell->getPort(id_I0); + if (net->driver.cell && net->driver.cell->type == id_CC_ODDR && net->users.entries() == 1) { + do_net = net; + packed_cells.insert(net->driver.cell->name); + // Inverting both input is equal to inverter at output + is_inverted[0] = true; + is_inverted[1] = true; + } + } if (do_net->driver.cell && do_net->driver.cell->type == id_CC_ODDR && do_net->users.entries() == 1) { CellInfo *oddr = do_net->driver.cell; ci.params[id_OUT1_FF] = Property(Property::State::S1); @@ -512,7 +523,7 @@ void GateMatePacker::pack_io_sel() ci.disconnectPort(id_A); oddr->movePortTo(id_D0, &ci, id_OUT2); oddr->movePortTo(id_D1, &ci, id_OUT1); - const auto &pad = ctx->get_package_pin(ctx->id(loc)); + const auto &pad = uarch->bel_to_pad[ci.bel]; int die = uarch->tile_extra_data(ci.bel.tile)->die; auto [cpe_half, cpe_ramio] = ddr[die][pad->pad_bank]; if (cpe_half) { @@ -558,8 +569,8 @@ void GateMatePacker::pack_io_sel() Loc root_loc = ctx->getBelLocation(ci.bel); for (int i = 0; i < 4; i++) { CellInfo *cpe = move_ram_o_fixed(&ci, ctx->idf("OUT%d", i + 1), root_loc).first; - if (cpe && i == 2) - cpe->params[id_INIT_L10] = Property(LUT_INV_D0, 4); // Invert CPE out for output enable (OUT3) + if (cpe && is_inverted[i]) + cpe->params[id_INIT_L10] = Property(LUT_INV_D0, 4); } } flush_cells(); diff --git a/himbaechel/uarch/gatemate/pack_mult.cc b/himbaechel/uarch/gatemate/pack_mult.cc index 2ec849a3..c6c1b10c 100644 --- a/himbaechel/uarch/gatemate/pack_mult.cc +++ b/himbaechel/uarch/gatemate/pack_mult.cc @@ -573,6 +573,7 @@ void GateMatePacker::pack_mult() // we also constrain it to proper Z location auto *root = m.cols[0].b_passthru.upper; root->cluster = root->name; + root->region = mult->region; root->constr_abs_z = true; root->constr_z = CPE_LT_U_Z; @@ -581,6 +582,7 @@ void GateMatePacker::pack_mult() return; root->constr_children.push_back(cell); cell->cluster = root->name; + cell->region = root->region; cell->constr_abs_z = true; cell->constr_x = x_offset; cell->constr_y = y_offset; diff --git a/himbaechel/uarch/gatemate/tests/lut.cc b/himbaechel/uarch/gatemate/tests/lut.cc index 053b0556..5b74799a 100644 --- a/himbaechel/uarch/gatemate/tests/lut.cc +++ b/himbaechel/uarch/gatemate/tests/lut.cc @@ -49,11 +49,11 @@ TEST_F(GateMateTest, remove_lut1_zero) CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf"); - lut1->connectPorts(id_O, obuf, id_A); + direct_connect(lut1, id_O, obuf, id_A); ASSERT_EQ(ctx->cells.size(), 2LU); ctx->uarch->pack(); - ASSERT_EQ(ctx->cells.size(), 1LU); + ASSERT_EQ(ctx->cells.size(), 4LU); } TEST_F(GateMateTest, remove_lut1_one) @@ -63,11 +63,11 @@ TEST_F(GateMateTest, remove_lut1_one) CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf"); - lut1->connectPorts(id_O, obuf, id_A); + direct_connect(lut1, id_O, obuf, id_A); ASSERT_EQ(ctx->cells.size(), 2LU); ctx->uarch->pack(); - ASSERT_EQ(ctx->cells.size(), 1LU); + ASSERT_EQ(ctx->cells.size(), 4LU); } TEST_F(GateMateTest, remove_lut1_pass) @@ -78,14 +78,14 @@ TEST_F(GateMateTest, remove_lut1_pass) CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf"); CellInfo *ibuf = create_cell_ptr(id_CC_IBUF, "ibuf"); - ibuf->connectPorts(id_Y, lut1, id_I0); - lut1->connectPorts(id_O, obuf, id_A); + direct_connect(ibuf, id_Y, lut1, id_I0); + direct_connect(lut1, id_O, obuf, id_A); ASSERT_EQ(ctx->cells.size(), 3LU); ctx->uarch->pack(); // Expect IBUF -> CPE -> OBUF // LUT removed, but CPE for driving OBUF added - ASSERT_EQ(ctx->cells.size(), 4LU); + ASSERT_EQ(ctx->cells.size(), 8LU); } TEST_F(GateMateTest, remove_lut1_inv) @@ -96,14 +96,14 @@ TEST_F(GateMateTest, remove_lut1_inv) CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf"); CellInfo *ibuf = create_cell_ptr(id_CC_IBUF, "ibuf"); - ibuf->connectPorts(id_Y, lut1, id_I0); - lut1->connectPorts(id_O, obuf, id_A); + direct_connect(ibuf, id_Y, lut1, id_I0); + direct_connect(lut1, id_O, obuf, id_A); ASSERT_EQ(ctx->cells.size(), 3LU); ctx->uarch->pack(); // Expect IBUF -> CPE -> OBUF // LUT merged, but CPE for driving OBUF added - ASSERT_EQ(ctx->cells.size(), 5LU); + ASSERT_EQ(ctx->cells.size(), 9LU); } TEST_F(GateMateTest, remove_lut1_not_driven) @@ -123,5 +123,5 @@ TEST_F(GateMateTest, remove_lut1_not_driven) ctx->uarch->pack(); // Expect IBUF -> CPE -> OBUF // LUT1 removed as not used, but CPE for driving OBUF added - ASSERT_EQ(ctx->cells.size(), 4LU); + ASSERT_EQ(ctx->cells.size(), 8LU); } diff --git a/himbaechel/uarch/gatemate/tests/testing.cc b/himbaechel/uarch/gatemate/tests/testing.cc index 190e19e8..4306e657 100644 --- a/himbaechel/uarch/gatemate/tests/testing.cc +++ b/himbaechel/uarch/gatemate/tests/testing.cc @@ -201,4 +201,11 @@ CellInfo *GateMateTest::create_cell_ptr(IdString type, std::string name) return cell; } +void GateMateTest::direct_connect(CellInfo *o_cell, IdString o_port, CellInfo *i_cell, IdString i_port) +{ + NetInfo *net = ctx->createNet(ctx->idf("%s_%s", o_cell->name.c_str(ctx), o_port.c_str(ctx))); + o_cell->connectPort(o_port, net); + i_cell->connectPort(i_port, net); +} + NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/tests/testing.h b/himbaechel/uarch/gatemate/tests/testing.h index 73c1110e..576729cd 100644 --- a/himbaechel/uarch/gatemate/tests/testing.h +++ b/himbaechel/uarch/gatemate/tests/testing.h @@ -34,6 +34,7 @@ class GateMateTest : public ::testing::Test virtual void TearDown() override; CellInfo *create_cell_ptr(IdString type, std::string name); + void direct_connect(CellInfo *o_cell, IdString o_port, CellInfo *i_cell, IdString i_port); ArchArgs chipArgs; Context *ctx;