diff options
author | Miodrag Milanovic <mmicko@gmail.com> | 2019-12-28 13:54:06 +0100 |
---|---|---|
committer | Miodrag Milanovic <mmicko@gmail.com> | 2019-12-28 13:54:06 +0100 |
commit | 796d6489953927105d3b0ed22308f29676b168fa (patch) | |
tree | bc0f470642c0943713c441aa7c3e9e310cb23ccc /ecp5 | |
parent | 50f87a6024859d197eefa8de0b0b616b1e03e239 (diff) | |
parent | 0d43aff2682d91817ea4a1fb5dff6e169ae9a659 (diff) | |
download | nextpnr-796d6489953927105d3b0ed22308f29676b168fa.tar.gz nextpnr-796d6489953927105d3b0ed22308f29676b168fa.tar.bz2 nextpnr-796d6489953927105d3b0ed22308f29676b168fa.zip |
Merge remote-tracking branch 'origin/master' into mmicko/ecp5_gui
Diffstat (limited to 'ecp5')
-rw-r--r-- | ecp5/arch.cc | 12 | ||||
-rw-r--r-- | ecp5/arch.h | 8 | ||||
-rw-r--r-- | ecp5/arch_place.cc | 24 | ||||
-rw-r--r-- | ecp5/arch_pybindings.cc | 2 | ||||
-rw-r--r-- | ecp5/archdefs.h | 6 | ||||
-rw-r--r-- | ecp5/bitstream.cc | 22 | ||||
-rw-r--r-- | ecp5/cells.cc | 48 | ||||
-rw-r--r-- | ecp5/constids.inc | 8 | ||||
-rw-r--r-- | ecp5/family.cmake | 4 | ||||
-rw-r--r-- | ecp5/globals.cc | 4 | ||||
-rw-r--r-- | ecp5/pack.cc | 304 | ||||
-rw-r--r-- | ecp5/synth/.gitignore | 3 | ||||
-rw-r--r-- | ecp5/synth/blinky.v | 77 | ||||
-rw-r--r-- | ecp5/synth/blinky.ys | 2 | ||||
-rw-r--r-- | ecp5/synth/ulx3s_empty.config | 439 | ||||
-rwxr-xr-x | ecp5/trellis_import.py | 8 |
16 files changed, 412 insertions, 559 deletions
diff --git a/ecp5/arch.cc b/ecp5/arch.cc index e678d3b3..994e8660 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -117,6 +117,9 @@ Arch::Arch(ArchArgs args) : args(args) log_error("Unsupported ECP5 chip type.\n"); } #endif + if (chip_info->const_id_count != DB_CONST_ID_COUNT) + log_error("Chip database 'bba' and nextpnr code are out of sync; please rebuild (or contact distribution " + "maintainer)!\n"); package_info = nullptr; for (int i = 0; i < chip_info->num_packages; i++) { if (args.package == chip_info->package_info[i].name.get()) { @@ -475,7 +478,13 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const } }; - auto src_loc = est_location(src), dst_loc = est_location(dst); + auto src_loc = est_location(src); + std::pair<int, int> dst_loc; + if (wire_loc_overrides.count(dst)) { + dst_loc = wire_loc_overrides.at(dst); + } else { + dst_loc = est_location(dst); + } int dx = abs(src_loc.first - dst_loc.first), dy = abs(src_loc.second - dst_loc.second); @@ -560,6 +569,7 @@ bool Arch::place() bool Arch::route() { + setupWireLocations(); route_ecp5_globals(getCtx()); assignArchInfo(); assign_budget(getCtx(), true); diff --git a/ecp5/arch.h b/ecp5/arch.h index a0254965..b3e36e52 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -196,6 +196,7 @@ NPNR_PACKED_STRUCT(struct ChipInfoPOD { int32_t num_tiles; int32_t num_location_types; int32_t num_packages, num_pios; + int32_t const_id_count; RelPtr<LocationTypePOD> locations; RelPtr<int32_t> location_type; RelPtr<GlobalInfoPOD> location_glbinfo; @@ -1034,7 +1035,7 @@ struct Arch : BaseCtx if (chip_info->tiletype_names[tileloc.tile_names[j].type_idx].get() == type) return tileloc.tile_names[j].name.get(); } - NPNR_ASSERT_FALSE_STR("no with type " + type); + NPNR_ASSERT_FALSE_STR("no tile with type " + type); } GlobalInfoPOD globalInfoAtLoc(Location loc); @@ -1054,6 +1055,11 @@ struct Arch : BaseCtx // Special case for delay estimates due to its physical location // being far from the logical location of its primitive WireId gsrclk_wire; + // Improves directivity of routing to DSP inputs, avoids issues + // with different routes to the same physical reset wire causing + // conflicts and slow routing + std::unordered_map<WireId, std::pair<int, int>> wire_loc_overrides; + void setupWireLocations(); mutable std::unordered_map<DelayKey, std::pair<bool, DelayInfo>> celldelay_cache; diff --git a/ecp5/arch_place.cc b/ecp5/arch_place.cc index d5c345af..6057605b 100644 --- a/ecp5/arch_place.cc +++ b/ecp5/arch_place.cc @@ -196,4 +196,28 @@ void Arch::permute_luts() } } +void Arch::setupWireLocations() +{ + wire_loc_overrides.clear(); + for (auto cell : sorted(cells)) { + CellInfo *ci = cell.second; + if (ci->bel == BelId()) + continue; + if (ci->type == id_MULT18X18D || ci->type == id_DCUA) { + for (auto &port : ci->ports) { + if (port.second.type != PORT_IN || port.second.net == nullptr) + continue; + WireId pw = getBelPinWire(ci->bel, port.first); + if (pw == WireId()) + continue; + for (auto uh : getPipsUphill(pw)) { + WireId pip_src = getPipSrcWire(uh); + wire_loc_overrides[pw] = std::make_pair(pip_src.location.x, pip_src.location.y); + break; + } + } + } + } +} + NEXTPNR_NAMESPACE_END diff --git a/ecp5/arch_pybindings.cc b/ecp5/arch_pybindings.cc index da6d3e50..cd5e31c3 100644 --- a/ecp5/arch_pybindings.cc +++ b/ecp5/arch_pybindings.cc @@ -49,6 +49,7 @@ void arch_wrap_python() typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap; typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap; typedef std::unordered_map<IdString, IdString> AliasMap; + typedef std::unordered_map<IdString, HierarchicalCell> HierarchyMap; auto belpin_cls = class_<ContextualWrapper<BelPin>>("BelPin", no_init); readonly_wrapper<BelPin, decltype(&BelPin::bel), &BelPin::bel, conv_to_str<BelId>>::def_wrap(belpin_cls, "bel"); @@ -64,6 +65,7 @@ void arch_wrap_python() WRAP_MAP_UPTR(CellMap, "IdCellMap"); WRAP_MAP_UPTR(NetMap, "IdNetMap"); + WRAP_MAP(HierarchyMap, wrap_context<HierarchicalCell &>, "HierarchyMap"); } NEXTPNR_NAMESPACE_END diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h index d155d672..b176eec0 100644 --- a/ecp5/archdefs.h +++ b/ecp5/archdefs.h @@ -52,17 +52,23 @@ struct DelayInfo // ----------------------------------------------------------------------- +// https://bugreports.qt.io/browse/QTBUG-80789 + +#ifndef Q_MOC_RUN enum ConstIds { ID_NONE #define X(t) , ID_##t #include "constids.inc" #undef X + , + DB_CONST_ID_COUNT }; #define X(t) static constexpr auto id_##t = IdString(ID_##t); #include "constids.inc" #undef X +#endif NPNR_PACKED_STRUCT(struct LocationPOD { int16_t x, y; }); diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index acab95dd..66bd639a 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -759,6 +759,10 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex str_or_default(ci->params, ctx->id("REG0_REGSET"), "RESET")); cc.tiles[tname].add_enum(slice + ".REG1.REGSET", str_or_default(ci->params, ctx->id("REG1_REGSET"), "RESET")); + cc.tiles[tname].add_enum(slice + ".REG0.LSRMODE", + str_or_default(ci->params, ctx->id("REG0_LSRMODE"), "LSR")); + cc.tiles[tname].add_enum(slice + ".REG1.LSRMODE", + str_or_default(ci->params, ctx->id("REG1_LSRMODE"), "LSR")); cc.tiles[tname].add_enum(slice + ".CEMUX", str_or_default(ci->params, ctx->id("CEMUX"), "1")); if (ci->sliceInfo.using_dff) { @@ -865,6 +869,16 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex if (ci->attrs.count(ctx->id("DIFFRESISTOR"))) cc.tiles[pio_tile].add_enum(pio + ".DIFFRESISTOR", str_or_default(ci->attrs, ctx->id("DIFFRESISTOR"), "OFF")); + if (ci->attrs.count(ctx->id("DRIVE"))) { + static bool drive_3v3_warning_done = false; + if (iotype == "LVCMOS33") { + cc.tiles[pio_tile].add_enum(pio + ".DRIVE", str_or_default(ci->attrs, ctx->id("DRIVE"), "8")); + } else { + if (!drive_3v3_warning_done) + log_warning("Trellis limitation: DRIVE can only be set on 3V3 IO pins.\n"); + drive_3v3_warning_done = true; + } + } if (ci->attrs.count(ctx->id("TERMINATION"))) { auto vccio = get_vccio(ioType_from_str(iotype)); switch (vccio) { @@ -1283,6 +1297,9 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex else cc.tiles[pic_tile].add_enum(prim + "." + param.first.str(ctx), param.second.as_string()); } + if (get_net_or_empty(ci, id_LOADN) != nullptr) { + cc.tiles[pic_tile].add_enum(prim + ".LOADNMUX", "LOADN"); + } } else if (ci->type == id_DCUA) { TileGroup tg; tg.tiles = get_dcu_tiles(ctx, ci->bel); @@ -1384,8 +1401,9 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex Loc loc = ctx->getBelLocation(ci->bel); bool u = loc.y<15, r = loc.x> 15; std::string tiletype = fmt_str("DDRDLL_" << (u ? 'U' : 'L') << (r ? 'R' : 'L')); - if (ctx->args.type == ArchArgs::LFE5U_25F || ctx->args.type == ArchArgs::LFE5UM_25F || - ctx->args.type == ArchArgs::LFE5UM5G_25F) + if ((ctx->args.type == ArchArgs::LFE5U_25F || ctx->args.type == ArchArgs::LFE5UM_25F || + ctx->args.type == ArchArgs::LFE5UM5G_25F) && + u) tiletype += "A"; std::string tile = ctx->getTileByType(tiletype); cc.tiles[tile].add_enum("DDRDLL.MODE", "DDRDLLA"); diff --git a/ecp5/cells.cc b/ecp5/cells.cc index 37b6ac8b..c630c2c3 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -233,6 +233,8 @@ static void replace_port_safe(bool has_ff, CellInfo *ff, IdString ff_port, CellI void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut) { + if (lc->hierpath == IdString()) + lc->hierpath = ff->hierpath; bool has_ff = lc->ports.at(ctx->id("Q0")).net != nullptr || lc->ports.at(ctx->id("Q1")).net != nullptr; std::string reg = "REG" + std::to_string(index); set_param_safe(has_ff, lc, ctx->id("SRMODE"), str_or_default(ff->params, ctx->id("SRMODE"), "LSR_OVER_CE")); @@ -243,6 +245,7 @@ void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool drive lc->params[ctx->id(reg + "_SD")] = std::string(driven_by_lut ? "1" : "0"); lc->params[ctx->id(reg + "_REGSET")] = str_or_default(ff->params, ctx->id("REGSET"), "RESET"); + lc->params[ctx->id(reg + "_LSRMODE")] = str_or_default(ff->params, ctx->id("LSRMODE"), "LSR"); replace_port_safe(has_ff, ff, ctx->id("CLK"), lc, ctx->id("CLK")); if (ff->ports.find(ctx->id("LSR")) != ff->ports.end()) replace_port_safe(has_ff, ff, ctx->id("LSR"), lc, ctx->id("LSR")); @@ -250,15 +253,28 @@ void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool drive replace_port_safe(has_ff, ff, ctx->id("CE"), lc, ctx->id("CE")); replace_port(ff, ctx->id("Q"), lc, ctx->id("Q" + std::to_string(index))); - if (driven_by_lut) { - replace_port(ff, ctx->id("DI"), lc, ctx->id("DI" + std::to_string(index))); + if (get_net_or_empty(ff, ctx->id("M")) != nullptr) { + // PRLD FFs that use both M and DI + NPNR_ASSERT(!driven_by_lut); + // As M is used; must route DI through a new LUT + lc->params[ctx->id(reg + "_SD")] = std::string("1"); + lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = Property(0xFF00, 16); + replace_port(ff, ctx->id("DI"), lc, ctx->id("D" + std::to_string(index))); + replace_port(ff, ctx->id("M"), lc, ctx->id("M" + std::to_string(index))); + connect_ports(ctx, lc, ctx->id("F" + std::to_string(index)), lc, ctx->id("DI" + std::to_string(index))); } else { - replace_port(ff, ctx->id("DI"), lc, ctx->id("M" + std::to_string(index))); + if (driven_by_lut) { + replace_port(ff, ctx->id("DI"), lc, ctx->id("DI" + std::to_string(index))); + } else { + replace_port(ff, ctx->id("DI"), lc, ctx->id("M" + std::to_string(index))); + } } } void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index) { + if (lc->hierpath == IdString()) + lc->hierpath = lut->hierpath; lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = get_or_default(lut->params, ctx->id("INIT"), Property(0, 16)); replace_port(lut, ctx->id("A"), lc, ctx->id("A" + std::to_string(index))); @@ -270,6 +286,8 @@ void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index) void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc) { + if (lc->hierpath == IdString()) + lc->hierpath = ccu->hierpath; lc->params[ctx->id("MODE")] = std::string("CCU2"); lc->params[ctx->id("LUT0_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT0"), Property(0, 16)); lc->params[ctx->id("LUT1_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT1"), Property(0, 16)); @@ -297,6 +315,8 @@ void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc) void dram_to_ramw(Context *ctx, CellInfo *ram, CellInfo *lc) { + if (lc->hierpath == IdString()) + lc->hierpath = ram->hierpath; lc->params[ctx->id("MODE")] = std::string("RAMW"); replace_port(ram, ctx->id("WAD[0]"), lc, ctx->id("D0")); replace_port(ram, ctx->id("WAD[1]"), lc, ctx->id("B0")); @@ -328,6 +348,8 @@ static unsigned get_dram_init(const Context *ctx, const CellInfo *ram, int bit) void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw, int index) { + if (lc->hierpath == IdString()) + lc->hierpath = ram->hierpath; lc->params[ctx->id("MODE")] = std::string("DPRAM"); lc->params[ctx->id("WREMUX")] = str_or_default(ram->params, ctx->id("WREMUX"), "WRE"); lc->params[ctx->id("WCKMUX")] = str_or_default(ram->params, ctx->id("WCKMUX"), "WCK"); @@ -416,7 +438,25 @@ void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector<std::u } else { NPNR_ASSERT(false); } - NetInfo *donet = trio->ports.at(ctx->id("I")).net; + NetInfo *donet = trio->ports.at(ctx->id("I")).net, *dinet = trio->ports.at(ctx->id("O")).net; + + // Rename I/O nets to avoid conflicts + if (donet != nullptr && donet->name == nxio->name) + rename_net(ctx, donet, ctx->id(donet->name.str(ctx) + "$TRELLIS_IO_OUT")); + if (dinet != nullptr && dinet->name == nxio->name) + rename_net(ctx, dinet, ctx->id(dinet->name.str(ctx) + "$TRELLIS_IO_IN")); + + // Create a new top port net for accurate IO timing analysis and simulation netlists + if (ctx->ports.count(nxio->name)) { + IdString tn_netname = nxio->name; + NPNR_ASSERT(!ctx->nets.count(tn_netname)); + std::unique_ptr<NetInfo> toplevel_net{new NetInfo}; + toplevel_net->name = tn_netname; + connect_port(ctx, toplevel_net.get(), trio, ctx->id("B")); + ctx->ports[nxio->name].net = toplevel_net.get(); + ctx->nets[tn_netname] = std::move(toplevel_net); + } + CellInfo *tbuf = net_driven_by( ctx, donet, [](const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("$_TBUF_"); }, ctx->id("Y")); diff --git a/ecp5/constids.inc b/ecp5/constids.inc index b38e23ef..e5ec1c3e 100644 --- a/ecp5/constids.inc +++ b/ecp5/constids.inc @@ -1329,3 +1329,11 @@ X(WIRE_TYPE_G_HPBX) X(WIRE_TYPE_G_VPTX) X(WIRE_TYPE_L_HPBX) X(WIRE_TYPE_R_HPBX) + +X(IOLOGIC_MODE_IDDRX1F) +X(IOLOGIC_MODE_IDDRX2F) +X(IOLOGIC_MODE_IREG) +X(IOLOGIC_MODE_ODDRX1F) +X(IOLOGIC_MODE_ODDRX2F) +X(IOLOGIC_MODE_OREG) +X(IOLOGIC_MODE_TSREG) diff --git a/ecp5/family.cmake b/ecp5/family.cmake index b0b520d7..860e648d 100644 --- a/ecp5/family.cmake +++ b/ecp5/family.cmake @@ -49,7 +49,7 @@ if (NOT EXTERNAL_CHIPDB) else() add_custom_command(OUTPUT ${DEV_CC_BBA_DB} COMMAND ${ENV_CMD} ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_CONSTIDS_INC} -g ${DEV_GFXH} ${dev} > ${DEV_CC_BBA_DB} - DEPENDS ${DEV_CONSTIDS_INC} ${DEV_GFXH} ${DB_PY} ${PREV_DEV_CC_BBA_DB} + DEPENDS ${DB_PY} ${DEV_CONSTIDS_INC} ${DEV_GFXH} ${PREV_DEV_CC_BBA_DB} ) add_custom_command(OUTPUT ${DEV_CC_DB} COMMAND bbasm ${BBASM_ENDIAN_FLAG} ${DEV_CC_BBA_DB} ${DEV_CC_DB} @@ -82,7 +82,7 @@ if (NOT EXTERNAL_CHIPDB) add_custom_command(OUTPUT ${DEV_CC_BBA_DB} COMMAND ${ENV_CMD} ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_CONSTIDS_INC} -g ${DEV_GFXH} ${dev} > ${DEV_CC_BBA_DB}.new COMMAND mv ${DEV_CC_BBA_DB}.new ${DEV_CC_BBA_DB} - DEPENDS ${DEV_CONSTIDS_INC} ${DEV_GFXH} ${DB_PY} ${PREV_DEV_CC_BBA_DB} + DEPENDS ${DB_PY} ${DEV_CONSTIDS_INC} ${DEV_GFXH} ${PREV_DEV_CC_BBA_DB} ) add_custom_command(OUTPUT ${DEV_CC_DB} COMMAND bbasm --c ${BBASM_ENDIAN_FLAG} ${DEV_CC_BBA_DB} ${DEV_CC_DB}.new diff --git a/ecp5/globals.cc b/ecp5/globals.cc index da2ba8f0..c0f4b504 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -58,7 +58,7 @@ class Ecp5GlobalRouter if (user.cell->type == id_DCUA && (user.port == id_CH0_FF_RXI_CLK || user.port == id_CH1_FF_RXI_CLK || user.port == id_CH0_FF_TXI_CLK || user.port == id_CH1_FF_TXI_CLK)) return true; - if ((user.cell->type == id_IOLOGIC || user.cell->type == id_SIOLOGIC) && user.port == id_CLK) + if ((user.cell->type == id_IOLOGIC || user.cell->type == id_SIOLOGIC) && (user.port == id_CLK)) return true; return false; } @@ -74,6 +74,8 @@ class Ecp5GlobalRouter clockCount[ni->name]++; if (user.cell->type == id_DCUA) clockCount[ni->name] += 100; + if (user.cell->type == id_IOLOGIC || user.cell->type == id_SIOLOGIC) + clockCount[ni->name] += 10; } } // log_info("clkcount %s: %d\n", ni->name.c_str(ctx),clockCount[ni->name]); diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 244a79d5..3867ab3d 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -56,6 +56,52 @@ class Ecp5Packer new_cells.clear(); } + // Print logic usgage + int available_slices = 0; + void print_logic_usage() + { + int total_luts = 0, total_ffs = 0; + int total_ramluts = 0, total_ramwluts = 0; + for (auto bel : ctx->getBels()) { + if (ctx->getBelType(bel) == id_TRELLIS_SLICE) { + available_slices += 1; + total_luts += 2; + total_ffs += 2; + Loc l = ctx->getBelLocation(bel); + if (l.z == 0 || l.z == 1) + total_ramluts += 2; + if (l.z == 2) + total_ramwluts += 2; + } + } + int used_lgluts = 0, used_cyluts = 0, used_ramluts = 0, used_ramwluts = 0, used_ffs = 0; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (is_lut(ctx, ci)) + ++used_lgluts; + if (is_carry(ctx, ci)) + used_cyluts += 2; + if (is_dpram(ctx, ci)) { + used_ramluts += 4; + used_ramwluts += 2; + } + if (is_ff(ctx, ci)) + used_ffs += 2; + } + log_info("Logic utilisation before packing:\n"); + auto pc = [](int used, int total) { return 100 * used / total; }; + int used_luts = used_lgluts + used_cyluts + used_ramluts + used_ramwluts; + log_info(" Total LUT4s: %5d/%5d %5d%%\n", used_luts, total_luts, pc(used_luts, total_luts)); + log_info(" logic LUTs: %5d/%5d %5d%%\n", used_lgluts, total_luts, pc(used_lgluts, total_luts)); + log_info(" carry LUTs: %5d/%5d %5d%%\n", used_cyluts, total_luts, pc(used_cyluts, total_luts)); + log_info(" RAM LUTs: %5d/%5d %5d%%\n", used_ramluts, total_ramluts, pc(used_ramluts, total_ramluts)); + log_info(" RAMW LUTs: %5d/%5d %5d%%\n", used_ramwluts, total_ramwluts, + pc(used_ramwluts, total_ramwluts)); + log_break(); + log_info(" Total DFFs: %5d/%5d %5d%%\n", used_ffs, total_ffs, pc(used_ffs, total_ffs)); + log_break(); + } + // Find FFs associated with LUTs, or LUT expansion muxes void find_lutff_pairs() { @@ -66,7 +112,8 @@ class Ecp5Packer NetInfo *znet = ci->ports.at(ctx->id("Z")).net; if (znet != nullptr) { CellInfo *ff = net_only_drives(ctx, znet, is_ff, ctx->id("DI"), false); - if (ff != nullptr) { + // Can't combine preload FF with LUT due to conflict on M + if (ff != nullptr && get_net_or_empty(ff, ctx->id("M")) == nullptr) { lutffPairs[ci->name] = ff->name; fflutPairs[ff->name] = ci->name; } @@ -75,6 +122,58 @@ class Ecp5Packer } } + // Check if a flipflop is available in a slice + bool is_ff_available(CellInfo *slice, int ff) + { + if (get_net_or_empty(slice, (ff == 1) ? id_Q1 : id_Q0) != nullptr) + return false; + if (get_net_or_empty(slice, (ff == 1) ? id_M1 : id_M0) != nullptr) + return false; + return true; + } + + // Check if a flipflop can be added to a slice + bool can_add_ff_to_slice(CellInfo *slice, CellInfo *ff) + { + std::string clkmux = str_or_default(ff->params, ctx->id("CLKMUX"), "CLK"); + std::string lsrmux = str_or_default(ff->params, ctx->id("LSRMUX"), "LSR"); + + bool has_dpram = str_or_default(slice->params, ctx->id("MODE"), "LOGIC") == "DPRAM"; + if (has_dpram) { + std::string wckmux = str_or_default(slice->params, ctx->id("WCKMUX"), "WCK"); + std::string wremux = str_or_default(slice->params, ctx->id("WREMUX"), "WRE"); + if (wckmux != clkmux && !(wckmux == "WCK" && clkmux == "CLK")) + return false; + if (wremux != lsrmux && !(wremux == "WRE" && lsrmux == "LSR")) + return false; + } + bool has_ff0 = get_net_or_empty(slice, id_Q0) != nullptr; + bool has_ff1 = get_net_or_empty(slice, id_Q1) != nullptr; + if (!has_ff0 && !has_ff1) + return true; + if (str_or_default(ff->params, ctx->id("GSR"), "DISABLED") != + str_or_default(slice->params, ctx->id("GSR"), "DISABLED")) + return false; + if (str_or_default(ff->params, ctx->id("SRMODE"), "LSR_OVER_CE") != + str_or_default(slice->params, ctx->id("SRMODE"), "LSR_OVER_CE")) + return false; + if (str_or_default(ff->params, ctx->id("CEMUX"), "1") != str_or_default(slice->params, ctx->id("CEMUX"), "1")) + return false; + if (str_or_default(ff->params, ctx->id("LSRMUX"), "LSR") != + str_or_default(slice->params, ctx->id("LSRMUX"), "LSR")) + return false; + if (str_or_default(ff->params, ctx->id("CLKMUX"), "CLK") != + str_or_default(slice->params, ctx->id("CLKMUX"), "CLK")) + return false; + if (net_or_nullptr(ff, ctx->id("CLK")) != net_or_nullptr(slice, ctx->id("CLK"))) + return false; + if (net_or_nullptr(ff, ctx->id("CE")) != net_or_nullptr(slice, ctx->id("CE"))) + return false; + if (net_or_nullptr(ff, ctx->id("LSR")) != net_or_nullptr(slice, ctx->id("LSR"))) + return false; + return true; + } + const NetInfo *net_or_nullptr(CellInfo *cell, IdString port) { auto fnd = cell->ports.find(port); @@ -134,6 +233,8 @@ class Ecp5Packer // Return true if a FF can be added to a DPRAM slice bool can_pack_ff_dram(CellInfo *dpram, CellInfo *ff) { + if (get_net_or_empty(ff, ctx->id("M")) != nullptr) + return false; // skip PRLD FFs due to M/DI conflict std::string wckmux = str_or_default(dpram->params, ctx->id("WCKMUX"), "WCK"); std::string clkmux = str_or_default(ff->params, ctx->id("CLKMUX"), "CLK"); if (wckmux != clkmux && !(wckmux == "WCK" && clkmux == "CLK")) @@ -362,8 +463,7 @@ class Ecp5Packer for (auto &port : ci->ports) disconnect_port(ctx, ci, port.first); } else if (trio != nullptr) { - // Trivial case, TRELLIS_IO used. Just destroy the net and the - // iobuf + // Trivial case, TRELLIS_IO used. Just remove the IOBUF log_info("%s feeds TRELLIS_IO %s, removing %s %s.\n", ci->name.c_str(ctx), trio->name.c_str(ctx), ci->type.c_str(ctx), ci->name.c_str(ctx)); @@ -384,14 +484,6 @@ class Ecp5Packer std::swap(net->clkconstr, onet->clkconstr); } } - ctx->nets.erase(net->name); - trio->ports.at(ctx->id("B")).net = nullptr; - } - if (ci->type == ctx->id("$nextpnr_iobuf")) { - NetInfo *net2 = ci->ports.at(ctx->id("I")).net; - if (net2 != nullptr) { - ctx->nets.erase(net2->name); - } } } else if (drives_top_port(ionet, tp)) { log_info("%s feeds %s %s.%s, removing %s %s.\n", ci->name.c_str(ctx), tp.cell->type.c_str(ctx), @@ -414,7 +506,8 @@ class Ecp5Packer new_cells.push_back(std::move(tr_cell)); trio = new_cells.back().get(); } - + for (auto port : ci->ports) + disconnect_port(ctx, ci, port.first); packed_cells.insert(ci->name); if (trio != nullptr) { for (const auto &attr : ci->attrs) @@ -1033,17 +1126,117 @@ class Ecp5Packer flush_cells(); } + // Find a cell that meets some criterea near an origin cell + // Used for packing an FF into a nearby SLICE + template <typename TFunc> CellInfo *find_nearby_cell(CellInfo *origin, TFunc Func) + { + std::unordered_set<CellInfo *> visited_cells; + std::queue<CellInfo *> to_visit; + visited_cells.insert(origin); + to_visit.push(origin); + int iter = 0; + while (!to_visit.empty() && iter < 10000) { + CellInfo *cursor = to_visit.front(); + to_visit.pop(); + if (Func(cursor)) + return cursor; + for (const auto &port : cursor->ports) { + NetInfo *pn = port.second.net; + if (pn == nullptr) + continue; + // Skip high-fanout nets that are unlikely to be relevant + if (pn->users.size() > 25) + continue; + // Add other ports on this net if not already visited + auto visit_port = [&](const PortRef &port) { + if (port.cell == nullptr) + return; + if (visited_cells.count(port.cell)) + return; + // If not already visited; add the cell of this port to the queue + to_visit.push(port.cell); + visited_cells.insert(port.cell); + }; + visit_port(pn->driver); + for (const auto &usr : pn->users) + visit_port(usr); + } + ++iter; + } + return nullptr; + } + // Pack flipflops that weren't paired with a LUT + float dense_pack_mode_thresh = 0.95f; void pack_remaining_ffs() { + // Enter dense flipflop packing mode once utilisation exceeds a threshold (default: 95%) + int used_slices = 0; + for (auto &cell : ctx->cells) + if (cell.second->type == id_TRELLIS_SLICE) + ++used_slices; + log_info("Packing unpaired FFs into a SLICE...\n"); for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; if (is_ff(ctx, ci)) { + bool pack_dense = used_slices > (dense_pack_mode_thresh * available_slices); + bool requires_m = get_net_or_empty(ci, ctx->id("M")) != nullptr; + if (pack_dense && !requires_m) { + // If dense packing threshold exceeded; always try and pack the FF into an existing slice + // Find a SLICE with space "near" the flipflop in the netlist + std::vector<CellInfo *> ltile; + CellInfo *target = find_nearby_cell(ci, [&](CellInfo *cursor) { + if (cursor->type != id_TRELLIS_SLICE) + return false; + if (!cursor->constr_children.empty() || cursor->constr_parent != nullptr) { + auto &constr_children = (cursor->constr_parent != nullptr) + ? cursor->constr_parent->constr_children + : cursor->constr_children; + // Skip big chains for performance + if (constr_children.size() > 8) + return false; + // Have to check the whole of the tile for legality when dealing with chains, not just slice + ltile.clear(); + if (cursor->constr_parent != nullptr) + ltile.push_back(cursor->constr_parent); + else + ltile.push_back(cursor); + for (auto c : constr_children) + ltile.push_back(c); + if (!can_add_ff_to_tile(ltile, cursor)) + return false; + } + if (!can_add_ff_to_slice(cursor, ci)) + return false; + for (int i = 0; i < 2; i++) + if (is_ff_available(cursor, i)) + return true; + return false; + }); + + // If found, add the FF to this slice instead of creating a new one + if (target != nullptr) { + for (int i = 0; i < 2; i++) { + if (is_ff_available(target, i)) { + ff_to_slice(ctx, ci, target, i, false); + goto ff_packed; + } + } + } + + if (false) { + ff_packed: + packed_cells.insert(ci->name); + continue; + } + } + std::unique_ptr<CellInfo> slice = create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), ci->name.str(ctx) + "_SLICE"); ff_to_slice(ctx, ci, slice.get(), 0, false); new_cells.push_back(std::move(slice)); + ++used_slices; packed_cells.insert(ci->name); } } @@ -1986,7 +2179,8 @@ class Ecp5Packer iol->params[ctx->id("DELAY.DEL_VALUE")] = lookup_delay(str_or_default(ci->params, ctx->id("DEL_MODE"), "USER_DEFINED")); if (ci->params.count(ctx->id("DEL_VALUE")) && - std::string(ci->params.at(ctx->id("DEL_VALUE")).as_string()).substr(0, 5) != "DELAY") + (!ci->params.at(ctx->id("DEL_VALUE")).is_string || + std::string(ci->params.at(ctx->id("DEL_VALUE")).as_string()).substr(0, 5) != "DELAY")) iol->params[ctx->id("DELAY.DEL_VALUE")] = ci->params.at(ctx->id("DEL_VALUE")); if (ci->ports.count(id_LOADN)) replace_port(ci, id_LOADN, iol, id_LOADN); @@ -2353,6 +2547,7 @@ class Ecp5Packer for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; if (ci->type == id_ECLKBRIDGECS) { + Loc loc; NetInfo *i0 = get_net_or_empty(ci, id_CLK0), *i1 = get_net_or_empty(ci, id_CLK1), *o = get_net_or_empty(ci, id_ECSOUT); for (NetInfo *input : {i0, i1}) { @@ -2366,24 +2561,53 @@ class Ecp5Packer for (auto bel : ctx->getBels()) { if (ctx->getBelType(bel) != id_ECLKBRIDGECS) continue; - Loc loc = ctx->getBelLocation(bel); + loc = ctx->getBelLocation(bel); if (loc.x == user_loc.x) { ci->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx); - if (o != nullptr) - for (auto user2 : o->users) { - // Set side hint to ensure edge clock choice is routeable - if (user2.cell->type == id_ECLKSYNCB && user2.port == id_ECLKI) { - NetInfo *synco = get_net_or_empty(user2.cell, id_ECLKO); - if (synco != nullptr) - bridge_side_hint[synco] = (loc.x > 1) ? 0 : 1; - } - } goto eclkbridge_done; } } } + if (input->driver.cell != nullptr) { + CellInfo *drv = input->driver.cell; + if (!drv->attrs.count(ctx->id("BEL"))) + continue; + Loc drv_loc = ctx->getBelLocation( + ctx->getBelByName(ctx->id(drv->attrs.at(ctx->id("BEL")).as_string()))); + BelId closest; + int closest_x = -1; // aim for same side of chip + for (auto bel : ctx->getBels()) { + if (ctx->getBelType(bel) != id_ECLKBRIDGECS) + continue; + loc = ctx->getBelLocation(bel); + if (closest_x == -1 || std::abs(loc.x - drv_loc.x) < std::abs(closest_x - drv_loc.x)) { + closest_x = loc.x; + closest = bel; + } + } + NPNR_ASSERT(closest != BelId()); + loc = ctx->getBelLocation(closest); + ci->attrs[ctx->id("BEL")] = ctx->getBelName(closest).str(ctx); + goto eclkbridge_done; + } + } + // If all else fails, place randomly + for (auto bel : ctx->getBels()) { + if (ctx->getBelType(bel) != id_ECLKBRIDGECS) + continue; + loc = ctx->getBelLocation(bel); + ci->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx); } eclkbridge_done: + if (o != nullptr) + for (auto user2 : o->users) { + // Set side hint to ensure edge clock choice is routeable + if (user2.cell->type == id_ECLKSYNCB && user2.port == id_ECLKI) { + NetInfo *synco = get_net_or_empty(user2.cell, id_ECLKO); + if (synco != nullptr) + bridge_side_hint[synco] = (loc.x > 1) ? 0 : 1; + } + } continue; } } @@ -2468,12 +2692,37 @@ class Ecp5Packer const NetInfo *clk = net_or_nullptr(ci, id_CLK); if (clk == nullptr) log_error("DDRDLLA '%s' has disconnected port CLK\n", ci->name.c_str(ctx)); + + bool left_bank_users = false, right_bank_users = false; + // Check which side the delay codes (DDRDEL) are used on + const NetInfo *ddrdel = net_or_nullptr(ci, id_DDRDEL); + if (ddrdel != nullptr) { + for (auto &usr : ddrdel->users) { + const CellInfo *uc = usr.cell; + if (uc->type != id_DQSBUFM || !uc->attrs.count(ctx->id("BEL"))) + continue; + BelId dqsb_bel = ctx->getBelByName(ctx->id(uc->attrs.at(ctx->id("BEL")).as_string())); + Loc dqsb_loc = ctx->getBelLocation(dqsb_bel); + if (dqsb_loc.x > 15) + right_bank_users = true; + if (dqsb_loc.x < 15) + left_bank_users = true; + } + } + + if (left_bank_users && right_bank_users) + log_error("DDRDLLA '%s' has DDRDEL uses on both sides of the chip.\n", ctx->nameOf(ci)); + for (auto &eclk : eclks) { if (eclk.second.unbuf == clk) { for (auto bel : ctx->getBels()) { if (ctx->getBelType(bel) != id_DDRDLL) continue; Loc loc = ctx->getBelLocation(bel); + if (loc.x > 15 && left_bank_users) + continue; + if (loc.x < 15 && right_bank_users) + continue; int ddrdll_bank = -1; if (loc.x < 15 && loc.y < 15) ddrdll_bank = 7; @@ -2485,6 +2734,7 @@ class Ecp5Packer ddrdll_bank = 3; if (eclk.first.first != ddrdll_bank) continue; + log_info("Constraining DDRDLLA '%s' to bel '%s'\n", ctx->nameOf(ci), ctx->nameOfBel(bel)); ci->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx); make_eclk(ci->ports.at(id_CLK), ci, bel, eclk.first.first); goto ddrdll_done; @@ -2583,7 +2833,7 @@ class Ecp5Packer std::unordered_set<IdString> changed_cells; for (auto net : changed_nets) for (auto &user : ctx->nets.at(net)->users) - if (user.port == id_CLKI || user.port == id_ECLKI) + if (user.port == id_CLKI || user.port == id_ECLKI || user.port == id_CLK0 || user.port == id_CLK1) changed_cells.insert(user.cell->name); changed_nets.clear(); for (auto cell : sorted(changed_cells)) { @@ -2600,6 +2850,9 @@ class Ecp5Packer copy_constraint(ci, id_CLKI, id_CDIVX, ratio); } else if (ci->type == id_ECLKSYNCB || ci->type == id_TRELLIS_ECLKBUF) { copy_constraint(ci, id_ECLKI, id_ECLKO, 1); + } else if (ci->type == id_ECLKBRIDGECS) { + copy_constraint(ci, id_CLK0, id_ECSOUT, 1); + copy_constraint(ci, id_CLK1, id_ECSOUT, 1); } else if (ci->type == id_DCCA) { copy_constraint(ci, id_CLKI, id_CLKO, 1); } else if (ci->type == id_EHXPLLL) { @@ -2657,14 +2910,15 @@ class Ecp5Packer void pack() { prepack_checks(); + print_logic_usage(); pack_io(); pack_dqsbuf(); + preplace_plls(); pack_iologic(); pack_ebr(); pack_dsps(); pack_dcus(); pack_misc(); - preplace_plls(); pack_constants(); pack_dram(); pack_carries(); diff --git a/ecp5/synth/.gitignore b/ecp5/synth/.gitignore deleted file mode 100644 index f4dfa215..00000000 --- a/ecp5/synth/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.bit -*_out.config - diff --git a/ecp5/synth/blinky.v b/ecp5/synth/blinky.v deleted file mode 100644 index 9c6b187b..00000000 --- a/ecp5/synth/blinky.v +++ /dev/null @@ -1,77 +0,0 @@ -module top(input clk_pin, input btn_pin, output [7:0] led_pin, output gpio0_pin); - - wire clk; - wire [7:0] led; - wire btn; - wire gpio0; - - (* LOC="G2" *) (* IO_TYPE="LVCMOS33" *) - TRELLIS_IO #(.DIR("INPUT")) clk_buf (.B(clk_pin), .O(clk)); - - (* LOC="R1" *) (* IO_TYPE="LVCMOS33" *) - TRELLIS_IO #(.DIR("INPUT")) btn_buf (.B(btn_pin), .O(btn)); - - (* LOC="B2" *) (* IO_TYPE="LVCMOS33" *) - TRELLIS_IO #(.DIR("OUTPUT")) led_buf_0 (.B(led_pin[0]), .I(led[0])); - (* LOC="C2" *) (* IO_TYPE="LVCMOS33" *) - TRELLIS_IO #(.DIR("OUTPUT")) led_buf_1 (.B(led_pin[1]), .I(led[1])); - (* LOC="C1" *) (* IO_TYPE="LVCMOS33" *) - TRELLIS_IO #(.DIR("OUTPUT")) led_buf_2 (.B(led_pin[2]), .I(led[2])); - (* LOC="D2" *) (* IO_TYPE="LVCMOS33" *) - TRELLIS_IO #(.DIR("OUTPUT")) led_buf_3 (.B(led_pin[3]), .I(led[3])); - - (* LOC="D1" *) (* IO_TYPE="LVCMOS33" *) - TRELLIS_IO #(.DIR("OUTPUT")) led_buf_4 (.B(led_pin[4]), .I(led[4])); - (* LOC="E2" *) (* IO_TYPE="LVCMOS33" *) - TRELLIS_IO #(.DIR("OUTPUT")) led_buf_5 (.B(led_pin[5]), .I(led[5])); - (* LOC="E1" *) (* IO_TYPE="LVCMOS33" *) - TRELLIS_IO #(.DIR("OUTPUT")) led_buf_6 (.B(led_pin[6]), .I(led[6])); - (* LOC="H3" *) (* IO_TYPE="LVCMOS33" *) - TRELLIS_IO #(.DIR("OUTPUT")) led_buf_7 (.B(led_pin[7]), .I(led[7])); - - - (* LOC="L2" *) (* IO_TYPE="LVCMOS33" *) - TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0)); - - localparam ctr_width = 24; - localparam ctr_max = 2**ctr_width - 1; - reg [ctr_width-1:0] ctr = 0; - reg [9:0] pwm_ctr = 0; - reg dir = 0; - - always@(posedge clk) begin - ctr <= btn ? ctr : (dir ? ctr - 1'b1 : ctr + 1'b1); - if (ctr[ctr_width-1 : ctr_width-3] == 0 && dir == 1) - dir <= 1'b0; - else if (ctr[ctr_width-1 : ctr_width-3] == 7 && dir == 0) - dir <= 1'b1; - pwm_ctr <= pwm_ctr + 1'b1; - end - - reg [9:0] brightness [0:7]; - localparam bright_max = 2**10 - 1; - reg [7:0] led_reg; - - genvar i; - generate - for (i = 0; i < 8; i=i+1) begin - always @ (posedge clk) begin - if (ctr[ctr_width-1 : ctr_width-3] == i) - brightness[i] <= bright_max; - else if (ctr[ctr_width-1 : ctr_width-3] == (i - 1)) - brightness[i] <= ctr[ctr_width-4:ctr_width-13]; - else if (ctr[ctr_width-1 : ctr_width-3] == (i + 1)) - brightness[i] <= bright_max - ctr[ctr_width-4:ctr_width-13]; - else - brightness[i] <= 0; - led_reg[i] <= pwm_ctr < brightness[i]; - end - end - endgenerate - - assign led = led_reg; - - // Tie GPIO0, keep board from rebooting - assign gpio0 = 1'b1; - -endmodule diff --git a/ecp5/synth/blinky.ys b/ecp5/synth/blinky.ys deleted file mode 100644 index fb359380..00000000 --- a/ecp5/synth/blinky.ys +++ /dev/null @@ -1,2 +0,0 @@ -read_verilog blinky.v -synth_ecp5 -noccu2 -nomux -nodram -json blinky.json diff --git a/ecp5/synth/ulx3s_empty.config b/ecp5/synth/ulx3s_empty.config deleted file mode 100644 index 815e7f0d..00000000 --- a/ecp5/synth/ulx3s_empty.config +++ /dev/null @@ -1,439 +0,0 @@ -.device LFE5U-45F - -.tile CIB_R10C3:PVT_COUNT2 -unknown: F2B0 -unknown: F3B0 -unknown: F5B0 -unknown: F11B0 -unknown: F13B0 - -.tile CIB_R5C1:CIB_PLL1 -enum: CIB.JA3MUX 0 -enum: CIB.JB3MUX 0 - - -.tile CIB_R5C89:CIB_PLL1 -enum: CIB.JA3MUX 0 -enum: CIB.JB3MUX 0 - - -.tile CIB_R70C3:CIB_PLL3 -enum: CIB.JA3MUX 0 -enum: CIB.JB3MUX 0 - - -.tile CIB_R70C42:VCIB_DCU0 -enum: CIB.JA1MUX 0 -enum: CIB.JA3MUX 0 -enum: CIB.JA5MUX 0 -enum: CIB.JA7MUX 0 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JC0MUX 0 -enum: CIB.JC2MUX 0 -enum: CIB.JC4MUX 0 -enum: CIB.JC6MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C43:VCIB_DCUA -enum: CIB.JA1MUX 0 -enum: CIB.JA3MUX 0 -enum: CIB.JA5MUX 0 -enum: CIB.JA7MUX 0 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JC0MUX 0 -enum: CIB.JC2MUX 0 -enum: CIB.JC4MUX 0 -enum: CIB.JC6MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C44:VCIB_DCUB -enum: CIB.JA1MUX 0 -enum: CIB.JA3MUX 0 -enum: CIB.JA5MUX 0 -enum: CIB.JA7MUX 0 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JC0MUX 0 -enum: CIB.JC2MUX 0 -enum: CIB.JC4MUX 0 -enum: CIB.JC6MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C45:VCIB_DCUC -enum: CIB.JA1MUX 0 -enum: CIB.JA3MUX 0 -enum: CIB.JA5MUX 0 -enum: CIB.JA7MUX 0 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JC0MUX 0 -enum: CIB.JC2MUX 0 -enum: CIB.JC4MUX 0 -enum: CIB.JC6MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C46:VCIB_DCUD -enum: CIB.JA1MUX 0 -enum: CIB.JA5MUX 0 -enum: CIB.JA7MUX 0 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JC0MUX 0 -enum: CIB.JC2MUX 0 -enum: CIB.JC4MUX 0 -enum: CIB.JC6MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C47:VCIB_DCUF -enum: CIB.JA1MUX 0 -enum: CIB.JA3MUX 0 -enum: CIB.JA5MUX 0 -enum: CIB.JA7MUX 0 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JC0MUX 0 -enum: CIB.JC2MUX 0 -enum: CIB.JC4MUX 0 -enum: CIB.JC6MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C48:VCIB_DCU3 -enum: CIB.JA5MUX 0 -enum: CIB.JA7MUX 0 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JC0MUX 0 -enum: CIB.JC4MUX 0 -enum: CIB.JC6MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C49:VCIB_DCU2 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C50:VCIB_DCUG -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C51:VCIB_DCUH -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C52:VCIB_DCUI -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C53:VCIB_DCU1 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 - - -.tile CIB_R70C69:VCIB_DCU0 -enum: CIB.JA1MUX 0 -enum: CIB.JA3MUX 0 -enum: CIB.JA5MUX 0 -enum: CIB.JA7MUX 0 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JC0MUX 0 -enum: CIB.JC2MUX 0 -enum: CIB.JC4MUX 0 -enum: CIB.JC6MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C6:CIB_EFB0 -enum: CIB.JB3MUX 0 -enum: CIB.JC6MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C70:VCIB_DCUA -enum: CIB.JA1MUX 0 -enum: CIB.JA3MUX 0 -enum: CIB.JA5MUX 0 -enum: CIB.JA7MUX 0 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JC0MUX 0 -enum: CIB.JC2MUX 0 -enum: CIB.JC4MUX 0 -enum: CIB.JC6MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C71:VCIB_DCUB -enum: CIB.JA1MUX 0 -enum: CIB.JA3MUX 0 -enum: CIB.JA5MUX 0 -enum: CIB.JA7MUX 0 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JC0MUX 0 -enum: CIB.JC2MUX 0 -enum: CIB.JC4MUX 0 -enum: CIB.JC6MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C72:VCIB_DCUC -enum: CIB.JA1MUX 0 -enum: CIB.JA3MUX 0 -enum: CIB.JA5MUX 0 -enum: CIB.JA7MUX 0 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JC0MUX 0 -enum: CIB.JC2MUX 0 -enum: CIB.JC4MUX 0 -enum: CIB.JC6MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C73:VCIB_DCUD -enum: CIB.JA1MUX 0 -enum: CIB.JA5MUX 0 -enum: CIB.JA7MUX 0 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JC0MUX 0 -enum: CIB.JC2MUX 0 -enum: CIB.JC4MUX 0 -enum: CIB.JC6MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C74:VCIB_DCUF -enum: CIB.JA1MUX 0 -enum: CIB.JA3MUX 0 -enum: CIB.JA5MUX 0 -enum: CIB.JA7MUX 0 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JC0MUX 0 -enum: CIB.JC2MUX 0 -enum: CIB.JC4MUX 0 -enum: CIB.JC6MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C75:VCIB_DCU3 -enum: CIB.JA5MUX 0 -enum: CIB.JA7MUX 0 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JC0MUX 0 -enum: CIB.JC4MUX 0 -enum: CIB.JC6MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C76:VCIB_DCU2 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C77:VCIB_DCUG -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C78:VCIB_DCUH -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C79:VCIB_DCUI -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB7MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD6MUX 0 - - -.tile CIB_R70C7:CIB_EFB1 -enum: CIB.JA3MUX 0 -enum: CIB.JA4MUX 0 -enum: CIB.JA5MUX 0 -enum: CIB.JA6MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB4MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JB6MUX 0 -enum: CIB.JC3MUX 0 -enum: CIB.JC4MUX 0 -enum: CIB.JC5MUX 0 -enum: CIB.JD3MUX 0 -enum: CIB.JD4MUX 0 -enum: CIB.JD5MUX 0 - - -.tile CIB_R70C80:VCIB_DCU1 -enum: CIB.JB1MUX 0 -enum: CIB.JB3MUX 0 -enum: CIB.JB5MUX 0 -enum: CIB.JD0MUX 0 -enum: CIB.JD2MUX 0 - - -.tile CIB_R70C87:CIB_PLL3 -enum: CIB.JA3MUX 0 -enum: CIB.JB3MUX 0 - - -.tile MIB_R10C40:CMUX_UL_0 -arc: G_DCS0CLK0 G_VPFN0000 - - -.tile MIB_R10C41:CMUX_UR_0 -arc: G_DCS0CLK1 G_VPFN0000 - - -.tile MIB_R58C40:CMUX_LL_0 -arc: G_DCS1CLK0 G_VPFN0000 - - -.tile MIB_R58C41:CMUX_LR_0 -arc: G_DCS1CLK1 G_VPFN0000 - - -.tile MIB_R71C4:EFB0_PICB0 -unknown: F54B1 -unknown: F56B1 -unknown: F82B1 -unknown: F94B1 - -.tile MIB_R71C3:BANKREF8 -unknown: F18B0 - diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py index 9f8a9482..c8589b6c 100755 --- a/ecp5/trellis_import.py +++ b/ecp5/trellis_import.py @@ -320,6 +320,8 @@ def process_timing_data(): max_delay = min(entry["rising"][2], entry["falling"][2]) delays.append((constids[from_pin], constids[to_pin], min_delay, max_delay)) elif entry["type"] == "SetupHold": + if type(entry["pin"]) is list: + continue pin = constids[entry["pin"]] clock = constids[entry["clock"][1]] min_setup = entry["setup"][0] @@ -580,6 +582,7 @@ def write_database(dev_name, chip, ddrg, endianness): bba.u32(len(location_types), "num_location_types") bba.u32(len(packages), "num_packages") bba.u32(len(pindata), "num_pios") + bba.u32(const_id_count, "const_id_count") bba.r("locations", "locations") bba.r("location_types", "location_type") @@ -596,11 +599,12 @@ def write_database(dev_name, chip, ddrg, endianness): dev_names = {"25k": "LFE5UM5G-25F", "45k": "LFE5UM5G-45F", "85k": "LFE5UM5G-85F"} def main(): - global max_row, max_col + global max_row, max_col, const_id_count pytrellis.load_database(database.get_db_root()) args = parser.parse_args() # Read port pin file + const_id_count = 1 # count ID_NONE with open(args.constids) as f: for line in f: line = line.replace("(", " ") @@ -612,7 +616,7 @@ def main(): assert line[0] == "X" idx = len(constids) + 1 constids[line[1]] = idx - + const_id_count += 1 constids["SLICE"] = constids["TRELLIS_SLICE"] constids["PIO"] = constids["TRELLIS_IO"] |