diff options
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | common/arch_pybindings_shared.h | 106 | ||||
-rw-r--r-- | ecp5/arch.cc | 21 | ||||
-rw-r--r-- | ecp5/archdefs.h | 4 | ||||
-rw-r--r-- | ecp5/bitstream.cc | 37 | ||||
-rw-r--r-- | ecp5/pack.cc | 57 |
6 files changed, 153 insertions, 76 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index e0e578df..fc38c130 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,9 +12,7 @@ option(STATIC_BUILD "Create static build" OFF) option(EXTERNAL_CHIPDB "Create build with pre-built chipdb binaries" OFF) option(SERIALIZE_CHIPDB "Never build chipdb in parallel to reduce peak memory use" ON) -if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(Boost_NO_BOOST_CMAKE ON) -endif() +set(Boost_NO_BOOST_CMAKE ON) set(link_param "") if (STATIC_BUILD) diff --git a/common/arch_pybindings_shared.h b/common/arch_pybindings_shared.h index b90e80ec..f681af92 100644 --- a/common/arch_pybindings_shared.h +++ b/common/arch_pybindings_shared.h @@ -1,114 +1,112 @@ // Common Python bindings #included by all arches readonly_wrapper<Context, decltype(&Context::cells), &Context::cells, wrap_context<CellMap &>>::def_wrap(ctx_cls, -"cells"); -readonly_wrapper<Context, decltype(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls, -"nets"); -readonly_wrapper<Context, decltype(&Context::net_aliases), &Context::net_aliases, - wrap_context<AliasMap &>>::def_wrap(ctx_cls, "net_aliases"); + "cells"); +readonly_wrapper<Context, decltype(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls, "nets"); +readonly_wrapper<Context, decltype(&Context::net_aliases), &Context::net_aliases, wrap_context<AliasMap &>>::def_wrap( + ctx_cls, "net_aliases"); fn_wrapper_1a<Context, decltype(&Context::getNetByAlias), &Context::getNetByAlias, deref_and_wrap<NetInfo>, -conv_from_str<IdString>>::def_wrap(ctx_cls, "getNetByAlias"); + conv_from_str<IdString>>::def_wrap(ctx_cls, "getNetByAlias"); fn_wrapper_2a_v<Context, decltype(&Context::addClock), &Context::addClock, conv_from_str<IdString>, -pass_through<float>>::def_wrap(ctx_cls, "addClock"); + pass_through<float>>::def_wrap(ctx_cls, "addClock"); fn_wrapper_5a_v<Context, decltype(&Context::createRectangularRegion), &Context::createRectangularRegion, - conv_from_str<IdString>, pass_through<int>, pass_through<int>, pass_through<int>, -pass_through<int>>::def_wrap(ctx_cls, "createRectangularRegion"); + conv_from_str<IdString>, pass_through<int>, pass_through<int>, pass_through<int>, + pass_through<int>>::def_wrap(ctx_cls, "createRectangularRegion"); fn_wrapper_2a_v<Context, decltype(&Context::addBelToRegion), &Context::addBelToRegion, conv_from_str<IdString>, -conv_from_str<BelId>>::def_wrap(ctx_cls, "addBelToRegion"); + conv_from_str<BelId>>::def_wrap(ctx_cls, "addBelToRegion"); fn_wrapper_2a_v<Context, decltype(&Context::constrainCellToRegion), &Context::constrainCellToRegion, - conv_from_str<IdString>, conv_from_str<IdString>>::def_wrap(ctx_cls, "constrainCellToRegion"); - + conv_from_str<IdString>, conv_from_str<IdString>>::def_wrap(ctx_cls, "constrainCellToRegion"); fn_wrapper_1a<Context, decltype(&Context::createNet), &Context::createNet, deref_and_wrap<NetInfo>, -conv_from_str<IdString>>::def_wrap(ctx_cls, "createNet"); -fn_wrapper_3a_v<Context, decltype(&Context::connectPort), &Context::connectPort, conv_from_str<IdString>, conv_from_str<IdString>, -conv_from_str<IdString>>::def_wrap(ctx_cls, "connectPort"); + conv_from_str<IdString>>::def_wrap(ctx_cls, "createNet"); +fn_wrapper_3a_v<Context, decltype(&Context::connectPort), &Context::connectPort, conv_from_str<IdString>, + conv_from_str<IdString>, conv_from_str<IdString>>::def_wrap(ctx_cls, "connectPort"); fn_wrapper_2a_v<Context, decltype(&Context::disconnectPort), &Context::disconnectPort, conv_from_str<IdString>, -conv_from_str<IdString>>::def_wrap(ctx_cls, "disconnectPort"); + conv_from_str<IdString>>::def_wrap(ctx_cls, "disconnectPort"); fn_wrapper_1a_v<Context, decltype(&Context::ripupNet), &Context::ripupNet, conv_from_str<IdString>>::def_wrap( ctx_cls, "ripupNet"); -fn_wrapper_1a_v<Context, decltype(&Context::lockNetRouting), &Context::lockNetRouting, conv_from_str<IdString>>::def_wrap( - ctx_cls, "lockNetRouting"); +fn_wrapper_1a_v<Context, decltype(&Context::lockNetRouting), &Context::lockNetRouting, + conv_from_str<IdString>>::def_wrap(ctx_cls, "lockNetRouting"); fn_wrapper_2a<Context, decltype(&Context::createCell), &Context::createCell, deref_and_wrap<CellInfo>, -conv_from_str<IdString>, conv_from_str<IdString>>::def_wrap(ctx_cls, "createCell"); -fn_wrapper_2a_v<Context, decltype(&Context::copyBelPorts), &Context::copyBelPorts, -conv_from_str<IdString>, conv_from_str<BelId>>::def_wrap(ctx_cls, "copyBelPorts"); + conv_from_str<IdString>, conv_from_str<IdString>>::def_wrap(ctx_cls, "createCell"); +fn_wrapper_2a_v<Context, decltype(&Context::copyBelPorts), &Context::copyBelPorts, conv_from_str<IdString>, + conv_from_str<BelId>>::def_wrap(ctx_cls, "copyBelPorts"); fn_wrapper_1a<Context, decltype(&Context::getBelType), &Context::getBelType, conv_to_str<IdString>, -conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelType"); + conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelType"); fn_wrapper_1a<Context, decltype(&Context::checkBelAvail), &Context::checkBelAvail, pass_through<bool>, -conv_from_str<BelId>>::def_wrap(ctx_cls, "checkBelAvail"); + conv_from_str<BelId>>::def_wrap(ctx_cls, "checkBelAvail"); fn_wrapper_1a<Context, decltype(&Context::getBelChecksum), &Context::getBelChecksum, pass_through<uint32_t>, -conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelChecksum"); + conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelChecksum"); fn_wrapper_3a_v<Context, decltype(&Context::bindBel), &Context::bindBel, conv_from_str<BelId>, -addr_and_unwrap<CellInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindBel"); + addr_and_unwrap<CellInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindBel"); fn_wrapper_1a_v<Context, decltype(&Context::unbindBel), &Context::unbindBel, conv_from_str<BelId>>::def_wrap( ctx_cls, "unbindBel"); fn_wrapper_1a<Context, decltype(&Context::getBoundBelCell), &Context::getBoundBelCell, deref_and_wrap<CellInfo>, -conv_from_str<BelId>>::def_wrap(ctx_cls, "getBoundBelCell"); + conv_from_str<BelId>>::def_wrap(ctx_cls, "getBoundBelCell"); fn_wrapper_1a<Context, decltype(&Context::getConflictingBelCell), &Context::getConflictingBelCell, - deref_and_wrap<CellInfo>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell"); + deref_and_wrap<CellInfo>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell"); fn_wrapper_0a<Context, decltype(&Context::getBels), &Context::getBels, wrap_context<BelRange>>::def_wrap(ctx_cls, -"getBels"); + "getBels"); fn_wrapper_2a<Context, decltype(&Context::getBelPinWire), &Context::getBelPinWire, conv_to_str<WireId>, -conv_from_str<BelId>, conv_from_str<IdString>>::def_wrap(ctx_cls, "getBelPinWire"); + conv_from_str<BelId>, conv_from_str<IdString>>::def_wrap(ctx_cls, "getBelPinWire"); fn_wrapper_1a<Context, decltype(&Context::getWireBelPins), &Context::getWireBelPins, wrap_context<BelPinRange>, -conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireBelPins"); + conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireBelPins"); fn_wrapper_1a<Context, decltype(&Context::getWireChecksum), &Context::getWireChecksum, pass_through<uint32_t>, -conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireChecksum"); + conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireChecksum"); fn_wrapper_3a_v<Context, decltype(&Context::bindWire), &Context::bindWire, conv_from_str<WireId>, -addr_and_unwrap<NetInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindWire"); + addr_and_unwrap<NetInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindWire"); fn_wrapper_1a_v<Context, decltype(&Context::unbindWire), &Context::unbindWire, conv_from_str<WireId>>::def_wrap( ctx_cls, "unbindWire"); fn_wrapper_1a<Context, decltype(&Context::checkWireAvail), &Context::checkWireAvail, pass_through<bool>, -conv_from_str<WireId>>::def_wrap(ctx_cls, "checkWireAvail"); + conv_from_str<WireId>>::def_wrap(ctx_cls, "checkWireAvail"); fn_wrapper_1a<Context, decltype(&Context::getBoundWireNet), &Context::getBoundWireNet, deref_and_wrap<NetInfo>, -conv_from_str<WireId>>::def_wrap(ctx_cls, "getBoundWireNet"); + conv_from_str<WireId>>::def_wrap(ctx_cls, "getBoundWireNet"); fn_wrapper_1a<Context, decltype(&Context::getConflictingWireNet), &Context::getConflictingWireNet, - deref_and_wrap<NetInfo>, conv_from_str<WireId>>::def_wrap(ctx_cls, "getConflictingWireNet"); + deref_and_wrap<NetInfo>, conv_from_str<WireId>>::def_wrap(ctx_cls, "getConflictingWireNet"); -fn_wrapper_0a<Context, decltype(&Context::getWires), &Context::getWires, wrap_context<WireRange>>::def_wrap( - ctx_cls, "getWires"); +fn_wrapper_0a<Context, decltype(&Context::getWires), &Context::getWires, wrap_context<WireRange>>::def_wrap(ctx_cls, + "getWires"); -fn_wrapper_0a<Context, decltype(&Context::getPips), &Context::getPips, wrap_context<AllPipRange>>::def_wrap( - ctx_cls, "getPips"); +fn_wrapper_0a<Context, decltype(&Context::getPips), &Context::getPips, wrap_context<AllPipRange>>::def_wrap(ctx_cls, + "getPips"); fn_wrapper_1a<Context, decltype(&Context::getPipChecksum), &Context::getPipChecksum, pass_through<uint32_t>, -conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipChecksum"); -fn_wrapper_3a_v<Context, decltype(&Context::bindPip), &Context::bindPip, conv_from_str<PipId>, -addr_and_unwrap<NetInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindPip"); + conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipChecksum"); +fn_wrapper_3a_v<Context, decltype(&Context::bindPip), &Context::bindPip, conv_from_str<PipId>, addr_and_unwrap<NetInfo>, + pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindPip"); fn_wrapper_1a_v<Context, decltype(&Context::unbindPip), &Context::unbindPip, conv_from_str<PipId>>::def_wrap( ctx_cls, "unbindPip"); fn_wrapper_1a<Context, decltype(&Context::checkPipAvail), &Context::checkPipAvail, pass_through<bool>, -conv_from_str<PipId>>::def_wrap(ctx_cls, "checkPipAvail"); + conv_from_str<PipId>>::def_wrap(ctx_cls, "checkPipAvail"); fn_wrapper_1a<Context, decltype(&Context::getBoundPipNet), &Context::getBoundPipNet, deref_and_wrap<NetInfo>, -conv_from_str<PipId>>::def_wrap(ctx_cls, "getBoundPipNet"); + conv_from_str<PipId>>::def_wrap(ctx_cls, "getBoundPipNet"); fn_wrapper_1a<Context, decltype(&Context::getConflictingPipNet), &Context::getConflictingPipNet, - deref_and_wrap<NetInfo>, conv_from_str<PipId>>::def_wrap(ctx_cls, "getConflictingPipNet"); + deref_and_wrap<NetInfo>, conv_from_str<PipId>>::def_wrap(ctx_cls, "getConflictingPipNet"); fn_wrapper_1a<Context, decltype(&Context::getPipsDownhill), &Context::getPipsDownhill, wrap_context<PipRange>, -conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsDownhill"); + conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsDownhill"); fn_wrapper_1a<Context, decltype(&Context::getPipsUphill), &Context::getPipsUphill, wrap_context<PipRange>, -conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsUphill"); + conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsUphill"); fn_wrapper_1a<Context, decltype(&Context::getWireAliases), &Context::getWireAliases, wrap_context<PipRange>, -conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireAliases"); + conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireAliases"); fn_wrapper_1a<Context, decltype(&Context::getPipSrcWire), &Context::getPipSrcWire, conv_to_str<WireId>, -conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipSrcWire"); + conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipSrcWire"); fn_wrapper_1a<Context, decltype(&Context::getPipDstWire), &Context::getPipDstWire, conv_to_str<WireId>, -conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDstWire"); + conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDstWire"); fn_wrapper_1a<Context, decltype(&Context::getPipDelay), &Context::getPipDelay, pass_through<DelayInfo>, -conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDelay"); + conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDelay"); fn_wrapper_1a<Context, decltype(&Context::getPackagePinBel), &Context::getPackagePinBel, conv_to_str<BelId>, -pass_through<std::string>>::def_wrap(ctx_cls, "getPackagePinBel"); + pass_through<std::string>>::def_wrap(ctx_cls, "getPackagePinBel"); fn_wrapper_1a<Context, decltype(&Context::getBelPackagePin), &Context::getBelPackagePin, pass_through<std::string>, -conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelPackagePin"); + conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelPackagePin"); fn_wrapper_0a<Context, decltype(&Context::getChipName), &Context::getChipName, pass_through<std::string>>::def_wrap( ctx_cls, "getChipName"); fn_wrapper_0a<Context, decltype(&Context::archId), &Context::archId, conv_to_str<IdString>>::def_wrap(ctx_cls, -"archId"); + "archId"); diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 8ba1af4d..348f2192 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -922,28 +922,41 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port } } else if (cell->type == id_DP16KD) { std::string port_name = port.str(this); + IdString half_clock; for (auto c : boost::adaptors::reverse(port_name)) { if (std::isdigit(c)) continue; if (c == 'A') { - info.clock_port = id_CLKA; + half_clock = id_CLKA; break; } else if (c == 'B') { - info.clock_port = id_CLKB; + half_clock = id_CLKB; break; } else NPNR_ASSERT_FALSE_STR("bad ram port " + port.str(this)); } + if (cell->ramInfo.is_pdp) { + bool is_output = cell->ports.at(port).type == PORT_OUT; + // In PDP mode, all read signals are in CLKB domain and write signals in CLKA domain + if (is_output || port == id_OCEB || port == id_CEB || port == id_ADB5 || port == id_ADB6 || + port == id_ADB7 || port == id_ADB8 || port == id_ADB9 || port == id_ADB10 || port == id_ADB11 || + port == id_ADB12 || port == id_ADB13) + info.clock_port = id_CLKB; + else + info.clock_port = id_CLKA; + } else { + info.clock_port = half_clock; + } info.edge = (str_or_default(cell->params, info.clock_port == id_CLKB ? id("CLKBMUX") : id("CLKAMUX"), "CLK") == "INV") ? FALLING_EDGE : RISING_EDGE; if (cell->ports.at(port).type == PORT_OUT) { - bool is_path = getDelayFromTimingDatabase(id_DP16KD_REGMODE_A_NOREG_REGMODE_B_NOREG, info.clock_port, port, + bool is_path = getDelayFromTimingDatabase(id_DP16KD_REGMODE_A_NOREG_REGMODE_B_NOREG, half_clock, port, info.clockToQ); NPNR_ASSERT(is_path); } else { - getSetupHoldFromTimingDatabase(id_DP16KD_REGMODE_A_NOREG_REGMODE_B_NOREG, info.clock_port, port, info.setup, + getSetupHoldFromTimingDatabase(id_DP16KD_REGMODE_A_NOREG_REGMODE_B_NOREG, half_clock, port, info.setup, info.hold); } } else if (cell->type == id_DCUA) { diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h index d7ea0a8e..da12eeaa 100644 --- a/ecp5/archdefs.h +++ b/ecp5/archdefs.h @@ -163,6 +163,10 @@ struct ArchCellInfo IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode; int sd0, sd1; } sliceInfo; + struct + { + bool is_pdp; + } ramInfo; }; NEXTPNR_NAMESPACE_END diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index f010d7dd..d2a90b86 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -927,21 +927,25 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex tg.tiles = get_bram_tiles(ctx, ci->bel); std::string ebr = "EBR" + std::to_string(loc.z); - tg.config.add_enum(ebr + ".MODE", "DP16KD"); + if (ci->ramInfo.is_pdp) { + tg.config.add_enum(ebr + ".MODE", "PDPW16KD"); + tg.config.add_enum(ebr + ".PDPW16KD.DATA_WIDTH_R", + intstr_or_default(ci->params, ctx->id("DATA_WIDTH_B"), "36")); + } else { + tg.config.add_enum(ebr + ".MODE", "DP16KD"); + tg.config.add_enum(ebr + ".DP16KD.DATA_WIDTH_A", + intstr_or_default(ci->params, ctx->id("DATA_WIDTH_A"), "18")); + tg.config.add_enum(ebr + ".DP16KD.DATA_WIDTH_B", + intstr_or_default(ci->params, ctx->id("DATA_WIDTH_B"), "18")); + tg.config.add_enum(ebr + ".DP16KD.WRITEMODE_A", + str_or_default(ci->params, ctx->id("WRITEMODE_A"), "NORMAL")); + tg.config.add_enum(ebr + ".DP16KD.WRITEMODE_B", + str_or_default(ci->params, ctx->id("WRITEMODE_B"), "NORMAL")); + } auto csd_a = str_to_bitvector(str_or_default(ci->params, ctx->id("CSDECODE_A"), "0b000"), 3), csd_b = str_to_bitvector(str_or_default(ci->params, ctx->id("CSDECODE_B"), "0b000"), 3); - tg.config.add_enum(ebr + ".DP16KD.DATA_WIDTH_A", - intstr_or_default(ci->params, ctx->id("DATA_WIDTH_A"), "18")); - tg.config.add_enum(ebr + ".DP16KD.DATA_WIDTH_B", - intstr_or_default(ci->params, ctx->id("DATA_WIDTH_B"), "18")); - - tg.config.add_enum(ebr + ".DP16KD.WRITEMODE_A", - str_or_default(ci->params, ctx->id("WRITEMODE_A"), "NORMAL")); - tg.config.add_enum(ebr + ".DP16KD.WRITEMODE_B", - str_or_default(ci->params, ctx->id("WRITEMODE_B"), "NORMAL")); - tg.config.add_enum(ebr + ".REGMODE_A", str_or_default(ci->params, ctx->id("REGMODE_A"), "NOREG")); tg.config.add_enum(ebr + ".REGMODE_B", str_or_default(ci->params, ctx->id("REGMODE_B"), "NOREG")); @@ -955,6 +959,8 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex // Tie signals as appropriate for (auto port : ci->ports) { + if (ci->ramInfo.is_pdp && (port.first == id_WEA || port.first == id_WEB || port.first == id_ADA4)) + continue; if (port.second.net == nullptr && port.second.type == PORT_IN) { if (port.first == id_CLKA || port.first == id_CLKB || port.first == id_WEA || port.first == id_WEB || port.first == id_RSTA || port.first == id_RSTB) { @@ -987,7 +993,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex } // Invert CSDECODE bits to emulate inversion muxes on CSA/CSB signals - for (auto port : {std::make_pair("CSA", std::ref(csd_a)), std::make_pair("CSB", std::ref(csd_b))}) { + for (auto &port : {std::make_pair("CSA", std::ref(csd_a)), std::make_pair("CSB", std::ref(csd_b))}) { for (int bit = 0; bit < 3; bit++) { std::string sig = port.first + std::to_string(bit); if (str_or_default(ci->params, ctx->id(sig + "MUX"), sig) == "INV") @@ -1000,9 +1006,10 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex tg.config.add_enum(ebr + ".RSTAMUX", str_or_default(ci->params, ctx->id("RSTAMUX"), "RSTA")); tg.config.add_enum(ebr + ".RSTBMUX", str_or_default(ci->params, ctx->id("RSTBMUX"), "RSTB")); - tg.config.add_enum(ebr + ".WEAMUX", str_or_default(ci->params, ctx->id("WEAMUX"), "WEA")); - tg.config.add_enum(ebr + ".WEBMUX", str_or_default(ci->params, ctx->id("WEBMUX"), "WEB")); - + if (!ci->ramInfo.is_pdp) { + tg.config.add_enum(ebr + ".WEAMUX", str_or_default(ci->params, ctx->id("WEAMUX"), "WEA")); + tg.config.add_enum(ebr + ".WEBMUX", str_or_default(ci->params, ctx->id("WEBMUX"), "WEB")); + } tg.config.add_enum(ebr + ".CEAMUX", str_or_default(ci->params, ctx->id("CEAMUX"), "CEA")); tg.config.add_enum(ebr + ".CEBMUX", str_or_default(ci->params, ctx->id("CEBMUX"), "CEB")); tg.config.add_enum(ebr + ".OCEAMUX", str_or_default(ci->params, ctx->id("OCEAMUX"), "OCEA")); diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 509d33f3..408ff77d 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -1253,6 +1253,61 @@ class Ecp5Packer { // Autoincrement WID (starting from 3 seems to match vendor behaviour?) int wid = 3; + auto rename_bus = [&](CellInfo *c, const std::string &oldname, const std::string &newname, int width, + int oldoffset, int newoffset) { + for (int i = 0; i < width; i++) + rename_port(ctx, c, ctx->id(oldname + std::to_string(i + oldoffset)), + ctx->id(newname + std::to_string(i + newoffset))); + }; + auto rename_param = [&](CellInfo *c, const std::string &oldname, const std::string &newname) { + IdString o = ctx->id(oldname), n = ctx->id(newname); + if (!c->params.count(o)) + return; + c->params[n] = c->params[o]; + c->params.erase(o); + }; + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + // Convert 36-bit PDP RAMs to regular 18-bit DP ones that match the Bel + if (ci->type == ctx->id("PDPW16KD")) { + ci->params[ctx->id("DATA_WIDTH_A")] = 36; // force PDP mode + ci->params.erase(ctx->id("DATA_WIDTH_W")); + rename_bus(ci, "BE", "ADA", 4, 0, 0); + rename_bus(ci, "ADW", "ADA", 9, 0, 5); + rename_bus(ci, "ADR", "ADB", 14, 0, 0); + rename_bus(ci, "CSW", "CSA", 3, 0, 0); + rename_bus(ci, "CSR", "CSB", 3, 0, 0); + rename_bus(ci, "DI", "DIA", 18, 0, 0); + rename_bus(ci, "DI", "DIB", 18, 18, 0); + rename_bus(ci, "DO", "DOA", 18, 18, 0); + rename_bus(ci, "DO", "DOB", 18, 0, 0); + rename_port(ctx, ci, ctx->id("CLKW"), ctx->id("CLKA")); + rename_port(ctx, ci, ctx->id("CLKR"), ctx->id("CLKB")); + rename_port(ctx, ci, ctx->id("CEW"), ctx->id("CEA")); + rename_port(ctx, ci, ctx->id("CER"), ctx->id("CEB")); + rename_port(ctx, ci, ctx->id("OCER"), ctx->id("OCEB")); + rename_param(ci, "CLKWMUX", "CLKAMUX"); + if (str_or_default(ci->params, ctx->id("CLKAMUX")) == "CLKW") + ci->params[ctx->id("CLKAMUX")] = std::string("CLKA"); + if (str_or_default(ci->params, ctx->id("CLKBMUX")) == "CLKR") + ci->params[ctx->id("CLKBMUX")] = std::string("CLKB"); + rename_param(ci, "CLKRMUX", "CLKRMUX"); + rename_param(ci, "CSDECODE_W", "CSDECODE_A"); + rename_param(ci, "CSDECODE_R", "CSDECODE_B"); + rename_param(ci, "REGMODE", "REGMODE_B"); + rename_param(ci, "DATA_WIDTH_R", "DATA_WIDTH_B"); + if (ci->ports.count(id_RST)) { + autocreate_empty_port(ci, id_RSTA); + autocreate_empty_port(ci, id_RSTB); + NetInfo *rst = ci->ports.at(id_RST).net; + connect_port(ctx, rst, ci, id_RSTA); + connect_port(ctx, rst, ci, id_RSTB); + disconnect_port(ctx, ci, id_RST); + ci->ports.erase(id_RST); + } + ci->type = id_DP16KD; + } + } for (auto cell : sorted(ctx->cells)) { CellInfo *ci = cell.second; if (ci->type == id_DP16KD) { @@ -2516,6 +2571,8 @@ void Arch::assignArchInfo() if (ci->ports.count(id_FXA) && ci->ports[id_FXA].net != nullptr && ci->ports[id_FXA].net->driver.port == id_OFX0) ci->sliceInfo.has_l6mux = true; + } else if (ci->type == id_DP16KD) { + ci->ramInfo.is_pdp = (int_or_default(ci->params, id("DATA_WIDTH_A"), 0) == 36); } } for (auto net : sorted(nets)) { |