aboutsummaryrefslogtreecommitdiffstats
path: root/nexus
diff options
context:
space:
mode:
Diffstat (limited to 'nexus')
-rw-r--r--nexus/arch.cc15
-rw-r--r--nexus/constids.inc7
-rw-r--r--nexus/fasm.cc11
-rw-r--r--nexus/global.cc2
-rw-r--r--nexus/pack.cc27
5 files changed, 58 insertions, 4 deletions
diff --git a/nexus/arch.cc b/nexus/arch.cc
index 0241e832..39e51a5b 100644
--- a/nexus/arch.cc
+++ b/nexus/arch.cc
@@ -486,6 +486,12 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
if (fromPort == id_CLK)
return false; // don't include delays that are actually clock-to-out here
return lookup_cell_delay(cell->tmg_index, lookup_port(fromPort), lookup_port(toPort), delay);
+ } else if(cell->type == id_DCS) {
+ if (fromPort == id_SELFORCE || fromPort == id_SEL) {
+ return false;
+ }
+ int index = get_cell_timing_idx(id_DCS, id_DCS);
+ return lookup_cell_delay(index, fromPort, toPort, delay);
}
return false;
}
@@ -553,6 +559,15 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
return TMG_GEN_CLOCK;
else if (port == id_CE)
return TMG_COMB_INPUT;
+ } else if (cell->type == id_DCS) {
+ // FIXME: Making inputs TMG_CLOCK_INPUT and the output TMG_CLOCK_GEN
+ // yielded in error in the timing analyzer. For now keep those as
+ // regular combinational ports.
+ if (port == id_CLK0 || port == id_CLK1)
+ return TMG_COMB_INPUT;
+ else if (port == id_DCSOUT) {
+ return TMG_COMB_OUTPUT;
+ }
return TMG_IGNORE;
}
return TMG_IGNORE;
diff --git a/nexus/constids.inc b/nexus/constids.inc
index 11726d3b..bdf8867b 100644
--- a/nexus/constids.inc
+++ b/nexus/constids.inc
@@ -532,6 +532,13 @@ X(SCLK)
X(LOCAL_VCC)
+X(DCS)
+X(CLK0)
+X(CLK1)
+X(SELFORCE)
+X(DCSOUT)
+X(DCSMODE)
+
X(BEL_TYPE)
X(BEL_Z)
X(CEOUTMUX)
diff --git a/nexus/fasm.cc b/nexus/fasm.cc
index c8404587..e2eb2305 100644
--- a/nexus/fasm.cc
+++ b/nexus/fasm.cc
@@ -536,6 +536,15 @@ struct NexusFasmWriter
write_cell_muxes(cell);
pop(2);
}
+ // Write config for DCS
+ void write_dcs(const CellInfo *cell)
+ {
+ BelId bel = cell->bel;
+ push_tile(bel.tile, ctx->id("CMUX_0"));
+ push_belname(bel);
+ write_enum(cell, "DCSMODE", "VCC");
+ pop(2);
+ }
// Write config for an OXIDE_EBR cell
void write_bram(const CellInfo *cell)
{
@@ -939,6 +948,8 @@ struct NexusFasmWriter
write_iol(ci);
else if (ci->type == id_DCC)
write_dcc(ci);
+ else if (ci->type == id_DCS)
+ write_dcs(ci);
blank();
}
// Handle DCC route-throughs
diff --git a/nexus/global.cc b/nexus/global.cc
index 31bf0a6b..37629f64 100644
--- a/nexus/global.cc
+++ b/nexus/global.cc
@@ -193,7 +193,7 @@ struct NexusGlobalRouter
CellInfo *drv = ni->driver.cell;
if (drv == nullptr)
continue;
- if (drv->type == id_DCC) {
+ if (drv->type == id_DCC || drv->type == id_DCS) {
route_clk_net(ni);
continue;
}
diff --git a/nexus/pack.cc b/nexus/pack.cc
index 66d897a8..bb62ec85 100644
--- a/nexus/pack.cc
+++ b/nexus/pack.cc
@@ -860,6 +860,9 @@ struct NexusPacker
// Skip undriven nets; and nets that are already global
if (ni->driver.cell == nullptr)
continue;
+ if (ni->driver.cell->type == id_DCS) {
+ continue;
+ }
if (ni->driver.cell->type == id_DCC) {
--available_globals;
continue;
@@ -1981,19 +1984,37 @@ struct NexusPacker
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))
- changed_cells.insert(drv.cell->name);
+ if (iter == 1 && drv.cell != nullptr) {
+ if (drv.cell->type == id_OSC_CORE && (drv.port == id_HFCLKOUT || drv.port == id_LFCLKOUT))
+ changed_cells.insert(drv.cell->name);
+ if (drv.cell->type == id_DCC && drv.port == id_CLKO)
+ changed_cells.insert(drv.cell->name);
+ if (drv.cell->type == id_DCS && drv.port == id_DCSOUT)
+ changed_cells.insert(drv.cell->name);
+ }
}
changed_nets.clear();
for (auto cell : changed_cells) {
CellInfo *ci = ctx->cells.at(cell).get();
if (ci->type == id_DCC) {
copy_constraint(ci, id_CLKI, id_CLKO, 1);
+ } else if (ci->type == id_DCS) {
+ // For DCC copy the worst case ("fastest") constraint
+ delay_t period_clk0 = 0, period_clk1 = 0;
+ bool have_clk0 = get_period(ci, id_CLK0, period_clk0);
+ bool have_clk1 = get_period(ci, id_CLK1, period_clk1);
+ if (have_clk0 && !have_clk1) {
+ copy_constraint(ci, id_CLK0, id_DCSOUT);
+ } else if (!have_clk0 && have_clk1) {
+ copy_constraint(ci, id_CLK1, id_DCSOUT);
+ } else if ( have_clk0 && have_clk1) {
+ set_period(ci, id_DCSOUT, std::min(period_clk0, period_clk1));
+ }
} else if (ci->type == id_OSC_CORE) {
int div = int_or_default(ci->params, id_HF_CLK_DIV, 128);
const float tol = 1.07f; // OSCA has +/-7% frequency tolerance, assume the worst case.
set_period(ci, id_HFCLKOUT, delay_t((1.0e6 / 450) * (div + 1) / tol));
- set_period(ci, id_LFCLKOUT, delay_t((1.0e3 / 10) / tol));
+ set_period(ci, id_LFCLKOUT, delay_t((1.0e9 / 32) / tol));
} 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,