broken packing stuff
This commit is contained in:
parent
8c2f47094b
commit
c89027ca5d
@ -332,8 +332,8 @@ const GlobalAliasPOD* aliasLookup(const GlobalAliasPOD *first, int len, const Gl
|
||||
|
||||
Arch::Arch(ArchArgs args) : args(args)
|
||||
{
|
||||
family = "GW1N-1";
|
||||
device = "GW1N-1";
|
||||
family = "GW1N-9";
|
||||
device = "GW1N-9";
|
||||
speed = "C6/E5"; // or whatever
|
||||
package = "QFN48"; // or something
|
||||
|
||||
@ -469,7 +469,7 @@ Arch::Arch(ArchArgs args) : args(args)
|
||||
portname = pairLookup(bel->ports.get(), bel->num_ports, -1, ID_OE)->src_id;
|
||||
snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
|
||||
wirename = id(buf);
|
||||
addBelInput(belname, id_OE, wirename);
|
||||
addBelInput(belname, id_OEN, wirename);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -503,7 +503,7 @@ Arch::Arch(ArchArgs args) : args(args)
|
||||
delay.delay = 0.1; // TODO
|
||||
// local alias
|
||||
auto local_alias = pairLookup(tile->aliases.get(), tile->num_aliases, -1, srcid.index);
|
||||
std::cout << "srcid " << srcid.str(this) << std::endl;
|
||||
// std::cout << "srcid " << srcid.str(this) << std::endl;
|
||||
if(local_alias!=nullptr) {
|
||||
srcid = local_alias->src_id;
|
||||
gsrcname = wireToGlobal(srcrow, srccol, db, srcid);
|
||||
@ -689,8 +689,8 @@ const std::map<IdString, std::string> &Arch::getPipAttrs(PipId pip) const { retu
|
||||
|
||||
uint32_t Arch::getPipChecksum(PipId wire) const
|
||||
{
|
||||
// FIXME
|
||||
return 0;
|
||||
// FIXEDME?
|
||||
return wire.index;
|
||||
}
|
||||
|
||||
void Arch::bindPip(PipId pip, NetInfo *net, PlaceStrength strength)
|
||||
|
134
gowin/cells.cc
134
gowin/cells.cc
@ -21,12 +21,12 @@
|
||||
#include "design_utils.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
#include <iostream>
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir)
|
||||
void add_port(const Context *ctx, CellInfo *cell, IdString id, PortType dir)
|
||||
{
|
||||
IdString id = ctx->id(name);
|
||||
NPNR_ASSERT(cell->ports.count(id) == 0);
|
||||
cell->ports[id] = PortInfo{id, nullptr, dir};
|
||||
}
|
||||
@ -41,27 +41,31 @@ std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::
|
||||
new_cell->name = ctx->id(name);
|
||||
}
|
||||
new_cell->type = type;
|
||||
if (type == ctx->id("GENERIC_SLICE")) {
|
||||
new_cell->params[ctx->id("K")] = ctx->args.K;
|
||||
new_cell->params[ctx->id("INIT")] = 0;
|
||||
new_cell->params[ctx->id("FF_USED")] = 0;
|
||||
if (type == id_SLICE) {
|
||||
new_cell->params[id_INIT] = 0;
|
||||
new_cell->params[id_FF_USED] = 0;
|
||||
new_cell->params[id_FF_TYPE] = id_DFF.str(ctx);
|
||||
|
||||
for (int i = 0; i < ctx->args.K; i++)
|
||||
add_port(ctx, new_cell.get(), "I[" + std::to_string(i) + "]", PORT_IN);
|
||||
IdString names[4] = {id_A, id_B, id_C, id_D};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
add_port(ctx, new_cell.get(), names[i], PORT_IN);
|
||||
}
|
||||
|
||||
add_port(ctx, new_cell.get(), "CLK", PORT_IN);
|
||||
add_port(ctx, new_cell.get(), id_CLK, PORT_IN);
|
||||
|
||||
add_port(ctx, new_cell.get(), "F", PORT_OUT);
|
||||
add_port(ctx, new_cell.get(), "Q", PORT_OUT);
|
||||
} else if (type == ctx->id("GENERIC_IOB")) {
|
||||
new_cell->params[ctx->id("INPUT_USED")] = 0;
|
||||
new_cell->params[ctx->id("OUTPUT_USED")] = 0;
|
||||
new_cell->params[ctx->id("ENABLE_USED")] = 0;
|
||||
add_port(ctx, new_cell.get(), id_F, PORT_OUT);
|
||||
add_port(ctx, new_cell.get(), id_Q, PORT_OUT);
|
||||
add_port(ctx, new_cell.get(), id_CE, PORT_IN);
|
||||
add_port(ctx, new_cell.get(), id_LSR, PORT_IN);
|
||||
} else if (type == id_IOB) {
|
||||
new_cell->params[id_INPUT_USED] = 0;
|
||||
new_cell->params[id_OUTPUT_USED] = 0;
|
||||
new_cell->params[id_ENABLE_USED] = 0;
|
||||
|
||||
add_port(ctx, new_cell.get(), "PAD", PORT_INOUT);
|
||||
add_port(ctx, new_cell.get(), "I", PORT_IN);
|
||||
add_port(ctx, new_cell.get(), "EN", PORT_IN);
|
||||
add_port(ctx, new_cell.get(), "O", PORT_OUT);
|
||||
add_port(ctx, new_cell.get(), id_PAD, PORT_INOUT);
|
||||
add_port(ctx, new_cell.get(), id_I, PORT_IN);
|
||||
add_port(ctx, new_cell.get(), id_EN, PORT_IN);
|
||||
add_port(ctx, new_cell.get(), id_O, PORT_OUT);
|
||||
} else {
|
||||
log_error("unable to create generic cell of type %s", type.c_str(ctx));
|
||||
}
|
||||
@ -70,78 +74,74 @@ std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::
|
||||
|
||||
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
|
||||
{
|
||||
lc->params[ctx->id("INIT")] = lut->params[ctx->id("INIT")];
|
||||
for(auto f : lut->ports) {
|
||||
std::cout << f.first.str(ctx) << std::endl;
|
||||
}
|
||||
lc->params[id_INIT] = lut->params[id_INIT];
|
||||
|
||||
int lut_k = int_or_default(lut->params, ctx->id("K"), 4);
|
||||
NPNR_ASSERT(lut_k <= ctx->args.K);
|
||||
|
||||
for (int i = 0; i < lut_k; i++) {
|
||||
IdString port = ctx->id("I[" + std::to_string(i) + "]");
|
||||
replace_port(lut, port, lc, port);
|
||||
IdString sim_names[4] = {id_I0, id_I1, id_I2, id_I3};
|
||||
IdString wire_names[4] = {id_A, id_B, id_C, id_D};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
replace_port(lut, sim_names[i], lc, wire_names[i]);
|
||||
}
|
||||
|
||||
if (no_dff) {
|
||||
lc->params[ctx->id("FF_USED")] = 0;
|
||||
replace_port(lut, ctx->id("Q"), lc, ctx->id("F"));
|
||||
lc->params[id_FF_USED] = 0;
|
||||
replace_port(lut, id_F, lc, id_F);
|
||||
}
|
||||
}
|
||||
|
||||
void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut)
|
||||
{
|
||||
lc->params[ctx->id("FF_USED")] = 1;
|
||||
replace_port(dff, ctx->id("CLK"), lc, ctx->id("CLK"));
|
||||
|
||||
lc->params[id_FF_USED] = 1;
|
||||
lc->params[id_FF_TYPE] = dff->type.str(ctx);
|
||||
replace_port(dff, id_CLK, lc, id_CLK);
|
||||
replace_port(dff, id_CE, lc, id_CE);
|
||||
replace_port(dff, id_SET, lc, id_LSR);
|
||||
replace_port(dff, id_RESET, lc, id_LSR);
|
||||
replace_port(dff, id_CLEAR, lc, id_LSR);
|
||||
replace_port(dff, id_PRESET, lc, id_LSR);
|
||||
for(auto f : dff->ports) {
|
||||
std::cout << f.first.str(ctx) << std::endl;
|
||||
}
|
||||
if (pass_thru_lut) {
|
||||
// Fill LUT with alternating 10
|
||||
const int init_size = 1 << lc->params[ctx->id("K")].as_int64();
|
||||
const int init_size = 1 << 4;
|
||||
std::string init;
|
||||
init.reserve(init_size);
|
||||
for (int i = 0; i < init_size; i += 2)
|
||||
init.append("10");
|
||||
lc->params[ctx->id("INIT")] = Property::from_string(init);
|
||||
lc->params[id_INIT] = Property::from_string(init);
|
||||
|
||||
replace_port(dff, ctx->id("D"), lc, ctx->id("I[0]"));
|
||||
replace_port(dff, id_D, lc, id_A);
|
||||
}
|
||||
|
||||
replace_port(dff, ctx->id("Q"), lc, ctx->id("Q"));
|
||||
replace_port(dff, id_Q, lc, id_Q);
|
||||
}
|
||||
|
||||
void nxio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, std::unordered_set<IdString> &todelete_cells)
|
||||
void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, std::unordered_set<IdString> &todelete_cells)
|
||||
{
|
||||
if (nxio->type == ctx->id("$nextpnr_ibuf")) {
|
||||
iob->params[ctx->id("INPUT_USED")] = 1;
|
||||
replace_port(nxio, ctx->id("O"), iob, ctx->id("O"));
|
||||
} else if (nxio->type == ctx->id("$nextpnr_obuf")) {
|
||||
iob->params[ctx->id("OUTPUT_USED")] = 1;
|
||||
replace_port(nxio, ctx->id("I"), iob, ctx->id("I"));
|
||||
} else if (nxio->type == ctx->id("$nextpnr_iobuf")) {
|
||||
// N.B. tristate will be dealt with below
|
||||
iob->params[ctx->id("INPUT_USED")] = 1;
|
||||
iob->params[ctx->id("OUTPUT_USED")] = 1;
|
||||
replace_port(nxio, ctx->id("I"), iob, ctx->id("I"));
|
||||
replace_port(nxio, ctx->id("O"), iob, ctx->id("O"));
|
||||
if (nxio->type == id_IBUF) {
|
||||
iob->params[id_INPUT_USED] = 1;
|
||||
replace_port(nxio, id_O, iob, id_O);
|
||||
} else if (nxio->type == id_OBUF) {
|
||||
iob->params[id_OUTPUT_USED] = 1;
|
||||
replace_port(nxio, id_I, iob, id_I);
|
||||
} else if (nxio->type == id_TBUF) {
|
||||
iob->params[id_ENABLE_USED] = 1;
|
||||
iob->params[id_OUTPUT_USED] = 1;
|
||||
replace_port(nxio, id_I, iob, id_I);
|
||||
replace_port(nxio, id_OEN, iob, id_OEN);
|
||||
} else if (nxio->type == id_IOBUF) {
|
||||
iob->params[id_ENABLE_USED] = 1;
|
||||
iob->params[id_INPUT_USED] = 1;
|
||||
iob->params[id_OUTPUT_USED] = 1;
|
||||
replace_port(nxio, id_I, iob, id_I);
|
||||
replace_port(nxio, id_O, iob, id_O);
|
||||
replace_port(nxio, id_OEN, iob, id_OEN);
|
||||
} else {
|
||||
NPNR_ASSERT(false);
|
||||
}
|
||||
NetInfo *donet = iob->ports.at(ctx->id("I")).net;
|
||||
CellInfo *tbuf = net_driven_by(
|
||||
ctx, donet, [](const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("$_TBUF_"); },
|
||||
ctx->id("Y"));
|
||||
if (tbuf) {
|
||||
iob->params[ctx->id("ENABLE_USED")] = 1;
|
||||
replace_port(tbuf, ctx->id("A"), iob, ctx->id("I"));
|
||||
replace_port(tbuf, ctx->id("E"), iob, ctx->id("EN"));
|
||||
|
||||
if (donet->users.size() > 1) {
|
||||
for (auto user : donet->users)
|
||||
log_info(" remaining tristate user: %s.%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx));
|
||||
log_error("unsupported tristate IO pattern for IO buffer '%s', "
|
||||
"instantiate GENERIC_IOB manually to ensure correct behaviour\n",
|
||||
nxio->name.c_str(ctx));
|
||||
}
|
||||
ctx->nets.erase(donet->name);
|
||||
todelete_cells.insert(tbuf->name);
|
||||
}
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -29,12 +29,50 @@ NEXTPNR_NAMESPACE_BEGIN
|
||||
std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::string name = "");
|
||||
|
||||
// Return true if a cell is a LUT
|
||||
inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("LUT"); }
|
||||
inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell)
|
||||
{
|
||||
switch (cell->type.index) {
|
||||
case ID_LUT1:
|
||||
case ID_LUT2:
|
||||
case ID_LUT3:
|
||||
case ID_LUT4:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if a cell is a flipflop
|
||||
inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("DFF"); }
|
||||
inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell)
|
||||
{
|
||||
switch (cell->type.index) {
|
||||
case ID_DFF:
|
||||
case ID_DFFE:
|
||||
case ID_DFFS:
|
||||
case ID_DFFSE:
|
||||
case ID_DFFR:
|
||||
case ID_DFFRE:
|
||||
case ID_DFFP:
|
||||
case ID_DFFPE:
|
||||
case ID_DFFC:
|
||||
case ID_DFFCE:
|
||||
case ID_DFFN:
|
||||
case ID_DFFNE:
|
||||
case ID_DFFNS:
|
||||
case ID_DFFNSE:
|
||||
case ID_DFFNR:
|
||||
case ID_DFFNRE:
|
||||
case ID_DFFNP:
|
||||
case ID_DFFNPE:
|
||||
case ID_DFFNC:
|
||||
case ID_DFFNCE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("GENERIC_SLICE"); }
|
||||
inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SLICE; }
|
||||
|
||||
// Convert a LUT primitive to (part of) an GENERIC_SLICE, swapping ports
|
||||
// as needed. Set no_dff if a DFF is not being used, so that the output
|
||||
@ -47,8 +85,8 @@ void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff = tr
|
||||
// ignored
|
||||
void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut = false);
|
||||
|
||||
// Convert a nextpnr IO buffer to a GENERIC_IOB
|
||||
void nxio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *sbio, std::unordered_set<IdString> &todelete_cells);
|
||||
// Convert a Gowin IO buffer to a IOB bel
|
||||
void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *sbio, std::unordered_set<IdString> &todelete_cells);
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
|
@ -328,6 +328,7 @@ X(D)
|
||||
X(IOB)
|
||||
X(I)
|
||||
X(O)
|
||||
X(IO)
|
||||
X(OE)
|
||||
// bels
|
||||
X(DFF0)
|
||||
@ -355,4 +356,55 @@ X(IOBF)
|
||||
X(IOBG)
|
||||
X(IOBH)
|
||||
X(IOBI)
|
||||
X(IOBJ)
|
||||
X(IOBJ)
|
||||
|
||||
// DFF types
|
||||
X(DFF)
|
||||
X(DFFE)
|
||||
X(DFFS)
|
||||
X(DFFSE)
|
||||
X(DFFR)
|
||||
X(DFFRE)
|
||||
X(DFFP)
|
||||
X(DFFPE)
|
||||
X(DFFC)
|
||||
X(DFFCE)
|
||||
X(DFFN)
|
||||
X(DFFNE)
|
||||
X(DFFNS)
|
||||
X(DFFNSE)
|
||||
X(DFFNR)
|
||||
X(DFFNRE)
|
||||
X(DFFNP)
|
||||
X(DFFNPE)
|
||||
X(DFFNC)
|
||||
X(DFFNCE)
|
||||
|
||||
// IOB types
|
||||
X(IBUF)
|
||||
X(OBUF)
|
||||
X(IOBUF)
|
||||
X(TBUF)
|
||||
|
||||
// primitive attributes
|
||||
X(INIT)
|
||||
X(FF_USED)
|
||||
X(FF_TYPE)
|
||||
X(INPUT_USED)
|
||||
X(OUTPUT_USED)
|
||||
X(ENABLE_USED)
|
||||
|
||||
// ports
|
||||
X(EN)
|
||||
X(E)
|
||||
X(Y)
|
||||
X(PAD)
|
||||
X(RESET)
|
||||
X(SET)
|
||||
X(PRESET)
|
||||
X(CLEAR)
|
||||
X(I0)
|
||||
X(I1)
|
||||
X(I2)
|
||||
X(I3)
|
||||
X(OEN)
|
@ -24,6 +24,7 @@
|
||||
#include "design_utils.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
#include <iostream>
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
@ -40,14 +41,14 @@ static void pack_lut_lutffs(Context *ctx)
|
||||
log_info("cell '%s' is of type '%s'\n", ci->name.c_str(ctx), ci->type.c_str(ctx));
|
||||
if (is_lut(ctx, ci)) {
|
||||
std::unique_ptr<CellInfo> packed =
|
||||
create_generic_cell(ctx, ctx->id("GENERIC_SLICE"), ci->name.str(ctx) + "_LC");
|
||||
create_generic_cell(ctx, ctx->id("SLICE"), ci->name.str(ctx) + "_LC");
|
||||
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(packed->attrs, packed->attrs.begin()));
|
||||
packed_cells.insert(ci->name);
|
||||
if (ctx->verbose)
|
||||
log_info("packed cell %s into %s\n", ci->name.c_str(ctx), packed->name.c_str(ctx));
|
||||
// See if we can pack into a DFF
|
||||
// TODO: LUT cascade
|
||||
NetInfo *o = ci->ports.at(ctx->id("Q")).net;
|
||||
NetInfo *o = ci->ports.at(ctx->id("F")).net;
|
||||
CellInfo *dff = net_only_drives(ctx, o, is_ff, ctx->id("D"), true);
|
||||
auto lut_bel = ci->attrs.find(ctx->id("BEL"));
|
||||
bool packed_dff = false;
|
||||
@ -95,7 +96,7 @@ static void pack_nonlut_ffs(Context *ctx)
|
||||
CellInfo *ci = cell.second;
|
||||
if (is_ff(ctx, ci)) {
|
||||
std::unique_ptr<CellInfo> packed =
|
||||
create_generic_cell(ctx, ctx->id("GENERIC_SLICE"), ci->name.str(ctx) + "_DFFLC");
|
||||
create_generic_cell(ctx, ctx->id("SLICE"), ci->name.str(ctx) + "_DFFLC");
|
||||
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(packed->attrs, packed->attrs.begin()));
|
||||
if (ctx->verbose)
|
||||
log_info("packed cell %s into %s\n", ci->name.c_str(ctx), packed->name.c_str(ctx));
|
||||
@ -137,7 +138,7 @@ static void pack_constants(Context *ctx)
|
||||
{
|
||||
log_info("Packing constants..\n");
|
||||
|
||||
std::unique_ptr<CellInfo> gnd_cell = create_generic_cell(ctx, ctx->id("GENERIC_SLICE"), "$PACKER_GND");
|
||||
std::unique_ptr<CellInfo> gnd_cell = create_generic_cell(ctx, ctx->id("SLICE"), "$PACKER_GND");
|
||||
gnd_cell->params[ctx->id("INIT")] = Property(0, 1 << ctx->args.K);
|
||||
std::unique_ptr<NetInfo> gnd_net = std::unique_ptr<NetInfo>(new NetInfo);
|
||||
gnd_net->name = ctx->id("$PACKER_GND_NET");
|
||||
@ -145,7 +146,7 @@ static void pack_constants(Context *ctx)
|
||||
gnd_net->driver.port = ctx->id("F");
|
||||
gnd_cell->ports.at(ctx->id("F")).net = gnd_net.get();
|
||||
|
||||
std::unique_ptr<CellInfo> vcc_cell = create_generic_cell(ctx, ctx->id("GENERIC_SLICE"), "$PACKER_VCC");
|
||||
std::unique_ptr<CellInfo> vcc_cell = create_generic_cell(ctx, ctx->id("SLICE"), "$PACKER_VCC");
|
||||
// Fill with 1s
|
||||
vcc_cell->params[ctx->id("INIT")] = Property(Property::S1).extract(0, (1 << ctx->args.K), Property::S1);
|
||||
std::unique_ptr<NetInfo> vcc_net = std::unique_ptr<NetInfo>(new NetInfo);
|
||||
@ -188,13 +189,24 @@ static void pack_constants(Context *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_nextpnr_iob(Context *ctx, CellInfo *cell)
|
||||
static bool is_nextpnr_iob(const Context *ctx, CellInfo *cell)
|
||||
{
|
||||
return cell->type == ctx->id("$nextpnr_ibuf") || cell->type == ctx->id("$nextpnr_obuf") ||
|
||||
cell->type == ctx->id("$nextpnr_iobuf");
|
||||
}
|
||||
|
||||
static bool is_generic_iob(const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("GENERIC_IOB"); }
|
||||
static bool is_gowin_iob(const Context *ctx, const CellInfo *cell) {
|
||||
switch (cell->type.index)
|
||||
{
|
||||
case ID_IBUF:
|
||||
case ID_OBUF:
|
||||
case ID_IOBUF:
|
||||
case ID_TBUF:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Pack IO buffers
|
||||
static void pack_io(Context *ctx)
|
||||
@ -207,49 +219,42 @@ static void pack_io(Context *ctx)
|
||||
|
||||
for (auto cell : sorted(ctx->cells)) {
|
||||
CellInfo *ci = cell.second;
|
||||
if (is_nextpnr_iob(ctx, ci)) {
|
||||
if (is_gowin_iob(ctx, ci)) {
|
||||
std::cout << ci->type.str(ctx) << std::endl;
|
||||
for(auto p : ci->ports) {
|
||||
std::cout << p.first.str(ctx) << std::endl;
|
||||
}
|
||||
CellInfo *iob = nullptr;
|
||||
if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) {
|
||||
iob = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_generic_iob, ctx->id("PAD"), true, ci);
|
||||
|
||||
} else if (ci->type == ctx->id("$nextpnr_obuf")) {
|
||||
NetInfo *net = ci->ports.at(ctx->id("I")).net;
|
||||
iob = net_only_drives(ctx, net, is_generic_iob, ctx->id("PAD"), true, ci);
|
||||
switch (ci->type.index)
|
||||
{
|
||||
case ID_IBUF:
|
||||
iob = net_driven_by(ctx, ci->ports.at(id_I).net, is_nextpnr_iob, id_O);
|
||||
break;
|
||||
case ID_OBUF:
|
||||
iob = net_only_drives(ctx, ci->ports.at(id_O).net, is_nextpnr_iob, id_I);
|
||||
break;
|
||||
case ID_IOBUF:
|
||||
case ID_TBUF:
|
||||
log_error("untested tristate stuff");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (iob != nullptr) {
|
||||
// Trivial case, GENERIC_IOB used. Just destroy the net and the
|
||||
// iobuf
|
||||
log_info("%s feeds GENERIC_IOB %s, removing %s %s.\n", ci->name.c_str(ctx), iob->name.c_str(ctx),
|
||||
ci->type.c_str(ctx), ci->name.c_str(ctx));
|
||||
NetInfo *net = iob->ports.at(ctx->id("PAD")).net;
|
||||
if (((ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) &&
|
||||
net->users.size() > 1) ||
|
||||
(ci->type == ctx->id("$nextpnr_obuf") && (net->users.size() > 2 || net->driver.cell != nullptr)))
|
||||
log_error("PAD of %s '%s' connected to more than a single top level IO.\n", iob->type.c_str(ctx),
|
||||
iob->name.c_str(ctx));
|
||||
|
||||
if (net != nullptr) {
|
||||
delete_nets.insert(net->name);
|
||||
iob->ports.at(ctx->id("PAD")).net = nullptr;
|
||||
// delete the $nexpnr_[io]buf
|
||||
for (auto &p : iob->ports) {
|
||||
disconnect_port(ctx, iob, p.first);
|
||||
delete_nets.insert(p.second.net->name);
|
||||
}
|
||||
if (ci->type == ctx->id("$nextpnr_iobuf")) {
|
||||
NetInfo *net2 = ci->ports.at(ctx->id("I")).net;
|
||||
if (net2 != nullptr) {
|
||||
delete_nets.insert(net2->name);
|
||||
}
|
||||
}
|
||||
} else if (bool_or_default(ctx->settings, ctx->id("disable_iobs"))) {
|
||||
// No IO buffer insertion; just remove nextpnr_[io]buf
|
||||
for (auto &p : ci->ports)
|
||||
disconnect_port(ctx, ci, p.first);
|
||||
} else {
|
||||
// Create a GENERIC_IOB buffer
|
||||
std::unique_ptr<CellInfo> ice_cell =
|
||||
create_generic_cell(ctx, ctx->id("GENERIC_IOB"), ci->name.str(ctx) + "$iob");
|
||||
nxio_to_iob(ctx, ci, ice_cell.get(), packed_cells);
|
||||
new_cells.push_back(std::move(ice_cell));
|
||||
iob = new_cells.back().get();
|
||||
packed_cells.insert(iob->name);
|
||||
}
|
||||
// Create a IOB buffer
|
||||
std::unique_ptr<CellInfo> ice_cell =
|
||||
create_generic_cell(ctx, id_IOB, ci->name.str(ctx) + "$iob");
|
||||
gwio_to_iob(ctx, ci, ice_cell.get(), packed_cells);
|
||||
new_cells.push_back(std::move(ice_cell));
|
||||
iob = new_cells.back().get();
|
||||
|
||||
packed_cells.insert(ci->name);
|
||||
if (iob != nullptr)
|
||||
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(iob->attrs, iob->attrs.begin()));
|
||||
|
Loading…
Reference in New Issue
Block a user