diff options
-rw-r--r-- | ecp5/arch.cc | 4 | ||||
-rw-r--r-- | ecp5/arch.h | 6 | ||||
-rw-r--r-- | ecp5/arch_place.cc | 253 | ||||
-rw-r--r-- | ecp5/constids.inc | 17 |
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) |