aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
author1138-4EB <1138-4EB@users.noreply.github.com>2019-08-16 22:07:46 +0200
committertgingold <tgingold@users.noreply.github.com>2019-08-16 22:07:46 +0200
commitd359d6deb55e5c51707c86263b090fabbc5c41b2 (patch)
treee916cb1f11ec2687bfedbd6ad02f08c3a5b068db /src
parentc924837dd7f9164aabb37983fb036ee34a9f1e40 (diff)
downloadghdl-yosys-plugin-d359d6deb55e5c51707c86263b090fabbc5c41b2.tar.gz
ghdl-yosys-plugin-d359d6deb55e5c51707c86263b090fabbc5c41b2.tar.bz2
ghdl-yosys-plugin-d359d6deb55e5c51707c86263b090fabbc5c41b2.zip
Run testsuite in Travis CI with docker images (#31)
* makefile: use '--build' shortcut to build ghdl.so * move: rename subdir 'ghdl' to 'src' * travis: add travis config file, build script and utils script * testsuite: do not call ghdl explicitly * readme: update
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.inc2
-rw-r--r--src/ghdl.cc580
2 files changed, 582 insertions, 0 deletions
diff --git a/src/Makefile.inc b/src/Makefile.inc
new file mode 100644
index 0000000..a68e7d1
--- /dev/null
+++ b/src/Makefile.inc
@@ -0,0 +1,2 @@
+
+OBJS += frontends/ghdl/ghdl.o
diff --git a/src/ghdl.cc b/src/ghdl.cc
new file mode 100644
index 0000000..e92e7e9
--- /dev/null
+++ b/src/ghdl.cc
@@ -0,0 +1,580 @@
+/*
+ Copyright (C) 2016 Tristan Gingold <tgingold@free.fr>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+USING_YOSYS_NAMESPACE
+
+#ifdef YOSYS_ENABLE_GHDL
+
+#include "ghdlsynth.h"
+
+using namespace GhdlSynth;
+
+static std::string to_str(Sname name)
+{
+ std::string res;
+ bool is_sys = false;
+
+ for (Sname pfx = name; is_valid(pfx); pfx = get_sname_prefix(pfx)) {
+ switch (get_sname_kind(pfx)) {
+ case Sname_Artificial:
+ is_sys = true;
+ // fallthrough
+ case Sname_User:
+ res = '.' + string(get_cstr(get_sname_suffix(pfx))) + res;
+ break;
+ case Sname_Version:
+ res = '%' + stringf("%u", get_sname_version(pfx)) + res;
+ break;
+ }
+ }
+ res[0] = is_sys ? '$' : '\\';
+ return res;
+}
+
+static RTLIL::SigSpec get_src(std::vector<RTLIL::Wire *> &net_map, Net n)
+{
+ log_assert(n.id != 0);
+
+ // Search if N is the output of a cell.
+ Wire *res = n.id < net_map.size() ? net_map.at(n.id) : nullptr;
+ if (res != nullptr)
+ return res;
+
+ Instance inst = get_net_parent(n);
+ switch(get_id(inst)) {
+#define IN(N) get_src(net_map, get_input_net(inst, (N)))
+ case Id_Signal:
+ case Id_Isignal:
+ case Id_Port:
+ return IN(0);
+ case Id_Uextend:
+ {
+ RTLIL::SigSpec res = IN(0);
+ res.extend_u0(get_width(n), false);
+ return res;
+ }
+ case Id_Utrunc:
+ case Id_Strunc:
+ {
+ RTLIL::SigSpec res = IN(0);
+ return res.extract(0, get_width(n));
+ }
+ case Id_Const_UB32:
+ return SigSpec(get_param_uns32(inst, 0), get_width(n));
+ case Id_Const_UL32:
+ {
+ std::vector<RTLIL::State> bits(get_width(n));
+ unsigned int val01 = get_param_uns32(inst, 0);
+ unsigned int valzx = get_param_uns32(inst, 1);
+ for (unsigned int i = 0; i < get_width(n); i++) {
+ switch(((val01 >> i)&1)+((valzx >> i)&1)*2) {
+ case 0:
+ bits[i] = RTLIL::State::S0;
+ break;
+ case 1:
+ bits[i] = RTLIL::State::S1;
+ break;
+ case 2:
+ bits[i] = RTLIL::State::Sz;
+ break;
+ case 3:
+ bits[i] = RTLIL::State::Sx;
+ break;
+ }
+ }
+ return RTLIL::SigSpec(RTLIL::Const(bits));
+ }
+ case Id_Extract:
+ {
+ RTLIL::SigSpec res = IN(0);
+ return res.extract(get_param_uns32(inst, 0), get_width(n));
+ }
+ case Id_Insert:
+ {
+ int pos = get_param_uns32(inst, 0);
+ RTLIL::SigSpec in0 = IN(0);
+ int size0 = in0.size();
+ RTLIL::SigSpec in1 = IN(1);
+ int size1 = in1.size();
+ RTLIL::SigSpec res;
+
+ for (int i = 0; i < size0; i++) {
+ res.append((i >= pos && i < pos + size1) ?
+ in1[i - pos] : in0[i]);
+ }
+ return res;
+ }
+ case Id_Concat2:
+ case Id_Concat3:
+ case Id_Concat4:
+ {
+ RTLIL::SigSpec res;
+ unsigned nbr_in = get_nbr_inputs(get_module(inst));
+ // ConcatN means { I0; I1; .. IN}, but append() adds
+ // bits to the MSB side.
+ for (unsigned i = nbr_in; i > 0; i--)
+ res.append(IN(i - 1));
+ return res;
+ }
+ case Id_Concatn:
+ {
+ RTLIL::SigSpec res;
+ unsigned nbr_in = get_param_uns32(inst, 0);
+ // ConcatN means { I0; I1; .. IN}, but append() adds
+ // bits to the MSB side.
+ for (unsigned i = nbr_in; i > 0; i--)
+ res.append(IN(i - 1));
+ return res;
+ }
+ default:
+ log_cmd_error("wire not found for %s\n", to_str(get_module_name(get_module(inst))).c_str());
+ break;
+ }
+ return SigSpec();
+}
+
+static bool is_set(std::vector<RTLIL::Wire *> &net_map, Net n)
+{
+ // If not in the map, then certainly not present.
+ if (n.id >= net_map.size())
+ return false;
+
+ Wire *res = net_map[n.id];
+ return (res != nullptr);
+}
+
+static void set_src(std::vector<RTLIL::Wire *> &net_map, Net n, Wire *wire)
+{
+ if (n.id >= net_map.size())
+ net_map.resize(n.id + 1, nullptr);
+ log_assert(net_map[n.id] == nullptr);
+ net_map[n.id] = wire;
+}
+
+static void import_module(RTLIL::Design *design, GhdlSynth::Module m)
+{
+ std::string module_name = to_str(get_module_name(m));
+
+ if (design->has(module_name)) {
+ log_cmd_error("Re-definition of module `%s'.\n", module_name.c_str());
+ return;
+ }
+
+ RTLIL::Module *module = new RTLIL::Module;
+ module->name = module_name;
+ design->add(module);
+
+ log("Importing module %s.\n", RTLIL::id2cstr(module->name));
+
+ // TODO: support submodules
+ if (is_valid(get_first_sub_module(m))) {
+ log_cmd_error("Unsupported: submodules in `%s'.\n", module_name.c_str());
+ return;
+ }
+
+ Instance self_inst = get_self_instance (m);
+ if (!is_valid(self_inst)) { // blackbox
+ module->set_bool_attribute("\\blackbox");
+
+ Port_Idx nbr_inputs = get_nbr_inputs(m);
+ for (Port_Idx idx = 0; idx < nbr_inputs; idx++) {
+ RTLIL::Wire *wire = module->addWire(
+ to_str(get_input_name(m, idx)),
+ get_input_width(m, idx));
+ wire->port_input = true;
+ }
+ Port_Idx nbr_outputs = get_nbr_outputs(m);
+ for (Port_Idx idx = 0; idx < nbr_outputs; idx++) {
+ RTLIL::Wire *wire = module->addWire(
+ to_str(get_output_name(m, idx)),
+ get_output_width(m, idx));
+ wire->port_output = true;
+ }
+ module->fixup_ports();
+ return;
+ }
+
+ // Create input ports.
+ // They correspond to ouputs of the self instance.
+ std::vector<RTLIL::Wire *> net_map;
+ Port_Idx nbr_inputs = get_nbr_inputs(m);
+ for (Port_Idx idx = 0; idx < nbr_inputs; idx++) {
+ Net port = get_output(self_inst, idx);
+
+ RTLIL::Wire *wire = module->addWire(to_str(get_input_name(m, idx)));
+ wire->port_id = idx + 1;
+ wire->port_input = true;
+ wire->width = get_width(port);
+ set_src(net_map, port, wire);
+ }
+ // Create output ports
+ Port_Idx nbr_outputs = get_nbr_outputs(m);
+ for (Port_Idx idx = 0; idx < nbr_outputs; idx++) {
+ Net output_out = get_input_net(self_inst, idx);
+
+ // Create wire
+ RTLIL::Wire *wire = module->addWire(to_str(get_output_name(m, idx)));
+ wire->port_id = nbr_inputs + idx + 1;
+ wire->port_output = true;
+ wire->width = get_width(output_out);
+ set_src(net_map, output_out, wire);
+
+ if (0) {
+ // If the driver for this output drives only this output,
+ // reuse this wire.
+ Instance output_inst = get_net_parent(output_out);
+ log_assert(get_id(get_module(output_inst)) == Id_Output);
+ Net output_drv = get_input_net(output_inst, 0);
+ if (has_one_connection (output_drv))
+ set_src(net_map, output_drv, wire);
+ }
+ }
+
+ // Create wires for outputs of (real) cells.
+ for (Instance inst = get_first_instance(m);
+ is_valid(inst);
+ inst = get_next_instance(inst)) {
+ GhdlSynth::Module im = get_module(inst);
+ Module_Id id = get_id(im);
+ switch (id) {
+ case Id_And:
+ case Id_Or:
+ case Id_Xor:
+ case Id_Nand:
+ case Id_Nor:
+ case Id_Xnor:
+ case Id_Add:
+ case Id_Sub:
+ case Id_Mux2:
+ case Id_Mux4:
+ case Id_Dff:
+ case Id_Adff:
+ case Id_Idff:
+ case Id_Eq:
+ case Id_Ne:
+ case Id_Ult:
+ case Id_Ule:
+ case Id_Ugt:
+ case Id_Uge:
+ case Id_Slt:
+ case Id_Sle:
+ case Id_Sgt:
+ case Id_Sge:
+ case Id_Not:
+ case Id_Red_Or:
+ case Id_Red_And:
+ case Id_Assert: // No output
+ case Id_Assume: // No output
+ case Id_User_None:
+ for (Port_Idx idx = 0; idx < get_nbr_outputs(im); idx++) {
+ Net o = get_output(inst, idx);
+ // The wire may have been created for an output
+ if (!is_set(net_map, o)) {
+ RTLIL::Wire *wire =
+ module->addWire(NEW_ID, get_width(o));
+ set_src(net_map, o, wire);
+ }
+ }
+ break;
+ case Id_Signal:
+ case Id_Isignal:
+ case Id_Output:
+ case Id_Port:
+ case Id_Const_UB32:
+ case Id_Const_UL32:
+ case Id_Uextend:
+ case Id_Utrunc:
+ case Id_Strunc:
+ case Id_Extract:
+ case Id_Insert:
+ case Id_Concat2:
+ case Id_Concat3:
+ case Id_Concat4:
+ case Id_Concatn:
+ // Skip: these won't create cells.
+ break;
+ case Id_Edge:
+ // The cell is ignored.
+ break;
+ default:
+ log_cmd_error("Unsupported(1): instance %s of %s.\n",
+ to_str(get_instance_name(inst)).c_str(),
+ to_str(get_module_name(get_module(inst))).c_str());
+ return;
+ }
+ }
+
+ // Create cells and connect.
+ for (Instance inst = get_first_instance(m);
+ is_valid(inst);
+ inst = get_next_instance(inst)) {
+ Module_Id id = get_id(inst);
+ Sname iname = get_instance_name(inst);
+ switch (id) {
+#define IN(N) get_src(net_map, get_input_net(inst, (N)))
+#define OUT(N) get_src(net_map, get_output(inst, (N)))
+ case Id_And:
+ module->addAnd(to_str(iname), IN(0), IN(1), OUT(0));
+ break;
+ case Id_Or:
+ module->addOr(to_str(iname), IN(0), IN(1), OUT(0));
+ break;
+ case Id_Xor:
+ module->addXor(to_str(iname), IN(0), IN(1), OUT(0));
+ break;
+ case Id_Nand:
+ {
+ SigSpec r = OUT(0);
+ RTLIL::Wire *w = module->addWire(NEW_ID, r.size());
+ module->addAnd(NEW_ID, IN(0), IN(1), w);
+ module->addNot(to_str(iname), w, r);
+ }
+ break;
+ case Id_Nor:
+ {
+ SigSpec r = OUT(0);
+ RTLIL::Wire *w = module->addWire(NEW_ID, r.size());
+ module->addOr(NEW_ID, IN(0), IN(1), w);
+ module->addNot(to_str(iname), w, r);
+ }
+ break;
+ case Id_Xnor:
+ module->addXnor(to_str(iname), IN(0), IN(1), OUT(0));
+ break;
+ case Id_Add:
+ module->addAdd(to_str(iname), IN(0), IN(1), OUT(0));
+ break;
+ case Id_Sub:
+ module->addSub(to_str(iname), IN(0), IN(1), OUT(0));
+ break;
+ case Id_Not:
+ module->addNot(to_str(iname), IN(0), OUT(0));
+ break;
+ case Id_Eq:
+ module->addEq(to_str(iname), IN(0), IN(1), OUT(0));
+ break;
+ case Id_Ne:
+ module->addNe(to_str(iname), IN(0), IN(1), OUT(0));
+ break;
+ case Id_Ult:
+ module->addLt(to_str(iname), IN(0), IN(1), OUT(0));
+ break;
+ case Id_Ule:
+ module->addLe(to_str(iname), IN(0), IN(1), OUT(0));
+ break;
+ case Id_Ugt:
+ module->addGt(to_str(iname), IN(0), IN(1), OUT(0));
+ break;
+ case Id_Uge:
+ module->addGe(to_str(iname), IN(0), IN(1), OUT(0));
+ break;
+ case Id_Slt:
+ module->addLt(to_str(iname), IN(0), IN(1), OUT(0), true);
+ break;
+ case Id_Sle:
+ module->addLe(to_str(iname), IN(0), IN(1), OUT(0), true);
+ break;
+ case Id_Sgt:
+ module->addGt(to_str(iname), IN(0), IN(1), OUT(0), true);
+ break;
+ case Id_Sge:
+ module->addGe(to_str(iname), IN(0), IN(1), OUT(0), true);
+ break;
+ case Id_Red_Or:
+ module->addReduceOr(to_str(iname), IN(0), OUT(0));
+ break;
+ case Id_Red_And:
+ module->addReduceAnd(to_str(iname), IN(0), OUT(0));
+ break;
+ case Id_Mux2:
+ module->addMux(to_str(iname), IN(1), IN(2), IN(0), OUT(0));
+ break;
+ case Id_Dff:
+ case Id_Idff:
+ module->addDff(to_str(iname), IN(0), IN(1), OUT(0));
+ // For idff, the initial value is set on the output
+ // wire.
+ if (id == Id_Idff) {
+ net_map[get_output(inst, 0).id]->attributes["\\init"] = IN(2).as_const();
+ }
+ break;
+ case Id_Adff:
+ module->addAdff(to_str(iname), IN(0), IN(2), IN(1), OUT(0), IN(3).as_const());
+ break;
+ case Id_Mux4:
+ {
+ SigSpec Sel0 = IN(0).extract(0, 1);
+ SigSpec Sel1 = IN(0).extract(1, 1);
+ SigSpec in1 = IN(1);
+ RTLIL::Wire *w0 = module->addWire(NEW_ID, in1.size());
+ RTLIL::Wire *w1 = module->addWire(NEW_ID, in1.size());
+ module->addMux(NEW_ID, in1, IN (2), Sel0, w0);
+ module->addMux(NEW_ID, IN (3), IN (4), Sel0, w1);
+ module->addMux(NEW_ID, w0, w1, Sel1, OUT (0));
+ }
+ break;
+ case Id_User_None:
+ {
+ RTLIL::Cell *cell = module->addCell(
+ to_str(iname),
+ to_str(get_module_name(get_module(inst))));
+ GhdlSynth::Module submod = get_module(inst);
+ Port_Idx nbr_inputs = get_nbr_inputs(submod);
+ for (Port_Idx idx = 0; idx < nbr_inputs; idx++) {
+ cell->setPort(to_str(get_input_name(submod, idx)), IN(idx));
+ }
+ Port_Idx nbr_outputs = get_nbr_outputs(submod);
+ for (Port_Idx idx = 0; idx < nbr_outputs; idx++) {
+ cell->setPort(to_str(get_output_name(submod, idx)), OUT(idx));
+ }
+ break;
+ }
+ case Id_Signal:
+ case Id_Isignal:
+ {
+ Net sig = get_input_net(inst, 0);
+ if (is_set(net_map, sig)) {
+ Wire *w = net_map.at(sig.id);
+ if (w)
+ module->rename(w, to_str(iname));
+ }
+ }
+ break;
+ case Id_Output:
+ case Id_Port:
+ module->connect(OUT (0), IN (0));
+ break;
+ case Id_Assert:
+ module->addAssert(to_str(iname), IN(0), State::S1);
+ break;
+ case Id_Assume:
+ module->addAssume(to_str(iname), IN(0), State::S1);
+ break;
+ case Id_Const_UB32:
+ case Id_Const_UL32:
+ case Id_Uextend:
+ case Id_Utrunc:
+ case Id_Strunc:
+ case Id_Extract:
+ case Id_Insert:
+ case Id_Concat2:
+ case Id_Concat3:
+ case Id_Concat4:
+ case Id_Edge:
+ break;
+#undef IN
+#undef OUT
+ default:
+ log_cmd_error("Unsupported(2): instance %s of %s.\n",
+ to_str(get_instance_name(inst)).c_str(),
+ to_str(get_module_name(get_module(inst))).c_str());
+ return;
+ }
+ }
+
+ // Connect output drivers to output
+ for (Port_Idx idx = 0; idx < nbr_outputs; idx++) {
+ Net output_out = get_input_net(self_inst, idx);
+ Instance output_inst = get_net_parent(output_out);
+ log_assert(get_id(get_module(output_inst)) == Id_Output);
+ Net output_drv = get_input_net(output_inst, 0);
+ if (!has_one_connection (output_drv))
+ module->connect(get_src(net_map, output_out), get_src(net_map, output_drv));
+ }
+
+ module->fixup_ports();
+}
+
+static void import_netlist(RTLIL::Design *design, GhdlSynth::Module top)
+{
+ for (GhdlSynth::Module m = get_first_sub_module (top);
+ is_valid(m);
+ m = get_next_sub_module (m)) {
+ if (get_id (m) < Id_User_None)
+ continue;
+ import_module(design, m);
+ }
+}
+
+#endif /* YOSYS_ENABLE_GHDL */
+
+YOSYS_NAMESPACE_BEGIN
+
+struct GhdlPass : public Pass {
+ GhdlPass() : Pass("ghdl", "load VHDL designs using GHDL") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+#if 0
+ log("\n");
+ log(" ghdl -a [OPTIONS] <vhdl-file>..\n");
+ log("\n");
+ log("Analyze the specified VHDL files.\n");
+ log("\n");
+#endif
+ log("\n");
+ log(" ghdl [FILES... -e] UNIT\n");
+ log("\n");
+ log("Elaborate the design and import to Yosys\n");
+ log("\n");
+ }
+#ifdef YOSYS_ENABLE_GHDL
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ static bool initialized;
+ log_header(design, "Executing GHDL.\n");
+
+ // Initialize the library.
+ if (!initialized) {
+ initialized = 1;
+ libghdl_init ();
+ ghdlsynth__init_for_ghdl_synth();
+ }
+
+ if (args.size() == 2 && args[1] == "--disp-config") {
+ ghdlcomp__disp_config();
+ }
+ else {
+ int cmd_argc = args.size() - 1;
+ const char **cmd_argv = new const char *[cmd_argc];
+ for (int i = 0; i < cmd_argc; i++)
+ cmd_argv[i] = args[i + 1].c_str();
+
+ GhdlSynth::Module top;
+ top = ghdl_synth(cmd_argc, cmd_argv);
+ if (!is_valid(top)) {
+ log_cmd_error("vhdl import failed.\n");
+ }
+ import_netlist(design, top);
+ }
+ }
+#else /* YOSYS_ENABLE_GHDL */
+ virtual void execute(std::vector<std::string>, RTLIL::Design *) {
+ log_cmd_error("This version of Yosys is built without GHDL support.\n");
+ }
+#endif
+} GhdlPass;
+
+YOSYS_NAMESPACE_END