mirror of https://github.com/YosysHQ/nextpnr.git
nexus: Initial support for DDR3 memory interfaces
Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
parent
ddc6c8c8b9
commit
c2cbdc7efa
|
|
@ -962,6 +962,26 @@ std::string Arch::get_pad_functions(const PadInfoPOD *pad) const
|
|||
return s;
|
||||
}
|
||||
|
||||
BelId Arch::get_bank_vref_io_bel(int bank, int vref) const
|
||||
{
|
||||
IdString func = idf("VREF%d_%d", bank, vref);
|
||||
for (auto &pad : chip_info->pads) {
|
||||
if (pad.bank != bank)
|
||||
continue;
|
||||
bool found = false;
|
||||
for (auto f : pad.func_strs) {
|
||||
if (IdString(f) == func) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
continue;
|
||||
return get_pad_pio_bel(&pad);
|
||||
}
|
||||
return BelId();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
// Helper for cell timing lookups
|
||||
|
|
|
|||
|
|
@ -1501,6 +1501,7 @@ struct Arch : BaseArch<ArchRanges>
|
|||
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;
|
||||
BelId get_bank_vref_io_bel(int bank, int vref) const;
|
||||
|
||||
// -------------------------------------------------
|
||||
// Data about different IO standard, mostly used by bitgen
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ X(CIB_LR)
|
|||
|
||||
X(IO_TYPE)
|
||||
X(SLEWRATE)
|
||||
|
||||
X(VREF)
|
||||
|
||||
X(OSCA)
|
||||
X(OSC)
|
||||
|
|
@ -534,6 +534,10 @@ X(STOP)
|
|||
X(ECLKIN)
|
||||
X(DIVOUT)
|
||||
X(ECLKOUT)
|
||||
X(TESTINP0)
|
||||
X(TESTINP1)
|
||||
X(TESTINP2)
|
||||
X(TESTINP3)
|
||||
|
||||
X(IDDRX1)
|
||||
X(ODDRX1)
|
||||
|
|
@ -559,6 +563,56 @@ X(TSHX2DQS)
|
|||
X(TSHX4DQ)
|
||||
X(TSHX4DQS)
|
||||
|
||||
X(DQSBUF)
|
||||
X(DQSBUF_CORE)
|
||||
X(DDRDLL)
|
||||
X(DDRDLL_CORE)
|
||||
|
||||
X(ENABLE_FIFO)
|
||||
X(FORCE_READ)
|
||||
X(FREE_WHEEL)
|
||||
X(MODX)
|
||||
X(MT_EN_READ)
|
||||
X(MT_EN_WRITE)
|
||||
X(MT_EN_WRITE_LEVELING)
|
||||
X(RD_PNTR)
|
||||
X(WR_PNTR)
|
||||
X(READ_ENABLE)
|
||||
X(RX_CENTERED)
|
||||
X(S_READ)
|
||||
X(S_WRITE)
|
||||
X(SIGN_READ)
|
||||
X(SIGN_WRITE)
|
||||
X(UPDATE_QU)
|
||||
X(WRITE_ENABLE)
|
||||
X(SEL_READ_BIT_ENABLE_CYCLES)
|
||||
X(BYPASS_WR_LEVEL_SMTH_LATCH)
|
||||
X(BYPASS_WR_SMTH_LATCH)
|
||||
X(BYPASS_READ_SMTH_LATCH)
|
||||
|
||||
X(DQSI)
|
||||
X(PAUSE)
|
||||
X(RDCLKSEL3)
|
||||
X(RDCLKSEL2)
|
||||
X(RDCLKSEL1)
|
||||
X(RDCLKSEL0)
|
||||
X(RDDIR)
|
||||
X(RDLOADN)
|
||||
X(READ3)
|
||||
X(READ2)
|
||||
X(READ1)
|
||||
X(READ0)
|
||||
X(READCOUT)
|
||||
X(READMOVE)
|
||||
X(SELCLK)
|
||||
X(WRDIR)
|
||||
X(WRLOAD_N)
|
||||
X(WRLVDIR)
|
||||
X(WRLVLOAD_N)
|
||||
X(WRLVMOVE)
|
||||
X(WRMOVE)
|
||||
X(RSTSMCNT)
|
||||
|
||||
X(TXDATA0)
|
||||
X(TXDATA1)
|
||||
X(TXDATA2)
|
||||
|
|
@ -614,6 +668,12 @@ X(T2)
|
|||
X(T3)
|
||||
X(SCLK)
|
||||
|
||||
X(ENA_ROUNDOFF)
|
||||
X(FORCE_MAX_DELAY)
|
||||
X(CLKIN)
|
||||
X(FREEZE)
|
||||
X(UDDCNTL_N)
|
||||
|
||||
X(ALIGNWD)
|
||||
X(DQSR90)
|
||||
X(DQSW)
|
||||
|
|
|
|||
100
nexus/fasm.cc
100
nexus/fasm.cc
|
|
@ -412,6 +412,7 @@ struct NexusFasmWriter
|
|||
bool lvds_used = false;
|
||||
bool slvs_used = false;
|
||||
bool dphy_used = false;
|
||||
std::array<bool, 2> vref_used{false, false};
|
||||
};
|
||||
|
||||
std::map<int, BankConfig> bank_cfg;
|
||||
|
|
@ -459,6 +460,13 @@ struct NexusFasmWriter
|
|||
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");
|
||||
write_ioattr(cell, "VREF");
|
||||
auto vref_cfg = str_or_default(cell->attrs, id_VREF, "");
|
||||
if (vref_cfg == "VREF1_LOAD") {
|
||||
bank.vref_used[0] = true;
|
||||
} else if (vref_cfg == "VREF2_LOAD") {
|
||||
bank.vref_used[0] = true;
|
||||
}
|
||||
write_ioattr(cell, "SLEWRATE", str_or_default(cell->attrs, id_SLEWRATE, "MED").c_str());
|
||||
pop();
|
||||
write_cell_muxes(cell);
|
||||
|
|
@ -586,6 +594,59 @@ struct NexusFasmWriter
|
|||
write_enum(cell, "GSR", "DISABLED");
|
||||
pop();
|
||||
}
|
||||
// Write config for ECLKSYNC
|
||||
void write_eclksync(const CellInfo *cell)
|
||||
{
|
||||
BelId bel = cell->bel;
|
||||
push_bel(bel);
|
||||
write_enum(cell, "STOP_EN");
|
||||
pop();
|
||||
}
|
||||
// Write config for DQSBUF
|
||||
void write_dqsbuf(const CellInfo *cell)
|
||||
{
|
||||
BelId bel = cell->bel;
|
||||
push_bel(bel);
|
||||
write_enum(cell, "ENABLE_FIFO");
|
||||
write_enum(cell, "GSR", "DISABLED");
|
||||
write_enum(cell, "ENABLE_FIFO");
|
||||
write_enum(cell, "FORCE_READ");
|
||||
write_enum(cell, "FREE_WHEEL");
|
||||
write_enum(cell, "MODX");
|
||||
write_enum(cell, "MT_EN_READ");
|
||||
write_enum(cell, "MT_EN_WRITE");
|
||||
write_enum(cell, "MT_EN_WRITE_LEVELING");
|
||||
write_enum(cell, "READ_ENABLE");
|
||||
write_enum(cell, "RX_CENTERED", "ENABLED");
|
||||
// write_enum(cell, "SIGN_READ", "POSITIVE");
|
||||
write_bit("SIGN_READ.COMPLEMENT");
|
||||
write_enum(cell, "SIGN_WRITE", "POSITIVE");
|
||||
write_enum(cell, "UPDATE_QU", "UP1_AND_UP0_SAME");
|
||||
write_enum(cell, "WRITE_ENABLE");
|
||||
write_enum(cell, "SEL_READ_BIT_ENABLE_CYCLES", "NORMAL");
|
||||
write_enum(cell, "BYPASS_WR_LEVEL_SMTH_LATCH", "SMOOTHING_PATH");
|
||||
write_enum(cell, "BYPASS_WR_SMTH_LATCH", "SMOOTHING_PATH");
|
||||
write_enum(cell, "BYPASS_READ_SMTH_LATCH", "SMOOTHING_PATH");
|
||||
write_bit("RSTMUX.RST"); // TODO: pins config
|
||||
write_bit("RSTSMCNTMUX.RSTSMCNT");
|
||||
write_bit("PAUSEMUX.PAUSE");
|
||||
// TODO: S_READ, S_WRITE: where do these magic numbers come from?
|
||||
write_bit("S_WRITE[8:0] = 9'b000100100");
|
||||
write_bit("S_READ[8:0] = 9'b110001010");
|
||||
|
||||
pop();
|
||||
}
|
||||
void write_ddrdll(const CellInfo *cell)
|
||||
{
|
||||
BelId bel = cell->bel;
|
||||
push_bel(bel);
|
||||
write_enum(cell, "ENA_OSC", "ENABLED");
|
||||
write_enum(cell, "GSR", "DISABLED");
|
||||
write_enum(cell, "ENA_ROUNDOFF", "ENABLED");
|
||||
write_enum(cell, "FORCE_MAX_DELAY");
|
||||
write_bit("RSTMUX.RST"); // TODO: pins config
|
||||
pop();
|
||||
}
|
||||
// Write config for an OXIDE_EBR cell
|
||||
void write_bram(const CellInfo *cell)
|
||||
{
|
||||
|
|
@ -684,6 +745,7 @@ struct NexusFasmWriter
|
|||
if (cell->type == id_IOLOGIC) {
|
||||
write_enum(cell, "IDDRXN.DDRMODE");
|
||||
write_enum(cell, "ODDRXN.DDRMODE");
|
||||
write_enum(cell, "MIDDRXN.DDRMODE");
|
||||
write_enum(cell, "MODDRXN.DDRMODE");
|
||||
write_enum(cell, "MTDDRXN.DDRMODE");
|
||||
}
|
||||
|
|
@ -1092,7 +1154,12 @@ struct NexusFasmWriter
|
|||
write_bit("LVDS_IO.ON", bank.lvds_used);
|
||||
write_bit("SLVS_IO.ON", bank.slvs_used);
|
||||
write_bit("MIPI_DPHY_IO.ON", bank.dphy_used);
|
||||
|
||||
for (int vref = 1; vref <= 2; vref++) {
|
||||
if (!bank.vref_used[vref - 1])
|
||||
continue;
|
||||
write_bit(stringf("VREF%d_USED.ON", vref));
|
||||
write_bit("REF_IO.ON");
|
||||
}
|
||||
pop();
|
||||
} else {
|
||||
if (is_lifcl_17 && (i != 0) && (i != 1))
|
||||
|
|
@ -1105,6 +1172,27 @@ struct NexusFasmWriter
|
|||
}
|
||||
}
|
||||
blank();
|
||||
for (int i = 3; i <= 5; i++) {
|
||||
auto &bank = bank_cfg[i];
|
||||
for (int vref = 1; vref <= 2; vref++) {
|
||||
if (!bank.vref_used[vref - 1])
|
||||
continue;
|
||||
// Set VREF pin to VREFn_DRIVER
|
||||
BelId vref_pio = ctx->get_bank_vref_io_bel(i, vref);
|
||||
if (vref_pio == BelId())
|
||||
log_error("Unable to find pad for bank %d VREF%d\n", i, vref);
|
||||
if (!ctx->checkBelAvail(vref_pio))
|
||||
log_error("IO %s is required for bank %d VREF%d but is occupied by cell '%s'.\n",
|
||||
ctx->nameOfBel(vref_pio), i, vref, ctx->nameOf(ctx->getBoundBelCell(vref_pio)));
|
||||
used_io.insert(vref_pio);
|
||||
push_bel(vref_pio);
|
||||
push("SEIO18");
|
||||
write_bit(stringf("VREF%d_DRIVER.ON", vref));
|
||||
pop();
|
||||
pop();
|
||||
blank();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Write out FASM for the whole design
|
||||
void operator()()
|
||||
|
|
@ -1160,14 +1248,20 @@ struct NexusFasmWriter
|
|||
write_cfg_clkrst(ci);
|
||||
else if (ci->type == id_ECLKDIV_CORE)
|
||||
write_eclkdiv(ci);
|
||||
else if (ci->type == id_ECLKSYNC_CORE)
|
||||
write_eclksync(ci);
|
||||
else if (ci->type == id_DQSBUF_CORE)
|
||||
write_dqsbuf(ci);
|
||||
else if (ci->type == id_DDRDLL_CORE)
|
||||
write_ddrdll(ci);
|
||||
blank();
|
||||
}
|
||||
// Handle DCC route-throughs
|
||||
write_dcc_thru();
|
||||
// Write config for unused bels
|
||||
write_unused();
|
||||
// Write bank config
|
||||
write_bankcfg();
|
||||
// Write config for unused bels
|
||||
write_unused();
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
|
|
|||
155
nexus/pack.cc
155
nexus/pack.cc
|
|
@ -801,6 +801,9 @@ struct NexusPacker
|
|||
return true;
|
||||
}
|
||||
|
||||
if (strict_routing)
|
||||
return false;
|
||||
|
||||
// Unless in strict mode; check based on simple distance too
|
||||
BelId nearest = find_nearest_bel(pin_drv, cell->type, [&](BelId bel) { return !used_bels.count(bel); });
|
||||
|
||||
|
|
@ -1093,6 +1096,8 @@ struct NexusPacker
|
|||
{id_CONFIG_LMMI, id_CONFIG_LMMI_CORE},
|
||||
{id_ECLKSYNC, id_ECLKSYNC_CORE},
|
||||
{id_ECLKDIV, id_ECLKDIV_CORE},
|
||||
{id_DQSBUF, id_DQSBUF_CORE},
|
||||
{id_DDRDLL, id_DDRDLL_CORE},
|
||||
};
|
||||
|
||||
// extra prefix needed for this primitive for some reason
|
||||
|
|
@ -1233,11 +1238,11 @@ struct NexusPacker
|
|||
void pack_iodelay()
|
||||
{
|
||||
dict<IdString, XFormRule> base_iodelay_rules;
|
||||
base_iodelay_rules[id_DELAYB].new_type = id_IOLOGIC;
|
||||
|
||||
XFormRule delay_rule;
|
||||
delay_rule.param_xform[id_DEL_VALUE] = ctx->id("DELAY.DEL_VALUE");
|
||||
delay_rule.param_xform[id_COARSE_DELAY] = ctx->id("DELAY.COARSE_DELAY");
|
||||
delay_rule.new_type = id_IOLOGIC;
|
||||
|
||||
base_iodelay_rules[id_DELAYB] = delay_rule;
|
||||
base_iodelay_rules[id_DELAYA] = delay_rule;
|
||||
|
|
@ -1330,6 +1335,64 @@ struct NexusPacker
|
|||
iol_rules[itype].port_xform[ctx->idf("Q%d", i)] = ctx->idf("RXDATA%d", (10 - 2 * gear) + i);
|
||||
}
|
||||
|
||||
for (int gear : {2, 4}) {
|
||||
IdString oshxtype = ctx->idf("OSHX%d", gear);
|
||||
iol_rules[oshxtype].new_type = id_IOLOGIC;
|
||||
iol_rules[oshxtype].set_params.emplace_back(id_MODE, std::string("MIDDRXN_MODDRXN"));
|
||||
iol_rules[oshxtype].set_params.emplace_back(ctx->id("MODDRXN.DDRMODE"), stringf("MOSHX%d", gear));
|
||||
iol_rules[oshxtype].port_xform[id_ECLK] = id_ECLK;
|
||||
iol_rules[oshxtype].port_xform[id_SCLK] = id_SCLKOUT;
|
||||
iol_rules[oshxtype].port_xform[id_RST] = id_LSROUT;
|
||||
iol_rules[oshxtype].port_xform[id_Q] = id_DOUT;
|
||||
for (int i = 0; i < gear; i++)
|
||||
iol_rules[oshxtype].port_xform[ctx->idf("D%d", i)] = ctx->idf("TXDATA%d", i);
|
||||
|
||||
for (std::string dq : {"DQ", "DQS"}) {
|
||||
IdString tshxtype = ctx->idf("TSHX%d%s", gear, dq.c_str());
|
||||
iol_rules[tshxtype].new_type = id_IOLOGIC;
|
||||
iol_rules[tshxtype].set_params.emplace_back(id_MODE, std::string("MIDDRXN_MODDRXN"));
|
||||
iol_rules[tshxtype].set_params.emplace_back(ctx->id("MTDDRXN.DDRMODE"), stringf("MTSHX%d", gear));
|
||||
iol_rules[tshxtype].port_xform[id_ECLK] = id_ECLK;
|
||||
iol_rules[tshxtype].port_xform[id_SCLK] = id_SCLKOUT;
|
||||
iol_rules[tshxtype].port_xform[id_RST] = id_LSROUT;
|
||||
iol_rules[tshxtype].port_xform[id_Q] = id_TOUT;
|
||||
if (dq == "DQS")
|
||||
iol_rules[tshxtype].port_xform[id_DQSW] = id_DQSW;
|
||||
else
|
||||
iol_rules[tshxtype].port_xform[id_DQSW270] = id_DQSW270;
|
||||
for (int i = 0; i < gear; i++)
|
||||
iol_rules[tshxtype].port_xform[ctx->idf("T%d", i)] = ctx->idf("TSDATA%d", i);
|
||||
|
||||
IdString otype = ctx->idf("ODDRX%d%s", gear, dq.c_str());
|
||||
iol_rules[otype].new_type = id_IOLOGIC;
|
||||
iol_rules[otype].set_params.emplace_back(id_MODE, std::string("MIDDRXN_MODDRXN"));
|
||||
iol_rules[otype].set_params.emplace_back(ctx->id("MODDRXN.DDRMODE"),
|
||||
stringf("MODDRX%d_DQSW%s", gear, (dq == "DQS") ? "" : "270"));
|
||||
iol_rules[otype].port_xform[id_ECLK] = id_ECLK;
|
||||
iol_rules[otype].port_xform[id_SCLK] = id_SCLKOUT;
|
||||
iol_rules[otype].port_xform[id_RST] = id_LSROUT;
|
||||
iol_rules[otype].port_xform[id_Q] = id_DOUT;
|
||||
if (dq == "DQS")
|
||||
iol_rules[otype].port_xform[id_DQSW] = id_DQSW;
|
||||
else
|
||||
iol_rules[otype].port_xform[id_DQSW270] = id_DQSW270;
|
||||
for (int i = 0; i < 2 * gear; i++)
|
||||
iol_rules[otype].port_xform[ctx->idf("D%d", i)] = ctx->idf("TXDATA%d", i);
|
||||
}
|
||||
|
||||
IdString itype = ctx->idf("IDDRX%dDQ", gear);
|
||||
iol_rules[itype].new_type = id_IOLOGIC;
|
||||
iol_rules[itype].set_params.emplace_back(id_MODE, std::string("MIDDRXN_MODDRXN"));
|
||||
iol_rules[itype].set_params.emplace_back(ctx->id("MIDDRXN.DDRMODE"), stringf("MIDDRX%d", gear));
|
||||
iol_rules[itype].port_xform[id_ECLK] = id_ECLK;
|
||||
iol_rules[itype].port_xform[id_SCLK] = id_SCLKIN;
|
||||
iol_rules[itype].port_xform[id_RST] = id_LSRIN;
|
||||
iol_rules[itype].port_xform[id_DQSR90] = id_DQSR90;
|
||||
iol_rules[itype].port_xform[id_D] = id_DI;
|
||||
for (int i = 0; i < 2 * gear; i++)
|
||||
iol_rules[itype].port_xform[ctx->idf("Q%d", i)] = ctx->idf("RXDATA%d", (10 - 2 * gear) + i);
|
||||
}
|
||||
|
||||
generic_xform(iol_rules, true);
|
||||
}
|
||||
|
||||
|
|
@ -1341,6 +1404,18 @@ struct NexusPacker
|
|||
base->params[param.first] = param.second;
|
||||
}
|
||||
for (auto &port : mergee->ports) {
|
||||
if (base->getPort(port.first)) {
|
||||
if (!port.second.net)
|
||||
continue;
|
||||
if (base->getPort(port.first) != port.second.net) {
|
||||
log_error("Conflict between IOLOGIC cells '%s' and '%s' on port '%s': nets '%s' and '%s'\n",
|
||||
ctx->nameOf(base), ctx->nameOf(mergee), ctx->nameOf(port.first),
|
||||
ctx->nameOf(base->getPort(port.first)), ctx->nameOf(port.second.net));
|
||||
continue;
|
||||
}
|
||||
mergee->disconnectPort(port.first);
|
||||
continue;
|
||||
}
|
||||
mergee->movePortTo(port.first, base, port.first);
|
||||
}
|
||||
ctx->cells.erase(mergee->name);
|
||||
|
|
@ -1386,7 +1461,9 @@ struct NexusPacker
|
|||
// Deal with interconnectivity
|
||||
if (is_output) {
|
||||
// Configure delay for use with DDR
|
||||
logic_iol->params[id_DELAYMUX] = std::string("OUT_REG");
|
||||
std::string mode = str_or_default(logic_iol->params, id_MODE);
|
||||
if (mode != "IDDRXN" && mode != "ODDRXN" && mode != "MIDDRXN_MODDRXN")
|
||||
logic_iol->params[id_DELAYMUX] = std::string("OUT_REG");
|
||||
|
||||
NetInfo *out_net = delay_iol->getPort(id_DOUT);
|
||||
delay_iol->disconnectPort(id_DOUT);
|
||||
|
|
@ -2566,7 +2643,8 @@ struct NexusPacker
|
|||
{
|
||||
auto buffer =
|
||||
insert_buffer(eclk, id_ECLKSYNC_CORE, "edge_clk", id_ECLKIN, id_ECLKOUT, [&](const PortRef &port) {
|
||||
return port.port.in(id_ECLK, id_ECLKIN); // TODO: DDRDLL
|
||||
return port.port.in(id_ECLK, id_ECLKIN) ||
|
||||
(port.cell->type == id_DDRDLL_CORE && port.port == id_CLKIN);
|
||||
});
|
||||
buffer->addInput(id_STOP);
|
||||
buffer->connectPort(id_STOP, gnd_net);
|
||||
|
|
@ -2589,6 +2667,31 @@ struct NexusPacker
|
|||
bank_eclkdiv[bank].insert(bel);
|
||||
}
|
||||
dict<IdString, int> eclk_bank;
|
||||
// Prioritise DQSBUF in case an ECLK crosses banks
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo *ci = cell.second.get();
|
||||
if (ci->type != id_DQSBUF_CORE)
|
||||
continue;
|
||||
|
||||
NetInfo *eclk = ci->getPort(id_ECLKIN);
|
||||
if (!eclk)
|
||||
continue;
|
||||
if (eclk_bank.count(eclk->name))
|
||||
continue;
|
||||
|
||||
NetInfo *dqsi = ci->getPort(id_DQSI);
|
||||
if (!dqsi || !dqsi->driver.cell)
|
||||
continue;
|
||||
CellInfo *pio = dqsi->driver.cell;
|
||||
if (!pio->type.in(id_DIFFIO18_CORE, id_SEIO18_CORE))
|
||||
continue;
|
||||
BelId pio_bel = ctx->getBelByNameStr(pio->attrs.at(id_BEL).as_string());
|
||||
Loc pad_loc = ctx->getBelLocation(pio_bel);
|
||||
pad_loc.z = 0;
|
||||
int bank = ctx->get_bel_pad(ctx->getBelByLocation(pad_loc))->bank;
|
||||
eclk_bank[eclk->name] = bank;
|
||||
}
|
||||
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo *iol = cell.second.get();
|
||||
if (iol->type != id_IOLOGIC)
|
||||
|
|
@ -2622,7 +2725,7 @@ struct NexusPacker
|
|||
log_error("Unable to place ECLKSYNC '%s': none remaining for bank %d.\n", ctx->nameOf(eclksync),
|
||||
eclk_pair.second);
|
||||
BelId eclksync_bel = *(eclksyncs.begin());
|
||||
ctx->bindBel(eclksync_bel, eclksync, STRENGTH_LOCKED);
|
||||
eclksync->attrs[id_BEL] = ctx->getBelName(eclksync_bel).str(ctx);
|
||||
log_info(" placed ECLKSYNC '%s' at bel '%s'\n", ctx->nameOf(eclksync), ctx->nameOfBel(eclksync_bel));
|
||||
eclksyncs.erase(eclksync_bel);
|
||||
|
||||
|
|
@ -2635,11 +2738,52 @@ struct NexusPacker
|
|||
log_error("Unable to place ECLKDIV '%s': none remaining for bank %d.\n", ctx->nameOf(usr.cell),
|
||||
eclk_pair.second);
|
||||
BelId eclkdiv_bel = *(eclkdivs.begin());
|
||||
ctx->bindBel(eclkdiv_bel, usr.cell, STRENGTH_LOCKED);
|
||||
usr.cell->attrs[id_BEL] = ctx->getBelName(eclkdiv_bel).str(ctx);
|
||||
log_info(" placed ECLKDIV '%s' at bel '%s'\n", ctx->nameOf(usr.cell), ctx->nameOfBel(eclkdiv_bel));
|
||||
eclkdivs.erase(eclkdiv_bel);
|
||||
}
|
||||
}
|
||||
// Place DDRDLL based on ECLK
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo *ddrdll = cell.second.get();
|
||||
if (ddrdll->type != id_DDRDLL_CORE)
|
||||
continue;
|
||||
bool placed = preplace_prim(ddrdll, id_CLKIN, true);
|
||||
if (!placed)
|
||||
log_error("Failed to find a placement for DDRDLL '%s'.\n", ctx->nameOf(ddrdll));
|
||||
}
|
||||
}
|
||||
|
||||
void place_dqsbuf()
|
||||
{
|
||||
log_info("Placing DQSBUFs...\n");
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo *ci = cell.second.get();
|
||||
if (ci->type != id_DQSBUF_CORE)
|
||||
continue;
|
||||
NetInfo *dqsi = ci->getPort(id_DQSI);
|
||||
if (!dqsi || !dqsi->driver.cell)
|
||||
log_error("DQSBUF '%s' has disconnected DQSI.\n", ctx->nameOf(ci));
|
||||
CellInfo *pio = dqsi->driver.cell;
|
||||
if (!pio->type.in(id_DIFFIO18_CORE, id_SEIO18_CORE))
|
||||
log_error("DQSBUF '%s' has DQSI driven by illegal cell '%s'.\n", ctx->nameOf(ci), ctx->nameOf(pio));
|
||||
BelId pio_bel = ctx->getBelByNameStr(pio->attrs.at(id_BEL).as_string());
|
||||
WireId pio_wire = ctx->getBelPinWire(pio_bel, dqsi->driver.port);
|
||||
BelId dqsbuf_bel = BelId();
|
||||
for (PipId pip : ctx->getPipsDownhill(pio_wire)) {
|
||||
for (BelPin bp : ctx->getWireBelPins(ctx->getPipDstWire(pip))) {
|
||||
if (ctx->getBelType(bp.bel) == id_DQSBUF_CORE && bp.pin == id_DQSI) {
|
||||
dqsbuf_bel = bp.bel;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
done:
|
||||
if (dqsbuf_bel == BelId())
|
||||
log_error("Failed to find a placement for DQSBUF '%s'.\n", ctx->nameOf(ci));
|
||||
log_info(" constraining DQSBUF '%s' to bel '%s'\n", ctx->nameOf(ci), ctx->nameOfBel(dqsbuf_bel));
|
||||
ci->attrs[id_BEL] = ctx->getBelName(dqsbuf_bel).str(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
FFControlSet gather_ff_settings(CellInfo *cell)
|
||||
|
|
@ -2824,6 +2968,7 @@ struct NexusPacker
|
|||
pack_ip();
|
||||
handle_iologic();
|
||||
|
||||
place_dqsbuf();
|
||||
place_eclk();
|
||||
|
||||
if (!bool_or_default(ctx->settings, id_no_pack_lutff)) {
|
||||
|
|
|
|||
|
|
@ -269,6 +269,11 @@ static const dict<IdString, Arch::CellPinsData> base_cell_pin_data = {
|
|||
{
|
||||
{{id_ECLKIN}, PINSTYLE_DEDI},
|
||||
{{id_DIVOUT}, PINSTYLE_DEDI},
|
||||
{{id_TESTINP0}, PINSTYLE_PU},
|
||||
{{id_TESTINP1}, PINSTYLE_PU},
|
||||
{{id_TESTINP2}, PINSTYLE_PU},
|
||||
{{id_TESTINP3}, PINSTYLE_PU},
|
||||
|
||||
{{}, PINSTYLE_CIB},
|
||||
}},
|
||||
{id_ECLKSYNC_CORE,
|
||||
|
|
|
|||
Loading…
Reference in New Issue