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 alms; + // Control set wires + std::array clk_wires, ena_wires; + std::array 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 // ------------------------------------------------- // 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 std::unordered_map wires; std::unordered_map bels; + // List of LABs + std::vector labs; + // WIP to link without failure std::vector 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 + * + * 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 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