diff options
author | David Shah <dave@ds0.me> | 2020-02-15 23:34:50 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-15 23:34:50 +0000 |
commit | affb12cc27ebf409eade062c4c59bb98569d8147 (patch) | |
tree | 4d79e9dcc6b476e8b88023ca3244ae23fea6ec5a /common | |
parent | e0b4f0ee63072f0e5aa86b4fa9ca1f488ae57790 (diff) | |
parent | b6158f94f6e83e7fac67c678effd21ec932cd0c2 (diff) | |
download | nextpnr-affb12cc27ebf409eade062c4c59bb98569d8147.tar.gz nextpnr-affb12cc27ebf409eade062c4c59bb98569d8147.tar.bz2 nextpnr-affb12cc27ebf409eade062c4c59bb98569d8147.zip |
Merge pull request #398 from YosysHQ/svg
svg: Basic SVG graphics rendering
Diffstat (limited to 'common')
-rw-r--r-- | common/arch_pybindings_shared.h | 3 | ||||
-rw-r--r-- | common/command.cc | 7 | ||||
-rw-r--r-- | common/nextpnr.h | 5 | ||||
-rw-r--r-- | common/svg.cc | 150 |
4 files changed, 165 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 |