diff options
| -rw-r--r-- | cyclonev/arch.cc | 4 | ||||
| -rw-r--r-- | cyclonev/arch.h | 16 | ||||
| -rw-r--r-- | cyclonev/constids.inc | 5 | ||||
| -rw-r--r-- | cyclonev/lab.cc | 186 | 
4 files changed, 207 insertions, 4 deletions
| diff --git a/cyclonev/arch.cc b/cyclonev/arch.cc index 64444695..f41eb10e 100644 --- a/cyclonev/arch.cc +++ b/cyclonev/arch.cc @@ -261,7 +261,7 @@ bool Arch::pack() { return true; }  bool Arch::place() { return true; }  bool Arch::route() { return true; } -BelId Arch::add_bel(int x, int y, IdString name, IdString type, IdString bucket) +BelId Arch::add_bel(int x, int y, IdString name, IdString type)  {      // TODO: nothing else is using this BelId system yet...      // TODO (tomorrow?): we probably want a belsByTile type arrangement, similar for wires and pips, for better spacial @@ -274,7 +274,7 @@ BelId Arch::add_bel(int x, int y, IdString name, IdString type, IdString bucket)      auto &bel = bels[id];      bel.name = name;      bel.type = type; -    bel.bucket = bucket; +    bel.bucket = type;      return id;  } diff --git a/cyclonev/arch.h b/cyclonev/arch.h index 1f545134..0614860d 100644 --- a/cyclonev/arch.h +++ b/cyclonev/arch.h @@ -49,6 +49,10 @@ struct ALMInfo  struct LABInfo  {      std::array<ALMInfo, 10> alms; +    //  Control set wires +    std::array<WireId, 3> clk_wires, ena_wires; +    std::array<WireId, 2> aclr_wires; +    WireId sclr_wire, sload_wire;      // TODO: LAB configuration (control set etc)  }; @@ -74,7 +78,7 @@ struct BelInfo              uint32_t lab; // index into the list of LABs              uint8_t alm;  // ALM index inside LAB              uint8_t idx;  // LUT or FF index inside ALM -        } labData; +        } lab_data;      };  }; @@ -304,12 +308,17 @@ struct Arch : BaseArch<ArchRanges>      // -------------------------------------------------      // Functions for device setup -    BelId add_bel(int x, int y, IdString name, IdString type, IdString bucket); +    BelId add_bel(int x, int y, IdString name, IdString type);      WireId add_wire(int x, int y, IdString name, uint64_t flags = 0);      PipId add_pip(WireId src, WireId dst);      void add_bel_pin(BelId bel, IdString pin, PortType dir, WireId wire); +    WireId get_port(CycloneV::block_type_t bt, int x, int y, int bi, CycloneV::port_type_t port, int pi = -1) const +    { +        return WireId(cyclonev->pnode_to_rnode(CycloneV::pnode(bt, x, y, port, bi, pi))); +    } +      void create_lab(int x, int y);      void create_gpio(int x, int y); @@ -323,6 +332,9 @@ struct Arch : BaseArch<ArchRanges>      std::unordered_map<WireId, WireInfo> wires;      std::unordered_map<BelId, BelInfo> bels; +    // List of LABs +    std::vector<LABInfo> labs; +      // WIP to link without failure      std::vector<BelPin> empty_belpin_list; diff --git a/cyclonev/constids.inc b/cyclonev/constids.inc index bb9fee55..a22bda47 100644 --- a/cyclonev/constids.inc +++ b/cyclonev/constids.inc @@ -36,6 +36,11 @@ X(SHAREIN)  X(COUT)  X(SHAREOUT) +X(CARRY_START) +X(SHARE_START) +X(CARRY_END) +X(SHARE_END) +  X(MISTRAL_ALUT6)  X(MISTRAL_ALUT5)  X(MISTRAL_ALUT4) diff --git a/cyclonev/lab.cc b/cyclonev/lab.cc new file mode 100644 index 00000000..b464b88a --- /dev/null +++ b/cyclonev/lab.cc @@ -0,0 +1,186 @@ +/* + *  nextpnr -- Next Generation Place and Route + * + *  Copyright (C) 2021  gatecat <gatecat@ds0.me> + * + *  Permission to use, copy, modify, and/or distribute this software for any + *  purpose with or without fee is hereby granted, provided that the above + *  copyright notice and this permission notice appear in all copies. + * + *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "log.h" +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +// This file contains functions related to our custom LAB structure, including creating the LAB bels; checking the +// legality of LABs; and manipulating LUT inputs and equations +namespace { +static void create_alm(Arch *arch, int x, int y, int z, uint32_t lab_idx) +{ +    auto &lab = arch->labs.at(lab_idx); +    // Create the combinational part of ALMs. +    // There are two of these, for the two LUT outputs, and these also contain the carry chain and associated logic +    // Each one has all 8 ALM inputs as input pins. In many cases only a subset of these are used; depending on mode; +    // and the bel-cell pin mappings are used to handle this post-placement without losing flexibility +    for (int i = 0; i < 2; i++) { +        // Carry/share wires are a bit tricky due to all the different permutations +        WireId carry_in, share_in; +        WireId carry_out, share_out; +        if (z == 0 && i == 0) { +            if (y == 0) { +                // Base case +                carry_in = arch->add_wire(x, y, id_CARRY_START); +                share_in = arch->add_wire(x, y, id_CARRY_START); +            } else { +                // Output of last tile +                carry_in = arch->add_wire(x, y - 1, id_COUT); +                share_in = arch->add_wire(x, y - 1, id_SHAREOUT); +            } +        } else { +            // Output from last combinational unit +            carry_in = arch->add_wire(x, y, arch->id(stringf("CARRY[%d]", (z * 2 + i) - 1))); +            share_in = arch->add_wire(x, y, arch->id(stringf("SHARE[%d]", (z * 2 + i) - 1))); +        } +        if (z == 9 && i == 1) { +            carry_out = arch->add_wire(x, y, id_COUT); +            share_out = arch->add_wire(x, y, id_SHAREOUT); +        } else { +            carry_out = arch->add_wire(x, y, arch->id(stringf("CARRY[%d]", z * 2 + i))); +            share_out = arch->add_wire(x, y, arch->id(stringf("SHARE[%d]", z * 2 + i))); +        } + +        BelId bel = arch->add_bel(x, y, arch->id(stringf("ALM%d_COMB%d", z, i)), id_MISTRAL_COMB); +        // LUT/MUX inputs +        arch->add_bel_pin(bel, id_A, PORT_IN, arch->get_port(CycloneV::LAB, x, y, z, CycloneV::A)); +        arch->add_bel_pin(bel, id_B, PORT_IN, arch->get_port(CycloneV::LAB, x, y, z, CycloneV::B)); +        arch->add_bel_pin(bel, id_C, PORT_IN, arch->get_port(CycloneV::LAB, x, y, z, CycloneV::C)); +        arch->add_bel_pin(bel, id_D, PORT_IN, arch->get_port(CycloneV::LAB, x, y, z, CycloneV::D)); +        arch->add_bel_pin(bel, id_E0, PORT_IN, arch->get_port(CycloneV::LAB, x, y, z, CycloneV::E0)); +        arch->add_bel_pin(bel, id_E1, PORT_IN, arch->get_port(CycloneV::LAB, x, y, z, CycloneV::E1)); +        arch->add_bel_pin(bel, id_F0, PORT_IN, arch->get_port(CycloneV::LAB, x, y, z, CycloneV::F0)); +        arch->add_bel_pin(bel, id_F1, PORT_IN, arch->get_port(CycloneV::LAB, x, y, z, CycloneV::F1)); +        // Carry/share chain +        arch->add_bel_pin(bel, id_CIN, PORT_IN, carry_in); +        arch->add_bel_pin(bel, id_SHAREIN, PORT_IN, share_in); +        arch->add_bel_pin(bel, id_CIN, PORT_OUT, carry_in); +        arch->add_bel_pin(bel, id_SHAREIN, PORT_OUT, share_out); +        // Combinational output +        WireId comb_out = arch->add_wire(x, y, arch->id(stringf("COMBOUT[%d]", z * 2 + i))); +        arch->add_bel_pin(bel, id_COMBOUT, PORT_OUT, comb_out); +        // Assign indexing +        lab.alms.at(z).lut_bels.at(i) = bel; +        arch->bels.at(bel).lab_data.lab = lab_idx; +        arch->bels.at(bel).lab_data.alm = z; +        arch->bels.at(bel).lab_data.idx = i; +    } +    // Create the control set and E/F selection - which is per pair of FF +    std::array<WireId, 2> sel_clk, sel_ena, sel_aclr, sel_ef; +    for (int i = 0; i < 2; i++) { +        // Wires +        sel_clk[i] = arch->add_wire(x, y, arch->id(stringf("CLK%c[%d]", i ? 'B' : 'T', z))); +        sel_ena[i] = arch->add_wire(x, y, arch->id(stringf("ENA%c[%d]", i ? 'B' : 'T', z))); +        sel_aclr[i] = arch->add_wire(x, y, arch->id(stringf("ACLR%c[%d]", i ? 'B' : 'T', z))); +        sel_ef[i] = arch->add_wire(x, y, arch->id(stringf("%cEF[%d]", i ? 'B' : 'T', z))); +        // Muxes - three CLK/ENA per LAB, two ACLR +        for (int j = 0; j < 3; i++) { +            arch->add_pip(lab.clk_wires[j], sel_clk[i]); +            arch->add_pip(lab.ena_wires[j], sel_ena[i]); +            if (j < 2) +                arch->add_pip(lab.aclr_wires[j], sel_aclr[i]); +        } +        // E/F pips +        arch->add_pip(arch->get_port(CycloneV::LAB, x, y, z, i ? CycloneV::E1 : CycloneV::E0), sel_ef[i]); +        arch->add_pip(arch->get_port(CycloneV::LAB, x, y, z, i ? CycloneV::F1 : CycloneV::F0), sel_ef[i]); +    } + +    // Create the flipflops and associated routing +    const CycloneV::port_type_t outputs[4] = {CycloneV::FFT0, CycloneV::FFT1, CycloneV::FFB0, CycloneV::FFB1}; +    const CycloneV::port_type_t l_outputs[4] = {CycloneV::FFT1L, CycloneV::FFB1L}; + +    for (int i = 0; i < 4; i++) { +        // FF input, selected by *PKREG* +        WireId comb_out = arch->add_wire(x, y, arch->id(stringf("COMBOUT[%d]", (z * 2) + (i / 2)))); +        WireId ff_in = arch->add_wire(x, y, arch->id(stringf("FFIN[%d]", (z * 4) + i))); +        arch->add_pip(comb_out, ff_in); +        arch->add_pip(sel_ef[i / 2], ff_in); +        // FF bel +        BelId bel = arch->add_bel(x, y, arch->id(stringf("ALM%d_FF%d", z, i)), id_MISTRAL_FF); +        arch->add_bel_pin(bel, id_CLK, PORT_IN, sel_clk[i / 2]); +        arch->add_bel_pin(bel, id_ENA, PORT_IN, sel_ena[i / 2]); +        arch->add_bel_pin(bel, id_ACLR, PORT_IN, sel_aclr[i / 2]); +        arch->add_bel_pin(bel, id_SCLR, PORT_IN, lab.sclr_wire); +        arch->add_bel_pin(bel, id_SLOAD, PORT_IN, lab.sload_wire); +        arch->add_bel_pin(bel, id_DATAIN, PORT_IN, ff_in); +        arch->add_bel_pin(bel, id_SDATA, PORT_IN, sel_ef[i / 2]); + +        // FF output +        WireId ff_out = arch->add_wire(x, y, arch->id(stringf("FFOUT[%d]", (z * 4) + i))); +        arch->add_bel_pin(bel, id_Q, PORT_OUT, ff_out); +        // Output mux (*DFF*) +        WireId out = arch->get_port(CycloneV::LAB, x, y, z, outputs[i]); +        arch->add_pip(ff_out, out); +        arch->add_pip(comb_out, out); +        // 'L' output mux where applicable +        if (i == 1 || i == 3) { +            WireId l_out = arch->get_port(CycloneV::LAB, x, y, z, l_outputs[i / 2]); +            arch->add_pip(ff_out, l_out); +            arch->add_pip(comb_out, l_out); +        } + +        lab.alms.at(z).ff_bels.at(i) = bel; +        arch->bels.at(bel).lab_data.lab = lab_idx; +        arch->bels.at(bel).lab_data.alm = z; +        arch->bels.at(bel).lab_data.idx = i; +    } +} +} // namespace + +void Arch::create_lab(int x, int y) +{ +    uint32_t lab_idx = labs.size(); +    labs.emplace_back(); + +    auto &lab = labs.back(); + +    // Create common control set configuration. This is actually a subset of what's possible, but errs on the side of +    // caution due to incomplete documentation + +    // Clocks - hardcode to CLKA choices, as both CLKA and CLKB coming from general routing causes unexpected +    // permutations +    for (int i = 0; i < 3; i++) { +        lab.clk_wires[i] = add_wire(x, y, id(stringf("CLK%d", i))); +        add_pip(get_port(CycloneV::LAB, x, y, -1, CycloneV::CLKIN, 0), lab.clk_wires[i]);  // dedicated routing +        add_pip(get_port(CycloneV::LAB, x, y, -1, CycloneV::DATAIN, 0), lab.clk_wires[i]); // general routing +    } + +    // Enables - while it looks from the config like there are choices for these, it seems like EN0_SEL actually selects +    // SCLR not ENA0 and EN1_SEL actually selects SLOAD? +    lab.ena_wires[0] = get_port(CycloneV::LAB, x, y, -1, CycloneV::DATAIN, 2); +    lab.ena_wires[1] = get_port(CycloneV::LAB, x, y, -1, CycloneV::DATAIN, 3); +    lab.ena_wires[2] = get_port(CycloneV::LAB, x, y, -1, CycloneV::DATAIN, 0); + +    // ACLRs - only consider general routing for now +    lab.aclr_wires[0] = get_port(CycloneV::LAB, x, y, -1, CycloneV::DATAIN, 3); +    lab.aclr_wires[1] = get_port(CycloneV::LAB, x, y, -1, CycloneV::DATAIN, 2); + +    // SCLR and SLOAD - as above it seems like these might be selectable using the "EN*_SEL" bits but play it safe for +    // now +    lab.sclr_wire = get_port(CycloneV::LAB, x, y, -1, CycloneV::DATAIN, 3); +    lab.sload_wire = get_port(CycloneV::LAB, x, y, -1, CycloneV::DATAIN, 1); + +    for (int i = 0; i < 10; i++) { +        create_alm(this, x, y, i, lab_idx); +    } +} + +NEXTPNR_NAMESPACE_END
\ No newline at end of file | 
