aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nexus/constids.inc68
-rw-r--r--nexus/fasm.cc56
-rw-r--r--nexus/pack.cc76
-rw-r--r--nexus/pins.cc16
4 files changed, 213 insertions, 3 deletions
diff --git a/nexus/constids.inc b/nexus/constids.inc
index 9b12d197..f8939263 100644
--- a/nexus/constids.inc
+++ b/nexus/constids.inc
@@ -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)
diff --git a/nexus/fasm.cc b/nexus/fasm.cc
index 5da809c6..fcbe0b8c 100644
--- a/nexus/fasm.cc
+++ b/nexus/fasm.cc
@@ -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
diff --git a/nexus/pack.cc b/nexus/pack.cc
index b4c1566b5..eb1ac560 100644
--- a/nexus/pack.cc
+++ b/nexus/pack.cc
@@ -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();
diff --git a/nexus/pins.cc b/nexus/pins.cc
index 0587c032..05bffb1e 100644
--- a/nexus/pins.cc
+++ b/nexus/pins.cc
@@ -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; }