Merge pull request #338 from YosysHQ/docs

Documentation Improvements
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e0e578d..fc38c13 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,9 +12,7 @@
 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 b90e80e..f681af9 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 8ba1af4..348f219 100644
--- a/ecp5/arch.cc
+++ b/ecp5/arch.cc
@@ -922,28 +922,41 @@
         }
     } 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 d7ea0a8..da12eea 100644
--- a/ecp5/archdefs.h
+++ b/ecp5/archdefs.h
@@ -163,6 +163,10 @@
         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 f010d7d..d2a90b8 100644
--- a/ecp5/bitstream.cc
+++ b/ecp5/bitstream.cc
@@ -927,21 +927,25 @@
             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 @@
 
             // 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 @@
             }
 
             // 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 @@
 
             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 509d33f..408ff77 100644
--- a/ecp5/pack.cc
+++ b/ecp5/pack.cc
@@ -1253,6 +1253,61 @@
     {
         // 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 @@
             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)) {