From 3313d5267a4e4609d694d15e66040b0829788bb4 Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 13 May 2021 22:47:29 +0100 Subject: [PATCH] mistral: Adding FF control set reservation Signed-off-by: gatecat --- mistral/arch.cc | 17 ++++++ mistral/arch.h | 34 ++++++++--- mistral/lab.cc | 159 ++++++++++++++++++++++++++++++++---------------- 3 files changed, 150 insertions(+), 60 deletions(-) diff --git a/mistral/arch.cc b/mistral/arch.cc index 5fc2a0b4..ffe6e833 100644 --- a/mistral/arch.cc +++ b/mistral/arch.cc @@ -305,6 +305,23 @@ WireId Arch::add_wire(int x, int y, IdString name, uint64_t flags) } } +void Arch::reserve_route(WireId src, WireId dst) +{ + auto &dst_data = wires.at(dst); + int idx = -1; + + for (int i = 0; i < int(dst_data.wires_uphill.size()); i++) { + if (dst_data.wires_uphill.at(i) == src) { + idx = i; + break; + } + } + + NPNR_ASSERT(idx != -1); + + dst_data.flags = WireInfo::RESERVED_ROUTE | unsigned(idx); +} + bool Arch::wires_connected(WireId src, WireId dst) const { PipId pip(src.node, dst.node); diff --git a/mistral/arch.h b/mistral/arch.h index e22d26fd..c0ab86e5 100644 --- a/mistral/arch.h +++ b/mistral/arch.h @@ -106,6 +106,10 @@ struct WireInfo // flags for special wires (currently unused) uint64_t flags; + + // if the RESERVED_ROUTE mask is set in flags, then only wires_uphill[flags&0xFF] may drive this wire - used for + // control set preallocations + static const uint64_t RESERVED_ROUTE = 0x100; }; // This transforms a WireIds, and adds the mising half of the pair to create a PipId @@ -259,13 +263,8 @@ enum CellPinStyle PINSTYLE_COMB = 0x017, // combinational signal, defaults low, can be inverted and tied PINSTYLE_CLK = 0x107, // CLK type signal, invertible and defaults to disconnected - // Technically speaking CE and RSTs should be invertible, too. But we don't use this currently due to the possible - // need to route one CE to two different LAB wires if both inverted and non-inverted variants are used in the same - // LAB This should be acheiveable by prerouting the LAB wiring inside assign_control_sets, but let's pass on this - // for a first attempt. - - PINSTYLE_CE = 0x023, // CE type signal, ~~invertible~~ and defaults to enabled - PINSTYLE_RST = 0x013, // RST type signal, ~~invertible~~ and defaults to not reset + PINSTYLE_CE = 0x027, // CE type signal, invertible and defaults to enabled + PINSTYLE_RST = 0x017, // RST type signal, invertible and defaults to not reset PINSTYLE_DEDI = 0x000, // dedicated signals, leave alone PINSTYLE_INP = 0x001, // general inputs, no inversion/tieing but defaults low PINSTYLE_PU = 0x022, // signals that float high and default high @@ -337,6 +336,8 @@ struct Arch : BaseArch AllWireRange getWires() const override { return AllWireRange(wires); } bool wires_connected(WireId src, WireId dst) const; + // Only allow src, and not any other wire, to drive dst + void reserve_route(WireId src, WireId dst); // ------------------------------------------------- @@ -356,6 +357,25 @@ struct Arch : BaseArch return UpDownhillPipRange(wires.at(wire).wires_uphill, wire, true); } + bool checkPipAvail(PipId pip) const override + { + // Check reserved routes + WireId dst(pip.dst); + const auto &dst_data = wires.at(dst); + if ((dst_data.flags & WireInfo::RESERVED_ROUTE) != 0) { + if (WireId(pip.src) != dst_data.wires_uphill.at(dst_data.flags & 0xFF)) + return false; + } + return BaseArch::checkPipAvail(pip); + } + + bool checkPipAvailForNet(PipId pip, NetInfo *net) const override + { + if (!checkPipAvail(pip)) + return false; + return BaseArch::checkPipAvailForNet(pip, net); + } + // ------------------------------------------------- delay_t estimateDelay(WireId src, WireId dst) const override; diff --git a/mistral/lab.cc b/mistral/lab.cc index 11b347ca..7d798cbc 100644 --- a/mistral/lab.cc +++ b/mistral/lab.cc @@ -430,67 +430,82 @@ template bool check_assign_sig(std::array &sig_set, co } return false; }; -}; // namespace -bool Arch::is_lab_ctrlset_legal(uint32_t lab) const +// DATAIN mapping rules - which LAB DATAIN signals can be used for ENA and ACLR +static constexpr std::array ena_datain{2, 3, 0}; +static constexpr std::array aclr_datain{3, 2}; + +struct LabCtrlSetWorker { - // Strictly speaking the constraint is up to 2 unique CLK and 3 CLK+ENA pairs. For now we simplify this to 1 CLK and - // 3 ENA though. + ControlSig clk{}, sload{}, sclr{}; std::array aclr{}; std::array ena{}; - for (uint8_t alm = 0; alm < 10; alm++) { - for (uint8_t i = 0; i < 4; i++) { - const CellInfo *ff = getBoundBelCell(labs.at(lab).alms.at(alm).ff_bels.at(i)); - if (ff == nullptr) - continue; - - if (!check_assign_sig(clk, ff->ffInfo.ctrlset.clk)) - return false; - if (!check_assign_sig(sload, ff->ffInfo.ctrlset.sload)) - return false; - if (!check_assign_sig(sclr, ff->ffInfo.ctrlset.sclr)) - return false; - if (!check_assign_sig(aclr, ff->ffInfo.ctrlset.aclr)) - return false; - if (!check_assign_sig(ena, ff->ffInfo.ctrlset.ena)) - return false; - } - } - - // Check for overuse of the shared, LAB-wide datain signals std::array datain{}; - if (clk.net != nullptr && !clk.net->is_global) - if (!check_assign_sig(datain[0], clk)) // CLK only needs DATAIN[0] if it's not global + + bool run(const Arch *arch, uint32_t lab) + { + // Strictly speaking the constraint is up to 2 unique CLK and 3 CLK+ENA pairs. For now we simplify this to 1 CLK + // and 3 ENA though. + for (uint8_t alm = 0; alm < 10; alm++) { + for (uint8_t i = 0; i < 4; i++) { + const CellInfo *ff = arch->getBoundBelCell(arch->labs.at(lab).alms.at(alm).ff_bels.at(i)); + if (ff == nullptr) + continue; + + if (!check_assign_sig(clk, ff->ffInfo.ctrlset.clk)) + return false; + if (!check_assign_sig(sload, ff->ffInfo.ctrlset.sload)) + return false; + if (!check_assign_sig(sclr, ff->ffInfo.ctrlset.sclr)) + return false; + if (!check_assign_sig(aclr, ff->ffInfo.ctrlset.aclr)) + return false; + if (!check_assign_sig(ena, ff->ffInfo.ctrlset.ena)) + return false; + } + } + // Check for overuse of the shared, LAB-wide datain signals + if (clk.net != nullptr && !clk.net->is_global) + if (!check_assign_sig(datain[0], clk)) // CLK only needs DATAIN[0] if it's not global + return false; + if (!check_assign_sig(datain[1], sload)) return false; - if (!check_assign_sig(datain[1], sload)) - return false; - if (!check_assign_sig(datain[3], sclr)) - return false; - for (const auto &aclr_sig : aclr) { - // Check both possibilities that ACLR can map to - // TODO: ACLR could be global, too - if (check_assign_sig(datain[3], aclr_sig)) - continue; - if (check_assign_sig(datain[2], aclr_sig)) - continue; - // Failed to find any free ACLR-capable DATAIN - return false; + if (!check_assign_sig(datain[3], sclr)) + return false; + for (const auto &aclr_sig : aclr) { + // Check both possibilities that ACLR can map to + // TODO: ACLR could be global, too + if (check_assign_sig(datain[aclr_datain[0]], aclr_sig)) + continue; + if (check_assign_sig(datain[aclr_datain[1]], aclr_sig)) + continue; + // Failed to find any free ACLR-capable DATAIN + return false; + } + for (const auto &ena_sig : ena) { + // Check all 3 possibilities that ACLR can map to + // TODO: ACLR could be global, too + if (check_assign_sig(datain[ena_datain[0]], ena_sig)) + continue; + if (check_assign_sig(datain[ena_datain[1]], ena_sig)) + continue; + if (check_assign_sig(datain[ena_datain[2]], ena_sig)) + continue; + // Failed to find any free ENA-capable DATAIN + return false; + } + return true; } - for (const auto &ena_sig : ena) { - // Check all 3 possibilities that ACLR can map to - // TODO: ACLR could be global, too - if (check_assign_sig(datain[2], ena_sig)) - continue; - if (check_assign_sig(datain[3], ena_sig)) - continue; - if (check_assign_sig(datain[0], ena_sig)) - continue; - // Failed to find any free ENA-capable DATAIN - return false; - } - return true; +}; + +}; // namespace + +bool Arch::is_lab_ctrlset_legal(uint32_t lab) const +{ + LabCtrlSetWorker worker; + return worker.run(this, lab); } void Arch::lab_pre_route() @@ -506,9 +521,47 @@ void Arch::lab_pre_route() void Arch::assign_control_sets(uint32_t lab) { - // TODO: set up reservations for checkPipAvailForNet for control set signals + // Set up reservations for checkPipAvail for control set signals // This will be needed because clock and CE are routed together and must be kept together, there isn't free choice // e.g. CLK0 & ENA0 must be use for one control set, and CLK1 & ENA1 for another, they can't be mixed and matched + // Similarly for how inverted & noninverted variants must be kept separate + LabCtrlSetWorker worker; + bool legal = worker.run(this, lab); + NPNR_ASSERT(legal); + auto &lab_data = labs.at(lab); + for (uint8_t alm = 0; alm < 10; alm++) { + for (uint8_t i = 0; i < 4; i++) { + BelId ff_bel = lab_data.alms.at(alm).ff_bels.at(i); + const CellInfo *ff = getBoundBelCell(ff_bel); + if (ff == nullptr) + continue; + ControlSig ena_sig = ff->ffInfo.ctrlset.ena; + WireId ena_wire = getBelPinWire(ff_bel, id_ENA); + for (int i = 0; i < 3; i++) { + if (ena_sig == worker.datain[ena_datain[i]]) { + if (getCtx()->debug) { + log_info("Assigned CLK/ENA set %d to FF %s (%s)\n", i, nameOf(ff), getCtx()->nameOfBel(ff_bel)); + } + reserve_route(lab_data.ena_wires[i], ena_wire); + // TODO: lock clock according to ENA choice, too + break; + } + } + + ControlSig aclr_sig = ff->ffInfo.ctrlset.aclr; + WireId aclr_wire = getBelPinWire(ff_bel, id_ACLR); + for (int i = 0; i < 2; i++) { + // TODO: could be global ACLR, too + if (aclr_sig == worker.datain[aclr_datain[i]]) { + if (getCtx()->debug) { + log_info("Assigned ACLR set %d to FF %s (%s)\n", i, nameOf(ff), getCtx()->nameOfBel(ff_bel)); + } + reserve_route(lab_data.aclr_wires[i], aclr_wire); + break; + } + } + } + } } namespace {