diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index ec5116ba..d9a7ca2b 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -1091,6 +1091,7 @@ X(XD4) X(XD5) // HCLK wires +X(HCLK) X(HCLK_OUT0) X(HCLK_OUT1) X(HCLK_OUT2) diff --git a/himbaechel/uarch/gowin/globals.cc b/himbaechel/uarch/gowin/globals.cc index 365924fc..0a9ca691 100644 --- a/himbaechel/uarch/gowin/globals.cc +++ b/himbaechel/uarch/gowin/globals.cc @@ -49,8 +49,8 @@ struct GowinGlobalRouter IdString src_type = ctx->getWireType(ctx->getPipSrcWire(pip)); IdString dst_type = ctx->getWireType(ctx->getPipDstWire(pip)); - bool src_valid = src_type.in(id_GLOBAL_CLK, id_IO_O, id_PLL_O); - bool dst_valid = dst_type.in(id_GLOBAL_CLK, id_TILE_CLK, id_PLL_I, id_IO_I); + bool src_valid = src_type.in(id_GLOBAL_CLK, id_IO_O, id_PLL_O, id_HCLK); + bool dst_valid = dst_type.in(id_GLOBAL_CLK, id_TILE_CLK, id_PLL_I, id_IO_I, id_HCLK); bool res = (src_valid && dst_valid) || (src_valid && is_local(dst_type)) || (is_local(src_type) && dst_valid); if (ctx->debug && res && false) { diff --git a/himbaechel/uarch/gowin/gowin.cc b/himbaechel/uarch/gowin/gowin.cc index 538dc294..2ff3555f 100644 --- a/himbaechel/uarch/gowin/gowin.cc +++ b/himbaechel/uarch/gowin/gowin.cc @@ -38,10 +38,6 @@ struct GowinImpl : HimbaechelAPI bool isValidBelForCellType(IdString cell_type, BelId bel) const override; - // placer hits - void notifyBelChange(BelId bel, CellInfo *cell) override; - bool checkBelAvail(BelId bel) const override; - private: HimbaechelHelpers h; GowinUtils gwu; @@ -182,13 +178,13 @@ void GowinImpl::postRoute() if (is_iologic(ci) && !ci->type.in(id_ODDR, id_ODDRC, id_IDDR, id_IDDRC)) { if (visited_hclk_users.find(ci->name) == visited_hclk_users.end()) { // mark FCLK<-HCLK connections - ci->setAttr(id_IOLOGIC_FCLK, Property("UNKNOWN")); const NetInfo *h_net = ci->getPort(id_FCLK); if (h_net) { for (auto &user : h_net->users) { if (user.port != id_FCLK) { continue; } + user.cell->setAttr(id_IOLOGIC_FCLK, Property("UNKNOWN")); visited_hclk_users.insert(user.cell->name); // XXX Based on the implementation, perhaps a function // is needed to get Pip from a Wire @@ -395,27 +391,6 @@ bool GowinImpl::slice_valid(int x, int y, int z) const } return true; } -// placer hits -void GowinImpl::notifyBelChange(BelId bel, CellInfo *cell) -{ - if (cell != nullptr) { - // OSER8 took both IOLOGIC bels in the tile - if (cell->type == id_OSER8) { - Loc loc = ctx->getBelLocation(bel); - inactive_bels.insert(ctx->getBelByLocation(get_pair_iologic_bel(loc))); - } - } else { - // the unbind is about to happen - CellInfo *ci = ctx->getBoundBelCell(bel); - // OSER8 took both IOLOGIC bels in the tile - if (ci->type == id_OSER8) { - Loc loc = ctx->getBelLocation(bel); - inactive_bels.erase(ctx->getBelByLocation(get_pair_iologic_bel(loc))); - } - } -} - -bool GowinImpl::checkBelAvail(BelId bel) const { return inactive_bels.find(bel) == inactive_bels.end(); } } // namespace diff --git a/himbaechel/uarch/gowin/gowin.h b/himbaechel/uarch/gowin/gowin.h index 66da2d4e..233fc2ed 100644 --- a/himbaechel/uarch/gowin/gowin.h +++ b/himbaechel/uarch/gowin/gowin.h @@ -5,6 +5,60 @@ NEXTPNR_NAMESPACE_BEGIN +namespace { +// Return true if a cell is a LUT +inline bool type_is_lut(IdString cell_type) { return cell_type.in(id_LUT1, id_LUT2, id_LUT3, id_LUT4); } +inline bool is_lut(const CellInfo *cell) { return type_is_lut(cell->type); } +// Return true if a cell is a DFF +inline bool type_is_dff(IdString cell_type) +{ + return cell_type.in(id_DFF, id_DFFE, id_DFFN, id_DFFNE, id_DFFS, id_DFFSE, id_DFFNS, id_DFFNSE, id_DFFR, id_DFFRE, + id_DFFNR, id_DFFNRE, id_DFFP, id_DFFPE, id_DFFNP, id_DFFNPE, id_DFFC, id_DFFCE, id_DFFNC, + id_DFFNCE); +} +inline bool is_dff(const CellInfo *cell) { return type_is_dff(cell->type); } +// Return true if a cell is a ALU +inline bool type_is_alu(IdString cell_type) { return cell_type == id_ALU; } +inline bool is_alu(const CellInfo *cell) { return type_is_alu(cell->type); } + +inline bool type_is_diffio(IdString cell_type) +{ + return cell_type.in(id_ELVDS_IOBUF, id_ELVDS_IBUF, id_ELVDS_TBUF, id_ELVDS_OBUF, id_TLVDS_IOBUF, id_TLVDS_IBUF, + id_TLVDS_TBUF, id_TLVDS_OBUF); +} +inline bool is_diffio(const CellInfo *cell) { return type_is_diffio(cell->type); } + +inline bool type_is_iologic(IdString cell_type) +{ + return cell_type.in(id_ODDR, id_ODDRC, id_OSER4, id_OSER8, id_OSER10, id_OVIDEO); +} +inline bool is_iologic(const CellInfo *cell) { return type_is_iologic(cell->type); } + +// Return true if a cell is a SSRAM +inline bool type_is_ssram(IdString cell_type) { return cell_type.in(id_RAM16SDP1, id_RAM16SDP2, id_RAM16SDP4); } +inline bool is_ssram(const CellInfo *cell) { return type_is_ssram(cell->type); } + +// extra data in the chip db +NPNR_PACKED_STRUCT(struct Bottom_io_cnd_POD { + int32_t wire_a_net; + int32_t wire_b_net; +}); + +NPNR_PACKED_STRUCT(struct Bottom_io_POD { + // simple OBUF + static constexpr int8_t NORMAL = 0; + // DDR + static constexpr int8_t DDR = 1; + RelSlice conditions; +}); + +NPNR_PACKED_STRUCT(struct Extra_chip_data_POD { + Bottom_io_POD bottom_io; + RelSlice diff_io_types; +}); + +} // namespace + // Bels Z ranges. It is desirable that these numbers be synchronized with the chipdb generator namespace BelZ { enum @@ -30,101 +84,5 @@ enum }; } -namespace BelFlags { -static constexpr uint32_t FLAG_SIMPLE_IO = 0x100; -} - -namespace { -// Return true if a cell is a LUT -inline bool type_is_lut(IdString cell_type) { return cell_type.in(id_LUT1, id_LUT2, id_LUT3, id_LUT4); } -inline bool is_lut(const CellInfo *cell) { return type_is_lut(cell->type); } -// Return true if a cell is a DFF -inline bool type_is_dff(IdString cell_type) -{ - return cell_type.in(id_DFF, id_DFFE, id_DFFN, id_DFFNE, id_DFFS, id_DFFSE, id_DFFNS, id_DFFNSE, id_DFFR, id_DFFRE, - id_DFFNR, id_DFFNRE, id_DFFP, id_DFFPE, id_DFFNP, id_DFFNPE, id_DFFC, id_DFFCE, id_DFFNC, - id_DFFNCE); -} -inline bool is_dff(const CellInfo *cell) { return type_is_dff(cell->type); } -// Return true if a cell is a ALU -inline bool type_is_alu(IdString cell_type) { return cell_type == id_ALU; } -inline bool is_alu(const CellInfo *cell) { return type_is_alu(cell->type); } - -inline bool type_is_diffio(IdString cell_type) -{ - return cell_type.in(id_ELVDS_IOBUF, id_ELVDS_IBUF, id_ELVDS_TBUF, id_ELVDS_OBUF, id_TLVDS_IOBUF, id_TLVDS_IBUF, - id_TLVDS_TBUF, id_TLVDS_OBUF); -} -inline bool is_diffio(const CellInfo *cell) { return type_is_diffio(cell->type); } - -inline bool type_is_iologic(IdString cell_type) { return cell_type.in(id_ODDR, id_ODDRC, id_OSER4, id_OSER8); } -inline bool is_iologic(const CellInfo *cell) { return type_is_iologic(cell->type); } - -// Return true if a cell is a SSRAM -inline bool type_is_ssram(IdString cell_type) { return cell_type.in(id_RAM16SDP1, id_RAM16SDP2, id_RAM16SDP4); } -inline bool is_ssram(const CellInfo *cell) { return type_is_ssram(cell->type); } - -// extra data in the chip db -NPNR_PACKED_STRUCT(struct Bottom_io_cnd_POD { - int32_t wire_a_net; - int32_t wire_b_net; -}); - -NPNR_PACKED_STRUCT(struct Bottom_io_POD { - // simple OBUF - static constexpr int8_t NORMAL = 0; - // DDR - static constexpr int8_t DDR = 1; - RelSlice conditions; -}); - -NPNR_PACKED_STRUCT(struct Extra_chip_data_POD { - Bottom_io_POD bottom_io; - RelSlice diff_io_types; -}); - -inline bool is_diff_io_supported(const ChipInfoPOD *chip, IdString type) -{ - const Extra_chip_data_POD *extra = reinterpret_cast(chip->extra_data.get()); - for (auto &dtype : extra->diff_io_types) { - if (IdString(dtype) == type) { - return true; - } - } - return false; -} - -inline bool have_bottom_io_cnds(const ChipInfoPOD *chip) -{ - const Extra_chip_data_POD *extra = reinterpret_cast(chip->extra_data.get()); - return extra->bottom_io.conditions.size() != 0; -} - -inline IdString get_bottom_io_wire_a_net(const ChipInfoPOD *chip, int8_t condition) -{ - const Extra_chip_data_POD *extra = reinterpret_cast(chip->extra_data.get()); - return IdString(extra->bottom_io.conditions[condition].wire_a_net); -} - -inline IdString get_bottom_io_wire_b_net(const ChipInfoPOD *chip, int8_t condition) -{ - const Extra_chip_data_POD *extra = reinterpret_cast(chip->extra_data.get()); - return IdString(extra->bottom_io.conditions[condition].wire_b_net); -} - -// Bels and pips -inline bool is_simple_io_bel(const ChipInfoPOD *chip, BelId bel) -{ - return chip_bel_info(chip, bel).flags & BelFlags::FLAG_SIMPLE_IO; -} - -inline Loc get_pair_iologic_bel(Loc loc) -{ - loc.z = BelZ::IOLOGICA_Z + (1 - (loc.z - BelZ::IOLOGICA_Z)); - return loc; -} - -} // namespace - NEXTPNR_NAMESPACE_END #endif diff --git a/himbaechel/uarch/gowin/gowin_utils.cc b/himbaechel/uarch/gowin/gowin_utils.cc index 7cc82fed..3c23be50 100644 --- a/himbaechel/uarch/gowin/gowin_utils.cc +++ b/himbaechel/uarch/gowin/gowin_utils.cc @@ -1,9 +1,14 @@ -#include "gowin_utils.h" - #include "log.h" #include "nextpnr.h" #include "util.h" +#define HIMBAECHEL_CONSTIDS "uarch/gowin/constids.inc" +#include "himbaechel_constids.h" +#include "himbaechel_helpers.h" + +#include "gowin.h" +#include "gowin_utils.h" + NEXTPNR_NAMESPACE_BEGIN // pin functions: GCLKT_4, SSPI_CS, READY etc @@ -22,4 +27,50 @@ IdStringList GowinUtils::get_pin_funcs(BelId bel) return IdStringList(); } +bool GowinUtils::is_simple_io_bel(BelId bel) +{ + return chip_bel_info(ctx->chip_info, bel).flags & BelFlags::FLAG_SIMPLE_IO; +} + +Loc GowinUtils::get_pair_iologic_bel(Loc loc) +{ + loc.z = BelZ::IOLOGICA_Z + (1 - (loc.z - BelZ::IOLOGICA_Z)); + return loc; +} + +BelId GowinUtils::get_io_bel_from_iologic(BelId bel) +{ + Loc loc = ctx->getBelLocation(bel); + loc.z = BelZ::IOBA_Z + loc.z - BelZ::IOLOGICA_Z; + return ctx->getBelByLocation(loc); +} + +bool GowinUtils::is_diff_io_supported(IdString type) +{ + const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); + for (auto &dtype : extra->diff_io_types) { + if (IdString(dtype) == type) { + return true; + } + } + return false; +} + +bool GowinUtils::have_bottom_io_cnds(void) +{ + const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); + return extra->bottom_io.conditions.size() != 0; +} + +IdString GowinUtils::get_bottom_io_wire_a_net(int8_t condition) +{ + const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); + return IdString(extra->bottom_io.conditions[condition].wire_a_net); +} + +IdString GowinUtils::get_bottom_io_wire_b_net(int8_t condition) +{ + const Extra_chip_data_POD *extra = reinterpret_cast(ctx->chip_info->extra_data.get()); + return IdString(extra->bottom_io.conditions[condition].wire_b_net); +} NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gowin/gowin_utils.h b/himbaechel/uarch/gowin/gowin_utils.h index ba5b7389..5d544a74 100644 --- a/himbaechel/uarch/gowin/gowin_utils.h +++ b/himbaechel/uarch/gowin/gowin_utils.h @@ -7,6 +7,10 @@ NEXTPNR_NAMESPACE_BEGIN +namespace BelFlags { +static constexpr uint32_t FLAG_SIMPLE_IO = 0x100; +} + struct GowinUtils { Context *ctx; @@ -17,6 +21,16 @@ struct GowinUtils // pin functions: GCLKT_4, SSPI_CS, READY etc IdStringList get_pin_funcs(BelId bel); + + // Bels and pips + bool is_simple_io_bel(BelId bel); + Loc get_pair_iologic_bel(Loc loc); + BelId get_io_bel_from_iologic(BelId bel); + + bool is_diff_io_supported(IdString type); + bool have_bottom_io_cnds(void); + IdString get_bottom_io_wire_a_net(int8_t condition); + IdString get_bottom_io_wire_b_net(int8_t condition); }; NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gowin/pack.cc b/himbaechel/uarch/gowin/pack.cc index f6d6f07f..064d3e3a 100644 --- a/himbaechel/uarch/gowin/pack.cc +++ b/himbaechel/uarch/gowin/pack.cc @@ -7,6 +7,7 @@ #include "himbaechel_helpers.h" #include "gowin.h" +#include "gowin_utils.h" #include "pack.h" NEXTPNR_NAMESPACE_BEGIN @@ -16,8 +17,13 @@ struct GowinPacker { Context *ctx; HimbaechelHelpers h; + GowinUtils gwu; - GowinPacker(Context *ctx) : ctx(ctx) { h.init(ctx); } + GowinPacker(Context *ctx) : ctx(ctx) + { + h.init(ctx); + gwu.init(ctx); + } // =================================== // IO @@ -60,7 +66,7 @@ struct GowinPacker void config_bottom_row(CellInfo &ci, Loc loc, uint8_t cnd = Bottom_io_POD::NORMAL) { - if (!have_bottom_io_cnds(ctx->chip_info)) { + if (!gwu.have_bottom_io_cnds()) { return; } if (!ci.type.in(id_OBUF, id_TBUF, id_IOBUF)) { @@ -82,10 +88,10 @@ struct GowinPacker } }; - IdString wire_a_net = get_bottom_io_wire_a_net(ctx->chip_info, cnd); + IdString wire_a_net = gwu.get_bottom_io_wire_a_net(cnd); connect_io_wire(id_BOTTOM_IO_PORT_A, wire_a_net); - IdString wire_b_net = get_bottom_io_wire_b_net(ctx->chip_info, cnd); + IdString wire_b_net = gwu.get_bottom_io_wire_b_net(cnd); connect_io_wire(id_BOTTOM_IO_PORT_B, wire_b_net); } @@ -168,7 +174,7 @@ struct GowinPacker if (io_loc.y == ctx->getGridDimY() - 1) { config_bottom_row(ci, io_loc); } - if (is_simple_io_bel(ctx->chip_info, io_bel)) { + if (gwu.is_simple_io_bel(io_bel)) { config_simple_io(ci); } make_iob_nets(ci); @@ -281,7 +287,7 @@ struct GowinPacker if (!is_diffio(&ci)) { continue; } - if (!is_diff_io_supported(ctx->chip_info, ci.type)) { + if (!gwu.is_diff_io_supported(ci.type)) { log_error("%s is not supported\n", ci.type.c_str(ctx)); } cells_to_remove.push_back(ci.name); @@ -332,7 +338,7 @@ struct GowinPacker IdString out_port = id_Q0; IdString tx_port = id_Q1; - CellInfo *out_iob = net_only_drives(ctx, ci.ports.at(out_port).net, is_iob, id_I); + CellInfo *out_iob = net_only_drives(ctx, ci.ports.at(out_port).net, is_iob, id_I, true); NPNR_ASSERT(out_iob != nullptr && out_iob->bel != BelId()); BelId iob_bel = out_iob->bel; @@ -368,11 +374,15 @@ struct GowinPacker out_iob->disconnectPort(id_I); ci.disconnectPort(out_port); set_daaj_nets(ci, iob_bel); - config_bottom_row(*out_iob, ctx->getBelLocation(iob_bel), Bottom_io_POD::DDR); + + Loc io_loc = ctx->getBelLocation(iob_bel); + if (io_loc.y == ctx->getGridDimY() - 1) { + config_bottom_row(*out_iob, io_loc, Bottom_io_POD::DDR); + } // if Q1 is connected then disconnect it too if (port_used(&ci, tx_port)) { - NPNR_ASSERT(out_iob == net_only_drives(ctx, ci.ports.at(tx_port).net, is_iob, id_OEN)); + NPNR_ASSERT(out_iob == net_only_drives(ctx, ci.ports.at(tx_port).net, is_iob, id_OEN, true)); nets_to_remove.push_back(ci.getPort(tx_port)->name); out_iob->disconnectPort(id_OEN); ci.disconnectPort(tx_port); @@ -380,6 +390,51 @@ struct GowinPacker make_iob_nets(*out_iob); } + void pack_single_output_iol(CellInfo &ci, std::vector &cells_to_remove, + std::vector &nets_to_remove) + { + IdString out_port = id_Q; + + CellInfo *out_iob = net_only_drives(ctx, ci.ports.at(out_port).net, is_iob, id_I, true); + NPNR_ASSERT(out_iob != nullptr && out_iob->bel != BelId()); + BelId iob_bel = out_iob->bel; + + BelId l_bel = get_iologic_bel(out_iob); + if (l_bel == BelId()) { + log_error("Can't place IOLOGIC %s at %s\n", ctx->nameOf(&ci), ctx->nameOfBel(iob_bel)); + } + + if (!ctx->checkBelAvail(l_bel)) { + log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci), + ctx->nameOfBel(l_bel), ctx->nameOf(ctx->getBoundBelCell(l_bel))); + } + ctx->bindBel(l_bel, &ci, PlaceStrength::STRENGTH_LOCKED); + std::string out_mode; + switch (ci.type.hash()) { + case ID_OVIDEO: + out_mode = "VIDEORX"; + break; + case ID_OSER10: + out_mode = "ODDRX5"; + break; + } + ci.setParam(ctx->id("OUTMODE"), out_mode); + + // mark IOB as used by IOLOGIC + out_iob->setParam(id_IOLOGIC_IOB, 1); + // disconnect Q output: it is wired internally + nets_to_remove.push_back(ci.getPort(out_port)->name); + out_iob->disconnectPort(id_I); + ci.disconnectPort(out_port); + set_daaj_nets(ci, iob_bel); + + Loc io_loc = ctx->getBelLocation(iob_bel); + if (io_loc.y == ctx->getGridDimY() - 1) { + config_bottom_row(*out_iob, io_loc, Bottom_io_POD::DDR); + } + make_iob_nets(*out_iob); + } + IdString create_aux_iologic_name(IdString main_name, int idx = 0) { std::string sfx(""); @@ -391,9 +446,11 @@ struct GowinPacker BelId get_aux_iologic_bel(const CellInfo &ci) { - return ctx->getBelByLocation(get_pair_iologic_bel(ctx->getBelLocation(ci.bel))); + return ctx->getBelByLocation(gwu.get_pair_iologic_bel(ctx->getBelLocation(ci.bel))); } + bool is_diff_io(BelId bel) { return ctx->getBoundBelCell(bel)->attrs.count(id_DIFF_TYPE) != 0; } + void create_aux_iologic_cells(CellInfo &ci) { if (ci.type.in(id_ODDR, id_ODDRC, id_OSER4)) { @@ -401,16 +458,23 @@ struct GowinPacker } IdString aux_name = create_aux_iologic_name(ci.name); BelId bel = get_aux_iologic_bel(ci); + BelId io_bel = gwu.get_io_bel_from_iologic(bel); + if (!ctx->checkBelAvail(io_bel)) { + if (!is_diff_io(io_bel)) { + log_error("Can't place %s at %s because of %s\n", ctx->nameOf(&ci), ctx->nameOfBel(bel), + ctx->nameOf(ctx->getBoundBelCell(io_bel))); + } + } + ctx->createCell(aux_name, id_IOLOGIC_DUMMY); CellInfo *aux = ctx->cells[aux_name].get(); aux->addInput(id_PCLK); ci.copyPortTo(id_PCLK, ctx->cells.at(aux_name).get(), id_PCLK); - aux->addInput(id_FCLK); - ci.copyPortTo(id_FCLK, ctx->cells.at(aux_name).get(), id_FCLK); aux->addInput(id_RESET); ci.copyPortTo(id_RESET, ctx->cells.at(aux_name).get(), id_RESET); ctx->cells.at(aux_name)->setParam(ctx->id("OUTMODE"), Property("DDRENABLE")); ctx->cells.at(aux_name)->setAttr(ctx->id("IOLOGIC_TYPE"), Property("DUMMY")); + ctx->cells.at(aux_name)->setAttr(ctx->id("MAIN_CELL"), Property(ci.name.str(ctx))); ctx->bindBel(bel, aux, PlaceStrength::STRENGTH_LOCKED); } @@ -432,6 +496,11 @@ struct GowinPacker create_aux_iologic_cells(ci); continue; } + if (ci.type.in(id_OVIDEO, id_OSER10)) { + pack_single_output_iol(ci, cells_to_remove, nets_to_remove); + create_aux_iologic_cells(ci); + continue; + } } for (auto cell : cells_to_remove) { @@ -957,13 +1026,29 @@ struct GowinPacker { handle_constants(); pack_iobs(); + ctx->check(); + pack_diff_iobs(); + ctx->check(); + pack_iologic(); + ctx->check(); + pack_gsr(); + ctx->check(); + pack_wideluts(); + ctx->check(); + pack_alus(); + ctx->check(); + constrain_lutffs(); + ctx->check(); + pack_pll(); + ctx->check(); + pack_ram16sdp4(); ctx->fixupHierarchy();