diff options
| author | gatecat <gatecat@ds0.me> | 2021-05-08 14:30:29 +0100 | 
|---|---|---|
| committer | gatecat <gatecat@ds0.me> | 2021-05-15 14:54:33 +0100 | 
| commit | d38ff142644479d9760d32f17bec291e96ab100c (patch) | |
| tree | 4004da8285f353062ad80ff41cfe6563a214fadd | |
| parent | e5e2f7bc62c1cfba2f43ea74199a5a3a379e5de7 (diff) | |
| download | nextpnr-d38ff142644479d9760d32f17bec291e96ab100c.tar.gz nextpnr-d38ff142644479d9760d32f17bec291e96ab100c.tar.bz2 nextpnr-d38ff142644479d9760d32f17bec291e96ab100c.zip  | |
mistral: Working on ALM input assignment
Signed-off-by: gatecat <gatecat@ds0.me>
| -rw-r--r-- | mistral/arch.h | 3 | ||||
| -rw-r--r-- | mistral/lab.cc | 117 | 
2 files changed, 118 insertions, 2 deletions
diff --git a/mistral/arch.h b/mistral/arch.h index dd684dfe..3d939c0d 100644 --- a/mistral/arch.h +++ b/mistral/arch.h @@ -43,7 +43,8 @@ struct ALMInfo      // Pointers to bels      std::array<BelId, 2> lut_bels;      std::array<BelId, 4> ff_bels; -    // TODO: ALM configuration (L5/L6 mode, LUT input permutation, etc) + +    bool l6_mode = false;  };  struct LABInfo diff --git a/mistral/lab.cc b/mistral/lab.cc index f01c6e95..a8dd8dd5 100644 --- a/mistral/lab.cc +++ b/mistral/lab.cc @@ -405,14 +405,129 @@ void Arch::assign_control_sets(uint32_t lab)      // e.g. CLK0 & ENA0 must be use for one control set, and CLK1 & ENA1 for another, they can't be mixed and matched  } +namespace { +// Gets the name of logical LUT pin i for a given cell +static IdString get_lut_pin(CellInfo *cell, int i) +{ +    const std::array<IdString, 6> log_pins{id_A, id_B, id_C, id_D, id_E, id_F}; +    const std::array<IdString, 5> log_pins_arith{id_A, id_B, id_C, id_D0, id_D1}; +    return (cell->type == id_MISTRAL_ALUT_ARITH) ? log_pins_arith.at(i) : log_pins.at(i); +} + +static void assign_lut6_inputs(CellInfo *cell, int lut) +{ +    std::array<IdString, 6> phys_pins{id_A, id_B, id_C, id_D, (lut == 1) ? id_E1 : id_E0, (lut == 1) ? id_F1 : id_F0}; +    int phys_idx = 0; +    for (int i = 0; i < 6; i++) { +        IdString log = get_lut_pin(cell, i); +        if (!cell->ports.count(log) || cell->ports.at(log).net == nullptr) +            continue; +        cell->pin_data[log].bel_pins.clear(); +        cell->pin_data[log].bel_pins.push_back(phys_pins.at(++phys_idx)); +    } +} +} // namespace +  void Arch::reassign_alm_inputs(uint32_t lab, uint8_t alm)  { -    // TODO: based on the usage of LUTs inside the ALM, set up cell-bel pin map for the combinational cells in the ALM +    // Based on the usage of LUTs inside the ALM, set up cell-bel pin map for the combinational cells in the ALM      // so that each physical bel pin is only used for one net; and the logical functions can be implemented correctly.      // This function should also insert route-through LUTs to legalise flipflop inputs as needed. +    auto &alm_data = labs.at(lab).alms.at(alm); +    alm_data.l6_mode = false; +    std::array<CellInfo *, 2> luts{getBoundBelCell(alm_data.lut_bels[0]), getBoundBelCell(alm_data.lut_bels[1])}; +    std::array<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])}; + +    for (int i = 0; i < 2; i++) { +        // Currently we treat LUT6s as a special case, as they never share inputs +        if (luts[i] != nullptr && luts[i]->type == id_MISTRAL_ALUT6) { +            alm_data.l6_mode = true; +            NPNR_ASSERT(luts[1 - i] == nullptr); // only allow one LUT6 per ALM and no other LUTs +            assign_lut6_inputs(luts[i], i); +        } +    } + +    if (!alm_data.l6_mode) { +        // In L5 mode; which is what we use in this case +        //  - A and B are shared +        //  - C, E0, and F0 are exclusive to the top LUT5 secion +        //  - D, E1, and F1 are exclusive to the bottom LUT5 section +        // First find up to two shared inputs +        std::unordered_map<IdString, int> shared_nets; +        if (luts[0] && luts[1]) { +            for (int i = 0; i < luts[0]->combInfo.lut_input_count; i++) { +                for (int j = 0; j < luts[1]->combInfo.lut_input_count; j++) { +                    if (luts[0]->combInfo.lut_in[i] == nullptr) +                        continue; +                    if (luts[0]->combInfo.lut_in[i] != luts[1]->combInfo.lut_in[j]) +                        continue; +                    IdString net = luts[0]->combInfo.lut_in[i]->name; +                    if (shared_nets.count(net)) +                        continue; +                    int idx = int(shared_nets.size()); +                    shared_nets[net] = idx; +                    if (shared_nets.size() >= 2) +                        goto shared_search_done; +                } +            } +        shared_search_done:; +        } +        // A and B can be used for half-specific nets if not assigned to shared nets +        bool a_avail = shared_nets.size() == 0, b_avail = shared_nets.size() <= 1; +        // Do the actual port assignment +        for (int i = 0; i < 2; i++) { +            if (!luts[i]) +                continue; +            // Work out which physical ports are available +            std::vector<IdString> avail_phys_ports; +            avail_phys_ports.push_back((i == 1) ? id_D : id_C); +            if (b_avail) +                avail_phys_ports.push_back(id_B); +            if (a_avail) +                avail_phys_ports.push_back(id_A); +            // In arithmetic mode, Ei can only be used for D0 and Fi can only be used for D1 +            if (!luts[i]->combInfo.is_carry) { +                avail_phys_ports.push_back((i == 1) ? id_E1 : id_E0); +                avail_phys_ports.push_back((i == 1) ? id_F1 : id_F0); +            } +            int phys_idx = 0; + +            for (int j = 0; j < luts[i]->combInfo.lut_input_count; j++) { +                IdString log = get_lut_pin(luts[i], j); +                auto &bel_pins = luts[i]->pin_data[log].bel_pins; +                bel_pins.clear(); + +                NetInfo *net = get_net_or_empty(luts[i], log); +                if (net == nullptr) { +                    // Disconnected inputs don't need to be allocated a pin, because the router won't be routing these +                    continue; +                } else if (shared_nets.count(net->name)) { +                    // This pin is to be allocated one of the shared nets +                    bel_pins.push_back(shared_nets.at(net->name) ? id_B : id_A); +                } else if (log == id_D0) { +                    // Arithmetic +                    bel_pins.push_back((i == 1) ? id_E1 : id_E0); // reserved +                } else if (log == id_D1) { +                    bel_pins.push_back((i == 1) ? id_F1 : id_F0); // reserved +                } else { +                    // Allocate from the general pool of available physical pins +                    IdString phys = avail_phys_ports.at(phys_idx++); +                    bel_pins.push_back(phys); +                    // Mark A/B unavailable for the other LUT, if needed +                    if (phys == id_A) +                        a_avail = false; +                    else if (phys == id_B) +                        b_avail = false; +                } +            } +        } +    } +      // TODO: in the future, as well as the reassignment here we will also have pseudo PIPs in front of the ALM so that      // the router can permute LUTs for routeability; too. Here we will need to lock out some of those PIPs depending on      // the usage of the ALM, as not all inputs are always interchangeable. +    // Get cells into an array for fast access  }  // This default cell-bel pin mapping is used to provide estimates during placement only. It will have errors and  | 
