diff options
-rw-r--r-- | nexus/arch.cc | 18 | ||||
-rw-r--r-- | nexus/arch.h | 7 | ||||
-rw-r--r-- | nexus/constids.inc | 19 | ||||
-rw-r--r-- | nexus/fasm.cc | 27 | ||||
-rw-r--r-- | nexus/pack.cc | 101 | ||||
-rw-r--r-- | nexus/pdc.cc | 8 |
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 <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; // ------------------------------------------------- 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 { |