nexus: Add PLL support
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
b666c85824
commit
86e6a2225c
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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,11 +1813,68 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
explicit NexusPacker(Context *ctx) : ctx(ctx) {}
|
||||
|
||||
void operator()()
|
||||
@ -1816,6 +1887,7 @@ struct NexusPacker
|
||||
pack_carries();
|
||||
pack_widefn();
|
||||
pack_ffs();
|
||||
pack_plls();
|
||||
pack_constants();
|
||||
pack_luts();
|
||||
promote_globals();
|
||||
|
@ -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; }
|
||||
|
Loading…
Reference in New Issue
Block a user