aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nexus/arch.cc18
-rw-r--r--nexus/arch.h7
-rw-r--r--nexus/constids.inc19
-rw-r--r--nexus/fasm.cc27
-rw-r--r--nexus/pack.cc101
-rw-r--r--nexus/pdc.cc8
6 files changed, 155 insertions, 25 deletions
diff --git a/nexus/arch.cc b/nexus/arch.cc
index f309c872..352f789a 100644
--- a/nexus/arch.cc
+++ b/nexus/arch.cc
@@ -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
diff --git a/nexus/arch.h b/nexus/arch.h
index 3beef3cd..2f84b25d 100644
--- a/nexus/arch.h
+++ b/nexus/arch.h
@@ -1429,15 +1429,16 @@ struct Arch : BaseCtx
bool nexus_logic_tile_valid(LogicTileStatus &lts) 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;
// -------------------------------------------------
diff --git a/nexus/constids.inc b/nexus/constids.inc
index 6bf32654..b4aa5077 100644
--- a/nexus/constids.inc
+++ b/nexus/constids.inc
@@ -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)
diff --git a/nexus/fasm.cc b/nexus/fasm.cc
index 0429e83a..e9a4572f 100644
--- a/nexus/fasm.cc
+++ b/nexus/fasm.cc
@@ -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)
diff --git a/nexus/pack.cc b/nexus/pack.cc
index 3b53ea55..0ef3eb79 100644
--- a/nexus/pack.cc
+++ b/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) {}
diff --git a/nexus/pdc.cc b/nexus/pdc.cc
index 60cc511c..98a44276 100644
--- a/nexus/pdc.cc
+++ b/nexus/pdc.cc
@@ -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 {