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)));
|
IdString param = id(stringf("%sMUX", pin.c_str(this)));
|
||||||
auto fnd_param = cell->params.find(param);
|
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++) {
|
for (size_t i = 0; i < chip_info->num_pads; i++) {
|
||||||
const PadInfoPOD *pad = &(chip_info->pads[i]);
|
const PadInfoPOD *pad = &(chip_info->pads[i]);
|
||||||
@ -537,9 +537,8 @@ Loc Arch::get_pad_loc(const PadInfoPOD *pad) const
|
|||||||
return loc;
|
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)
|
if (pad == nullptr)
|
||||||
return BelId();
|
return BelId();
|
||||||
return getBelByLocation(get_pad_loc(pad));
|
return getBelByLocation(get_pad_loc(pad));
|
||||||
@ -574,6 +573,17 @@ const PadInfoPOD *Arch::get_bel_pad(BelId bel) const
|
|||||||
return nullptr;
|
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
|
#ifdef WITH_HEAP
|
||||||
|
@ -1429,15 +1429,16 @@ struct Arch : BaseCtx
|
|||||||
|
|
||||||
bool nexus_logic_tile_valid(LogicTileStatus <s) const;
|
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);
|
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;
|
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;
|
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(INLP)
|
||||||
X(INADC)
|
X(INADC)
|
||||||
|
|
||||||
|
X(DIFFIO18_CORE)
|
||||||
|
X(HSRXEN)
|
||||||
|
X(HSTXEN)
|
||||||
|
|
||||||
X(LUT4)
|
X(LUT4)
|
||||||
X(INIT)
|
X(INIT)
|
||||||
X(Z)
|
X(Z)
|
||||||
@ -68,7 +72,6 @@ X(INIT0)
|
|||||||
X(INIT1)
|
X(INIT1)
|
||||||
|
|
||||||
X(INV)
|
X(INV)
|
||||||
X(BB)
|
|
||||||
X(VHI)
|
X(VHI)
|
||||||
X(VLO)
|
X(VLO)
|
||||||
|
|
||||||
@ -138,3 +141,17 @@ X(RSTA)
|
|||||||
X(RSTB)
|
X(RSTB)
|
||||||
|
|
||||||
X(LOC)
|
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) {}
|
NexusFasmWriter(const Context *ctx, std::ostream &out) : ctx(ctx), out(out) {}
|
||||||
std::string tile_name(int loc, const PhysicalTileInfoPOD &tile)
|
std::string tile_name(int loc, const PhysicalTileInfoPOD &tile)
|
||||||
{
|
{
|
||||||
@ -195,14 +206,16 @@ struct NexusFasmWriter
|
|||||||
push_tile(bel.tile);
|
push_tile(bel.tile);
|
||||||
push_belname(bel);
|
push_belname(bel);
|
||||||
const NetInfo *t = get_net_or_empty(cell, id_T);
|
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;
|
bool is_input = false, is_output = false;
|
||||||
if (t == nullptr || t->name == ctx->id("$PACKER_VCC_NET")) {
|
if (tmux == PINMUX_0) {
|
||||||
is_input = true;
|
|
||||||
} else if (t->name == ctx->id("$PACKER_GND_NET")) {
|
|
||||||
is_output = true;
|
is_output = true;
|
||||||
|
} else if (tmux == PINMUX_1 || t == nullptr) {
|
||||||
|
is_input = true;
|
||||||
}
|
}
|
||||||
const char *iodir = is_input ? "INPUT" : (is_output ? "OUTPUT" : "BIDIR");
|
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_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);
|
pop(2);
|
||||||
}
|
}
|
||||||
void write_io18(const CellInfo *cell)
|
void write_io18(const CellInfo *cell)
|
||||||
@ -212,14 +225,16 @@ struct NexusFasmWriter
|
|||||||
push_belname(bel);
|
push_belname(bel);
|
||||||
push("SEIO18");
|
push("SEIO18");
|
||||||
const NetInfo *t = get_net_or_empty(cell, id_T);
|
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;
|
bool is_input = false, is_output = false;
|
||||||
if (t == nullptr || t->name == ctx->id("$PACKER_VCC_NET")) {
|
if (tmux == PINMUX_0) {
|
||||||
is_input = true;
|
|
||||||
} else if (t->name == ctx->id("$PACKER_GND_NET")) {
|
|
||||||
is_output = true;
|
is_output = true;
|
||||||
|
} else if (tmux == PINMUX_1 || t == nullptr) {
|
||||||
|
is_input = true;
|
||||||
}
|
}
|
||||||
const char *iodir = is_input ? "INPUT" : (is_output ? "OUTPUT" : "BIDIR");
|
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_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);
|
pop(3);
|
||||||
}
|
}
|
||||||
void write_osc(const CellInfo *cell)
|
void write_osc(const CellInfo *cell)
|
||||||
|
@ -502,7 +502,7 @@ struct NexusPacker
|
|||||||
// Might have an output buffer (OB etc) connected to it
|
// Might have an output buffer (OB etc) connected to it
|
||||||
is_npnr_iob = true;
|
is_npnr_iob = true;
|
||||||
NetInfo *i = get_net_or_empty(ci, id_I);
|
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)
|
if (top_port.cell != nullptr)
|
||||||
log_error("Top level '%s' has multiple input/output buffers\n", ctx->nameOf(port.first));
|
log_error("Top level '%s' has multiple input/output buffers\n", ctx->nameOf(port.first));
|
||||||
top_port = i->driver;
|
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()
|
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)) {
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
CellInfo *ci = cell.second;
|
CellInfo *ci = cell.second;
|
||||||
if (ci->type == id_SEIO33_CORE || ci->type == id_SEIO18_CORE) {
|
// Iterate through all IO buffer primitives
|
||||||
auto fnd_loc = ci->attrs.find(id_LOC);
|
if (!iob_types.count(ci->type))
|
||||||
if (fnd_loc == ci->attrs.end())
|
|
||||||
continue;
|
continue;
|
||||||
BelId bel = ctx->get_pin_bel(fnd_loc->second.as_string());
|
// We need all IO constrained so we can pick the right IO bel type
|
||||||
if (bel == BelId())
|
// An improvement would be to allocate unconstrained IO here
|
||||||
log_error("cannot constrain IO '%s', no PIO pin named '%s'\n", ctx->nameOf(ci),
|
if (!ci->attrs.count(id_LOC))
|
||||||
fnd_loc->second.as_string().c_str());
|
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);
|
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) {}
|
explicit NexusPacker(Context *ctx) : ctx(ctx) {}
|
||||||
|
@ -304,8 +304,12 @@ struct PDCParser
|
|||||||
if (!val.is_string)
|
if (!val.is_string)
|
||||||
log_error("expecting string argument to -iobuf (line %d)\n", lineno);
|
log_error("expecting string argument to -iobuf (line %d)\n", lineno);
|
||||||
std::stringstream ss(val.str);
|
std::stringstream ss(val.str);
|
||||||
std::string k, v;
|
std::string kv;
|
||||||
while (ss >> k >> v) {
|
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;
|
args[ctx->id(k)] = v;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user