nexus: Add PLL support

Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
David Shah 2020-12-02 12:29:08 +00:00
parent b666c85824
commit 86e6a2225c
4 changed files with 213 additions and 3 deletions

View File

@ -375,3 +375,71 @@ X(SIGNED)
X(SUM0)
X(SUM1)
X(CINPUT)
X(PLL_CORE)
X(CLKOP)
X(CLKOS)
X(CLKOS2)
X(CLKOS3)
X(CLKOS4)
X(CLKOS5)
X(ENCLKOP)
X(ENCLKOS)
X(ENCLKOS2)
X(ENCLKOS3)
X(ENCLKOS4)
X(ENCLKOS5)
X(FBKCK)
X(LEGACY)
X(LMMICLK)
X(LMMIRESETN)
X(PLLRESET)
X(REFCK)
X(STDBY)
X(PLLPDN)
X(SCANRST)
X(SCANCLK)
X(ROTDEL)
X(DIRDEL)
X(ROTDELP1)
X(GRAYTEST0)
X(GRAYTEST1)
X(GRAYTEST2)
X(GRAYTEST3)
X(GRAYTEST4)
X(BINTEST0)
X(BINTEST1)
X(GRAYACT0)
X(GRAYACT1)
X(GRAYACT2)
X(GRAYACT3)
X(GRAYACT4)
X(BINACT0)
X(BINACT1)
X(LMMIWRRDN)
X(OPCGLDCK)
X(ZRSEL3)
X(ENEXT)
X(PLL)
X(LMMIWRRD_N)
X(LMMIRESET_N)
X(PLLPOWERDOWN_N)
X(FLOCK_EN)
X(FLOCK_CTRL)
X(FLOCK_SRC_SEL)
X(DIV_DEL)
X(FBK_PI_RC)
X(FBK_PR_IC)
X(DIVA)
X(DIVB)
X(DIVC)
X(DIVD)
X(DIVE)
X(DIVF)
X(REF_MMD_DIG)
X(FBK_MMD_DIG)
X(CLKMUX_FB)

View File

@ -542,6 +542,60 @@ struct NexusFasmWriter
write_cell_muxes(cell);
pop();
}
// Which PLL params are 'word' values
/* clang-format off */
const std::unordered_map<std::string, int> pll_word_params = {
{"DIVA", 7}, {"DELA", 7}, {"PHIA", 3}, {"DIVB", 7},
{"DELB", 7}, {"PHIB", 3}, {"DIVC", 7}, {"DELC", 7},
{"PHIC", 3}, {"DIVD", 7}, {"DELD", 7}, {"PHID", 3},
{"DIVE", 7}, {"DELE", 7}, {"PHIE", 3}, {"DIVF", 7},
{"DELF", 7}, {"PHIF", 3}, {"BW_CTL_BIAS", 4},
{"CLKOP_TRIM", 4}, {"CLKOS_TRIM", 4}, {"CLKOS2_TRIM", 4},
{"CLKOS3_TRIM", 4}, {"CLKOS4_TRIM", 4}, {"CLKOS5_TRIM", 4},
{"DIV_DEL", 7}, {"DYN_SEL", 3}, {"FBK_CUR_BLE", 8}, {"FBK_IF_TIMING_CTL", 2},
{"FBK_MASK", 8}, {"FBK_MMD_DIG", 8}, {"FBK_MMD_PULS_CTL", 4},
{"FBK_MODE", 2}, {"FBK_PI_RC", 4}, {"FBK_PR_CC", 4},
{"FBK_PR_IC", 4}, {"FBK_RSV", 16},
{"IPI_CMP", 4}, {"IPI_CMPN", 4},
{"IPP_CTRL", 4}, {"IPP_SEL", 4},
{"KP_VCO", 5},
{"MFG_CTRL", 4}, {"MFGOUT1_SEL", 3}, {"MFGOUT2_SEL", 3},
{"REF_MASK", 8}, {"REF_MMD_DIG", 8}, {"REF_MMD_IN", 8},
{"REF_MMD_PULS_CTL", 4}, {"REF_TIMING_CTL", 2},
{"RESERVED", 7}, {"SSC_DELTA", 15},
{"SSC_DELTA_CTL", 2}, {"SSC_F_CODE", 15},
{"SSC_N_CODE", 9}, {"SSC_REG_WEIGHTING_SEL", 3},
{"SSC_STEP_IN", 7}, {"SSC_TBASE", 12},
{"V2I_PP_ICTRL", 5},
};
/* clang-format on */
// Write out config for some kind of PLL cell
void write_pll(const CellInfo *cell)
{
BelId bel = cell->bel;
push_bel(bel);
write_bit("MODE.PLL_CORE");
write_enum(cell, "CLKMUX_FB");
write_cell_muxes(cell);
pop();
push(stringf("IP_%s", ctx->nameOf(ctx->bel_data(bel).name)));
for (auto param : sorted_cref(cell->params)) {
const std::string &name = param.first.str(ctx);
if (is_mux_param(name) || name == "CLKMUX_FB" || name == "SEL_FBK")
continue;
auto fnd_word = pll_word_params.find(name);
if (fnd_word != pll_word_params.end()) {
write_int_vector(stringf("%s[%d:0]", name.c_str(), fnd_word->second - 1),
ctx->parse_lattice_param(cell, param.first, fnd_word->second, 0).as_int64(),
fnd_word->second);
} else {
write_bit(stringf("%s.%s", name.c_str(), param.second.as_string().c_str()));
}
}
pop();
}
// Write out FASM for unused bels where needed
void write_unused()
{
@ -654,6 +708,8 @@ struct NexusFasmWriter
ci->type == id_MULT18X36_CORE || ci->type == id_MULT36_CORE || ci->type == id_REG18_CORE ||
ci->type == id_ACC54_CORE)
write_dsp(ci);
else if (ci->type == id_PLL_CORE)
write_pll(ci);
blank();
}
// Write config for unused bels

View File

@ -681,7 +681,8 @@ struct NexusPacker
std::unordered_set<BelId> seen_bels;
BelId bel = get_bel_attr(cell);
NPNR_ASSERT(bel != BelId());
if (bel == BelId())
return;
WireId start_wire = ctx->getBelPinWire(bel, port);
NPNR_ASSERT(start_wire != WireId());
PortType dir = ctx->getBelPinType(bel, port);
@ -899,6 +900,8 @@ struct NexusPacker
did_something |= preplace_singleton(ci);
else if (ci->type == id_DCC)
did_something |= preplace_prim(ci, id_CLKI, false);
else if (ci->type == id_PLL_CORE)
did_something |= preplace_prim(ci, id_REFCK, false);
}
}
}
@ -1023,6 +1026,7 @@ struct NexusPacker
static const std::unordered_map<IdString, IdString> prim_map = {
{id_OSCA, id_OSC_CORE}, {id_DP16K, id_DP16K_MODE}, {id_PDP16K, id_PDP16K_MODE},
{id_PDPSC16K, id_PDPSC16K_MODE}, {id_SP16K, id_SP16K_MODE}, {id_FIFO16K, id_FIFO16K_MODE},
{id_PLL, id_PLL_CORE},
};
for (auto cell : sorted(ctx->cells)) {
@ -1723,6 +1727,16 @@ struct NexusPacker
changed_nets.insert(net.first);
}
auto get_period = [&](CellInfo *ci, IdString port, delay_t &period) {
if (!ci->ports.count(port))
return false;
NetInfo *from = ci->ports.at(port).net;
if (from == nullptr || from->clkconstr == nullptr)
return false;
period = from->clkconstr->period.min_delay;
return true;
};
auto set_period = [&](CellInfo *ci, IdString port, delay_t period) {
if (!ci->ports.count(port))
return;
@ -1784,7 +1798,7 @@ struct NexusPacker
std::unordered_set<IdString> changed_cells;
for (auto net : changed_nets) {
for (auto &user : ctx->nets.at(net)->users)
if (user.port == id_CLKI)
if (user.port == id_CLKI || user.port == id_REFCK)
changed_cells.insert(user.cell->name);
auto &drv = ctx->nets.at(net)->driver;
if (iter == 1 && drv.cell != nullptr && (drv.port == id_HFCLKOUT || drv.port == id_LFCLKOUT))
@ -1799,7 +1813,64 @@ struct NexusPacker
int div = int_or_default(ci->params, ctx->id("HF_CLK_DIV"), 128);
set_period(ci, id_HFCLKOUT, delay_t((1.0e6 / 450) * (div + 1)));
set_period(ci, id_LFCLKOUT, delay_t((1.0e3 / 10)));
} else if (ci->type == id_PLL_CORE) {
static const std::array<IdString, 6> div{id_DIVA, id_DIVB, id_DIVC, id_DIVD, id_DIVE, id_DIVF};
static const std::array<IdString, 6> output{id_CLKOP, id_CLKOS, id_CLKOS2,
id_CLKOS3, id_CLKOS4, id_CLKOS5};
delay_t period_in;
if (!get_period(ci, id_REFCK, period_in))
continue;
log_info(" Input frequency of PLL '%s' is constrained to %.1f MHz\n", ci->name.c_str(ctx),
MHz(period_in));
int input_div = ctx->parse_lattice_param(ci, id_REF_MMD_DIG, 8, 1).as_int64();
period_in *= input_div;
int feedback_div = ctx->parse_lattice_param(ci, id_REF_MMD_DIG, 8, 1).as_int64();
bool found_fbk = false;
std::string clkmux_fb = str_or_default(ci->params, id_CLKMUX_FB, "CMUX_CLKOP");
for (int i = 0; i < 6; i++) {
// Find which output is being used for feedback
if (clkmux_fb != stringf("CMUX_%s", output[i].c_str(ctx)))
continue;
// Multiply feedback output divider with
feedback_div *= (ctx->parse_lattice_param(ci, div[i], 7, 0).as_int64() + 1);
found_fbk = true;
}
if (!found_fbk) {
log_warning("Unable to determine feedback path, skipping PLL timing constraint derivation for "
"'%s'\n",
ctx->nameOf(ci));
continue;
}
delay_t vco_period = period_in / feedback_div;
log_info(" Derived VCO frequency of PLL '%s' is %.1f MHz\n", ci->name.c_str(ctx),
MHz(vco_period));
for (int i = 0; i < 6; i++) {
set_period(ci, output[i],
(ctx->parse_lattice_param(ci, div[i], 7, 0).as_int64() + 1) * vco_period);
}
}
}
}
}
void pack_plls()
{
const std::unordered_map<IdString, std::string> pll_defaults = {
{id_FLOCK_CTRL, "2X"}, {id_FLOCK_EN, "ENABLED"}, {id_FLOCK_SRC_SEL, "REFCLK"},
{id_DIV_DEL, "0b0000001"}, {id_FBK_PI_RC, "0b1100"}, {id_FBK_PR_IC, "0b1000"},
};
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->type == id_PLL_CORE) {
// Extra log to phys rules
rename_port(ctx, ci, id_PLLPOWERDOWN_N, id_PLLPDN);
rename_port(ctx, ci, id_LMMIWRRD_N, id_LMMIWRRDN);
rename_port(ctx, ci, id_LMMIRESET_N, id_LMMIRESETN);
for (auto &defparam : pll_defaults)
if (!ci->params.count(defparam.first))
ci->params[defparam.first] = defparam.second;
}
}
}
@ -1816,6 +1887,7 @@ struct NexusPacker
pack_carries();
pack_widefn();
pack_ffs();
pack_plls();
pack_constants();
pack_luts();
promote_globals();

View File

@ -154,7 +154,21 @@ static const std::unordered_map<IdString, Arch::CellPinsData> base_cell_pin_data
{id_CEO, PINSTYLE_CE}, {id_CIN, PINSTYLE_CIB}, {id_SFTCTRL0, PINSTYLE_PU},
{id_SFTCTRL1, PINSTYLE_PU}, {id_SFTCTRL2, PINSTYLE_PU}, {id_SFTCTRL3, PINSTYLE_PU},
{{}, PINSTYLE_DEDI},
}}};
}},
{id_PLL_CORE,
{
{id_REFCK, PINSTYLE_DEDI},
{id_FBKCK, PINSTYLE_DEDI},
{id_SCANCLK, PINSTYLE_DEDI},
{id_SCANRST, PINSTYLE_DEDI},
{id_LMMICLK, PINSTYLE_CLK},
{id_LMMIRESETN, PINSTYLE_CE},
{id_OPCGLDCK, PINSTYLE_DEDI},
{id_ZRSEL3, PINSTYLE_DEDI},
{id_ENEXT, PINSTYLE_DEDI},
{{}, PINSTYLE_CIB},
}},
};
} // namespace
void Arch::init_cell_pin_data() { cell_pins_db = base_cell_pin_data; }