From cf5cbd1153c3ebaf6bcff98f2d936e1663407dff Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 18 Oct 2019 15:58:57 +0100 Subject: ecp5: Preserve top level IO properly Signed-off-by: David Shah --- common/design_utils.cc | 12 +++++++++++- common/design_utils.h | 3 +++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'common') diff --git a/common/design_utils.cc b/common/design_utils.cc index bdf5ca5c..10212a03 100644 --- a/common/design_utils.cc +++ b/common/design_utils.cc @@ -88,7 +88,7 @@ void connect_port(const Context *ctx, NetInfo *net, CellInfo *cell, IdString por NPNR_ASSERT(net->driver.cell == nullptr); net->driver.cell = cell; net->driver.port = port_name; - } else if (port.type == PORT_IN) { + } else if (port.type == PORT_IN || port.type == PORT_INOUT) { PortRef user; user.cell = cell; user.port = port_name; @@ -146,4 +146,14 @@ void rename_port(Context *ctx, CellInfo *cell, IdString old_name, IdString new_n cell->ports[new_name] = pi; } +void rename_net(Context *ctx, NetInfo *net, IdString new_name) +{ + if (net == nullptr) + return; + NPNR_ASSERT(!ctx->nets.count(new_name)); + std::swap(ctx->nets[net->name], ctx->nets[new_name]); + ctx->nets.erase(net->name); + net->name = new_name; +} + NEXTPNR_NAMESPACE_END diff --git a/common/design_utils.h b/common/design_utils.h index 3eb9024f..1ae1d648 100644 --- a/common/design_utils.h +++ b/common/design_utils.h @@ -94,6 +94,9 @@ void connect_ports(Context *ctx, CellInfo *cell1, IdString port1_name, CellInfo // Rename a port if it exists on a cell void rename_port(Context *ctx, CellInfo *cell, IdString old_name, IdString new_name); +// Rename a net without invalidating pointers to it +void rename_net(Context *ctx, NetInfo *net, IdString new_name); + void print_utilisation(const Context *ctx); NEXTPNR_NAMESPACE_END -- cgit v1.2.3 From c0484a317d1a25a23541b60dd3bfc800230a0536 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 19 Oct 2019 16:08:11 +0100 Subject: sdf: Framework for writing out SDF files Signed-off-by: David Shah --- common/sdf.cc | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 common/sdf.cc (limited to 'common') diff --git a/common/sdf.cc b/common/sdf.cc new file mode 100644 index 00000000..570b8aee --- /dev/null +++ b/common/sdf.cc @@ -0,0 +1,200 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2019 David Shah + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +namespace SDF { + +struct MinMaxTyp +{ + double min, typ, max; +}; + +struct RiseFallDelay +{ + MinMaxTyp rise, fall; +}; + +struct PortAndEdge +{ + std::string port; + ClockEdge edge; +}; + +struct IOPath +{ + std::string from, to; + RiseFallDelay delay; +}; + +struct TimingCheck +{ + enum CheckType + { + SETUPHOLD, + PERIOD, + WIDTH + } type; + PortAndEdge from, to; + RiseFallDelay delay; +}; + +struct Cell +{ + std::string celltype, instance; + std::vector iopaths; + std::vector checks; +}; + +struct CellPort +{ + std::string cell, port; +}; + +struct Interconnect +{ + CellPort from, to; + RiseFallDelay delay; +}; + +struct SDFWriter +{ + std::vector cells; + std::vector conn; + std::string sdfversion, design, vendor, program; + + std::string format_name(const std::string &name) + { + std::string fmt = "\""; + for (char c : name) { + if (c == '\\' || c == '\"') + fmt += "\""; + fmt += c; + } + fmt += "\""; + return fmt; + } + + std::string timing_check_name(TimingCheck::CheckType type) + { + switch (type) { + case TimingCheck::SETUPHOLD: + return "SETUPHOLD"; + case TimingCheck::PERIOD: + return "PERIOD"; + case TimingCheck::WIDTH: + return "WIDTH"; + default: + NPNR_ASSERT_FALSE("unknown timing check type"); + } + } + + void write_delay(std::ostream &out, const RiseFallDelay &delay) + { + write_delay(out, delay.rise); + out << " "; + write_delay(out, delay.fall); + } + + void write_delay(std::ostream &out, const MinMaxTyp &delay) + { + out << "(" << delay.min << ":" << delay.typ << ":" << delay.max << ")"; + } + + void write_port(std::ostream &out, const CellPort &port) { out << format_name(port.cell + "/" + port.port); } + + void write_portedge(std::ostream &out, const PortAndEdge &pe) + { + out << "(" << (pe.edge == RISING_EDGE ? "posedge" : "negedge") << " " << pe.port << ")"; + } + + void write(std::ostream &out) + { + out << "(DELAYFILE" << std::endl; + // Headers and metadata + out << " (SDFVERSION " << format_name(sdfversion) << ")" << std::endl; + out << " (DESIGN " << format_name(design) << ")" << std::endl; + out << " (VENDOR " << format_name(vendor) << ")" << std::endl; + out << " (PROGRAM " << format_name(program) << ")" << std::endl; + out << " (DIVIDER /)" << std::endl; + out << " (TIMESCALE 1ps)" << std::endl; + // Write cells + for (auto &cell : cells) { + out << " (CELL" << std::endl; + out << " (CELLTYPE " << format_name(cell.celltype) << ")" << std::endl; + out << " (INSTANCE " << format_name(cell.instance) << ")" << std::endl; + // IOPATHs (combinational delay and clock-to-q) + if (!cell.iopaths.empty()) { + out << " (DELAY" << std::endl; + out << " (ABSOLUTE" << std::endl; + for (auto &path : cell.iopaths) { + out << " (IOPATH " << path.from << " " << path.to << " "; + write_delay(out, path.delay); + out << ")" << std::endl; + } + out << " )" << std::endl; + out << " )" << std::endl; + } + // Timing Checks (setup/hold, period, width) + if (!cell.checks.empty()) { + out << " (TIMINGCHECK" << std::endl; + for (auto &check : cell.checks) { + out << " (" << timing_check_name(check.type) << " "; + write_portedge(out, check.from); + out << " "; + if (check.type == TimingCheck::SETUPHOLD) { + write_portedge(out, check.to); + out << " "; + } + if (check.type == TimingCheck::SETUPHOLD) + write_delay(out, check.delay); + else + write_delay(out, check.delay.rise); + out << ")" << std::endl; + } + out << " )" << std::endl; + } + out << " )" << std::endl; + } + // Write interconnect delays, with the main design begin a "cell" + out << " (CELL" << std::endl; + out << " (CELLTYPE " << format_name(design) << ")" << std::endl; + out << " (DELAY" << std::endl; + out << " (ABSOLUTE" << std::endl; + for (auto &ic : conn) { + out << " (INTERCONNECT "; + write_port(out, ic.from); + out << " "; + write_port(out, ic.to); + out << " "; + write_delay(out, ic.delay); + out << std::endl; + } + out << " )" << std::endl; + out << " )" << std::endl; + out << " )" << std::endl; + out << ")" << std::endl; + } +}; + +} // namespace SDF + +NEXTPNR_NAMESPACE_END \ No newline at end of file -- cgit v1.2.3 From 4775930e4943fef042b8a4c7e339fefeb1f251be Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 19 Oct 2019 16:52:47 +0100 Subject: sdf: Add basic support for writing SDF files Signed-off-by: David Shah --- common/command.cc | 10 ++++ common/nextpnr.h | 5 ++ common/sdf.cc | 161 +++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 155 insertions(+), 21 deletions(-) (limited to 'common') diff --git a/common/command.cc b/common/command.cc index ad5b6c54..f7f804a0 100644 --- a/common/command.cc +++ b/common/command.cc @@ -149,6 +149,8 @@ po::options_description CommandHandler::getGeneralOptions() general.add_options()("freq", po::value(), "set target frequency for design in MHz"); general.add_options()("timing-allow-fail", "allow timing to fail in design"); general.add_options()("no-tmdriv", "disable timing-driven placement"); + general.add_options()("sdf", po::value(), "SDF delay back-annotation file to write"); + return general; } @@ -336,6 +338,14 @@ int CommandHandler::executeMain(std::unique_ptr ctx) log_error("Saving design failed.\n"); } + if (vm.count("sdf")) { + std::string filename = vm["sdf"].as(); + std::ofstream f(filename); + if (!f) + log_error("Failed to open SDF file '%s' for writing.\n", filename.c_str()); + ctx->writeSDF(f); + } + #ifndef NO_PYTHON deinit_python(); #endif diff --git a/common/nextpnr.h b/common/nextpnr.h index bae828f6..89e9c4f0 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -808,6 +808,11 @@ struct Context : Arch, DeterministicRNG // -------------------------------------------------------------- + // provided by sdf.cc + void writeSDF(std::ostream &out) const; + + // -------------------------------------------------------------- + uint32_t checksum() const; void check() const; diff --git a/common/sdf.cc b/common/sdf.cc index 570b8aee..07773a6a 100644 --- a/common/sdf.cc +++ b/common/sdf.cc @@ -18,6 +18,7 @@ */ #include "nextpnr.h" +#include "util.h" NEXTPNR_NAMESPACE_BEGIN @@ -93,6 +94,17 @@ struct SDFWriter return fmt; } + std::string escape_name(const std::string &name) + { + std::string esc; + for (char c : name) { + if (c == '$' || c == '\\' || c == '[' || c == ']' || c == ':') + esc += '\\'; + esc += c; + } + return esc; + } + std::string timing_check_name(TimingCheck::CheckType type) { switch (type) { @@ -119,11 +131,11 @@ struct SDFWriter out << "(" << delay.min << ":" << delay.typ << ":" << delay.max << ")"; } - void write_port(std::ostream &out, const CellPort &port) { out << format_name(port.cell + "/" + port.port); } + void write_port(std::ostream &out, const CellPort &port) { out << escape_name(port.cell + "/" + port.port); } void write_portedge(std::ostream &out, const PortAndEdge &pe) { - out << "(" << (pe.edge == RISING_EDGE ? "posedge" : "negedge") << " " << pe.port << ")"; + out << "(" << (pe.edge == RISING_EDGE ? "posedge" : "negedge") << " " << escape_name(pe.port) << ")"; } void write(std::ostream &out) @@ -136,17 +148,35 @@ struct SDFWriter out << " (PROGRAM " << format_name(program) << ")" << std::endl; out << " (DIVIDER /)" << std::endl; out << " (TIMESCALE 1ps)" << std::endl; + // Write interconnect delays, with the main design begin a "cell" + out << " (CELL" << std::endl; + out << " (CELLTYPE " << format_name(design) << ")" << std::endl; + out << " (INSTANCE )" << std::endl; + out << " (DELAY" << std::endl; + out << " (ABSOLUTE" << std::endl; + for (auto &ic : conn) { + out << " (INTERCONNECT "; + write_port(out, ic.from); + out << " "; + write_port(out, ic.to); + out << " "; + write_delay(out, ic.delay); + out << ")" << std::endl; + } + out << " )" << std::endl; + out << " )" << std::endl; + out << " )" << std::endl; // Write cells for (auto &cell : cells) { out << " (CELL" << std::endl; out << " (CELLTYPE " << format_name(cell.celltype) << ")" << std::endl; - out << " (INSTANCE " << format_name(cell.instance) << ")" << std::endl; + out << " (INSTANCE " << escape_name(cell.instance) << ")" << std::endl; // IOPATHs (combinational delay and clock-to-q) if (!cell.iopaths.empty()) { out << " (DELAY" << std::endl; out << " (ABSOLUTE" << std::endl; for (auto &path : cell.iopaths) { - out << " (IOPATH " << path.from << " " << path.to << " "; + out << " (IOPATH " << escape_name(path.from) << " " << escape_name(path.to) << " "; write_delay(out, path.delay); out << ")" << std::endl; } @@ -174,27 +204,116 @@ struct SDFWriter } out << " )" << std::endl; } - // Write interconnect delays, with the main design begin a "cell" - out << " (CELL" << std::endl; - out << " (CELLTYPE " << format_name(design) << ")" << std::endl; - out << " (DELAY" << std::endl; - out << " (ABSOLUTE" << std::endl; - for (auto &ic : conn) { - out << " (INTERCONNECT "; - write_port(out, ic.from); - out << " "; - write_port(out, ic.to); - out << " "; - write_delay(out, ic.delay); - out << std::endl; - } - out << " )" << std::endl; - out << " )" << std::endl; - out << " )" << std::endl; out << ")" << std::endl; } }; } // namespace SDF +void Context::writeSDF(std::ostream &out) const +{ + using namespace SDF; + SDFWriter wr; + wr.design = str_or_default(attrs, id("module"), "top"); + wr.sdfversion = "3.0"; + wr.vendor = "nextpnr"; + wr.program = "nextpnr"; + + const double delay_scale = 1000; + // Convert from DelayInfo to SDF-friendly RiseFallDelay + auto convert_delay = [&](const DelayInfo &dly) { + RiseFallDelay rf; + rf.rise.min = getDelayNS(dly.minRaiseDelay()) * delay_scale; + rf.rise.typ = getDelayNS((dly.minRaiseDelay() + dly.maxRaiseDelay()) / 2) * delay_scale; // fixme: typ delays? + rf.rise.max = getDelayNS(dly.maxRaiseDelay()) * delay_scale; + rf.fall.min = getDelayNS(dly.minFallDelay()) * delay_scale; + rf.fall.typ = getDelayNS((dly.minFallDelay() + dly.maxFallDelay()) / 2) * delay_scale; // fixme: typ delays? + rf.fall.max = getDelayNS(dly.maxFallDelay()) * delay_scale; + return rf; + }; + + auto convert_setuphold = [&](const DelayInfo &setup, const DelayInfo &hold) { + RiseFallDelay rf; + rf.rise.min = getDelayNS(setup.minDelay()) * delay_scale; + rf.rise.typ = getDelayNS((setup.minDelay() + setup.maxDelay()) / 2) * delay_scale; // fixme: typ delays? + rf.rise.max = getDelayNS(setup.maxDelay()) * delay_scale; + rf.fall.min = getDelayNS(hold.minDelay()) * delay_scale; + rf.fall.typ = getDelayNS((hold.minDelay() + hold.maxDelay()) / 2) * delay_scale; // fixme: typ delays? + rf.fall.max = getDelayNS(hold.maxDelay()) * delay_scale; + return rf; + }; + + for (auto cell : sorted(cells)) { + Cell sc; + const CellInfo *ci = cell.second; + sc.instance = ci->name.str(this); + sc.celltype = ci->type.str(this); + for (auto port : ci->ports) { + int clockCount = 0; + TimingPortClass cls = getPortTimingClass(ci, port.first, clockCount); + if (cls == TMG_IGNORE) + continue; + if (port.second.type != PORT_IN) { + // Add combinational paths to this output (or inout) + for (auto other : ci->ports) { + if (other.second.type == PORT_OUT) + continue; + DelayInfo dly; + if (!getCellDelay(ci, other.first, port.first, dly)) + continue; + IOPath iop; + iop.from = other.first.str(this); + iop.to = port.first.str(this); + iop.delay = convert_delay(dly); + sc.iopaths.push_back(iop); + } + // Add clock-to-output delays, also as IOPaths + if (cls == TMG_REGISTER_OUTPUT) + for (int i = 0; i < clockCount; i++) { + auto clkInfo = getPortClockingInfo(ci, port.first, i); + IOPath cqp; + cqp.from = clkInfo.clock_port.str(this); + cqp.to = port.first.str(this); + cqp.delay = convert_delay(clkInfo.clockToQ); + sc.iopaths.push_back(cqp); + } + } + if (port.second.type != PORT_OUT && cls == TMG_REGISTER_INPUT) { + // Add setup/hold checks + for (int i = 0; i < clockCount; i++) { + auto clkInfo = getPortClockingInfo(ci, port.first, i); + TimingCheck chk; + chk.from.edge = RISING_EDGE; // Add setup/hold checks equally for rising and falling edges + chk.from.port = port.first.str(this); + chk.to.edge = clkInfo.edge; + chk.to.port = clkInfo.clock_port.str(this); + chk.type = TimingCheck::SETUPHOLD; + chk.delay = convert_setuphold(clkInfo.setup, clkInfo.hold); + sc.checks.push_back(chk); + chk.from.edge = FALLING_EDGE; + sc.checks.push_back(chk); + } + } + } + wr.cells.push_back(sc); + } + + for (auto net : sorted(nets)) { + NetInfo *ni = net.second; + if (ni->driver.cell == nullptr) + continue; + for (auto &usr : ni->users) { + Interconnect ic; + ic.from.cell = ni->driver.cell->name.str(this); + ic.from.port = ni->driver.port.str(this); + ic.to.cell = usr.cell->name.str(this); + ic.to.port = usr.port.str(this); + // FIXME: min/max routing delay - or at least constructing DelayInfo here + ic.delay = convert_delay(getDelayFromNS(getDelayNS(getNetinfoRouteDelay(ni, usr)))); + wr.conn.push_back(ic); + } + } + wr.write(out); +} + NEXTPNR_NAMESPACE_END \ No newline at end of file -- cgit v1.2.3 From 8343488bdf6ef21844913bd1043e27541b0573c1 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 24 Oct 2019 10:43:18 +0100 Subject: sdf: Improve SDF output Signed-off-by: David Shah --- common/sdf.cc | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'common') diff --git a/common/sdf.cc b/common/sdf.cc index 07773a6a..769728e4 100644 --- a/common/sdf.cc +++ b/common/sdf.cc @@ -253,9 +253,13 @@ void Context::writeSDF(std::ostream &out) const TimingPortClass cls = getPortTimingClass(ci, port.first, clockCount); if (cls == TMG_IGNORE) continue; + if (port.second.net == nullptr) + continue; // Ignore disconnected ports if (port.second.type != PORT_IN) { // Add combinational paths to this output (or inout) for (auto other : ci->ports) { + if (other.second.net == nullptr) + continue; if (other.second.type == PORT_OUT) continue; DelayInfo dly; -- cgit v1.2.3 From f2b9cc6d2311c437d740873111e806e9e5078736 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 24 Oct 2019 12:37:07 +0100 Subject: sdf: Working on support for CVC Signed-off-by: David Shah --- common/command.cc | 3 ++- common/nextpnr.h | 2 +- common/sdf.cc | 21 ++++++++++++++++----- 3 files changed, 19 insertions(+), 7 deletions(-) (limited to 'common') diff --git a/common/command.cc b/common/command.cc index f7f804a0..fd310789 100644 --- a/common/command.cc +++ b/common/command.cc @@ -150,6 +150,7 @@ po::options_description CommandHandler::getGeneralOptions() general.add_options()("timing-allow-fail", "allow timing to fail in design"); general.add_options()("no-tmdriv", "disable timing-driven placement"); general.add_options()("sdf", po::value(), "SDF delay back-annotation file to write"); + general.add_options()("sdf-cvc", "enable tweaks for SDF file compatibility with the CVC simulator"); return general; } @@ -343,7 +344,7 @@ int CommandHandler::executeMain(std::unique_ptr ctx) std::ofstream f(filename); if (!f) log_error("Failed to open SDF file '%s' for writing.\n", filename.c_str()); - ctx->writeSDF(f); + ctx->writeSDF(f, vm.count("sdf-cvc")); } #ifndef NO_PYTHON diff --git a/common/nextpnr.h b/common/nextpnr.h index 89e9c4f0..24f6948b 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -809,7 +809,7 @@ struct Context : Arch, DeterministicRNG // -------------------------------------------------------------- // provided by sdf.cc - void writeSDF(std::ostream &out) const; + void writeSDF(std::ostream &out, bool cvc_mode = false) const; // -------------------------------------------------------------- diff --git a/common/sdf.cc b/common/sdf.cc index 769728e4..b9606907 100644 --- a/common/sdf.cc +++ b/common/sdf.cc @@ -78,6 +78,7 @@ struct Interconnect struct SDFWriter { + bool cvc_mode = false; std::vector cells; std::vector conn; std::string sdfversion, design, vendor, program; @@ -98,7 +99,7 @@ struct SDFWriter { std::string esc; for (char c : name) { - if (c == '$' || c == '\\' || c == '[' || c == ']' || c == ':') + if (c == '$' || c == '\\' || c == '[' || c == ']' || c == ':' || (cvc_mode && c == '.')) esc += '\\'; esc += c; } @@ -128,10 +129,19 @@ struct SDFWriter void write_delay(std::ostream &out, const MinMaxTyp &delay) { - out << "(" << delay.min << ":" << delay.typ << ":" << delay.max << ")"; + if (cvc_mode) + out << "(" << int(delay.min) << ":" << int(delay.typ) << ":" << int(delay.max) << ")"; + else + out << "(" << delay.min << ":" << delay.typ << ":" << delay.max << ")"; } - void write_port(std::ostream &out, const CellPort &port) { out << escape_name(port.cell + "/" + port.port); } + void write_port(std::ostream &out, const CellPort &port) + { + if (cvc_mode) + out << escape_name(port.cell) + "." + escape_name(port.port); + else + out << escape_name(port.cell + "/" + port.port); + } void write_portedge(std::ostream &out, const PortAndEdge &pe) { @@ -146,7 +156,7 @@ struct SDFWriter out << " (DESIGN " << format_name(design) << ")" << std::endl; out << " (VENDOR " << format_name(vendor) << ")" << std::endl; out << " (PROGRAM " << format_name(program) << ")" << std::endl; - out << " (DIVIDER /)" << std::endl; + out << " (DIVIDER " << (cvc_mode ? "." : "/") << ")" << std::endl; out << " (TIMESCALE 1ps)" << std::endl; // Write interconnect delays, with the main design begin a "cell" out << " (CELL" << std::endl; @@ -210,10 +220,11 @@ struct SDFWriter } // namespace SDF -void Context::writeSDF(std::ostream &out) const +void Context::writeSDF(std::ostream &out, bool cvc_mode) const { using namespace SDF; SDFWriter wr; + wr.cvc_mode = cvc_mode; wr.design = str_or_default(attrs, id("module"), "top"); wr.sdfversion = "3.0"; wr.vendor = "nextpnr"; -- cgit v1.2.3