mistral: Some preps for generating bitstreams
Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
parent
2612853238
commit
c5d983066d
@ -303,6 +303,12 @@ WireId Arch::add_wire(int x, int y, IdString name, uint64_t flags)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Arch::wires_connected(WireId src, WireId dst) const
|
||||||
|
{
|
||||||
|
PipId pip(src.node, dst.node);
|
||||||
|
return getBoundPipNet(pip) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
PipId Arch::add_pip(WireId src, WireId dst)
|
PipId Arch::add_pip(WireId src, WireId dst)
|
||||||
{
|
{
|
||||||
wires[src].wires_downhill.push_back(dst);
|
wires[src].wires_downhill.push_back(dst);
|
||||||
|
@ -40,6 +40,10 @@ struct ArchArgs
|
|||||||
// These structures are used for fast ALM validity checking
|
// These structures are used for fast ALM validity checking
|
||||||
struct ALMInfo
|
struct ALMInfo
|
||||||
{
|
{
|
||||||
|
// Wires, so bitstream gen can determine connectivity
|
||||||
|
std::array<WireId, 2> comb_out;
|
||||||
|
std::array<WireId, 2> sel_clk, sel_ena, sel_aclr, sel_ef;
|
||||||
|
std::array<WireId, 4> ff_in, ff_out;
|
||||||
// Pointers to bels
|
// Pointers to bels
|
||||||
std::array<BelId, 2> lut_bels;
|
std::array<BelId, 2> lut_bels;
|
||||||
std::array<BelId, 4> ff_bels;
|
std::array<BelId, 4> ff_bels;
|
||||||
@ -326,6 +330,8 @@ struct Arch : BaseArch<ArchRanges>
|
|||||||
const std::vector<BelPin> &getWireBelPins(WireId wire) const override { return wires.at(wire).bel_pins; }
|
const std::vector<BelPin> &getWireBelPins(WireId wire) const override { return wires.at(wire).bel_pins; }
|
||||||
AllWireRange getWires() const override { return AllWireRange(wires); }
|
AllWireRange getWires() const override { return AllWireRange(wires); }
|
||||||
|
|
||||||
|
bool wires_connected(WireId src, WireId dst) const;
|
||||||
|
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
|
|
||||||
PipId getPipByName(IdStringList name) const override;
|
PipId getPipByName(IdStringList name) const override;
|
||||||
@ -461,9 +467,15 @@ struct Arch : BaseArch<ArchRanges>
|
|||||||
static const std::unordered_map<IdString, CellPinsData> cell_pins_db; // pins.cc
|
static const std::unordered_map<IdString, CellPinsData> cell_pins_db; // pins.cc
|
||||||
CellPinStyle get_cell_pin_style(const CellInfo *cell, IdString port) const; // pins.cc
|
CellPinStyle get_cell_pin_style(const CellInfo *cell, IdString port) const; // pins.cc
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
// List of IO constraints, used by QSF parser
|
// List of IO constraints, used by QSF parser
|
||||||
std::unordered_map<IdString, std::unordered_map<IdString, Property>> io_attr;
|
std::unordered_map<IdString, std::unordered_map<IdString, Property>> io_attr;
|
||||||
void read_qsf(std::istream &in); // qsf.cc
|
void read_qsf(std::istream &in); // qsf.cc
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
void init_base_bitstream(); // base_bitstream.cc
|
||||||
};
|
};
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
86
mistral/base_bitstream.cc
Normal file
86
mistral/base_bitstream.cc
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Device-specific default config for the sx120f die
|
||||||
|
void default_sx120f(CycloneV *cv)
|
||||||
|
{
|
||||||
|
// Default PMA config?
|
||||||
|
cv->bmux_m_set(CycloneV::PMA3, CycloneV::xy2pos(0, 11), CycloneV::FFPLL_IQCLK_DIRECTION, 0, CycloneV::TRISTATE);
|
||||||
|
cv->bmux_m_set(CycloneV::PMA3, CycloneV::xy2pos(0, 11), CycloneV::FFPLL_IQCLK_DIRECTION, 1, CycloneV::TRISTATE);
|
||||||
|
cv->bmux_m_set(CycloneV::PMA3, CycloneV::xy2pos(0, 23), CycloneV::FFPLL_IQCLK_DIRECTION, 0, CycloneV::DOWN);
|
||||||
|
cv->bmux_m_set(CycloneV::PMA3, CycloneV::xy2pos(0, 23), CycloneV::FFPLL_IQCLK_DIRECTION, 1, CycloneV::UP);
|
||||||
|
cv->bmux_m_set(CycloneV::PMA3, CycloneV::xy2pos(0, 35), CycloneV::FFPLL_IQCLK_DIRECTION, 0, CycloneV::UP);
|
||||||
|
cv->bmux_m_set(CycloneV::PMA3, CycloneV::xy2pos(0, 35), CycloneV::FFPLL_IQCLK_DIRECTION, 1, CycloneV::UP);
|
||||||
|
cv->bmux_b_set(CycloneV::PMA3, CycloneV::xy2pos(0, 35), CycloneV::FPLL_DRV_EN, -1, false);
|
||||||
|
cv->bmux_m_set(CycloneV::PMA3, CycloneV::xy2pos(0, 35), CycloneV::HCLK_TOP_OUT_DRIVER, -1, CycloneV::TRISTATE);
|
||||||
|
// Default PLL config
|
||||||
|
cv->bmux_b_set(CycloneV::FPLL, CycloneV::xy2pos(0, 73), CycloneV::PL_AUX_ATB_EN0, -1, true);
|
||||||
|
cv->bmux_b_set(CycloneV::FPLL, CycloneV::xy2pos(0, 73), CycloneV::PL_AUX_ATB_EN0_PRECOMP, -1, true);
|
||||||
|
cv->bmux_b_set(CycloneV::FPLL, CycloneV::xy2pos(0, 73), CycloneV::PL_AUX_ATB_EN1, -1, true);
|
||||||
|
cv->bmux_b_set(CycloneV::FPLL, CycloneV::xy2pos(0, 73), CycloneV::PL_AUX_ATB_EN1_PRECOMP, -1, true);
|
||||||
|
cv->bmux_b_set(CycloneV::FPLL, CycloneV::xy2pos(0, 73), CycloneV::PL_AUX_BG_KICKSTART, -1, true);
|
||||||
|
cv->bmux_b_set(CycloneV::FPLL, CycloneV::xy2pos(0, 73), CycloneV::PL_AUX_VBGMON_POWERDOWN, -1, true);
|
||||||
|
|
||||||
|
// Discover these mux values using
|
||||||
|
// grep 'i [_A-Z0-9.]* 1' empty.bt
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 12), 69), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 13), 4), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 34), 69), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 35), 4), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 37), 31), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 40), 43), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 46), 69), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 47), 53), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 53), 69), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 54), 4), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 73), 68), true);
|
||||||
|
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(9, 18), 66), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(9, 20), 8), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(9, 27), 69), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(9, 28), 43), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(9, 59), 66), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(9, 61), 8), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(9, 68), 69), true);
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(9, 69), 43), true);
|
||||||
|
|
||||||
|
for (int z = 10; z <= 45; z++)
|
||||||
|
cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(51, 80), z), true);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void Arch::init_base_bitstream()
|
||||||
|
{
|
||||||
|
switch (cyclonev->current_model()->variant.die.type) {
|
||||||
|
case CycloneV::SX120F:
|
||||||
|
default_sx120f(cyclonev);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log_error("FIXME: die type %s currently unsupported for bitgen.\n",
|
||||||
|
cyclonev->current_model()->variant.die.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
@ -32,6 +32,7 @@ namespace {
|
|||||||
static void create_alm(Arch *arch, int x, int y, int z, uint32_t lab_idx)
|
static void create_alm(Arch *arch, int x, int y, int z, uint32_t lab_idx)
|
||||||
{
|
{
|
||||||
auto &lab = arch->labs.at(lab_idx);
|
auto &lab = arch->labs.at(lab_idx);
|
||||||
|
auto &alm = lab.alms.at(z);
|
||||||
// Create the combinational part of ALMs.
|
// 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
|
// 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;
|
// Each one has all 8 ALM inputs as input pins. In many cases only a subset of these are used; depending on mode;
|
||||||
@ -79,33 +80,32 @@ static void create_alm(Arch *arch, int x, int y, int z, uint32_t lab_idx)
|
|||||||
arch->add_bel_pin(bel, id_COUT, PORT_OUT, carry_out);
|
arch->add_bel_pin(bel, id_COUT, PORT_OUT, carry_out);
|
||||||
arch->add_bel_pin(bel, id_SHAREOUT, PORT_OUT, share_out);
|
arch->add_bel_pin(bel, id_SHAREOUT, PORT_OUT, share_out);
|
||||||
// Combinational output
|
// Combinational output
|
||||||
WireId comb_out = arch->add_wire(x, y, arch->id(stringf("COMBOUT[%d]", z * 2 + i)));
|
alm.comb_out[i] = arch->add_wire(x, y, arch->id(stringf("COMBOUT[%d]", z * 2 + i)));
|
||||||
arch->add_bel_pin(bel, id_COMBOUT, PORT_OUT, comb_out);
|
arch->add_bel_pin(bel, id_COMBOUT, PORT_OUT, alm.comb_out[i]);
|
||||||
// Assign indexing
|
// Assign indexing
|
||||||
lab.alms.at(z).lut_bels.at(i) = bel;
|
alm.lut_bels.at(i) = bel;
|
||||||
auto &b = arch->bel_data(bel);
|
auto &b = arch->bel_data(bel);
|
||||||
b.lab_data.lab = lab_idx;
|
b.lab_data.lab = lab_idx;
|
||||||
b.lab_data.alm = z;
|
b.lab_data.alm = z;
|
||||||
b.lab_data.idx = i;
|
b.lab_data.idx = i;
|
||||||
}
|
}
|
||||||
// Create the control set and E/F selection - which is per pair of FF
|
// 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++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
// Wires
|
// Wires
|
||||||
sel_clk[i] = arch->add_wire(x, y, arch->id(stringf("CLK%c[%d]", i ? 'B' : 'T', z)));
|
alm.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)));
|
alm.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)));
|
alm.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)));
|
alm.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
|
// Muxes - three CLK/ENA per LAB, two ACLR
|
||||||
for (int j = 0; j < 3; j++) {
|
for (int j = 0; j < 3; j++) {
|
||||||
arch->add_pip(lab.clk_wires[j], sel_clk[i]);
|
arch->add_pip(lab.clk_wires[j], alm.sel_clk[i]);
|
||||||
arch->add_pip(lab.ena_wires[j], sel_ena[i]);
|
arch->add_pip(lab.ena_wires[j], alm.sel_ena[i]);
|
||||||
if (j < 2)
|
if (j < 2)
|
||||||
arch->add_pip(lab.aclr_wires[j], sel_aclr[i]);
|
arch->add_pip(lab.aclr_wires[j], alm.sel_aclr[i]);
|
||||||
}
|
}
|
||||||
// E/F pips
|
// 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::E1 : CycloneV::E0), alm.sel_ef[i]);
|
||||||
arch->add_pip(arch->get_port(CycloneV::LAB, x, y, z, i ? CycloneV::F1 : CycloneV::F0), sel_ef[i]);
|
arch->add_pip(arch->get_port(CycloneV::LAB, x, y, z, i ? CycloneV::F1 : CycloneV::F0), alm.sel_ef[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the flipflops and associated routing
|
// Create the flipflops and associated routing
|
||||||
@ -114,32 +114,31 @@ static void create_alm(Arch *arch, int x, int y, int z, uint32_t lab_idx)
|
|||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
// FF input, selected by *PKREG*
|
// FF input, selected by *PKREG*
|
||||||
WireId comb_out = arch->add_wire(x, y, arch->id(stringf("COMBOUT[%d]", (z * 2) + (i / 2))));
|
alm.ff_in[i] = arch->add_wire(x, y, arch->id(stringf("FFIN[%d]", (z * 4) + i)));
|
||||||
WireId ff_in = arch->add_wire(x, y, arch->id(stringf("FFIN[%d]", (z * 4) + i)));
|
arch->add_pip(alm.comb_out[i / 2], alm.ff_in[i]);
|
||||||
arch->add_pip(comb_out, ff_in);
|
arch->add_pip(alm.sel_ef[i / 2], alm.ff_in[i]);
|
||||||
arch->add_pip(sel_ef[i / 2], ff_in);
|
|
||||||
// FF bel
|
// FF bel
|
||||||
BelId bel = arch->add_bel(x, y, arch->id(stringf("ALM%d_FF%d", z, i)), id_MISTRAL_FF);
|
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_CLK, PORT_IN, alm.sel_clk[i / 2]);
|
||||||
arch->add_bel_pin(bel, id_ENA, PORT_IN, sel_ena[i / 2]);
|
arch->add_bel_pin(bel, id_ENA, PORT_IN, alm.sel_ena[i / 2]);
|
||||||
arch->add_bel_pin(bel, id_ACLR, PORT_IN, sel_aclr[i / 2]);
|
arch->add_bel_pin(bel, id_ACLR, PORT_IN, alm.sel_aclr[i / 2]);
|
||||||
arch->add_bel_pin(bel, id_SCLR, PORT_IN, lab.sclr_wire);
|
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_SLOAD, PORT_IN, lab.sload_wire);
|
||||||
arch->add_bel_pin(bel, id_DATAIN, PORT_IN, ff_in);
|
arch->add_bel_pin(bel, id_DATAIN, PORT_IN, alm.ff_in[i]);
|
||||||
arch->add_bel_pin(bel, id_SDATA, PORT_IN, sel_ef[i / 2]);
|
arch->add_bel_pin(bel, id_SDATA, PORT_IN, alm.sel_ef[i / 2]);
|
||||||
|
|
||||||
// FF output
|
// FF output
|
||||||
WireId ff_out = arch->add_wire(x, y, arch->id(stringf("FFOUT[%d]", (z * 4) + i)));
|
alm.ff_out[i] = arch->add_wire(x, y, arch->id(stringf("FFOUT[%d]", (z * 4) + i)));
|
||||||
arch->add_bel_pin(bel, id_Q, PORT_OUT, ff_out);
|
arch->add_bel_pin(bel, id_Q, PORT_OUT, alm.ff_out[i]);
|
||||||
// Output mux (*DFF*)
|
// Output mux (*DFF*)
|
||||||
WireId out = arch->get_port(CycloneV::LAB, x, y, z, outputs[i]);
|
WireId out = arch->get_port(CycloneV::LAB, x, y, z, outputs[i]);
|
||||||
arch->add_pip(ff_out, out);
|
arch->add_pip(alm.ff_out[i], out);
|
||||||
arch->add_pip(comb_out, out);
|
arch->add_pip(alm.comb_out[i / 2], out);
|
||||||
// 'L' output mux where applicable
|
// 'L' output mux where applicable
|
||||||
if (i == 1 || i == 3) {
|
if (i == 1 || i == 3) {
|
||||||
WireId l_out = arch->get_port(CycloneV::LAB, x, y, z, l_outputs[i / 2]);
|
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(alm.ff_out[i], l_out);
|
||||||
arch->add_pip(comb_out, l_out);
|
arch->add_pip(alm.comb_out[i / 2], l_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
lab.alms.at(z).ff_bels.at(i) = bel;
|
lab.alms.at(z).ff_bels.at(i) = bel;
|
||||||
|
Loading…
Reference in New Issue
Block a user