diff --git a/common/place/placer_heap.cc b/common/place/placer_heap.cc index 38c42df2..2bb9fec3 100644 --- a/common/place/placer_heap.cc +++ b/common/place/placer_heap.cc @@ -921,8 +921,10 @@ class HeAPPlacer y0 = std::max(y0, r.y0); x1 = std::min(x1, r.x1); y1 = std::min(y1, r.y1); - if (x0 > x1) std::swap(x0, x1); - if (y0 > y1) std::swap(y0, y1); + if (x0 > x1) + std::swap(x0, x1); + if (y0 > y1) + std::swap(y0, y1); } // Pick a random X and Y location within our search radius / search box diff --git a/himbaechel/uarch/gatemate/cells.cc b/himbaechel/uarch/gatemate/cells.cc index 47fdc933..44cdc042 100644 --- a/himbaechel/uarch/gatemate/cells.cc +++ b/himbaechel/uarch/gatemate/cells.cc @@ -125,6 +125,11 @@ CellInfo *GateMatePacker::create_cell_ptr(IdString type, IdString name) } else if (type.in(id_CPE_IBUF)) { add_port(id_Y, PORT_OUT); add_port(id_I, PORT_IN); + } else if (type.in(id_CPE_IOBUF)) { + add_port(id_Y, PORT_OUT); + add_port(id_T, PORT_IN); + add_port(id_A, PORT_IN); + add_port(id_IO, PORT_INOUT); } else if (type.in(id_PLL)) { add_port(id_CLK_REF, PORT_IN); add_port(id_USR_CLK_REF, PORT_IN); diff --git a/himbaechel/uarch/gatemate/gatemate.h b/himbaechel/uarch/gatemate/gatemate.h index 8c61a42b..dbeea417 100644 --- a/himbaechel/uarch/gatemate/gatemate.h +++ b/himbaechel/uarch/gatemate/gatemate.h @@ -91,9 +91,12 @@ struct GateMateImpl : HimbaechelAPI int fpga_mode; int timing_mode; std::map global_signals; + dict, NetInfo *> global_mapping; + dict, IdString> global_clk_mapping; std::vector clkin; std::vector glbout; std::vector pll; + pool ignore; private: bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc, diff --git a/himbaechel/uarch/gatemate/pack.cc b/himbaechel/uarch/gatemate/pack.cc index 1ae99985..d8cde875 100644 --- a/himbaechel/uarch/gatemate/pack.cc +++ b/himbaechel/uarch/gatemate/pack.cc @@ -404,10 +404,32 @@ void GateMateImpl::pack() parse_ccf(args.options.at("ccf")); } + MultiDieStrategy strategy; + if (args.options.count("multi")) { + std::string val = args.options.at("multi"); + if (val == "mirror") { + strategy = MultiDieStrategy::CLOCK_MIRROR; + log_info("Multidie mode: CLOCK MIRROR\n"); + } else if (val == "clk1") { + strategy = MultiDieStrategy::REUSE_CLK1; + log_info("Multidie mode: REUSE CLK1\n"); + } else { + log_error("Unknown value for 'multi' option. Allowed values are 'mirror' and 'clk1'.\n"); + } + } else { + strategy = MultiDieStrategy::CLOCK_MIRROR; + if (dies != 1) + log_warning("Multi die clock placement strategy set to 'mirror'.\n"); + } + if (forced_die != IdString()) preferred_die = die_to_index[forced_die]; + if (strategy == MultiDieStrategy::REUSE_CLK1) + preferred_die = 0; + GateMatePacker packer(ctx, this); + packer.set_strategy(strategy); packer.pack_constants(); packer.cleanup(); packer.pack_io(); diff --git a/himbaechel/uarch/gatemate/pack.h b/himbaechel/uarch/gatemate/pack.h index 7e247ffb..3d5e9eaf 100644 --- a/himbaechel/uarch/gatemate/pack.h +++ b/himbaechel/uarch/gatemate/pack.h @@ -44,6 +44,12 @@ enum CPELut LUT_ONE = 0b1111 }; +enum MultiDieStrategy +{ + CLOCK_MIRROR, + REUSE_CLK1, +}; + struct GateMatePacker { GateMatePacker(Context *ctx, GateMateImpl *uarch) : ctx(ctx), uarch(uarch) { h.init(ctx); }; @@ -72,6 +78,8 @@ struct GateMatePacker void reassign_clocks(); void copy_clocks(); + void set_strategy(MultiDieStrategy strategy) { this->strategy = strategy; } + private: void rename_param(CellInfo *cell, IdString name, IdString new_name, int width); void dff_to_cpe(CellInfo *dff); @@ -88,6 +96,9 @@ struct GateMatePacker void move_connections(NetInfo *from_net, NetInfo *to_net); void remap_ram_half(CellInfo *half, CellInfo *cell, int num); + void strategy_mirror(); + void strategy_clk1(); + PllCfgRecord get_pll_settings(double f_ref, double f_core, int mode, int low_jitter, bool pdiv0_mux, bool feedback); std::pair move_ram_i(CellInfo *cell, IdString origPort, bool place = true, @@ -121,6 +132,7 @@ struct GateMatePacker NetInfo *net_SER_CLK; int count; std::map count_per_type; + MultiDieStrategy strategy; }; NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/pack_clocking.cc b/himbaechel/uarch/gatemate/pack_clocking.cc index 929b98cb..b8b88193 100644 --- a/himbaechel/uarch/gatemate/pack_clocking.cc +++ b/himbaechel/uarch/gatemate/pack_clocking.cc @@ -682,7 +682,70 @@ void GateMatePacker::copy_clocks() { if (uarch->dies == 1) return; - log_info("Copy clocks..\n"); + switch (strategy) { + case MultiDieStrategy::REUSE_CLK1: + strategy_clk1(); + break; + case MultiDieStrategy::CLOCK_MIRROR: + strategy_mirror(); + break; + } +} + +void GateMatePacker::strategy_clk1() +{ + log_info("Reuse CLK1 for clock distribution..\n"); + NetInfo *net = uarch->glbout[0]->getPort(id_GLB0); + NetInfo *new_clk1 = ctx->createNet(ctx->id("$clk1$pin")); + for (int new_die = 0; new_die < uarch->dies; new_die++) { + CellInfo *iosel = create_cell_ptr(id_IOSEL, ctx->idf("$iosel_clk1$die%d", new_die)); + iosel->setParam(id_DELAY_IBF, Property(1, 16)); + iosel->setParam(id_INPUT_ENABLE, Property(1, 1)); + if (new_die == 0) { + // On die 0 it should be output as well + iosel->setParam(id_DELAY_OBF, Property(1, 16)); + iosel->setParam(id_OE_ENABLE, Property(1, 1)); + iosel->setParam(id_OUT_SIGNAL, Property(1, 1)); + iosel->setParam(id_SLEW, Property(1, 1)); + } + + BelId bel = ctx->getBelByLocation(uarch->locations[std::make_pair(ctx->id("IO_SB_A7"), new_die)]); + ctx->bindBel(bel, iosel, PlaceStrength::STRENGTH_FIXED); + + CellInfo *gpio = create_cell_ptr((new_die ? id_CPE_IBUF : id_CPE_IOBUF), ctx->idf("$clk1$die%d", new_die)); + Loc loc = ctx->getBelLocation(bel); + ctx->bindBel(ctx->getBelByLocation({loc.x, loc.y, 0}), gpio, PlaceStrength::STRENGTH_FIXED); + + uarch->clkin[new_die]->connectPort(id_CLK1, new_clk1); + uarch->clkin[new_die]->params[ctx->id("REF1")] = Property(1, 3); + uarch->glbout[new_die]->params[ctx->id("GLB1_EN")] = Property(Property::State::S1); + uarch->glbout[new_die]->params[ctx->id("GLB1_CFG")] = Property(0, 3); + uarch->clkin[new_die]->connectPorts(ctx->id("CLK_REF1"), uarch->glbout[new_die], ctx->id("CLK_REF_OUT1")); + + gpio->connectPorts(id_Y, iosel, id_GPIO_IN); + + if (new_die == 0) { + iosel->connectPort(id_OUT1, ctx->getNetByAlias(uarch->global_signals.begin()->first->name)); + CellInfo *cpe = move_ram_o_fixed(iosel, id_OUT1, loc).first; + uarch->ignore.emplace(cpe->name); + + iosel->connectPorts(id_GPIO_OUT, gpio, id_A); + iosel->connectPorts(id_GPIO_EN, gpio, id_T); + gpio->connectPort(id_IO, new_clk1); + } else + gpio->connectPort(id_I, new_clk1); + + NetInfo *new_signal = ctx->createNet(ctx->idf("%s$die%d", net->name.c_str(ctx), new_die)); + uarch->glbout[new_die]->connectPort(ctx->id("GLB1"), new_signal); + copy_constraint(net, new_signal); + uarch->global_mapping.emplace(std::make_pair(net->name, new_die), new_signal); + uarch->global_clk_mapping.emplace(std::make_pair(id_CLOCK1, new_die), id_CLOCK2); + } +} + +void GateMatePacker::strategy_mirror() +{ + log_info("Mirror clocks..\n"); // Save first CLKIN inputs std::vector clk_iosel(4, nullptr); @@ -793,8 +856,52 @@ void GateMatePacker::copy_clocks() ctx->idf("CLK_REF_OUT%d", i)); } } + for (int i = 0; i < 4; i++) { + NetInfo *net = uarch->glbout[0]->getPort(ctx->idf("GLB%d", i)); + if (net) { + if (new_die != 0) { + NetInfo *new_signal = ctx->createNet(ctx->idf("%s$die%d", net->name.c_str(ctx), new_die)); + uarch->glbout[new_die]->connectPort(ctx->idf("GLB%d", i), new_signal); + copy_constraint(net, new_signal); + uarch->global_mapping.emplace(std::make_pair(net->name, new_die), new_signal); + } else { + uarch->global_mapping.emplace(std::make_pair(net->name, new_die), net); + } + } + } } } + +static int clk_config_val(IdString name) +{ + switch (name.index) { + case id_CLOCK1.index: + return 0b00100011; + case id_CLOCK2.index: + return 0b00110011; + case id_CLOCK3.index: + return 0b00000011; + case id_CLOCK4.index: + return 0b00010011; + } + return 0; +} + +static int ioclk_config_val(IdString name) +{ + switch (name.index) { + case id_CLOCK1.index: + return 0; + case id_CLOCK2.index: + return 1; + case id_CLOCK3.index: + return 2; + case id_CLOCK4.index: + return 3; + } + return 0; +} + void GateMatePacker::reassign_clocks() { if (uarch->dies == 1) @@ -805,22 +912,55 @@ void GateMatePacker::reassign_clocks() 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); - } + if (uarch->global_mapping.count(std::make_pair(net->name, cell_die))) { + NetInfo *new_net = uarch->global_mapping.at(std::make_pair(net->name, cell_die)); + if (uarch->ignore.count(user.cell->name)) + continue; + + if (new_net == net) + continue; + user.cell->disconnectPort(user.port); - user.cell->connectPort(user.port, new_bufg[cell_die][index]); + + if (user.port.in(id_CLOCK1, id_CLOCK2, id_CLOCK3, id_CLOCK4) && + uarch->global_clk_mapping.count(std::make_pair(user.port, cell_die))) { + IdString newPort = uarch->global_clk_mapping.at(std::make_pair(user.port, cell_die)); + if (!user.cell->ports.count(newPort)) + user.cell->addInput(newPort); + user.cell->connectPort(newPort, new_net); + + if (user.cell->type == id_RAM) { + int a0_clk = int_or_default(user.cell->params, id_RAM_cfg_forward_a0_clk, 0); + int a1_clk = int_or_default(user.cell->params, id_RAM_cfg_forward_a1_clk, 0); + int b0_clk = int_or_default(user.cell->params, id_RAM_cfg_forward_b0_clk, 0); + int b1_clk = int_or_default(user.cell->params, id_RAM_cfg_forward_b1_clk, 0); + + if (a0_clk == clk_config_val(user.port)) + user.cell->params[id_RAM_cfg_forward_a0_clk] = Property(clk_config_val(newPort), 8); + if (a1_clk == clk_config_val(user.port)) + user.cell->params[id_RAM_cfg_forward_a1_clk] = Property(clk_config_val(newPort), 8); + if (b0_clk == clk_config_val(user.port)) + user.cell->params[id_RAM_cfg_forward_b0_clk] = Property(clk_config_val(newPort), 8); + if (b1_clk == clk_config_val(user.port)) + user.cell->params[id_RAM_cfg_forward_b1_clk] = Property(clk_config_val(newPort), 8); + } + if (user.cell->type == id_IOSEL) { + int in_clk = int_or_default(user.cell->params, id_IN_CLOCK, 0); + int out_clk = int_or_default(user.cell->params, id_OUT_CLOCK, 0); + if (in_clk == ioclk_config_val(user.port)) + user.cell->params[id_IN_CLOCK] = Property(ioclk_config_val(newPort), 2); + if (out_clk == ioclk_config_val(user.port)) + user.cell->params[id_OUT_CLOCK] = Property(ioclk_config_val(newPort), 2); + } + } else + user.cell->connectPort(user.port, new_net); count++; + } else { + log_error("Global signal '%s' is not available in die %d.\n", net->name.c_str(ctx), cell_die); } } if (count) diff --git a/himbaechel/uarch/gatemate/pack_io.cc b/himbaechel/uarch/gatemate/pack_io.cc index 31f7eaf8..d6f8e5d1 100644 --- a/himbaechel/uarch/gatemate/pack_io.cc +++ b/himbaechel/uarch/gatemate/pack_io.cc @@ -376,7 +376,14 @@ void GateMatePacker::pack_io_sel() return true; } else { int index = uarch->global_signals[clk_net]; - cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1)); + NetInfo *net = target->getPort(ctx->idf("CLOCK%d", index + 1)); + if (net) { + if (net != clk_net) + log_error("Not able to connected different CLK signal to cell '%s'.\n", + cell->name.c_str(ctx)); + } else { + cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1)); + } target->params[id_OUT_CLOCK] = Property(index, 2); } } @@ -396,7 +403,14 @@ void GateMatePacker::pack_io_sel() target->params[id_SEL_IN_CLOCK] = Property(Property::State::S1); } else { int index = uarch->global_signals[clk_net]; - cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1)); + NetInfo *net = target->getPort(ctx->idf("CLOCK%d", index + 1)); + if (net) { + if (net != clk_net) + log_error("Not able to connected different CLK signal to cell '%s'.\n", + cell->name.c_str(ctx)); + } else { + cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1)); + } target->params[id_IN_CLOCK] = Property(index, 2); } }