aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Shah <dave@ds0.me>2020-02-14 21:08:58 +0000
committerDavid Shah <dave@ds0.me>2020-02-15 11:35:51 +0000
commitb6158f94f6e83e7fac67c678effd21ec932cd0c2 (patch)
tree4d79e9dcc6b476e8b88023ca3244ae23fea6ec5a
parente0b4f0ee63072f0e5aa86b4fa9ca1f488ae57790 (diff)
downloadnextpnr-b6158f94f6e83e7fac67c678effd21ec932cd0c2.tar.gz
nextpnr-b6158f94f6e83e7fac67c678effd21ec932cd0c2.tar.bz2
nextpnr-b6158f94f6e83e7fac67c678effd21ec932cd0c2.zip
svg: Basic SVG graphics rendering
Signed-off-by: David Shah <dave@ds0.me>
-rw-r--r--common/arch_pybindings_shared.h3
-rw-r--r--common/command.cc7
-rw-r--r--common/nextpnr.h5
-rw-r--r--common/svg.cc150
-rw-r--r--python/svg_placement.py1
-rw-r--r--python/svg_routing.py1
-rw-r--r--python/svg_routing_fast.py1
7 files changed, 168 insertions, 0 deletions
diff --git a/common/arch_pybindings_shared.h b/common/arch_pybindings_shared.h
index 89a61dad..7b0fa501 100644
--- a/common/arch_pybindings_shared.h
+++ b/common/arch_pybindings_shared.h
@@ -114,3 +114,6 @@ fn_wrapper_0a<Context, decltype(&Context::getChipName), &Context::getChipName, p
ctx_cls, "getChipName");
fn_wrapper_0a<Context, decltype(&Context::archId), &Context::archId, conv_to_str<IdString>>::def_wrap(ctx_cls,
"archId");
+
+fn_wrapper_2a_v<Context, decltype(&Context::writeSVG), &Context::writeSVG, pass_through<std::string>,
+ pass_through<std::string>>::def_wrap(ctx_cls, "writeSVG");
diff --git a/common/command.cc b/common/command.cc
index f652ce67..7b4805ae 100644
--- a/common/command.cc
+++ b/common/command.cc
@@ -158,6 +158,9 @@ po::options_description CommandHandler::getGeneralOptions()
general.add_options()("sdf", po::value<std::string>(), "SDF delay back-annotation file to write");
general.add_options()("sdf-cvc", "enable tweaks for SDF file compatibility with the CVC simulator");
+ general.add_options()("placed-svg", po::value<std::string>(), "write render of placement to SVG file");
+ general.add_options()("routed-svg", po::value<std::string>(), "write render of routing to SVG file");
+
return general;
}
@@ -336,6 +339,8 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
if (!ctx->place() && !ctx->force)
log_error("Placing design failed.\n");
ctx->check();
+ if (vm.count("placed-svg"))
+ ctx->writeSVG(vm["placed-svg"].as<std::string>(), "scale=50 hide_routing");
}
if (do_route) {
@@ -343,6 +348,8 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
if (!ctx->route() && !ctx->force)
log_error("Routing design failed.\n");
run_script_hook("post-route");
+ if (vm.count("routed-svg"))
+ ctx->writeSVG(vm["routed-svg"].as<std::string>(), "scale=500");
}
customBitstream(ctx.get());
diff --git a/common/nextpnr.h b/common/nextpnr.h
index a786608d..66bba997 100644
--- a/common/nextpnr.h
+++ b/common/nextpnr.h
@@ -871,6 +871,11 @@ struct Context : Arch, DeterministicRNG
// --------------------------------------------------------------
+ // provided by svg.cc
+ void writeSVG(const std::string &filename, const std::string &flags = "") const;
+
+ // --------------------------------------------------------------
+
uint32_t checksum() const;
void check() const;
diff --git a/common/svg.cc b/common/svg.cc
new file mode 100644
index 00000000..0fe1cf16
--- /dev/null
+++ b/common/svg.cc
@@ -0,0 +1,150 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2020 David Shah <dave@ds0.me>
+ *
+ * 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 <boost/algorithm/string.hpp>
+#include <fstream>
+#include "log.h"
+#include "nextpnr.h"
+#include "util.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+namespace {
+struct SVGWriter
+{
+ const Context *ctx;
+ std::ostream &out;
+ float scale = 500.0;
+ bool hide_inactive = false;
+ SVGWriter(const Context *ctx, std::ostream &out) : ctx(ctx), out(out){};
+ const char *get_stroke_colour(GraphicElement::style_t style)
+ {
+ switch (style) {
+ case GraphicElement::STYLE_GRID:
+ return "#CCC";
+ case GraphicElement::STYLE_FRAME:
+ return "#808080";
+ case GraphicElement::STYLE_INACTIVE:
+ return "#C0C0C0";
+ case GraphicElement::STYLE_ACTIVE:
+ return "#FF3030";
+ default:
+ return "#000";
+ }
+ }
+
+ void write_decal(const DecalXY &dxy)
+ {
+ for (const auto &el : ctx->getDecalGraphics(dxy.decal)) {
+ if (el.style == GraphicElement::STYLE_HIDDEN ||
+ (hide_inactive && el.style == GraphicElement::STYLE_INACTIVE))
+ continue;
+ switch (el.type) {
+ case GraphicElement::TYPE_LINE:
+ case GraphicElement::TYPE_ARROW:
+ out << stringf("<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" stroke=\"%s\"/>", (el.x1 + dxy.x) * scale,
+ (el.y1 + dxy.y) * scale, (el.x2 + dxy.x) * scale, (el.y2 + dxy.y) * scale,
+ get_stroke_colour(el.style))
+ << std::endl;
+ break;
+ case GraphicElement::TYPE_BOX:
+ out << stringf("<rect x=\"%f\" y=\"%f\" width=\"%f\" height=\"%f\" stroke=\"%s\" fill=\"%s\"/>",
+ (el.x1 + dxy.x) * scale, (el.y1 + dxy.y) * scale, (el.x2 - el.x1) * scale,
+ (el.y2 - el.y1) * scale, get_stroke_colour(el.style),
+ el.style == GraphicElement::STYLE_ACTIVE ? "#FF8080" : "none")
+ << std::endl;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ void operator()(const std::string &flags)
+ {
+ std::vector<std::string> options;
+ boost::algorithm::split(options, flags, boost::algorithm::is_space());
+ bool noroute = false;
+ for (const auto &opt : options) {
+ if (boost::algorithm::starts_with(opt, "scale=")) {
+ scale = float(std::stod(opt.substr(6)));
+ continue;
+ } else if (opt == "hide_routing") {
+ noroute = true;
+ } else if (opt == "hide_inactive") {
+ hide_inactive = true;
+ } else {
+ log_error("Unknown SVG option '%s'\n", opt.c_str());
+ }
+ }
+ float max_x = 0, max_y = 0;
+ for (auto group : ctx->getGroups()) {
+ auto decal = ctx->getGroupDecal(group);
+ for (auto el : ctx->getDecalGraphics(decal.decal)) {
+ max_x = std::max(max_x, decal.x + el.x1 + 1);
+ max_y = std::max(max_y, decal.y + el.y1 + 1);
+ }
+ }
+ for (auto bel : ctx->getBels()) {
+ auto decal = ctx->getBelDecal(bel);
+ for (auto el : ctx->getDecalGraphics(decal.decal)) {
+ max_x = std::max(max_x, decal.x + el.x1 + 1);
+ max_y = std::max(max_y, decal.y + el.y1 + 1);
+ }
+ }
+ for (auto wire : ctx->getWires()) {
+ auto decal = ctx->getWireDecal(wire);
+ for (auto el : ctx->getDecalGraphics(decal.decal)) {
+ max_x = std::max(max_x, decal.x + el.x1 + 1);
+ max_y = std::max(max_y, decal.y + el.y1 + 1);
+ }
+ }
+ for (auto pip : ctx->getPips()) {
+ auto decal = ctx->getPipDecal(pip);
+ for (auto el : ctx->getDecalGraphics(decal.decal)) {
+ max_x = std::max(max_x, decal.x + el.x1 + 1);
+ max_y = std::max(max_y, decal.y + el.y1 + 1);
+ }
+ }
+ out << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" << std::endl;
+ out << stringf("<svg viewBox=\"0 0 %f %f\" width=\"%f\" height=\"%f\" xmlns=\"http://www.w3.org/2000/svg\">",
+ max_x * scale, max_y * scale, max_x * scale, max_y * scale)
+ << std::endl;
+ out << "<rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" stroke=\"#fff\" fill=\"#fff\"/>" << std::endl;
+ for (auto group : ctx->getGroups())
+ write_decal(ctx->getGroupDecal(group));
+ for (auto bel : ctx->getBels())
+ write_decal(ctx->getBelDecal(bel));
+ if (!noroute) {
+ for (auto wire : ctx->getWires())
+ write_decal(ctx->getWireDecal(wire));
+ for (auto pip : ctx->getPips())
+ write_decal(ctx->getPipDecal(pip));
+ }
+ out << "</svg>" << std::endl;
+ }
+};
+} // namespace
+
+void Context::writeSVG(const std::string &filename, const std::string &flags) const
+{
+ std::ofstream out(filename);
+ SVGWriter(this, out)(flags);
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/python/svg_placement.py b/python/svg_placement.py
new file mode 100644
index 00000000..6adf0afd
--- /dev/null
+++ b/python/svg_placement.py
@@ -0,0 +1 @@
+ctx.writeSVG(ctx.top_module + "_placed.svg", "scale=50 hide_routing")
diff --git a/python/svg_routing.py b/python/svg_routing.py
new file mode 100644
index 00000000..589b92b7
--- /dev/null
+++ b/python/svg_routing.py
@@ -0,0 +1 @@
+ctx.writeSVG(ctx.top_module + "_routed.svg", "scale=500")
diff --git a/python/svg_routing_fast.py b/python/svg_routing_fast.py
new file mode 100644
index 00000000..d30a9d66
--- /dev/null
+++ b/python/svg_routing_fast.py
@@ -0,0 +1 @@
+ctx.writeSVG(ctx.top_module + "_routed.svg", "scale=500 hide_inactive")