summaryrefslogtreecommitdiff
path: root/backends
diff options
context:
space:
mode:
Diffstat (limited to 'backends')
-rw-r--r--backends/autotest/Makefile.inc3
-rw-r--r--backends/autotest/autotest.cc309
-rw-r--r--backends/ilang/Makefile.inc3
-rw-r--r--backends/ilang/ilang_backend.cc306
-rw-r--r--backends/ilang/ilang_backend.h47
-rw-r--r--backends/verilog/Makefile.inc3
-rw-r--r--backends/verilog/verilog_backend.cc905
-rw-r--r--backends/verilog/verilog_backend.h39
8 files changed, 1615 insertions, 0 deletions
diff --git a/backends/autotest/Makefile.inc b/backends/autotest/Makefile.inc
new file mode 100644
index 00000000..9308dcd4
--- /dev/null
+++ b/backends/autotest/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/autotest/autotest.o
+
diff --git a/backends/autotest/autotest.cc b/backends/autotest/autotest.cc
new file mode 100644
index 00000000..36d5650f
--- /dev/null
+++ b/backends/autotest/autotest.cc
@@ -0,0 +1,309 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * 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 "kernel/register.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+#define NUM_ITER 1000
+
+static std::string id(std::string internal_id)
+{
+ const char *str = internal_id.c_str();
+ bool do_escape = false;
+
+ if (*str == '\\')
+ str++;
+
+ if ('0' <= *str && *str <= '9')
+ do_escape = true;
+
+ for (int i = 0; str[i]; i++) {
+ if ('0' <= str[i] && str[i] <= '9')
+ continue;
+ if ('a' <= str[i] && str[i] <= 'z')
+ continue;
+ if ('A' <= str[i] && str[i] <= 'Z')
+ continue;
+ if (str[i] == '_')
+ continue;
+ do_escape = true;
+ break;
+ }
+
+ if (do_escape)
+ return "\\" + std::string(str) + " ";
+ return std::string(str);
+}
+
+static std::string idx(std::string str)
+{
+ if (str[0] == '\\')
+ return str.substr(1);
+ return str;
+}
+
+static std::string idy(std::string str1, std::string str2 = std::string(), std::string str3 = std::string())
+{
+ str1 = idx(str1);
+ if (!str2.empty())
+ str1 += "_" + idx(str2);
+ if (!str3.empty())
+ str1 += "_" + idx(str3);
+ return id(str1);
+}
+
+static void autotest(FILE *f, RTLIL::Design *design)
+{
+ fprintf(f, "module testbench;\n\n");
+
+ fprintf(f, "integer i;\n\n");
+
+ fprintf(f, "reg [31:0] xorshift128_x = 123456789;\n");
+ fprintf(f, "reg [31:0] xorshift128_y = 362436069;\n");
+ fprintf(f, "reg [31:0] xorshift128_z = 521288629;\n");
+ fprintf(f, "reg [31:0] xorshift128_w = 88675123;\n");
+ fprintf(f, "reg [31:0] xorshift128_t;\n\n");
+ fprintf(f, "task xorshift128;\n");
+ fprintf(f, "begin\n");
+ fprintf(f, "\txorshift128_t = xorshift128_x ^ (xorshift128_x << 11);\n");
+ fprintf(f, "\txorshift128_x = xorshift128_y;\n");
+ fprintf(f, "\txorshift128_y = xorshift128_z;\n");
+ fprintf(f, "\txorshift128_z = xorshift128_w;\n");
+ fprintf(f, "\txorshift128_w = xorshift128_w ^ (xorshift128_w >> 19) ^ xorshift128_t ^ (xorshift128_t >> 8);\n");
+ fprintf(f, "end\n");
+ fprintf(f, "endtask\n\n");
+
+ for (auto it = design->modules.begin(); it != design->modules.end(); it++)
+ {
+ std::map<std::string, int> signal_in;
+ std::map<std::string, std::string> signal_const;
+ std::map<std::string, int> signal_clk;
+ std::map<std::string, int> signal_out;
+
+ RTLIL::Module *mod = it->second;
+ int count_ports = 0;
+ log("Generating test bench for module `%s'.\n", it->first.c_str());
+ for (auto it2 = mod->wires.begin(); it2 != mod->wires.end(); it2++) {
+ RTLIL::Wire *wire = it2->second;
+ if (wire->port_output) {
+ count_ports++;
+ signal_out[idy("sig", mod->name, wire->name)] = wire->width;
+ fprintf(f, "wire [%d:0] %s;\n", wire->width-1, idy("sig", mod->name, wire->name).c_str());
+ } else if (wire->port_input) {
+ count_ports++;
+ bool is_clksignal = wire->attributes.count("\\gentb_clock") > 0;
+ for (auto it3 = mod->processes.begin(); it3 != mod->processes.end(); it3++)
+ for (auto it4 = it3->second->syncs.begin(); it4 != it3->second->syncs.end(); it4++) {
+ if ((*it4)->type == RTLIL::ST0 || (*it4)->type == RTLIL::ST1)
+ continue;
+ RTLIL::SigSpec &signal = (*it4)->signal;
+ for (size_t i = 0; i < signal.chunks.size(); i++) {
+ if (signal.chunks[i].wire == wire)
+ is_clksignal = true;
+ }
+ }
+ if (is_clksignal && wire->attributes.count("\\gentb_constant") == 0) {
+ signal_clk[idy("sig", mod->name, wire->name)] = wire->width;
+ } else {
+ signal_in[idy("sig", mod->name, wire->name)] = wire->width;
+ if (wire->attributes.count("\\gentb_constant") > 0)
+ signal_const[idy("sig", mod->name, wire->name)] = wire->attributes["\\gentb_constant"].as_string();
+ }
+ fprintf(f, "reg [%d:0] %s;\n", wire->width-1, idy("sig", mod->name, wire->name).c_str());
+ }
+ }
+ fprintf(f, "%s %s(\n", id(mod->name).c_str(), idy("uut", mod->name).c_str());
+ for (auto it2 = mod->wires.begin(); it2 != mod->wires.end(); it2++) {
+ RTLIL::Wire *wire = it2->second;
+ if (wire->port_output || wire->port_input)
+ fprintf(f, "\t.%s(%s)%s\n", id(wire->name).c_str(),
+ idy("sig", mod->name, wire->name).c_str(), --count_ports ? "," : "");
+ }
+ fprintf(f, ");\n\n");
+
+ fprintf(f, "task %s;\n", idy(mod->name, "reset").c_str());
+ fprintf(f, "begin\n");
+ int delay_counter = 0;
+ for (auto it = signal_in.begin(); it != signal_in.end(); it++)
+ fprintf(f, "\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2);
+ for (auto it = signal_clk.begin(); it != signal_clk.end(); it++)
+ fprintf(f, "\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2);
+ for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
+ fprintf(f, "\t#100; %s <= 1;\n", it->first.c_str());
+ fprintf(f, "\t#100; %s <= 0;\n", it->first.c_str());
+ }
+ delay_counter = 0;
+ for (auto it = signal_in.begin(); it != signal_in.end(); it++)
+ fprintf(f, "\t%s <= #%d ~0;\n", it->first.c_str(), ++delay_counter*2);
+ for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
+ fprintf(f, "\t#100; %s <= 1;\n", it->first.c_str());
+ fprintf(f, "\t#100; %s <= 0;\n", it->first.c_str());
+ }
+ delay_counter = 0;
+ for (auto it = signal_in.begin(); it != signal_in.end(); it++) {
+ if (signal_const.count(it->first) == 0)
+ continue;
+ fprintf(f, "\t%s <= #%d 'b%s;\n", it->first.c_str(), ++delay_counter*2, signal_const[it->first].c_str());
+ }
+ fprintf(f, "end\n");
+ fprintf(f, "endtask\n\n");
+
+ fprintf(f, "task %s;\n", idy(mod->name, "update_data").c_str());
+ fprintf(f, "begin\n");
+ delay_counter = 0;
+ for (auto it = signal_in.begin(); it != signal_in.end(); it++) {
+ if (signal_const.count(it->first) > 0)
+ continue;
+ fprintf(f, "\txorshift128;\n");
+ fprintf(f, "\t%s <= #%d { xorshift128_x, xorshift128_y, xorshift128_z, xorshift128_w };\n", it->first.c_str(), ++delay_counter*2);
+ }
+ fprintf(f, "end\n");
+ fprintf(f, "endtask\n\n");
+
+ fprintf(f, "task %s;\n", idy(mod->name, "update_clock").c_str());
+ fprintf(f, "begin\n");
+ if (signal_clk.size()) {
+ fprintf(f, "\txorshift128;\n");
+ fprintf(f, "\t{");
+ int total_clock_bits = 0;
+ for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
+ fprintf(f, "%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
+ total_clock_bits += it->second;
+ }
+ fprintf(f, " } = {");
+ for (auto it = signal_clk.begin(); it != signal_clk.end(); it++)
+ fprintf(f, "%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
+ fprintf(f, " } ^ (%d'b1 << (xorshift128_w %% %d));\n", total_clock_bits, total_clock_bits);
+ }
+ fprintf(f, "end\n");
+ fprintf(f, "endtask\n\n");
+
+ char shorthand = 'A';
+ std::vector<std::string> header1;
+ std::string header2 = "";
+
+ fprintf(f, "task %s;\n", idy(mod->name, "print_status").c_str());
+ fprintf(f, "begin\n");
+ fprintf(f, "\t$display(\"%%b %%b %%b %%t %%d\", {");
+ if (signal_in.size())
+ for (auto it = signal_in.begin(); it != signal_in.end(); it++) {
+ fprintf(f, "%s %s", it == signal_in.begin() ? "" : ",", it->first.c_str());
+ int len = it->second;
+ if (len > 1)
+ header2 += "/", len--;
+ while (len > 1)
+ header2 += "-", len--;
+ if (len > 0)
+ header2 += shorthand, len--;
+ header1.push_back(" " + it->first);
+ header1.back()[0] = shorthand++;
+ }
+ else {
+ fprintf(f, " 1'bx");
+ header2 += "#";
+ }
+ fprintf(f, " }, {");
+ header2 += " ";
+ if (signal_clk.size()) {
+ for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) {
+ fprintf(f, "%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
+ int len = it->second;
+ if (len > 1)
+ header2 += "/", len--;
+ while (len > 1)
+ header2 += "-", len--;
+ if (len > 0)
+ header2 += shorthand, len--;
+ header1.push_back(" " + it->first);
+ header1.back()[0] = shorthand++;
+ }
+ } else {
+ fprintf(f, " 1'bx");
+ header2 += "#";
+ }
+ fprintf(f, " }, {");
+ header2 += " ";
+ if (signal_out.size()) {
+ for (auto it = signal_out.begin(); it != signal_out.end(); it++) {
+ fprintf(f, "%s %s", it == signal_out.begin() ? "" : ",", it->first.c_str());
+ int len = it->second;
+ if (len > 1)
+ header2 += "/", len--;
+ while (len > 1)
+ header2 += "-", len--;
+ if (len > 0)
+ header2 += shorthand, len--;
+ header1.push_back(" " + it->first);
+ header1.back()[0] = shorthand++;
+ }
+ } else {
+ fprintf(f, " 1'bx");
+ header2 += "#";
+ }
+ fprintf(f, " }, $time, i);\n");
+ fprintf(f, "end\n");
+ fprintf(f, "endtask\n\n");
+
+ fprintf(f, "task %s;\n", idy(mod->name, "print_header").c_str());
+ fprintf(f, "begin\n");
+ fprintf(f, "\t$display();\n");
+ for (auto &hdr : header1)
+ fprintf(f, "\t$display(\" %s\");\n", hdr.c_str());
+ fprintf(f, "\t$display();\n");
+ fprintf(f, "\t$display(\"%s\");\n", header2.c_str());
+ fprintf(f, "end\n");
+ fprintf(f, "endtask\n\n");
+
+ fprintf(f, "task %s;\n", idy(mod->name, "test").c_str());
+ fprintf(f, "begin\n");
+ fprintf(f, "\t$display(\"\\n==== %s ====\");\n", idy(mod->name).c_str());
+ fprintf(f, "\t%s;\n", idy(mod->name, "reset").c_str());
+ fprintf(f, "\tfor (i=0; i<%d; i=i+1) begin\n", NUM_ITER);
+ fprintf(f, "\t\tif (i %% 20 == 0) %s;\n", idy(mod->name, "print_header").c_str());
+ fprintf(f, "\t\t#100; %s;\n", idy(mod->name, "update_data").c_str());
+ fprintf(f, "\t\t#100; %s;\n", idy(mod->name, "update_clock").c_str());
+ fprintf(f, "\t\t#100; %s;\n", idy(mod->name, "print_status").c_str());
+ fprintf(f, "\tend\n");
+ fprintf(f, "end\n");
+ fprintf(f, "endtask\n\n");
+ }
+
+ fprintf(f, "initial begin\n");
+ fprintf(f, "\t// $dumpfile(\"testbench.vcd\");\n");
+ fprintf(f, "\t// $dumpvars(0, testbench);\n");
+ for (auto it = design->modules.begin(); it != design->modules.end(); it++)
+ fprintf(f, "\t%s;\n", idy(it->first, "test").c_str());
+ fprintf(f, "\t$finish;\n");
+ fprintf(f, "end\n\n");
+
+ fprintf(f, "endmodule\n");
+}
+
+struct AutotestBackend : public Backend {
+ AutotestBackend() : Backend("autotest") { }
+ virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header("Executing AUTOTEST backend (auto-generate pseudo-random test benches).\n");
+ extra_args(f, filename, args, 1);
+ autotest(f, design);
+ }
+} AutotestBackend;
+
diff --git a/backends/ilang/Makefile.inc b/backends/ilang/Makefile.inc
new file mode 100644
index 00000000..52fc2b89
--- /dev/null
+++ b/backends/ilang/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/ilang/ilang_backend.o
+
diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc
new file mode 100644
index 00000000..7e283723
--- /dev/null
+++ b/backends/ilang/ilang_backend.cc
@@ -0,0 +1,306 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * 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.
+ *
+ * ---
+ *
+ * A very simple and straightforward backend for the RTLIL text
+ * representation (as understood by the 'ilang' frontend).
+ *
+ */
+
+#include "ilang_backend.h"
+#include "kernel/register.h"
+#include "kernel/log.h"
+#include <string>
+#include <assert.h>
+
+using namespace ILANG_BACKEND;
+
+void ILANG_BACKEND::dump_const(FILE *f, const RTLIL::Const &data, int width, int offset, bool autoint)
+{
+ if (width < 0)
+ width = data.bits.size() - offset;
+ if (data.str.empty() || width != (int)data.bits.size()) {
+ if (width == 32 && autoint) {
+ int32_t val = 0;
+ for (int i = 0; i < width; i++) {
+ assert(offset+i < (int)data.bits.size());
+ switch (data.bits[offset+i]) {
+ case RTLIL::S0: break;
+ case RTLIL::S1: val |= 1 << i; break;
+ default: val = -1; break;
+ }
+ }
+ if (val >= 0) {
+ fprintf(f, "%d", val);
+ return;
+ }
+ }
+ fprintf(f, "%d'", width);
+ for (int i = offset+width-1; i >= offset; i--) {
+ assert(i < (int)data.bits.size());
+ switch (data.bits[i]) {
+ case RTLIL::S0: fprintf(f, "0"); break;
+ case RTLIL::S1: fprintf(f, "1"); break;
+ case RTLIL::Sx: fprintf(f, "x"); break;
+ case RTLIL::Sz: fprintf(f, "z"); break;
+ case RTLIL::Sa: fprintf(f, "-"); break;
+ case RTLIL::Sm: fprintf(f, "m"); break;
+ }
+ }
+ } else {
+ fprintf(f, "\"");
+ for (size_t i = 0; i < data.str.size(); i++) {
+ if (data.str[i] == '\n')
+ fprintf(f, "\\n");
+ else if (data.str[i] == '\t')
+ fprintf(f, "\\t");
+ else if (data.str[i] < 32)
+ fprintf(f, "\\%03o", data.str[i]);
+ else if (data.str[i] == '"')
+ fprintf(f, "\\\"");
+ else
+ fputc(data.str[i], f);
+ }
+ fprintf(f, "\"");
+ }
+}
+
+void ILANG_BACKEND::dump_sigchunk(FILE *f, const RTLIL::SigChunk &chunk, bool autoint)
+{
+ if (chunk.wire == NULL) {
+ dump_const(f, chunk.data, chunk.width, chunk.offset, autoint);
+ } else {
+ if (chunk.width == chunk.wire->width && chunk.offset == 0)
+ fprintf(f, "%s", chunk.wire->name.c_str());
+ else if (chunk.width == 1)
+ fprintf(f, "%s [%d]", chunk.wire->name.c_str(), chunk.offset);
+ else
+ fprintf(f, "%s [%d:%d]", chunk.wire->name.c_str(), chunk.offset+chunk.width-1, chunk.offset);
+ }
+}
+
+void ILANG_BACKEND::dump_sigspec(FILE *f, const RTLIL::SigSpec &sig, bool autoint)
+{
+ if (sig.chunks.size() == 1) {
+ dump_sigchunk(f, sig.chunks[0], autoint);
+ } else {
+ fprintf(f, "{ ");
+ for (auto it = sig.chunks.rbegin(); it != sig.chunks.rend(); it++) {
+ dump_sigchunk(f, *it, false);
+ fprintf(f, " ");
+ }
+ fprintf(f, "}");
+ }
+}
+
+void ILANG_BACKEND::dump_wire(FILE *f, std::string indent, const RTLIL::Wire *wire)
+{
+ for (auto it = wire->attributes.begin(); it != wire->attributes.end(); it++) {
+ fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
+ dump_const(f, it->second);
+ fprintf(f, "\n");
+ }
+ fprintf(f, "%s" "wire ", indent.c_str());
+ if (wire->auto_width)
+ fprintf(f, "auto ");
+ if (wire->width != 1)
+ fprintf(f, "width %d ", wire->width);
+ if (wire->start_offset != 0)
+ fprintf(f, "offset %d ", wire->start_offset);
+ if (wire->port_input && !wire->port_output)
+ fprintf(f, "input %d ", wire->port_id);
+ if (!wire->port_input && wire->port_output)
+ fprintf(f, "output %d ", wire->port_id);
+ if (wire->port_input && wire->port_output)
+ fprintf(f, "inout %d ", wire->port_id);
+ fprintf(f, "%s\n", wire->name.c_str());
+}
+
+void ILANG_BACKEND::dump_memory(FILE *f, std::string indent, const RTLIL::Memory *memory)
+{
+ for (auto it = memory->attributes.begin(); it != memory->attributes.end(); it++) {
+ fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
+ dump_const(f, it->second);
+ fprintf(f, "\n");
+ }
+ fprintf(f, "%s" "memory ", indent.c_str());
+ if (memory->width != 1)
+ fprintf(f, "width %d ", memory->width);
+ if (memory->size != 0)
+ fprintf(f, "size %d ", memory->size);
+ fprintf(f, "%s\n", memory->name.c_str());
+}
+
+void ILANG_BACKEND::dump_cell(FILE *f, std::string indent, const RTLIL::Cell *cell)
+{
+ for (auto it = cell->attributes.begin(); it != cell->attributes.end(); it++) {
+ fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
+ dump_const(f, it->second);
+ fprintf(f, "\n");
+ }
+ fprintf(f, "%s" "cell %s %s\n", indent.c_str(), cell->type.c_str(), cell->name.c_str());
+ for (auto it = cell->parameters.begin(); it != cell->parameters.end(); it++) {
+ fprintf(f, "%s parameter %s ", indent.c_str(), it->first.c_str());
+ dump_const(f, it->second);
+ fprintf(f, "\n");
+ }
+ for (auto it = cell->connections.begin(); it != cell->connections.end(); it++) {
+ fprintf(f, "%s connect %s ", indent.c_str(), it->first.c_str());
+ dump_sigspec(f, it->second);
+ fprintf(f, "\n");
+ }
+ fprintf(f, "%s" "end\n", indent.c_str());
+}
+
+void ILANG_BACKEND::dump_proc_case_body(FILE *f, std::string indent, const RTLIL::CaseRule *cs)
+{
+ for (auto it = cs->actions.begin(); it != cs->actions.end(); it++)
+ {
+ fprintf(f, "%s" "assign ", indent.c_str());
+ dump_sigspec(f, it->first);
+ fprintf(f, " ");
+ dump_sigspec(f, it->second);
+ fprintf(f, "\n");
+ }
+
+ for (auto it = cs->switches.begin(); it != cs->switches.end(); it++)
+ dump_proc_switch(f, indent, *it);
+}
+
+void ILANG_BACKEND::dump_proc_switch(FILE *f, std::string indent, const RTLIL::SwitchRule *sw)
+{
+ for (auto it = sw->attributes.begin(); it != sw->attributes.end(); it++) {
+ fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
+ dump_const(f, it->second);
+ fprintf(f, "\n");
+ }
+
+ fprintf(f, "%s" "switch ", indent.c_str());
+ dump_sigspec(f, sw->signal);
+ fprintf(f, "\n");
+
+ for (auto it = sw->cases.begin(); it != sw->cases.end(); it++)
+ {
+ fprintf(f, "%s case ", indent.c_str());
+ for (size_t i = 0; i < (*it)->compare.size(); i++) {
+ if (i > 0)
+ fprintf(f, ", ");
+ dump_sigspec(f, (*it)->compare[i]);
+ }
+ fprintf(f, "\n");
+
+ dump_proc_case_body(f, indent + " ", *it);
+ }
+
+ fprintf(f, "%s" "end\n", indent.c_str());
+}
+
+void ILANG_BACKEND::dump_proc_sync(FILE *f, std::string indent, const RTLIL::SyncRule *sy)
+{
+ fprintf(f, "%s" "sync ", indent.c_str());
+ switch (sy->type) {
+ if (0) case RTLIL::ST0: fprintf(f, "low ");
+ if (0) case RTLIL::ST1: fprintf(f, "high ");
+ if (0) case RTLIL::STp: fprintf(f, "posedge ");
+ if (0) case RTLIL::STn: fprintf(f, "negedge ");
+ if (0) case RTLIL::STe: fprintf(f, "edge ");
+ dump_sigspec(f, sy->signal);
+ fprintf(f, "\n");
+ break;
+ case RTLIL::STa: fprintf(f, "always\n"); break;
+ }
+
+ for (auto it = sy->actions.begin(); it != sy->actions.end(); it++) {
+ fprintf(f, "%s update ", indent.c_str());
+ dump_sigspec(f, it->first);
+ fprintf(f, " ");
+ dump_sigspec(f, it->second);
+ fprintf(f, "\n");
+ }
+}
+
+void ILANG_BACKEND::dump_proc(FILE *f, std::string indent, const RTLIL::Process *proc)
+{
+ for (auto it = proc->attributes.begin(); it != proc->attributes.end(); it++) {
+ fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
+ dump_const(f, it->second);
+ fprintf(f, "\n");
+ }
+ fprintf(f, "%s" "process %s\n", indent.c_str(), proc->name.c_str());
+ dump_proc_case_body(f, indent + " ", &proc->root_case);
+ for (auto it = proc->syncs.begin(); it != proc->syncs.end(); it++)
+ dump_proc_sync(f, indent + " ", *it);
+ fprintf(f, "%s" "end\n", indent.c_str());
+}
+
+void ILANG_BACKEND::dump_conn(FILE *f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
+{
+ fprintf(f, "%s" "connect ", indent.c_str());
+ dump_sigspec(f, left);
+ fprintf(f, " ");
+ dump_sigspec(f, right);
+ fprintf(f, "\n");
+}
+
+void ILANG_BACKEND::dump_module(FILE *f, std::string indent, const RTLIL::Module *module)
+{
+ for (auto it = module->attributes.begin(); it != module->attributes.end(); it++) {
+ fprintf(f, "%s" "attribute %s ", indent.c_str(), it->first.c_str());
+ dump_const(f, it->second);
+ fprintf(f, "\n");
+ }
+
+ fprintf(f, "%s" "module %s\n", indent.c_str(), module->name.c_str());
+
+ for (auto it = module->wires.begin(); it != module->wires.end(); it++)
+ dump_wire(f, indent + " ", it->second);
+
+ for (auto it = module->memories.begin(); it != module->memories.end(); it++)
+ dump_memory(f, indent + " ", it->second);
+
+ for (auto it = module->cells.begin(); it != module->cells.end(); it++)
+ dump_cell(f, indent + " ", it->second);
+
+ for (auto it = module->processes.begin(); it != module->processes.end(); it++)
+ dump_proc(f, indent + " ", it->second);
+
+ for (auto it = module->connections.begin(); it != module->connections.end(); it++)
+ dump_conn(f, indent + " ", it->first, it->second);
+
+ fprintf(f, "%s" "end\n", indent.c_str());
+}
+
+void ILANG_BACKEND::dump_design(FILE *f, const RTLIL::Design *design)
+{
+ for (auto it = design->modules.begin(); it != design->modules.end(); it++) {
+ if (it != design->modules.begin())
+ fprintf(f, "\n");
+ dump_module(f, "", it->second);
+ }
+}
+
+struct IlangBackend : public Backend {
+ IlangBackend() : Backend("ilang") { }
+ virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) {
+ log_header("Executing ILANG backend.\n");
+ extra_args(f, filename, args, 1);
+ log("Output filename: %s\n", filename.c_str());
+ ILANG_BACKEND::dump_design(f, design);
+ }
+} IlangBackend;
+
diff --git a/backends/ilang/ilang_backend.h b/backends/ilang/ilang_backend.h
new file mode 100644
index 00000000..e34c4e67
--- /dev/null
+++ b/backends/ilang/ilang_backend.h
@@ -0,0 +1,47 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * 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.
+ *
+ * ---
+ *
+ * A very simple and straightforward backend for the RTLIL text
+ * representation (as understood by the 'ilang' frontend).
+ *
+ */
+
+#ifndef ILANG_BACKEND_H
+#define ILANG_BACKEND_H
+
+#include "kernel/rtlil.h"
+#include <stdio.h>
+
+namespace ILANG_BACKEND {
+ void dump_const(FILE *f, const RTLIL::Const &data, int width = -1, int offset = 0, bool autoint = true);
+ void dump_sigchunk(FILE *f, const RTLIL::SigChunk &chunk, bool autoint = true);
+ void dump_sigspec(FILE *f, const RTLIL::SigSpec &sig, bool autoint = true);
+ void dump_wire(FILE *f, std::string indent, const RTLIL::Wire *wire);
+ void dump_memory(FILE *f, std::string indent, const RTLIL::Memory *memory);
+ void dump_cell(FILE *f, std::string indent, const RTLIL::Cell *cell);
+ void dump_proc_case_body(FILE *f, std::string indent, const RTLIL::CaseRule *cs);
+ void dump_proc_switch(FILE *f, std::string indent, const RTLIL::SwitchRule *sw);
+ void dump_proc_sync(FILE *f, std::string indent, const RTLIL::SyncRule *sy);
+ void dump_proc(FILE *f, std::string indent, const RTLIL::Process *proc);
+ void dump_conn(FILE *f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right);
+ void dump_module(FILE *f, std::string indent, const RTLIL::Module *module);
+ void dump_design(FILE *f, const RTLIL::Design *design);
+}
+
+#endif
diff --git a/backends/verilog/Makefile.inc b/backends/verilog/Makefile.inc
new file mode 100644
index 00000000..c2dffef7
--- /dev/null
+++ b/backends/verilog/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/verilog/verilog_backend.o
+
diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc
new file mode 100644
index 00000000..d7990800
--- /dev/null
+++ b/backends/verilog/verilog_backend.cc
@@ -0,0 +1,905 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * 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.
+ *
+ * ---
+ *
+ * A simple and straightforward verilog backend.
+ *
+ * Note that RTLIL processes can't always be mapped easily to a Verilog
+ * process. Therefore this frontend should only be used to export a
+ * Verilog netlist (i.e. after the "proc" pass has converted all processes
+ * to logic networks and registers).
+ *
+ */
+
+#include "verilog_backend.h"
+#include "kernel/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/log.h"
+#include <assert.h>
+#include <string>
+#include <sstream>
+#include <set>
+#include <map>
+
+namespace {
+
+bool norename, noattr, attr2comment, noexpr;
+int auto_name_counter, auto_name_offset, auto_name_digits;
+std::map<std::string, int> auto_name_map;
+
+std::set<std::string> reg_wires;
+
+CellTypes reg_ct;
+RTLIL::Module *active_module;
+
+void reset_auto_counter_id(const std::string &id, bool may_rename)
+{
+ const char *str = id.c_str();
+
+ if (*str == '$' && may_rename && !norename)
+ auto_name_map[id] = auto_name_counter++;
+
+ if (str[0] != '_' && str[1] != 0)
+ return;
+ for (int i = 0; str[i] != 0; i++) {
+ if (str[i] == '_')
+ continue;
+ if (str[i] < '0' || str[i] > '9')
+ return;
+ }
+
+ int num = atoi(str+1);
+ if (num >= auto_name_offset)
+ auto_name_offset = num + 1;
+}
+
+void reset_auto_counter(RTLIL::Module *module)
+{
+ auto_name_map.clear();
+ auto_name_counter = 0;
+ auto_name_offset = 0;
+
+ reset_auto_counter_id(module->name, false);
+
+ for (auto it = module->wires.begin(); it != module->wires.end(); it++)
+ reset_auto_counter_id(it->second->name, true);
+
+ for (auto it = module->cells.begin(); it != module->cells.end(); it++) {
+ reset_auto_counter_id(it->second->name, true);
+ reset_auto_counter_id(it->second->type, false);
+ }
+
+ for (auto it = module->processes.begin(); it != module->processes.end(); it++)
+ reset_auto_counter_id(it->second->name, false);
+
+ auto_name_digits = 1;
+ for (size_t i = 10; i < auto_name_offset + auto_name_map.size(); i = i*10)
+ auto_name_digits++;
+
+ for (auto it = auto_name_map.begin(); it != auto_name_map.end(); it++)
+ log(" renaming `%s' to `_%0*d_'.\n", it->first.c_str(), auto_name_digits, auto_name_offset + it->second);
+}
+
+std::string id(std::string internal_id, bool may_rename = true)
+{
+ const char *str = internal_id.c_str();
+ bool do_escape = false;
+
+ if (may_rename && auto_name_map.count(internal_id) != 0) {
+ char buffer[100];
+ snprintf(buffer, 100, "_%0*d_", auto_name_digits, auto_name_offset + auto_name_map[internal_id]);
+ return std::string(buffer);
+ }
+
+ if (*str == '\\')
+ str++;
+
+ if ('0' <= *str && *str <= '9')
+ do_escape = true;
+
+ for (int i = 0; str[i]; i++)
+ {
+ if ('0' <= str[i] && str[i] <= '9')
+ continue;
+ if ('a' <= str[i] && str[i] <= 'z')
+ continue;
+ if ('A' <= str[i] && str[i] <= 'Z')
+ continue;
+ if (str[i] == '_')
+ continue;
+ do_escape = true;
+ break;
+ }
+
+ if (do_escape)
+ return "\\" + std::string(str) + " ";
+ return std::string(str);
+}
+
+bool is_reg_wire(RTLIL::SigSpec sig, std::string &reg_name)
+{
+ sig.optimize();
+ if (sig.chunks.size() != 1 || sig.chunks[0].wire == NULL)
+ return false;
+ if (reg_wires.count(sig.chunks[0].wire->name) == 0)
+ return false;
+ reg_name = id(sig.chunks[0].wire->name);
+ if (sig.width != sig.chunks[0].wire->width)
+ if (sig.width == 1)
+ reg_name += stringf("[%d]", sig.chunks[0].wire->start_offset + sig.chunks[0].offset);
+ else
+ reg_name += stringf("[%d]", sig.chunks[0].wire->start_offset + sig.chunks[0].offset + sig.chunks[0].width - 1,
+ sig.chunks[0].wire->start_offset + sig.chunks[0].offset);
+ return true;
+}
+
+void dump_const(FILE *f, RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false)
+{
+ if (width < 0)
+ width = data.bits.size() - offset;
+ if (data.str.empty() || width != (int)data.bits.size()) {
+ if (width == 32 && !no_decimal) {
+ uint32_t val = 0;
+ for (int i = offset+width-1; i >= offset; i--) {
+ assert(i < (int)data.bits.size());
+ if (data.bits[i] != RTLIL::S0 && data.bits[i] != RTLIL::S1)
+ goto dump_bits;
+ if (data.bits[i] == RTLIL::S1)
+ val |= 1 << (i - offset);
+ }
+ fprintf(f, "%d", (int)val);
+ } else {
+ dump_bits:
+ fprintf(f, "%d'b", width);
+ for (int i = offset+width-1; i >= offset; i--) {
+ assert(i < (int)data.bits.size());
+ switch (data.bits[i]) {
+ case RTLIL::S0: fprintf(f, "0"); break;
+ case RTLIL::S1: fprintf(f, "1"); break;
+ case RTLIL::Sx: fprintf(f, "x"); break;
+ case RTLIL::Sz: fprintf(f, "z"); break;
+ case RTLIL::Sa: fprintf(f, "z"); break;
+ case RTLIL::Sm: log_error("Found marker state in final netlist.");
+ }
+ }
+ }
+ } else {
+ fprintf(f, "\"");
+ for (size_t i = 0; i < data.str.size(); i++) {
+ if (data.str[i] == '\n')
+ fprintf(f, "\\n");
+ else if (data.str[i] == '\t')
+ fprintf(f, "\\t");
+ else if (data.str[i] < 32)
+ fprintf(f, "\\%03o", data.str[i]);
+ else if (data.str[i] == '"')
+ fprintf(f, "\\\"");
+ else
+ fputc(data.str[i], f);
+ }
+ fprintf(f, "\"");
+ }
+}
+
+void dump_sigchunk(FILE *f, RTLIL::SigChunk &chunk, bool no_decimal = false)
+{
+ if (chunk.wire == NULL) {
+ dump_const(f, chunk.data, chunk.width, chunk.offset, no_decimal);
+ } else {
+ if (chunk.width == chunk.wire->width && chunk.offset == 0)
+ fprintf(f, "%s", id(chunk.wire->name).c_str());
+ else if (chunk.width == 1)
+ fprintf(f, "%s[%d]", id(chunk.wire->name).c_str(), chunk.offset + chunk.wire->start_offset);
+ else
+ fprintf(f, "%s[%d:%d]", id(chunk.wire->name).c_str(),
+ chunk.offset + chunk.wire->start_offset + chunk.width - 1,
+ chunk.offset + chunk.wire->start_offset);
+ }
+}
+
+void dump_sigspec(FILE *f, RTLIL::SigSpec &sig)
+{
+ if (sig.chunks.size() == 1) {
+ dump_sigchunk(f, sig.chunks[0]);
+ } else {
+ fprintf(f, "{ ");
+ for (auto it = sig.chunks.rbegin(); it != sig.chunks.rend(); it++) {
+ if (it != sig.chunks.rbegin())
+ fprintf(f, ", ");
+ dump_sigchunk(f, *it, true);
+ }
+ fprintf(f, " }");
+ }
+}
+
+void dump_attributes(FILE *f, std::string indent, std::map<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n')
+{
+ if (noattr)
+ return;
+ for (auto it = attributes.begin(); it != attributes.end(); it++) {
+ fprintf(f, "%s" "%s %s", indent.c_str(), attr2comment ? "/*" : "(*", id(it->first).c_str());
+ if (it->second.bits.size() > 0) {
+ fprintf(f, " = ");
+ dump_const(f, it->second);
+ }
+ fprintf(f, " %s%c", attr2comment ? "*/" : "*)", term);
+ }
+}
+
+void dump_wire(FILE *f, std::string indent, RTLIL::Wire *wire)
+{
+ dump_attributes(f, indent, wire->attributes);
+ if (wire->port_input && !wire->port_output)
+ fprintf(f, "%s" "input %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
+ else if (!wire->port_input && wire->port_output)
+ fprintf(f, "%s" "output %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
+ else if (wire->port_input && wire->port_output)
+ fprintf(f, "%s" "inout %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
+ else
+ fprintf(f, "%s" "%s ", indent.c_str(), reg_wires.count(wire->name) ? "reg" : "wire");
+ if (wire->width != 1)
+ fprintf(f, "[%d:%d] ", wire->width - 1 + wire->start_offset, wire->start_offset);
+ fprintf(f, "%s;\n", id(wire->name).c_str());
+}
+
+void dump_memory(FILE *f, std::string indent, RTLIL::Memory *memory)
+{
+ dump_attributes(f, indent, memory->attributes);
+ fprintf(f, "%s" "reg [%d:0] %s [%d:0];\n", indent.c_str(), memory->width-1, id(memory->name).c_str(), memory->size-1);
+}
+
+void dump_cell_expr_port(FILE *f, RTLIL::Cell *cell, std::string port, bool gen_signed = true)
+{
+ if (gen_signed && cell->parameters.count("\\" + port + "_SIGNED") > 0 && cell->parameters["\\" + port + "_SIGNED"].as_bool()) {
+ fprintf(f, "$signed(");
+ dump_sigspec(f, cell->connections["\\" + port]);
+ fprintf(f, ")");
+ } else
+ dump_sigspec(f, cell->connections["\\" + port]);
+}
+
+std::string cellname(RTLIL::Cell *cell)
+{
+ if (!norename && cell->name[0] == '$' && reg_ct.cell_known(cell->type) && cell->connections.count("\\Q") > 0)
+ {
+ RTLIL::SigSpec sig = cell->connections["\\Q"];
+ if (sig.width != 1 || sig.is_fully_const())
+ goto no_special_reg_name;
+
+ sig.optimize();
+ RTLIL::Wire *wire = sig.chunks[0].wire;
+
+ if (wire->name[0] != '\\')
+ goto no_special_reg_name;
+
+ std::string cell_name = wire->name;
+
+ size_t pos = cell_name.find('[');
+ if (pos != std::string::npos)
+ cell_name = cell_name.substr(0, pos) + "_reg" + cell_name.substr(pos);
+ else
+ cell_name = cell_name + "_reg";
+
+ if (wire->width != 1)
+ cell_name += stringf("[%d]", wire->start_offset + sig.chunks[0].offset);
+
+ if (active_module && active_module->count_id(cell_name) > 0)
+ goto no_special_reg_name;
+
+ return id(cell_name);
+ }
+ else
+ {
+no_special_reg_name:
+ return id(cell->name).c_str();
+ }
+}
+
+void dump_cell_expr_uniop(FILE *f, std::string indent, RTLIL::Cell *cell, std::string op)
+{
+ fprintf(f, "%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->connections["\\Y"]);
+ fprintf(f, " = %s ", op.c_str());
+ dump_attributes(f, "", cell->attributes, ' ');
+ dump_cell_expr_port(f, cell, "A", true);
+ fprintf(f, ";\n");
+}
+
+void dump_cell_expr_binop(FILE *f, std::string indent, RTLIL::Cell *cell, std::string op)
+{
+ fprintf(f, "%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->connections["\\Y"]);
+ fprintf(f, " = ");
+ dump_cell_expr_port(f, cell, "A", true);
+ fprintf(f, " %s ", op.c_str());
+ dump_attributes(f, "", cell->attributes, ' ');
+ dump_cell_expr_port(f, cell, "B", true);
+ fprintf(f, ";\n");
+}
+
+bool dump_cell_expr(FILE *f, std::string indent, RTLIL::Cell *cell)
+{
+ if (cell->type == "$_INV_") {
+ fprintf(f, "%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->connections["\\Y"]);
+ fprintf(f, " = ");
+ fprintf(f, "~");
+ dump_attributes(f, "", cell->attributes, ' ');
+ dump_cell_expr_port(f, cell, "A", false);
+ fprintf(f, ";\n");
+ return true;
+ }
+
+ if (cell->type == "$_AND_" || cell->type == "$_OR_" || cell->type == "$_XOR_") {
+ fprintf(f, "%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->connections["\\Y"]);
+ fprintf(f, " = ");
+ dump_cell_expr_port(f, cell, "A", false);
+ fprintf(f, " ");
+ if (cell->type == "$_AND_")
+ fprintf(f, "&");
+ if (cell->type == "$_OR_")
+ fprintf(f, "|");
+ if (cell->type == "$_XOR_")
+ fprintf(f, "^");
+ dump_attributes(f, "", cell->attributes, ' ');
+ fprintf(f, " ");
+ dump_cell_expr_port(f, cell, "B", false);
+ fprintf(f, ";\n");
+ return true;
+ }
+
+ if (cell->type == "$_MUX_") {
+ fprintf(f, "%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->connections["\\Y"]);
+ fprintf(f, " = ");
+ dump_cell_expr_port(f, cell, "S", false);
+ fprintf(f, " ? ");
+ dump_attributes(f, "", cell->attributes, ' ');
+ dump_cell_expr_port(f, cell, "B", false);
+ fprintf(f, " : ");
+ dump_cell_expr_port(f, cell, "A", false);
+ fprintf(f, ";\n");
+ return true;
+ }
+
+ if (cell->type.substr(0, 6) == "$_DFF_")
+ {
+ std::string reg_name = cellname(cell);
+ bool out_is_reg_wire = is_reg_wire(cell->connections["\\Q"], reg_name);
+
+ if (!out_is_reg_wire)
+ fprintf(f, "%s" "reg %s;\n", indent.c_str(), reg_name.c_str());
+
+ dump_attributes(f, indent, cell->attributes);
+ fprintf(f, "%s" "always @(%sedge ", indent.c_str(), cell->type[6] == 'P' ? "pos" : "neg");
+ dump_sigspec(f, cell->connections["\\C"]);
+ if (cell->type[7] != '_') {
+ fprintf(f, " or %sedge ", cell->type[7] == 'P' ? "pos" : "neg");
+ dump_sigspec(f, cell->connections["\\R"]);
+ }
+ fprintf(f, ")\n");
+
+ if (cell->type[7] != '_') {
+ fprintf(f, "%s" " if (%s", indent.c_str(), cell->type[7] == 'P' ? "" : "!");
+ dump_sigspec(f, cell->connections["\\R"]);
+ fprintf(f, ")\n");
+ fprintf(f, "%s" " %s <= %c;\n", indent.c_str(), reg_name.c_str(), cell->type[8]);
+ fprintf(f, "%s" " else\n", indent.c_str());
+ }
+
+ fprintf(f, "%s" " %s <= ", indent.c_str(), reg_name.c_str());
+ dump_cell_expr_port(f, cell, "D", false);
+ fprintf(f, ";\n");
+
+ if (!out_is_reg_wire) {
+ fprintf(f, "%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->connections["\\Q"]);
+ fprintf(f, " = %s;\n", reg_name.c_str());
+ }
+
+ return true;
+ }
+
+#define HANDLE_UNIOP(_type, _operator) \
+ if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator); return true; }
+#define HANDLE_BINOP(_type, _operator) \
+ if (cell->type ==_type) { dump_cell_expr_binop(f, indent, cell, _operator); return true; }
+
+ HANDLE_UNIOP("$not", "~")
+ HANDLE_UNIOP("$pos", "+")
+ HANDLE_UNIOP("$neg", "-")
+
+ HANDLE_BINOP("$and", "&")
+ HANDLE_BINOP("$or", "|")
+ HANDLE_BINOP("$xor", "^")
+ HANDLE_BINOP("$xnor", "~^")
+
+ HANDLE_UNIOP("$reduce_and", "&")
+ HANDLE_UNIOP("$reduce_or", "|")
+ HANDLE_UNIOP("$reduce_xor", "^")
+ HANDLE_UNIOP("$reduce_xnor", "~^")
+ HANDLE_UNIOP("$reduce_bool", "|")
+
+ HANDLE_BINOP("$shl", "<<")
+ HANDLE_BINOP("$shr", ">>")
+ HANDLE_BINOP("$sshl", "<<<")
+ HANDLE_BINOP("$sshr", ">>>")
+
+ HANDLE_BINOP("$lt", "<")
+ HANDLE_BINOP("$le", "<=")
+ HANDLE_BINOP("$eq", "==")
+ HANDLE_BINOP("$ne", "!=")
+ HANDLE_BINOP("$ge", ">=")
+ HANDLE_BINOP("$gt", ">")
+
+ HANDLE_BINOP("$add", "+")
+ HANDLE_BINOP("$sub", "-")
+ HANDLE_BINOP("$mul", "*")
+ HANDLE_BINOP("$div", "/")
+ HANDLE_BINOP("$mod", "%")
+ HANDLE_BINOP("$pow", "**")
+
+ HANDLE_UNIOP("$logic_not", "!")
+ HANDLE_BINOP("$logic_and", "&&")
+ HANDLE_BINOP("$logic_or", "||")
+
+#undef HANDLE_UNIOP
+#undef HANDLE_BINOP
+
+ if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$pmux_safe")
+ {
+ int width = cell->parameters["\\WIDTH"].as_int();
+ int s_width = cell->connections["\\S"].width;
+ std::string reg_name = cellname(cell);
+ fprintf(f, "%s" "reg [%d:0] %s;\n", indent.c_str(), width-1, reg_name.c_str());
+
+ dump_attributes(f, indent, cell->attributes);
+ if (!noattr)
+ fprintf(f, "%s" "(* parallel_case *)\n", indent.c_str());
+ fprintf(f, "%s" "always @*\n", indent.c_str());
+ fprintf(f, "%s" " casez (", indent.c_str());
+ dump_sigspec(f, cell->connections["\\S"]);
+ fprintf(f, noattr ? ") // synopsys parallel_case\n" : ")\n");
+
+ for (int i = 0; i < s_width; i++)
+ {
+ fprintf(f, "%s" " %d'b", indent.c_str(), s_width);
+
+ for (int j = s_width-1; j >= 0; j--)
+ fprintf(f, "%c", j == i ? '1' : cell->type == "$pmux_safe" ? '0' : '?');
+
+ fprintf(f, ":\n");
+ fprintf(f, "%s" " %s = ", indent.c_str(), reg_name.c_str());
+
+ RTLIL::SigSpec s = cell->connections["\\B"].extract(i * width, width);
+ dump_sigspec(f, s);
+ fprintf(f, ";\n");
+ }
+
+ fprintf(f, "%s" " default:\n", indent.c_str());
+ fprintf(f, "%s" " %s = ", indent.c_str(), reg_name.c_str());
+ dump_sigspec(f, cell->connections["\\A"]);
+ fprintf(f, ";\n");
+
+ fprintf(f, "%s" " endcase\n", indent.c_str());
+ fprintf(f, "%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->connections["\\Y"]);
+ fprintf(f, " = %s;\n", reg_name.c_str());
+ return true;
+ }
+
+ if (cell->type == "$dff" || cell->type == "$adff")
+ {
+ RTLIL::SigSpec sig_clk, sig_arst, val_arst;
+ bool pol_clk, pol_arst = false;
+
+ sig_clk = cell->connections["\\CLK"];
+ pol_clk = cell->parameters["\\CLK_POLARITY"].as_bool();
+
+ if (cell->type == "$adff") {
+ sig_arst = cell->connections["\\ARST"];
+ pol_arst = cell->parameters["\\ARST_POLARITY"].as_bool();
+ val_arst = RTLIL::SigSpec(cell->parameters["\\ARST_VALUE"]);
+ }
+
+ std::string reg_name = cellname(cell);
+ bool out_is_reg_wire = is_reg_wire(cell->connections["\\Q"], reg_name);
+
+ if (!out_is_reg_wire)
+ fprintf(f, "%s" "reg [%d:0] %s;\n", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str());
+
+ fprintf(f, "%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
+ dump_sigspec(f, sig_clk);
+ if (cell->type == "$adff") {
+ fprintf(f, " or %sedge ", pol_arst ? "pos" : "neg");
+ dump_sigspec(f, sig_arst);
+ }
+ fprintf(f, ")\n");
+
+ if (cell->type == "$adff") {
+ fprintf(f, "%s" " if (%s", indent.c_str(), pol_arst ? "" : "!");
+ dump_sigspec(f, sig_arst);
+ fprintf(f, ")\n");
+ fprintf(f, "%s" " %s <= ", indent.c_str(), reg_name.c_str());
+ dump_sigspec(f, val_arst);
+ fprintf(f, ";\n");
+ fprintf(f, "%s" " else\n", indent.c_str());
+ }
+
+ fprintf(f, "%s" " %s <= ", indent.c_str(), reg_name.c_str());
+ dump_cell_expr_port(f, cell, "D", false);
+ fprintf(f, ";\n");
+
+ if (!out_is_reg_wire) {
+ fprintf(f, "%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->connections["\\Q"]);
+ fprintf(f, " = %s;\n", reg_name.c_str());
+ }
+
+ return true;
+ }
+
+ // FIXME: $memrd, $memwr, $mem, $fsm
+
+ return false;
+}
+
+void dump_cell(FILE *f, std::string indent, RTLIL::Cell *cell)
+{
+ if (cell->type[0] == '$' && !noexpr) {
+ if (dump_cell_expr(f, indent, cell))
+ return;
+ }
+
+ dump_attributes(f, indent, cell->attributes);
+ fprintf(f, "%s" "%s", indent.c_str(), id(cell->type, false).c_str());
+
+ if (cell->parameters.size() > 0) {
+ fprintf(f, " #(");
+ for (auto it = cell->parameters.begin(); it != cell->parameters.end(); it++) {
+ if (it != cell->parameters.begin())
+ fprintf(f, ",");
+ fprintf(f, "\n%s .%s(", indent.c_str(), id(it->first).c_str());
+ dump_const(f, it->second);
+ fprintf(f, ")");
+ }
+ fprintf(f, "\n%s" ")", indent.c_str());
+ }
+
+ std::string cell_name = cellname(cell);
+ if (cell_name != id(cell->name))
+ fprintf(f, " %s /* %s */ (", cell_name.c_str(), id(cell->name).c_str());
+ else
+ fprintf(f, " %s (", cell_name.c_str());
+
+ bool first_arg = true;
+ std::set<std::string> numbered_ports;
+ for (int i = 1; true; i++) {
+ char str[16];
+ snprintf(str, 16, "$%d", i);
+ for (auto it = cell->connections.begin(); it != cell->connections.end(); it++) {
+ if (it->first != str)
+ continue;
+ if (!first_arg)
+ fprintf(f, ",");
+ first_arg = false;
+ fprintf(f, "\n%s ", indent.c_str());
+ dump_sigspec(f, it->second);
+ numbered_ports.insert(it->first);
+ goto found_numbered_port;
+ }
+ break;
+ found_numbered_port:;
+ }
+ for (auto it = cell->connections.begin(); it != cell->connections.end(); it++) {
+ if (numbered_ports.count(it->first))
+ continue;
+ if (!first_arg)
+ fprintf(f, ",");
+ first_arg = false;
+ fprintf(f, "\n%s .%s(", indent.c_str(), id(it->first).c_str());
+ if (it->second.width > 0)
+ dump_sigspec(f, it->second);
+ fprintf(f, ")");
+ }
+ fprintf(f, "\n%s" ");\n", indent.c_str());
+}
+
+void dump_conn(FILE *f, std::string indent, RTLIL::SigSpec &left, RTLIL::SigSpec &right)
+{
+ fprintf(f, "%s" "assign ", indent.c_str());
+ dump_sigspec(f, left);
+ fprintf(f, " = ");
+ dump_sigspec(f, right);
+ fprintf(f, ";\n");
+}
+
+void dump_proc_switch(FILE *f, std::string indent, RTLIL::SwitchRule *sw);
+
+void dump_case_body(FILE *f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false)
+{
+ int number_of_stmts = cs->switches.size() + cs->actions.size();
+
+ if (!omit_trailing_begin && number_of_stmts >= 2)
+ fprintf(f, "%s" "begin\n", indent.c_str());
+
+ for (auto it = cs->actions.begin(); it != cs->actions.end(); it++) {
+ if (it->first.width == 0)
+ continue;
+ fprintf(f, "%s ", indent.c_str());
+ dump_sigspec(f, it->first);
+ fprintf(f, " = ");
+ dump_sigspec(f, it->second);
+ fprintf(f, ";\n");
+ }
+
+ for (auto it = cs->switches.begin(); it != cs->switches.end(); it++)
+ dump_proc_switch(f, indent + " ", *it);
+
+ if (!omit_trailing_begin && number_of_stmts == 0)
+ fprintf(f, "%s /* empty */;\n", indent.c_str());
+
+ if (omit_trailing_begin || number_of_stmts >= 2)
+ fprintf(f, "%s" "end\n", indent.c_str());
+}
+
+void dump_proc_switch(FILE *f, std::string indent, RTLIL::SwitchRule *sw)
+{
+ if (sw->signal.width == 0) {
+ fprintf(f, "%s" "begin\n", indent.c_str());
+ for (auto it = sw->cases.begin(); it != sw->cases.end(); it++) {
+ if ((*it)->compare.size() == 0)
+ dump_case_body(f, indent + " ", *it);
+ }
+ fprintf(f, "%s" "end\n", indent.c_str());
+ return;
+ }
+
+ fprintf(f, "%s" "casez (", indent.c_str());
+ dump_sigspec(f, sw->signal);
+ fprintf(f, ")\n");
+
+ for (auto it = sw->cases.begin(); it != sw->cases.end(); it++) {
+ fprintf(f, "%s ", indent.c_str());
+ if ((*it)->compare.size() == 0)
+ fprintf(f, "default");
+ else {
+ for (size_t i = 0; i < (*it)->compare.size(); i++) {
+ if (i > 0)
+ fprintf(f, ", ");
+ dump_sigspec(f, (*it)->compare[i]);
+ }
+ }
+ fprintf(f, ":\n");
+ dump_case_body(f, indent + " ", *it);
+ }
+
+ fprintf(f, "%s" "endcase\n", indent.c_str());
+}
+
+void case_body_find_regs(RTLIL::CaseRule *cs)
+{
+ for (auto it = cs->switches.begin(); it != cs->switches.end(); it++)
+ for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++)
+ case_body_find_regs(*it2);
+
+ for (auto it = cs->actions.begin(); it != cs->actions.end(); it++) {
+ for (size_t i = 0; i < it->first.chunks.size(); i++)
+ if (it->first.chunks[i].wire)
+ reg_wires.insert(it->first.chunks[i].wire->name);
+ }
+}
+
+void dump_process(FILE *f, std::string indent, RTLIL::Process *proc, bool find_regs = false)
+{
+ if (find_regs) {
+ case_body_find_regs(&proc->root_case);
+ for (auto it = proc->syncs.begin(); it != proc->syncs.end(); it++)
+ for (auto it2 = (*it)->actions.begin(); it2 != (*it)->actions.end(); it2++) {
+ for (size_t i = 0; i < it2->first.chunks.size(); i++)
+ if (it2->first.chunks[i].wire)
+ reg_wires.insert(it2->first.chunks[i].wire->name);
+ }
+ return;
+ }
+
+ fprintf(f, "%s" "always @* begin\n", indent.c_str());
+ dump_case_body(f, indent, &proc->root_case, true);
+
+ std::string backup_indent = indent;
+
+ for (size_t i = 0; i < proc->syncs.size(); i++)
+ {
+ RTLIL::SyncRule *sync = proc->syncs[i];
+ indent = backup_indent;
+
+ if (sync->type == RTLIL::STa) {
+ fprintf(f, "%s" "always @* begin\n", indent.c_str());
+ } else {
+ fprintf(f, "%s" "always @(", indent.c_str());
+ if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1)
+ fprintf(f, "posedge ");
+ if (sync->type == RTLIL::STn || sync->type == RTLIL::ST0)
+ fprintf(f, "negedge ");
+ dump_sigspec(f, sync->signal);
+ fprintf(f, ") begin\n");
+ }
+ std::string ends = indent + "end\n";
+ indent += " ";
+
+ if (sync->type == RTLIL::ST0 || sync->type == RTLIL::ST1) {
+ fprintf(f, "%s" "if (%s", indent.c_str(), sync->type == RTLIL::ST0 ? "!" : "");
+ dump_sigspec(f, sync->signal);
+ fprintf(f, ") begin\n");
+ ends = indent + "end\n" + ends;
+ indent += " ";
+ }
+
+ if (sync->type == RTLIL::STp || sync->type == RTLIL::STn) {
+ for (size_t j = 0; j < proc->syncs.size(); j++) {
+ RTLIL::SyncRule *sync2 = proc->syncs[j];
+ if (sync2->type == RTLIL::ST0 || sync2->type == RTLIL::ST1) {
+ fprintf(f, "%s" "if (%s", indent.c_str(), sync2->type == RTLIL::ST1 ? "!" : "");
+ dump_sigspec(f, sync2->signal);
+ fprintf(f, ") begin\n");
+ ends = indent + "end\n" + ends;
+ indent += " ";
+ }
+ }
+ }
+
+ for (auto it = sync->actions.begin(); it != sync->actions.end(); it++) {
+ if (it->first.width == 0)
+ continue;
+ fprintf(f, "%s ", indent.c_str());
+ dump_sigspec(f, it->first);
+ fprintf(f, " <= ");
+ dump_sigspec(f, it->second);
+ fprintf(f, ";\n");
+ }
+
+ fprintf(f, "%s", ends.c_str());
+ }
+}
+
+void dump_module(FILE *f, std::string indent, RTLIL::Module *module)
+{
+ reg_wires.clear();
+ reset_auto_counter(module);
+ active_module = module;
+
+ for (auto it = module->processes.begin(); it != module->processes.end(); it++)
+ dump_process(f, indent + " ", it->second, true);
+
+ if (!noexpr)
+ {
+ std::set<std::pair<RTLIL::Wire*,int>> reg_bits;
+ for (auto &it : module->cells)
+ {
+ RTLIL::Cell *cell = it.second;
+ if (!reg_ct.cell_known(cell->type) || cell->connections.count("\\Q") == 0)
+ continue;
+
+ RTLIL::SigSpec sig = cell->connections["\\Q"];
+ sig.optimize();
+
+ if (sig.chunks.size() == 1 && sig.chunks[0].wire)
+ for (int i = 0; i < sig.chunks[0].width; i++)
+ reg_bits.insert(std::pair<RTLIL::Wire*,int>(sig.chunks[0].wire, sig.chunks[0].offset+i));
+ }
+ for (auto &it : module->wires)
+ {
+ RTLIL::Wire *wire = it.second;
+ for (int i = 0; i < wire->width; i++)
+ if (reg_bits.count(std::pair<RTLIL::Wire*,int>(wire, i)) == 0)
+ goto this_wire_aint_reg;
+ reg_wires.insert(wire->name);
+ this_wire_aint_reg:;
+ }
+ }
+
+ dump_attributes(f, indent, module->attributes);
+ fprintf(f, "%s" "module %s(", indent.c_str(), id(module->name, false).c_str());
+ bool keep_running = true;
+ for (int port_id = 1; keep_running; port_id++) {
+ keep_running = false;
+ for (auto it = module->wires.begin(); it != module->wires.end(); it++) {
+ RTLIL::Wire *wire = it->second;
+ if (wire->port_id == port_id) {
+ if (port_id != 1)
+ fprintf(f, ", ");
+ fprintf(f, "%s", id(wire->name).c_str());
+ keep_running = true;
+ continue;
+ }
+ }
+ }
+ fprintf(f, ");\n");
+
+ for (auto it = module->wires.begin(); it != module->wires.end(); it++)
+ dump_wire(f, indent + " ", it->second);
+
+ for (auto it = module->memories.begin(); it != module->memories.end(); it++)
+ dump_memory(f, indent + " ", it->second);
+
+ for (auto it = module->cells.begin(); it != module->cells.end(); it++)
+ dump_cell(f, indent + " ", it->second);
+
+ for (auto it = module->processes.begin(); it != module->processes.end(); it++)
+ dump_process(f, indent + " ", it->second);
+
+ for (auto it = module->connections.begin(); it != module->connections.end(); it++)
+ dump_conn(f, indent + " ", it->first, it->second);
+
+ fprintf(f, "%s" "endmodule\n", indent.c_str());
+ active_module = NULL;
+}
+
+} /* namespace */
+
+struct VerilogBackend : public Backend {
+ VerilogBackend() : Backend("verilog") { }
+ virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header("Executing Verilog backend.\n");
+
+ norename = false;
+ noattr = false;
+ attr2comment = false;
+ noexpr = false;
+
+ reg_ct.clear();
+ reg_ct.setup_stdcells_mem();
+ reg_ct.cell_types.insert("$dff");
+ reg_ct.cell_types.insert("$adff");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-norename") {
+ norename = true;
+ continue;
+ }
+ if (arg == "-noattr") {
+ noattr = true;
+ continue;
+ }
+ if (arg == "-attr2comment") {
+ attr2comment = true;
+ continue;
+ }
+ if (arg == "-noexpr") {
+ noexpr = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ for (auto it = design->modules.begin(); it != design->modules.end(); it++) {
+ log("Dumping module `%s'.\n", it->first.c_str());
+ if (it != design->modules.begin())
+ fprintf(f, "\n");
+ dump_module(f, "", it->second);
+ }
+
+ reg_ct.clear();
+ }
+} VerilogBackend;
+
diff --git a/backends/verilog/verilog_backend.h b/backends/verilog/verilog_backend.h
new file mode 100644
index 00000000..c40830ef
--- /dev/null
+++ b/backends/verilog/verilog_backend.h
@@ -0,0 +1,39 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * 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.
+ *
+ * ---
+ *
+ * A simple and straightforward verilog backend.
+ *
+ * Note that RTLIL processes can't always be mapped easily to a Verilog
+ * process. Therefore this frontend should only be used to export a
+ * Verilog netlist (i.e. after the "proc" pass has converted all processes
+ * to logic networks and registers).
+ *
+ */
+
+#ifndef VERILOG_BACKEND_H
+#define VERILOG_BACKEND_H
+
+#include "kernel/rtlil.h"
+#include <stdio.h>
+
+namespace VERILOG_BACKEND {
+ void verilog_backend(FILE *f, std::vector<std::string> args, RTLIL::Design *design);
+}
+
+#endif