aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ecp5/arch.cc4
-rw-r--r--ecp5/arch.h6
-rw-r--r--ecp5/arch_place.cc253
-rw-r--r--ecp5/constids.inc17
4 files changed, 280 insertions, 0 deletions
diff --git a/ecp5/arch.cc b/ecp5/arch.cc
index eb874704..a7041e04 100644
--- a/ecp5/arch.cc
+++ b/ecp5/arch.cc
@@ -634,6 +634,10 @@ bool Arch::place()
for (auto &cell : cells)
cell.second->belStrength = STRENGTH_LOCKED;
+ // Once placement is complete, DSP slices sharing a block may need
+ // CLK/CE/RST ports remapped to avoid conflicting assignments.
+ remap_dsp_blocks();
+
getCtx()->settings[id_place] = 1;
archInfoToAttributes();
diff --git a/ecp5/arch.h b/ecp5/arch.h
index 6ce3c4ce..40b931d6 100644
--- a/ecp5/arch.h
+++ b/ecp5/arch.h
@@ -1085,6 +1085,12 @@ struct Arch : BaseArch<ArchRanges>
std::vector<BelBucketId> buckets;
mutable std::vector<TileStatus> tile_status;
+
+ // -------------------------------------------------
+ bool is_dsp_location_valid(CellInfo* cell) const;
+ void remap_dsp_blocks();
+ void remap_dsp_cell(CellInfo* ci, const std::array<IdString, 4> &ports,
+ std::array<NetInfo*, 4> &assigned_nets);
};
NEXTPNR_NAMESPACE_END
diff --git a/ecp5/arch_place.cc b/ecp5/arch_place.cc
index 30ae9b1b..2bb601c2 100644
--- a/ecp5/arch_place.cc
+++ b/ecp5/arch_place.cc
@@ -190,12 +190,265 @@ bool Arch::isBelLocationValid(BelId bel, bool explain_invalid) const
} else if (cell->type.in(id_DCUA, id_EXTREFB, id_PCSCLKDIV)) {
return args.type != ArchArgs::LFE5U_25F && args.type != ArchArgs::LFE5U_45F &&
args.type != ArchArgs::LFE5U_85F;
+ } else if (cell->type.in(id_MULT18X18D, id_ALU54B)) {
+ return is_dsp_location_valid(cell);
} else {
return true;
}
}
}
+// Check if this DSP cell placement would result in more than four distinct
+// CLK/CE/RST signals per block of two DSP slices.
+bool Arch::is_dsp_location_valid(CellInfo* cell) const
+{
+ // Find the location of the DSP0 tile.
+ int block_x = cell->getLocation().x - cell->getLocation().z;
+ int block_y = cell->getLocation().y;
+
+ const std::array<std::array<IdString, 4>, 3> block_ports = {{
+ {id_CLK0, id_CLK1, id_CLK2, id_CLK3},
+ {id_CE0, id_CE1, id_CE2, id_CE3},
+ {id_RST0, id_RST1, id_RST2, id_RST3}
+ }};
+ const std::array<const char*, 3> port_names = {"CLK", "CE", "RST"};
+ std::array<std::set<NetInfo*>, 3> block_nets = {};
+ bool cells_locked = true;
+
+ // Count the number of distinct CLK, CE, and RST signals used by
+ // all the MULT18X18D and ALU54B bels in the DSP block.
+ for (int dx : {0, 1, 3, 4, 5, 7}) {
+ BelId dsp_bel = getBelByLocation(Loc(block_x + dx, block_y, dx));
+ CellInfo* dsp_cell = getBoundBelCell(dsp_bel);
+ if (dsp_cell == nullptr)
+ continue;
+
+ if (dsp_cell->belStrength < STRENGTH_LOCKED)
+ cells_locked = false;
+
+ for (size_t i = 0; i < block_ports.size(); i++) {
+ auto nets = &block_nets[i];
+ for (IdString port : block_ports[i]) {
+ NetInfo *net = dsp_cell->ports.at(port).net;
+ if (net == nullptr)
+ continue;
+ nets->insert(net);
+ if (nets->size() > 4) {
+ // When all cells considered so far are locked or manually
+ // placed, the placer cannot fix this problem, so report
+ // a specific error message.
+ if (cells_locked) {
+ log_error("DSP block containing %s '%s' has more than "
+ "four distinct %s signals.\n",
+ dsp_cell->type.c_str(getCtx()),
+ dsp_cell->name.c_str(getCtx()),
+ port_names[i]);
+ }
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+// Check all cells in the design to locate used DSP blocks, then remap
+// CLK, CE, and RST port and attribute assignments to ensure each port
+// is connected to the same net throughout each block.
+void Arch::remap_dsp_blocks()
+{
+ std::set<Location> processed_blocks;
+
+ const std::array<std::array<IdString, 4>, 3> block_ports = {{
+ {id_CLK0, id_CLK1, id_CLK2, id_CLK3},
+ {id_CE0, id_CE1, id_CE2, id_CE3},
+ {id_RST0, id_RST1, id_RST2, id_RST3},
+ }};
+
+ for (auto &cell: cells) {
+ CellInfo *ci = cell.second.get();
+ if (!ci->type.in(id_MULT18X18D, id_ALU54B))
+ continue;
+
+ // Locate DSP0 tile for block containing this cell.
+ Loc loc = ci->getLocation();
+ Location block_loc(loc.x - loc.z, loc.y);
+ if (processed_blocks.count(block_loc) == 1)
+ continue;
+ processed_blocks.insert(block_loc);
+
+ for (auto &ports : block_ports) {
+ // Store assigned nets for each port.
+ std::array<NetInfo*, 4> assigned_nets = {};
+
+ // Process each possible MULT18X18D or ALU54B in this block.
+ for (int dx : {0, 1, 3, 4, 5, 7}) {
+ Loc dsp_loc = Loc(block_loc.x + dx, block_loc.y, dx);
+ BelId dsp_bel = getBelByLocation(dsp_loc);
+ CellInfo* dsp_cell = getBoundBelCell(dsp_bel);
+ if (dsp_cell == nullptr)
+ continue;
+ remap_dsp_cell(dsp_cell, ports, assigned_nets);
+ }
+ }
+ }
+}
+
+// Remap CLK/CE/RST ports in a DSP cell so that:
+// * if a port's slot in assigned_nets already matches its net, no action
+// is taken.
+// * if a port's slot in assigned_nets is empty and that port's net isn't in
+// assigned_nets, the slot is set to that port's current net and no remapping
+// is performed.
+// * if a port's currently connected net is already present in a different slot
+// to that port, then remap references to that port to the already assigned
+// port instead.
+// * if a port's slot in assigned_nets refers to a different net than the one
+// the port is currently connected to, and the currently connected net isn't
+// present elsewhere in assigned_nets, then allocate a new port for this net
+// and remap references to the old port to refer to the new port.
+// This method is called with the same assigned_nets array for each cell
+// inside a single DSP block. The end result is to ensure that for all cells
+// in a single DSP block, all CLK/CE/RST ports are connected to the same net.
+//
+// ports: array of port names to remap, either CLK0-3 or CE0-3 or RST0-3
+// assigned_nets: array of final net assignments to those four ports for
+// the block this cell is in.
+void Arch::remap_dsp_cell(
+ CellInfo* ci,
+ const std::array<IdString, 4> &ports,
+ std::array<NetInfo*, 4> &assigned_nets
+) {
+ // New names to use in attributes that used to refer to an old port name.
+ std::array<IdString, 4> remap_ports = {};
+
+ // Parameters that might need updating when ports are remapped.
+ const std::array<IdString, 48> remap_params = {
+ id_REG_INPUTA_CLK, id_REG_INPUTA_CE, id_REG_INPUTA_RST,
+ id_REG_INPUTB_CLK, id_REG_INPUTB_CE, id_REG_INPUTB_RST,
+ id_REG_INPUTC_CLK, id_REG_INPUTC_CE, id_REG_INPUTC_RST,
+ id_REG_PIPELINE_CLK, id_REG_PIPELINE_CE, id_REG_PIPELINE_RST,
+ id_REG_OUTPUT_CLK, id_REG_OUTPUT_CE, id_REG_OUTPUT_RST,
+ id_REG_INPUTC0_CLK, id_REG_INPUTC0_CE, id_REG_INPUTC0_RST,
+ id_REG_INPUTC1_CLK, id_REG_INPUTC1_CE, id_REG_INPUTC1_RST,
+ id_REG_OPCODEOP0_0_CLK, id_REG_OPCODEOP0_0_CE, id_REG_OPCODEOP0_0_RST,
+ id_REG_OPCODEOP1_0_CLK,
+ id_REG_OPCODEOP0_1_CLK, id_REG_OPCODEOP0_1_CE, id_REG_OPCODEOP0_1_RST,
+ id_REG_OPCODEOP1_1_CLK,
+ id_REG_OPCODEIN_0_CLK, id_REG_OPCODEIN_0_CE, id_REG_OPCODEIN_0_RST,
+ id_REG_OPCODEIN_1_CLK, id_REG_OPCODEIN_1_CE, id_REG_OPCODEIN_1_RST,
+ id_REG_OUTPUT0_CLK, id_REG_OUTPUT0_CE, id_REG_OUTPUT0_RST,
+ id_REG_OUTPUT1_CLK, id_REG_OUTPUT1_CE, id_REG_OUTPUT1_RST,
+ id_REG_FLAG_CLK, id_REG_FLAG_CE, id_REG_FLAG_RST,
+ id_REG_INPUTCFB_CLK, id_REG_INPUTCFB_CE, id_REG_INPUTCFB_RST,
+ id_HIGHSPEED_CLK,
+ };
+
+ // First, go through each port and determine which new port to assign
+ // its net to, and what to remap any parmeters that reference it.
+ for (size_t i = 0; i < ports.size(); i++) {
+ IdString port = ports[i];
+ NetInfo *net = ci->ports.at(port).net;
+ if (net == nullptr)
+ continue;
+ auto assigned = std::find(assigned_nets.cbegin(), assigned_nets.cend(), net);
+ if (assigned == assigned_nets.cend()) {
+ if (assigned_nets[i] == nullptr) {
+ // If neither the net nor the port have been assigned
+ // yet, we can simply assign the net to its original
+ // port and don't need to change any params.
+ assigned_nets[i] = net;
+ } else {
+ // If the net hasn't been assigned but the port has,
+ // we need to assign the net to a different port and
+ // update any attributes that refer to it, while
+ // ensuring the net at the new port is preserved.
+ size_t j = std::distance(
+ assigned_nets.cbegin(),
+ std::find(assigned_nets.cbegin(), assigned_nets.cend(), nullptr));
+ if (j == assigned_nets.size()) {
+ log_error("DSP block containing %s '%s': no unused ports "
+ "to remap %s to; too many distinct signals in "
+ "block.\n",
+ ci->type.c_str(getCtx()),
+ ci->name.c_str(getCtx()),
+ port.c_str(getCtx()));
+ }
+ assigned_nets[j] = net;
+ remap_ports[i] = ports[j];
+ log_info("DSP: %s '%s': Connection to %s remapped to %s\n",
+ ci->type.c_str(getCtx()), ci->name.c_str(getCtx()),
+ ports[i].c_str(getCtx()), ports[j].c_str(getCtx()));
+ }
+ } else if (*assigned != assigned_nets[i]) {
+ // If the net has been assigned already and to a different
+ // port than this one, we'll remap the port and attributes
+ // to point to the already-assigned port.
+ size_t j = std::distance(assigned_nets.cbegin(), assigned);
+ remap_ports[i] = ports[j];
+ log_info("DSP: %s '%s': Connection to %s remapped to %s\n",
+ ci->type.c_str(getCtx()), ci->name.c_str(getCtx()),
+ ports[i].c_str(getCtx()), ports[j].c_str(getCtx()));
+ }
+ }
+
+ // Second, connect each port to its assigned net.
+ for (size_t i = 0; i < ports.size(); i++) {
+ IdString port = ports[i];
+ ci->disconnectPort(port);
+ if (assigned_nets[i] != nullptr) {
+ ci->connectPort(port, assigned_nets[i]);
+ }
+ }
+
+ // Third, remap any parameters that refer to old ports to refer to the
+ // new port instead.
+ for (auto remap_param : remap_params) {
+ auto param = ci->params.find(remap_param);
+ if (param == ci->params.end())
+ continue;
+ for (size_t i = 0; i < remap_ports.size(); i++) {
+ Property &prop = param->second;
+ if (remap_ports[i] != IdString()
+ && prop.is_string
+ && prop.str == ports[i].str(getCtx())
+ ) {
+ prop = Property(remap_ports[i].str(getCtx()));
+ break;
+ }
+ }
+ }
+
+ // Finally, only when remapping CLK ports, also move any `CLKn_DIV`
+ // to the new clock port.
+ const std::array<IdString, 4> clk_div_params = {
+ id_CLK0_DIV, id_CLK1_DIV, id_CLK2_DIV, id_CLK3_DIV};
+ std::array<Property, 4> new_clk_div_props = {};
+ if (ports[0] == id_CLK0) {
+ for (size_t i = 0; i < 4; i++) {
+ if (remap_ports[i] == IdString())
+ continue;
+ auto param = ci->params.find(clk_div_params[i]);
+ if (param == ci->params.end())
+ continue;
+ size_t j = std::distance(
+ ports.cbegin(),
+ std::find(ports.cbegin(), ports.cend(), remap_ports[i]));
+ if (j != ports.size()) {
+ new_clk_div_props[j] = param->second;
+ }
+ }
+
+ for (size_t i = 0; i < 4; i++) {
+ if (new_clk_div_props[i] != Property()) {
+ ci->params[clk_div_params[i]] = new_clk_div_props[i];
+ } else {
+ ci->params.erase(clk_div_params[i]);
+ }
+ }
+ }
+}
+
void Arch::setup_wire_locations()
{
wire_loc_overrides.clear();
diff --git a/ecp5/constids.inc b/ecp5/constids.inc
index c161f94f..6445b098 100644
--- a/ecp5/constids.inc
+++ b/ecp5/constids.inc
@@ -1769,6 +1769,8 @@ X(REGMODE_A)
X(REGMODE_B)
X(REGSET)
X(REG_FLAG_CLK)
+X(REG_FLAG_CE)
+X(REG_FLAG_RST)
X(REG_INPUTA_CE)
X(REG_INPUTA_CLK)
X(REG_INPUTA_RST)
@@ -1776,8 +1778,17 @@ X(REG_INPUTB_CE)
X(REG_INPUTB_CLK)
X(REG_INPUTB_RST)
X(REG_INPUTC0_CLK)
+X(REG_INPUTC0_CE)
+X(REG_INPUTC0_RST)
X(REG_INPUTC1_CLK)
+X(REG_INPUTC1_CE)
+X(REG_INPUTC1_RST)
X(REG_INPUTC_CLK)
+X(REG_INPUTC_CE)
+X(REG_INPUTC_RST)
+X(REG_INPUTCFB_CLK)
+X(REG_INPUTCFB_CE)
+X(REG_INPUTCFB_RST)
X(REG_OPCODEIN_0_CE)
X(REG_OPCODEIN_0_CLK)
X(REG_OPCODEIN_0_RST)
@@ -1793,12 +1804,18 @@ X(REG_OPCODEOP0_1_RST)
X(REG_OPCODEOP1_0_CLK)
X(REG_OPCODEOP1_1_CLK)
X(REG_OUTPUT0_CLK)
+X(REG_OUTPUT0_CE)
+X(REG_OUTPUT0_RST)
X(REG_OUTPUT1_CLK)
+X(REG_OUTPUT1_CE)
+X(REG_OUTPUT1_RST)
X(REG_OUTPUT_CLK)
+X(REG_OUTPUT_CE)
X(REG_OUTPUT_RST)
X(REG_PIPELINE_CE)
X(REG_PIPELINE_CLK)
X(REG_PIPELINE_RST)
+X(HIGHSPEED_CLK)
X(RESETMODE)
X(RNDPAT)
X(RSTAMUX)