aboutsummaryrefslogtreecommitdiffstats
path: root/ecp5
diff options
context:
space:
mode:
Diffstat (limited to 'ecp5')
-rw-r--r--ecp5/arch.cc78
-rw-r--r--ecp5/arch.h12
-rw-r--r--ecp5/arch_pybindings.cc4
-rw-r--r--ecp5/globals.cc7
4 files changed, 86 insertions, 15 deletions
diff --git a/ecp5/arch.cc b/ecp5/arch.cc
index 9001ce6c..fe6a9545 100644
--- a/ecp5/arch.cc
+++ b/ecp5/arch.cc
@@ -539,10 +539,10 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
return true;
}
#if 0 // FIXME
- if (fromPort == id_WCK && (toPort == id_F0 || toPort == id_F1)) {
- delay.delay = 717;
- return true;
- }
+ if (fromPort == id_WCK && (toPort == id_F0 || toPort == id_F1)) {
+ delay.delay = 717;
+ return true;
+ }
#endif
if ((fromPort == id_A0 && toPort == id_WADO3) || (fromPort == id_A1 && toPort == id_WDO1) ||
(fromPort == id_B0 && toPort == id_WADO1) || (fromPort == id_B1 && toPort == id_WDO3) ||
@@ -576,10 +576,10 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
}
}
-TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const
+TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const
{
auto disconnected = [cell](IdString p) { return !cell->ports.count(p) || cell->ports.at(p).net == nullptr; };
-
+ clockInfoCount = 0;
if (cell->type == id_TRELLIS_SLICE) {
int sd0 = int_or_default(cell->params, id("REG0_SD"), 0), sd1 = int_or_default(cell->params, id("REG1_SD"), 0);
if (port == id_CLK || port == id_WCK)
@@ -598,13 +598,13 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id
return TMG_COMB_OUTPUT;
if (port == id_DI0 || port == id_DI1 || port == id_CE || port == id_LSR || (sd0 == 1 && port == id_M0) ||
(sd1 == 1 && port == id_M1)) {
- clockPort = id_CLK;
+ clockInfoCount = 1;
return TMG_REGISTER_INPUT;
}
if (port == id_M0 || port == id_M1)
return TMG_COMB_INPUT;
if (port == id_Q0 || port == id_Q1) {
- clockPort = id_CLK;
+ clockInfoCount = 1;
return TMG_REGISTER_OUTPUT;
}
@@ -614,7 +614,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id
if (port == id_WD0 || port == id_WD1 || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 ||
port == id_WAD3 || port == id_WRE) {
- clockPort = id_WCK;
+ clockInfoCount = 1;
return TMG_REGISTER_INPUT;
}
@@ -638,10 +638,8 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id
for (auto c : boost::adaptors::reverse(port_name)) {
if (std::isdigit(c))
continue;
- if (c == 'A')
- clockPort = id_CLKA;
- else if (c == 'B')
- clockPort = id_CLKB;
+ if (c == 'A' || c == 'B')
+ clockInfoCount = 1;
else
NPNR_ASSERT_FALSE_STR("bad ram port");
return (cell->ports.at(port).type == PORT_OUT) ? TMG_REGISTER_OUTPUT : TMG_REGISTER_INPUT;
@@ -658,6 +656,60 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id
}
}
+TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
+{
+ TimingClockingInfo info;
+ info.setup.delay = 0;
+ info.hold.delay = 0;
+ info.clockToQ.delay = 0;
+ if (cell->type == id_TRELLIS_SLICE) {
+ int sd0 = int_or_default(cell->params, id("REG0_SD"), 0), sd1 = int_or_default(cell->params, id("REG1_SD"), 0);
+
+ if (port == id_WD0 || port == id_WD1 || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 ||
+ port == id_WAD3 || port == id_WRE) {
+ info.edge = RISING_EDGE;
+ info.clock_port = id_WCK;
+ info.setup.delay = 100;
+ info.hold.delay = 0;
+ } else if (port == id_DI0 || port == id_DI1 || port == id_CE || port == id_LSR || (sd0 == 1 && port == id_M0) ||
+ (sd1 == 1 && port == id_M1)) {
+ info.edge = cell->sliceInfo.clkmux == id("INV") ? FALLING_EDGE : RISING_EDGE;
+ info.clock_port = id_CLK;
+ info.setup.delay = 100;
+ info.hold.delay = 0;
+ } else {
+ info.edge = cell->sliceInfo.clkmux == id("INV") ? FALLING_EDGE : RISING_EDGE;
+ info.clock_port = id_CLK;
+ info.clockToQ.delay = 395;
+ }
+ } else if (cell->type == id_DP16KD) {
+ std::string port_name = port.str(this);
+ for (auto c : boost::adaptors::reverse(port_name)) {
+ if (std::isdigit(c))
+ continue;
+ if (c == 'A') {
+ info.clock_port = id_CLKA;
+ break;
+ } else if (c == 'B') {
+ info.clock_port = id_CLKB;
+ break;
+ } else
+ NPNR_ASSERT_FALSE_STR("bad ram port " + port.str(this));
+ }
+ info.edge = (str_or_default(cell->params, info.clock_port == id_CLKB ? id("CLKBMUX") : id("CLKAMUX"), "CLK") ==
+ "INV")
+ ? FALLING_EDGE
+ : RISING_EDGE;
+ if (cell->ports.at(port).type == PORT_OUT) {
+ info.clockToQ.delay = 4280;
+ } else {
+ info.setup.delay = 100;
+ info.hold.delay = 0;
+ }
+ }
+ return info;
+}
+
std::vector<std::pair<std::string, std::string>> Arch::getTilesAtLocation(int row, int col)
{
std::vector<std::pair<std::string, std::string>> ret;
diff --git a/ecp5/arch.h b/ecp5/arch.h
index 9daae11d..aa3c5348 100644
--- a/ecp5/arch.h
+++ b/ecp5/arch.h
@@ -859,6 +859,12 @@ struct Arch : BaseCtx
delay_t getDelayEpsilon() const { return 20; }
delay_t getRipupDelayPenalty() const { return 200; }
float getDelayNS(delay_t v) const { return v * 0.001; }
+ DelayInfo getDelayFromNS(float ns) const
+ {
+ DelayInfo del;
+ del.delay = delay_t(ns * 1000);
+ return del;
+ }
uint32_t getDelayChecksum(delay_t v) const { return v; }
bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const;
@@ -882,8 +888,10 @@ struct Arch : BaseCtx
// Get the delay through a cell from one port to another, returning false
// if no path exists
bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const;
- // Get the port class, also setting clockPort if applicable
- TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const;
+ // Get the port class, also setting clockInfoCount to the number of TimingClockingInfos associated with a port
+ TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const;
+ // Get the TimingClockingInfo of a port
+ TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const;
// Return true if a port is a net
bool isGlobalNet(const NetInfo *net) const;
diff --git a/ecp5/arch_pybindings.cc b/ecp5/arch_pybindings.cc
index 9312b4ad..5e73a673 100644
--- a/ecp5/arch_pybindings.cc
+++ b/ecp5/arch_pybindings.cc
@@ -130,6 +130,10 @@ void arch_wrap_python()
"cells");
readonly_wrapper<Context, decltype(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls,
"nets");
+
+ fn_wrapper_2a_v<Context, decltype(&Context::addClock), &Context::addClock, conv_from_str<IdString>,
+ pass_through<float>>::def_wrap(ctx_cls, "addClock");
+
WRAP_RANGE(Bel, conv_to_str<BelId>);
WRAP_RANGE(Wire, conv_to_str<WireId>);
WRAP_RANGE(AllPip, conv_to_str<PipId>);
diff --git a/ecp5/globals.cc b/ecp5/globals.cc
index 06412fef..9b0928a4 100644
--- a/ecp5/globals.cc
+++ b/ecp5/globals.cc
@@ -350,6 +350,13 @@ class Ecp5GlobalRouter
place_dcc(dcc.get());
+ if (net->clkconstr) {
+ glbnet->clkconstr = std::unique_ptr<ClockConstraint>(new ClockConstraint());
+ glbnet->clkconstr->low = net->clkconstr->low;
+ glbnet->clkconstr->high = net->clkconstr->high;
+ glbnet->clkconstr->period = net->clkconstr->period;
+ }
+
ctx->cells[dcc->name] = std::move(dcc);
NetInfo *glbptr = glbnet.get();
ctx->nets[glbnet->name] = std::move(glbnet);