aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgatecat <gatecat@ds0.me>2021-05-04 21:23:11 +0100
committergatecat <gatecat@ds0.me>2021-05-15 14:54:33 +0100
commitfbdcfa9c42a2a8ed69b0130fcce56644bce3c64e (patch)
treec30b711bcadd982777aee62b6030508ec36d312f
parent1cd22b81daa4c87870f65dedef74dba02adac8fe (diff)
downloadnextpnr-fbdcfa9c42a2a8ed69b0130fcce56644bce3c64e.tar.gz
nextpnr-fbdcfa9c42a2a8ed69b0130fcce56644bce3c64e.tar.bz2
nextpnr-fbdcfa9c42a2a8ed69b0130fcce56644bce3c64e.zip
cyclonev: First (untested) pass at ALM validity checking
Signed-off-by: gatecat <gatecat@ds0.me>
-rw-r--r--cyclonev/arch.cc6
-rw-r--r--cyclonev/lab.cc169
2 files changed, 135 insertions, 40 deletions
diff --git a/cyclonev/arch.cc b/cyclonev/arch.cc
index 62acf9e2..8dd21499 100644
--- a/cyclonev/arch.cc
+++ b/cyclonev/arch.cc
@@ -210,7 +210,8 @@ std::vector<IdString> Arch::getBelPins(BelId bel) const
return pins;
}
-bool Arch::isValidBelForCellType(IdString cell_type, BelId bel) const {
+bool Arch::isValidBelForCellType(IdString cell_type, BelId bel) const
+{
// Any combinational cell type can - theoretically - be placed at a combinational ALM bel
// The precise legality mechanics will be dealt with in isBelLocationValid.
IdString bel_type = getBelType(bel);
@@ -220,7 +221,8 @@ bool Arch::isValidBelForCellType(IdString cell_type, BelId bel) const {
return bel_type == cell_type;
}
-BelBucketId Arch::getBelBucketForCellType(IdString cell_type) const {
+BelBucketId Arch::getBelBucketForCellType(IdString cell_type) const
+{
if (is_comb_cell(cell_type))
return id_MISTRAL_COMB;
else
diff --git a/cyclonev/lab.cc b/cyclonev/lab.cc
index 2c2c619a..4e10fe59 100644
--- a/cyclonev/lab.cc
+++ b/cyclonev/lab.cc
@@ -190,16 +190,17 @@ void Arch::create_lab(int x, int y)
// Cell handling and annotation functions
namespace {
- ControlSig get_ctrlsig(const CellInfo *cell, IdString port) {
- ControlSig result;
- result.net = get_net_or_empty(cell, port);
- if (cell->pin_data.count(port))
- result.inverted = cell->pin_data.at(port).inverted;
- else
- result.inverted = false;
- return result;
- }
+ControlSig get_ctrlsig(const CellInfo *cell, IdString port)
+{
+ ControlSig result;
+ result.net = get_net_or_empty(cell, port);
+ if (cell->pin_data.count(port))
+ result.inverted = cell->pin_data.at(port).inverted;
+ else
+ result.inverted = false;
+ return result;
}
+} // namespace
bool Arch::is_comb_cell(IdString cell_type) const
{
@@ -238,35 +239,35 @@ void Arch::assign_comb_info(CellInfo *cell) const
} else {
cell->combInfo.lut_input_count = 0;
switch (cell->type.index) {
- case ID_MISTRAL_ALUT6:
- ++cell->combInfo.lut_input_count;
- cell->combInfo.lut_in[5] = get_net_or_empty(cell, id_F);
- [[fallthrough]];
- case ID_MISTRAL_ALUT5:
- ++cell->combInfo.lut_input_count;
- cell->combInfo.lut_in[4] = get_net_or_empty(cell, id_E);
- [[fallthrough]];
- case ID_MISTRAL_ALUT4:
- ++cell->combInfo.lut_input_count;
- cell->combInfo.lut_in[3] = get_net_or_empty(cell, id_D);
- [[fallthrough]];
- case ID_MISTRAL_ALUT3:
- ++cell->combInfo.lut_input_count;
- cell->combInfo.lut_in[2] = get_net_or_empty(cell, id_C);
- [[fallthrough]];
- case ID_MISTRAL_ALUT2:
- ++cell->combInfo.lut_input_count;
- cell->combInfo.lut_in[1] = get_net_or_empty(cell, id_B);
- [[fallthrough]];
- case ID_MISTRAL_NOT:
- ++cell->combInfo.lut_input_count;
- cell->combInfo.lut_in[0] = get_net_or_empty(cell, id_A);
- [[fallthrough]];
- case ID_MISTRAL_CONST:
- // MISTRAL_CONST is a nextpnr-inserted cell type for 0-input, constant-generating LUTs
- break;
- default:
- log_error("unexpected combinational cell type %s\n", getCtx()->nameOf(cell->type));
+ case ID_MISTRAL_ALUT6:
+ ++cell->combInfo.lut_input_count;
+ cell->combInfo.lut_in[5] = get_net_or_empty(cell, id_F);
+ [[fallthrough]];
+ case ID_MISTRAL_ALUT5:
+ ++cell->combInfo.lut_input_count;
+ cell->combInfo.lut_in[4] = get_net_or_empty(cell, id_E);
+ [[fallthrough]];
+ case ID_MISTRAL_ALUT4:
+ ++cell->combInfo.lut_input_count;
+ cell->combInfo.lut_in[3] = get_net_or_empty(cell, id_D);
+ [[fallthrough]];
+ case ID_MISTRAL_ALUT3:
+ ++cell->combInfo.lut_input_count;
+ cell->combInfo.lut_in[2] = get_net_or_empty(cell, id_C);
+ [[fallthrough]];
+ case ID_MISTRAL_ALUT2:
+ ++cell->combInfo.lut_input_count;
+ cell->combInfo.lut_in[1] = get_net_or_empty(cell, id_B);
+ [[fallthrough]];
+ case ID_MISTRAL_NOT:
+ ++cell->combInfo.lut_input_count;
+ cell->combInfo.lut_in[0] = get_net_or_empty(cell, id_A);
+ [[fallthrough]];
+ case ID_MISTRAL_CONST:
+ // MISTRAL_CONST is a nextpnr-inserted cell type for 0-input, constant-generating LUTs
+ break;
+ default:
+ log_error("unexpected combinational cell type %s\n", getCtx()->nameOf(cell->type));
}
// Note that this relationship won't hold for extended mode, when that is supported
cell->combInfo.lut_bits_count = (1 << cell->combInfo.lut_input_count);
@@ -284,4 +285,96 @@ void Arch::assign_ff_info(CellInfo *cell) const
cell->ffInfo.datain = get_net_or_empty(cell, id_DATAIN);
}
+// Validity checking functions
+bool Arch::is_alm_legal(uint32_t lab, uint8_t alm) const
+{
+ auto &alm_data = labs.at(lab).alms.at(alm);
+ // Get cells into an array for fast access
+ std::array<const CellInfo *, 2> luts{getBoundBelCell(alm_data.lut_bels[0]), getBoundBelCell(alm_data.lut_bels[1])};
+ std::array<const CellInfo *, 4> ffs{getBoundBelCell(alm_data.ff_bels[0]), getBoundBelCell(alm_data.ff_bels[1]),
+ getBoundBelCell(alm_data.ff_bels[2]), getBoundBelCell(alm_data.ff_bels[3])};
+ int used_lut_bits = 0;
+
+ int total_lut_inputs = 0;
+ // TODO: for more complex modes like extended/arithmetic, it might not always be possible for any LUT input to map
+ // to any of the ALM half inputs particularly shared and extended mode will need more thought and probably for this
+ // to be revisited
+ for (int i = 0; i < 2; i++) {
+ if (!luts[i])
+ continue;
+ total_lut_inputs += luts[i]->combInfo.lut_input_count;
+ used_lut_bits += luts[i]->combInfo.lut_bits_count;
+ }
+ // An ALM only has 64 bits of storage. In theory some of these cases might be legal because of overlap between the
+ // two functions, but the current placer is unlikely to stumble upon these cases frequently without anything to
+ // guide it, and the cost of checking them here almost certainly outweighs any marginal benefit in supporting them,
+ // at least for now.
+ if (used_lut_bits > 64)
+ return false;
+
+ if (total_lut_inputs > 8) {
+ NPNR_ASSERT(luts[0] && luts[1]); // something has gone badly wrong if this fails!
+ // Make sure that LUT inputs are not overprovisioned
+ int shared_lut_inputs = 0;
+ // Even though this N^2 search looks inefficient, it's unlikely a set lookup or similar is going to be much
+ // better given the low N.
+ for (int i = 0; i < luts[1]->combInfo.lut_input_count; i++) {
+ const NetInfo *sig = luts[1]->combInfo.lut_in[i];
+ for (int j = 0; j < luts[0]->combInfo.lut_input_count; j++) {
+ if (sig == luts[0]->combInfo.lut_in[j]) {
+ ++shared_lut_inputs;
+ break;
+ }
+ }
+ }
+ if ((total_lut_inputs - shared_lut_inputs) > 8)
+ return false;
+ }
+
+ // For each ALM half; check FF control set sharing and input routeability
+ for (int i = 0; i < 2; i++) {
+ // There are two ways to route from the fabric into FF data - either routing through a LUT or using the E/F
+ // signals and SLOAD=1 (*PKREF*)
+ bool route_thru_lut_avail = !luts[i] && (total_lut_inputs < 8) && (used_lut_bits < 64);
+ // E/F is available if the LUT is using less than 6 inputs - TODO: is this correct considering all possible LUT
+ // sharing
+ bool ef_available = (!luts[i] || luts[i]->combInfo.lut_input_count < 6);
+ // Control set checking
+ bool found_ff = false;
+
+ FFControlSet ctrlset;
+ for (int j = 0; j < 2; j++) {
+ const CellInfo *ff = ffs[i * 2 + j];
+ if (!ff)
+ continue;
+ if (found_ff) {
+ // Two FFs in the same half with an incompatible control set
+ if (ctrlset != ff->ffInfo.ctrlset)
+ return false;
+ } else {
+ ctrlset = ff->ffInfo.ctrlset;
+ }
+ // SDATA must use the E/F input
+ // TODO: rare case of two FFs with the same SDATA in the same ALM half
+ if (ff->ffInfo.sdata) {
+ if (!ef_available)
+ return false;
+ ef_available = false;
+ }
+ // Find a way of routing the input through fabric, if it's not driven by the LUT
+ if (ff->ffInfo.datain && (!luts[i] || (ff->ffInfo.datain != luts[i]->combInfo.comb_out))) {
+ if (route_thru_lut_avail)
+ route_thru_lut_avail = false;
+ else if (ef_available)
+ ef_available = false;
+ else
+ return false;
+ }
+ found_ff = true;
+ }
+ }
+
+ return true;
+}
+
NEXTPNR_NAMESPACE_END \ No newline at end of file