nexus: Add IO packing
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
518ead2e2d
commit
df3866a800
@ -459,7 +459,7 @@ bool Arch::route()
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
CellPinMux Arch::get_cell_pinmux(CellInfo *cell, IdString pin) const
|
||||
CellPinMux Arch::get_cell_pinmux(const CellInfo *cell, IdString pin) const
|
||||
{
|
||||
IdString param = id(stringf("%sMUX", pin.c_str(this)));
|
||||
auto fnd_param = cell->params.find(param);
|
||||
@ -503,7 +503,7 @@ void Arch::set_cell_pinmux(CellInfo *cell, IdString pin, CellPinMux state)
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
const PadInfoPOD *Arch::get_pin_data(const std::string &pin) const
|
||||
const PadInfoPOD *Arch::get_pkg_pin_data(const std::string &pin) const
|
||||
{
|
||||
for (size_t i = 0; i < chip_info->num_pads; i++) {
|
||||
const PadInfoPOD *pad = &(chip_info->pads[i]);
|
||||
@ -537,9 +537,8 @@ Loc Arch::get_pad_loc(const PadInfoPOD *pad) const
|
||||
return loc;
|
||||
}
|
||||
|
||||
BelId Arch::get_pin_bel(const std::string &pin) const
|
||||
BelId Arch::get_pad_pio_bel(const PadInfoPOD *pad) const
|
||||
{
|
||||
const PadInfoPOD *pad = get_pin_data(pin);
|
||||
if (pad == nullptr)
|
||||
return BelId();
|
||||
return getBelByLocation(get_pad_loc(pad));
|
||||
@ -574,6 +573,17 @@ const PadInfoPOD *Arch::get_bel_pad(BelId bel) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string Arch::get_pad_functions(const PadInfoPOD *pad) const
|
||||
{
|
||||
std::string s;
|
||||
for (size_t i = 0; i < pad->num_funcs; i++) {
|
||||
if (!s.empty())
|
||||
s += '/';
|
||||
s += IdString(pad->func_strs[i]).str(this);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#ifdef WITH_HEAP
|
||||
|
@ -1429,15 +1429,16 @@ struct Arch : BaseCtx
|
||||
|
||||
bool nexus_logic_tile_valid(LogicTileStatus <s) const;
|
||||
|
||||
CellPinMux get_cell_pinmux(CellInfo *cell, IdString pin) const;
|
||||
CellPinMux get_cell_pinmux(const CellInfo *cell, IdString pin) const;
|
||||
void set_cell_pinmux(CellInfo *cell, IdString pin, CellPinMux state);
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
const PadInfoPOD *get_pin_data(const std::string &pin) const;
|
||||
const PadInfoPOD *get_pkg_pin_data(const std::string &pin) const;
|
||||
Loc get_pad_loc(const PadInfoPOD *pad) const;
|
||||
BelId get_pin_bel(const std::string &pin) const;
|
||||
BelId get_pad_pio_bel(const PadInfoPOD *pad) const;
|
||||
const PadInfoPOD *get_bel_pad(BelId bel) const;
|
||||
std::string get_pad_functions(const PadInfoPOD *pad) const;
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
|
@ -59,6 +59,10 @@ X(DOLP)
|
||||
X(INLP)
|
||||
X(INADC)
|
||||
|
||||
X(DIFFIO18_CORE)
|
||||
X(HSRXEN)
|
||||
X(HSTXEN)
|
||||
|
||||
X(LUT4)
|
||||
X(INIT)
|
||||
X(Z)
|
||||
@ -68,7 +72,6 @@ X(INIT0)
|
||||
X(INIT1)
|
||||
|
||||
X(INV)
|
||||
X(BB)
|
||||
X(VHI)
|
||||
X(VLO)
|
||||
|
||||
@ -138,3 +141,17 @@ X(RSTA)
|
||||
X(RSTB)
|
||||
|
||||
X(LOC)
|
||||
|
||||
X(IB)
|
||||
X(OB)
|
||||
X(OBZ)
|
||||
X(BB)
|
||||
X(BB_I3C_A)
|
||||
|
||||
X(SEIO33)
|
||||
X(SEIO18)
|
||||
X(DIFFIO18)
|
||||
X(IOPAD)
|
||||
X(PADDO)
|
||||
X(PADDI)
|
||||
X(PADDT)
|
||||
|
@ -91,6 +91,17 @@ struct NexusFasmWriter
|
||||
}
|
||||
}
|
||||
|
||||
void write_ioattr(const CellInfo *cell, const std::string &name, const std::string &defval = "")
|
||||
{
|
||||
auto fnd = cell->attrs.find(ctx->id(name));
|
||||
if (fnd == cell->attrs.end()) {
|
||||
if (!defval.empty())
|
||||
write_bit(stringf("%s.%s", name.c_str(), defval.c_str()));
|
||||
} else {
|
||||
write_bit(stringf("%s.%s", name.c_str(), fnd->second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
NexusFasmWriter(const Context *ctx, std::ostream &out) : ctx(ctx), out(out) {}
|
||||
std::string tile_name(int loc, const PhysicalTileInfoPOD &tile)
|
||||
{
|
||||
@ -195,14 +206,16 @@ struct NexusFasmWriter
|
||||
push_tile(bel.tile);
|
||||
push_belname(bel);
|
||||
const NetInfo *t = get_net_or_empty(cell, id_T);
|
||||
auto tmux = ctx->get_cell_pinmux(cell, id_T);
|
||||
bool is_input = false, is_output = false;
|
||||
if (t == nullptr || t->name == ctx->id("$PACKER_VCC_NET")) {
|
||||
is_input = true;
|
||||
} else if (t->name == ctx->id("$PACKER_GND_NET")) {
|
||||
if (tmux == PINMUX_0) {
|
||||
is_output = true;
|
||||
} else if (tmux == PINMUX_1 || t == nullptr) {
|
||||
is_input = true;
|
||||
}
|
||||
const char *iodir = is_input ? "INPUT" : (is_output ? "OUTPUT" : "BIDIR");
|
||||
write_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS33").c_str()));
|
||||
write_ioattr(cell, "PULLMODE", "NONE");
|
||||
pop(2);
|
||||
}
|
||||
void write_io18(const CellInfo *cell)
|
||||
@ -212,14 +225,16 @@ struct NexusFasmWriter
|
||||
push_belname(bel);
|
||||
push("SEIO18");
|
||||
const NetInfo *t = get_net_or_empty(cell, id_T);
|
||||
auto tmux = ctx->get_cell_pinmux(cell, id_T);
|
||||
bool is_input = false, is_output = false;
|
||||
if (t == nullptr || t->name == ctx->id("$PACKER_VCC_NET")) {
|
||||
is_input = true;
|
||||
} else if (t->name == ctx->id("$PACKER_GND_NET")) {
|
||||
if (tmux == PINMUX_0) {
|
||||
is_output = true;
|
||||
} else if (tmux == PINMUX_1 || t == nullptr) {
|
||||
is_input = true;
|
||||
}
|
||||
const char *iodir = is_input ? "INPUT" : (is_output ? "OUTPUT" : "BIDIR");
|
||||
write_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS18H").c_str()));
|
||||
write_ioattr(cell, "PULLMODE", "NONE");
|
||||
pop(3);
|
||||
}
|
||||
void write_osc(const CellInfo *cell)
|
||||
|
101
nexus/pack.cc
101
nexus/pack.cc
@ -502,7 +502,7 @@ struct NexusPacker
|
||||
// Might have an output buffer (OB etc) connected to it
|
||||
is_npnr_iob = true;
|
||||
NetInfo *i = get_net_or_empty(ci, id_I);
|
||||
if (i == nullptr && i->driver.cell != nullptr) {
|
||||
if (i != nullptr && i->driver.cell != nullptr) {
|
||||
if (top_port.cell != nullptr)
|
||||
log_error("Top level '%s' has multiple input/output buffers\n", ctx->nameOf(port.first));
|
||||
top_port = i->driver;
|
||||
@ -531,21 +531,104 @@ struct NexusPacker
|
||||
}
|
||||
}
|
||||
|
||||
BelId get_io_bel(CellInfo *ci)
|
||||
{
|
||||
if (!ci->attrs.count(id_BEL))
|
||||
return BelId();
|
||||
return ctx->getBelByName(ctx->id(ci->attrs.at(id_BEL).as_string()));
|
||||
}
|
||||
|
||||
void pack_io()
|
||||
{
|
||||
std::unordered_set<IdString> iob_types = {id_IB, id_OB, id_OBZ, id_BB,
|
||||
id_BB_I3C_A, id_SEIO33, id_SEIO18, id_DIFFIO18,
|
||||
id_SEIO33_CORE, id_SEIO18_CORE, id_DIFFIO18_CORE};
|
||||
|
||||
std::unordered_map<IdString, XFormRule> io_rules;
|
||||
|
||||
// For the low level primitives, make sure we always preserve their type
|
||||
io_rules[id_SEIO33_CORE].new_type = id_SEIO33_CORE;
|
||||
io_rules[id_SEIO18_CORE].new_type = id_SEIO18_CORE;
|
||||
io_rules[id_DIFFIO18_CORE].new_type = id_DIFFIO18_CORE;
|
||||
|
||||
// Some IO buffer types need a bit of pin renaming, too
|
||||
io_rules[id_SEIO33].new_type = id_SEIO33_CORE;
|
||||
io_rules[id_SEIO33].port_xform[id_PADDI] = id_O;
|
||||
io_rules[id_SEIO33].port_xform[id_PADDO] = id_I;
|
||||
io_rules[id_SEIO33].port_xform[id_PADDT] = id_T;
|
||||
io_rules[id_SEIO33].port_xform[id_IOPAD] = id_B;
|
||||
|
||||
io_rules[id_BB_I3C_A] = io_rules[id_SEIO33];
|
||||
|
||||
io_rules[id_SEIO18] = io_rules[id_SEIO33];
|
||||
io_rules[id_SEIO18].new_type = id_SEIO18_CORE;
|
||||
|
||||
io_rules[id_DIFFIO18] = io_rules[id_SEIO33];
|
||||
io_rules[id_DIFFIO18].new_type = id_DIFFIO18_CORE;
|
||||
|
||||
// Stage 0: deal with top level inserted IO buffers
|
||||
prepare_io();
|
||||
|
||||
// Stage 1: setup constraints
|
||||
for (auto cell : sorted(ctx->cells)) {
|
||||
CellInfo *ci = cell.second;
|
||||
if (ci->type == id_SEIO33_CORE || ci->type == id_SEIO18_CORE) {
|
||||
auto fnd_loc = ci->attrs.find(id_LOC);
|
||||
if (fnd_loc == ci->attrs.end())
|
||||
continue;
|
||||
BelId bel = ctx->get_pin_bel(fnd_loc->second.as_string());
|
||||
if (bel == BelId())
|
||||
log_error("cannot constrain IO '%s', no PIO pin named '%s'\n", ctx->nameOf(ci),
|
||||
fnd_loc->second.as_string().c_str());
|
||||
// Iterate through all IO buffer primitives
|
||||
if (!iob_types.count(ci->type))
|
||||
continue;
|
||||
// We need all IO constrained so we can pick the right IO bel type
|
||||
// An improvement would be to allocate unconstrained IO here
|
||||
if (!ci->attrs.count(id_LOC))
|
||||
log_error("Found unconstrained IO '%s', these are currently unsupported\n", ctx->nameOf(ci));
|
||||
// Convert package pin constraint to bel constraint
|
||||
std::string loc = ci->attrs.at(id_LOC).as_string();
|
||||
auto pad_info = ctx->get_pkg_pin_data(loc);
|
||||
if (pad_info == nullptr)
|
||||
log_error("IO '%s' is constrained to invalid pin '%s'\n", ctx->nameOf(ci), loc.c_str());
|
||||
auto func = ctx->get_pad_functions(pad_info);
|
||||
BelId bel = ctx->get_pad_pio_bel(pad_info);
|
||||
|
||||
if (bel == BelId()) {
|
||||
log_error("IO '%s' is constrained to pin %s (%s) which is not a general purpose IO pin.\n",
|
||||
ctx->nameOf(ci), loc.c_str(), func.c_str());
|
||||
} else {
|
||||
|
||||
// Get IO type for reporting purposes
|
||||
std::string io_type = str_or_default(ci->attrs, id_IO_TYPE, "LVCMOS33");
|
||||
|
||||
log_info("Constraining %s IO '%s' to pin %s (%s%sbel %s)\n", io_type.c_str(), ctx->nameOf(ci),
|
||||
loc.c_str(), func.c_str(), func.empty() ? "" : "; ", ctx->nameOfBel(bel));
|
||||
ci->attrs[id_BEL] = ctx->getBelName(bel).str(ctx);
|
||||
}
|
||||
}
|
||||
// Stage 2: apply rules for primitives that need them
|
||||
generic_xform(io_rules, false);
|
||||
// Stage 3: all other IO primitives become their bel type
|
||||
for (auto cell : sorted(ctx->cells)) {
|
||||
CellInfo *ci = cell.second;
|
||||
// Iterate through all IO buffer primitives
|
||||
if (!iob_types.count(ci->type))
|
||||
continue;
|
||||
// Skip those dealt with in stage 2
|
||||
if (io_rules.count(ci->type))
|
||||
continue;
|
||||
// For non-bidirectional IO, we also need to configure tristate and rename B
|
||||
if (ci->type == id_IB) {
|
||||
ctx->set_cell_pinmux(ci, id_T, PINMUX_1);
|
||||
rename_port(ctx, ci, id_I, id_B);
|
||||
} else if (ci->type == id_OB) {
|
||||
ctx->set_cell_pinmux(ci, id_T, PINMUX_0);
|
||||
rename_port(ctx, ci, id_O, id_B);
|
||||
} else if (ci->type == id_OBZ) {
|
||||
ctx->set_cell_pinmux(ci, id_T, PINMUX_SIG);
|
||||
rename_port(ctx, ci, id_O, id_B);
|
||||
}
|
||||
// Get the IO bel
|
||||
BelId bel = get_io_bel(ci);
|
||||
// Set the cell type to the bel type
|
||||
IdString type = ctx->getBelType(bel);
|
||||
NPNR_ASSERT(type != IdString());
|
||||
ci->type = type;
|
||||
}
|
||||
}
|
||||
|
||||
explicit NexusPacker(Context *ctx) : ctx(ctx) {}
|
||||
|
@ -304,8 +304,12 @@ struct PDCParser
|
||||
if (!val.is_string)
|
||||
log_error("expecting string argument to -iobuf (line %d)\n", lineno);
|
||||
std::stringstream ss(val.str);
|
||||
std::string k, v;
|
||||
while (ss >> k >> v) {
|
||||
std::string kv;
|
||||
while (ss >> kv) {
|
||||
auto eqp = kv.find('=');
|
||||
if (eqp == std::string::npos)
|
||||
log_error("expected key-value pair separated by '=' (line %d)", lineno);
|
||||
std::string k = kv.substr(0, eqp), v = kv.substr(eqp + 1);
|
||||
args[ctx->id(k)] = v;
|
||||
}
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user