Update cells supported for verilog to FIRRTL conversion.
authorJim Lawson <ucbjrl@berkeley.edu>
Fri, 15 Feb 2019 19:14:17 +0000 (11:14 -0800)
committerJim Lawson <ucbjrl@berkeley.edu>
Fri, 15 Feb 2019 19:14:17 +0000 (11:14 -0800)
Issue warning messages for missing parameterized modules and attempts to set initial values.
Replace simple "if (cell-type)" with "else if" chain.
Fix FIRRTL shift handling.
Add support for parameterized modules, $shift, $shiftx.
Handle default output file.
Deal with no top module.
Automatically run pmuxtree pass.
Allow EXTRA_FLAGS and SEED parameters to be set in the environment for tests/tools/autotest.mk.
Support FIRRTL regression testing in tests/tools/autotest.sh
Add xfirrtl files to test directories to exclude files from FIRRTL regression tests that are known to fail.

backends/firrtl/firrtl.cc
tests/asicworld/xfirrtl [new file with mode: 0644]
tests/simple/xfirrtl [new file with mode: 0644]
tests/tools/autotest.mk
tests/tools/autotest.sh

index 32410a65139a35db734f5d00978251b2c208fe47..7ca5dc756dde2f786f5266e4e4d50acf8c38d3ca 100644 (file)
 #include "kernel/celltypes.h"
 #include "kernel/cellaigs.h"
 #include "kernel/log.h"
+#include <algorithm>
 #include <string>
+#include <regex>
+#include <vector>
+#include <cmath>
 
 USING_YOSYS_NAMESPACE
 PRIVATE_NAMESPACE_BEGIN
@@ -37,6 +41,7 @@ static const FDirection FD_NODIRECTION = 0x0;
 static const FDirection FD_IN = 0x1;
 static const FDirection FD_OUT = 0x2;
 static const FDirection FD_INOUT = 0x3;
+static const int FIRRTL_MAX_DSH_WIDTH_ERROR = 20; // For historic reasons, this is actually one greater than the maximum allowed shift width
 
 // Get a port direction with respect to a specific module.
 FDirection getPortFDirection(IdString id, Module *module)
@@ -91,6 +96,23 @@ const char *make_id(IdString id)
        return namecache.at(id).c_str();
 }
 
+static std::vector<string> Tokenize( const string str, const std::regex regex )
+{
+       using namespace std;
+
+       std::vector<string> result;
+
+       sregex_token_iterator it( str.begin(), str.end(), regex, -1 );
+       sregex_token_iterator reg_end;
+
+       for ( ; it != reg_end; ++it ) {
+               if ( !it->str().empty() ) //token could be empty:check
+                       result.emplace_back( it->str() );
+       }
+
+       return result;
+}
+
 struct FirrtlWorker
 {
        Module *module;
@@ -173,6 +195,26 @@ struct FirrtlWorker
        void process_instance(RTLIL::Cell *cell, vector<string> &wire_exprs)
        {
                std::string cell_type = fid(cell->type);
+               std::string instanceOf;
+               // If this is a parameterized module, its parent module is encoded in the cell type
+               if (cell->type.substr(0, 8) == "$paramod")
+               {
+                       std::string::iterator it;
+                       for (it = cell_type.begin(); it < cell_type.end(); it++)
+                       {
+                               switch (*it) {
+                                       case '\\': /* FALL_THROUGH */
+                                       case '=': /* FALL_THROUGH */
+                                       case '\'': /* FALL_THROUGH */
+                                       case '$': instanceOf.append("_"); break;
+                                       default: instanceOf.append(1, *it); break;
+                               }
+                       }
+               }
+               else
+               {
+                       instanceOf = cell_type;
+               }
 
                std::string cell_name = cellname(cell);
                std::string cell_name_comment;
@@ -182,7 +224,13 @@ struct FirrtlWorker
                        cell_name_comment = "";
                // Find the module corresponding to this instance.
                auto instModule = design->module(cell->type);
-               wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), cell_type.c_str()));
+               // If there is no instance for this, just return.
+               if (instModule == NULL)
+               {
+                       log_warning("No instance for %s.%s\n", cell_type.c_str(), cell_name.c_str());
+                       return;
+               }
+               wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceOf.c_str()));
 
                for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
                        if (it->second.size() > 0) {
@@ -194,20 +242,20 @@ struct FirrtlWorker
                                std::string source, sink;
                                switch (dir) {
                                        case FD_INOUT:
-                                               log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", log_id(cell_type), log_signal(it->second));
+                                               log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", cell_type.c_str(), log_signal(it->second));
                                        case FD_OUT:
                                                source = firstName;
                                                sink = secondName;
                                                break;
                                        case FD_NODIRECTION:
-                                               log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", log_id(cell_type), log_signal(it->second));
+                                               log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", cell_type.c_str(), log_signal(it->second));
                                                /* FALL_THROUGH */
                                        case FD_IN:
                                                source = secondName;
                                                sink = firstName;
                                                break;
                                        default:
-                                               log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", log_id(cell_type), log_signal(it->second), dir);
+                                               log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", cell_type.c_str(), log_signal(it->second), dir);
                                                break;
                                }
                                wire_exprs.push_back(stringf("\n%s%s <= %s", indent.c_str(), sink.c_str(), source.c_str()));
@@ -217,6 +265,20 @@ struct FirrtlWorker
 
        }
 
+       // Given an expression for a shift amount, and a maximum width,
+       //  generate the FIRRTL expression for equivalent dynamic shift taking into account FIRRTL shift semantics.
+       std::string gen_dshl(const string b_expr, const int b_padded_width)
+       {
+               string result = b_expr;
+               if (b_padded_width >= FIRRTL_MAX_DSH_WIDTH_ERROR) {
+                       int max_shift_width_bits = FIRRTL_MAX_DSH_WIDTH_ERROR - 1;
+                       string max_shift_string = stringf("UInt<%d>(%d)", max_shift_width_bits, (1<<max_shift_width_bits) - 1);
+                       // Deal with the difference in semantics between FIRRTL and verilog
+                       result = stringf("mux(gt(%s, %s), %s, bits(%s, %d, 0))", b_expr.c_str(), max_shift_string.c_str(), max_shift_string.c_str(), b_expr.c_str(), max_shift_width_bits - 1);
+               }
+               return result;
+       }
+
        void run()
        {
                f << stringf("  module %s:\n", make_id(module->name));
@@ -225,6 +287,12 @@ struct FirrtlWorker
                for (auto wire : module->wires())
                {
                        const auto wireName = make_id(wire->name);
+                       // If a wire has initial data, issue a warning since FIRRTL doesn't currently support it.
+                       if (wire->attributes.count("\\init")) {
+                               log_warning("Initial value (%s) for (%s.%s) not supported\n",
+                                                       wire->attributes.at("\\init").as_string().c_str(),
+                                                       log_id(module), log_id(wire));
+                       }
                        if (wire->port_id)
                        {
                                if (wire->port_input && wire->port_output)
@@ -240,7 +308,8 @@ struct FirrtlWorker
 
                for (auto cell : module->cells())
                {
-                 // Is this cell is a module instance?
+                       bool extract_y_bits = false;            // Assume no extraction of final bits will be required.
+                   // Is this cell is a module instance?
                        if (cell->type[0] != '$')
                        {
                                process_instance(cell, wire_exprs);
@@ -264,21 +333,21 @@ struct FirrtlWorker
                                }
 
                                string primop;
-                                bool always_uint = false;
+                               bool always_uint = false;
                                if (cell->type == "$not") primop = "not";
-                               if (cell->type == "$neg") primop = "neg";
-                               if (cell->type == "$logic_not") {
+                               else if (cell->type == "$neg") primop = "neg";
+                               else if (cell->type == "$logic_not") {
                                         primop = "eq";
                                         a_expr = stringf("%s, UInt(0)", a_expr.c_str());
                                 }
-                               if (cell->type == "$reduce_and") primop = "andr";
-                               if (cell->type == "$reduce_or") primop = "orr";
-                               if (cell->type == "$reduce_xor") primop = "xorr";
-                               if (cell->type == "$reduce_xnor") {
+                               else if (cell->type == "$reduce_and") primop = "andr";
+                               else if (cell->type == "$reduce_or") primop = "orr";
+                               else if (cell->type == "$reduce_xor") primop = "xorr";
+                               else if (cell->type == "$reduce_xnor") {
                                         primop = "not";
                                         a_expr = stringf("xorr(%s)", a_expr.c_str());
                                 }
-                               if (cell->type == "$reduce_bool") {
+                               else if (cell->type == "$reduce_bool") {
                                        primop = "neq";
                                        // Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand.
                                        bool a_signed = cell->parameters.at("\\A_SIGNED").as_bool();
@@ -297,14 +366,16 @@ struct FirrtlWorker
                                continue;
                        }
                        if (cell->type.in("$add", "$sub", "$mul", "$div", "$mod", "$xor", "$and", "$or", "$eq", "$eqx",
-                                        "$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$shr", "$sshr", "$sshl", "$shl",
-                                        "$logic_and", "$logic_or"))
+                                                         "$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$shr", "$sshr", "$sshl", "$shl",
+                                                         "$logic_and", "$logic_or"))
                        {
                                string y_id = make_id(cell->name);
                                bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool();
                                int y_width =  cell->parameters.at("\\Y_WIDTH").as_int();
                                string a_expr = make_expr(cell->getPort("\\A"));
+                               int a_padded_width = cell->parameters.at("\\A_WIDTH").as_int();
                                string b_expr = make_expr(cell->getPort("\\B"));
+                               int b_padded_width = cell->parameters.at("\\B_WIDTH").as_int();
                                wire_decls.push_back(stringf("    wire %s: UInt<%d>\n", y_id.c_str(), y_width));
 
                                if (cell->parameters.at("\\A_SIGNED").as_bool()) {
@@ -315,67 +386,93 @@ struct FirrtlWorker
                                        if (cell->parameters.at("\\B_SIGNED").as_bool()) {
                                                b_expr = "asSInt(" + b_expr + ")";
                                        }
-                                       b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width);
+                                       if (b_padded_width < y_width) {
+                                               auto b_sig = cell->getPort("\\B");
+                                               b_padded_width = y_width;
+                                       }
                                }
 
-                               a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
+                               auto a_sig = cell->getPort("\\A");
 
                                if (cell->parameters.at("\\A_SIGNED").as_bool()  & (cell->type == "$shr")) {
                                        a_expr = "asUInt(" + a_expr + ")";
                                }
 
                                string primop;
-                                bool always_uint = false;
+                               bool always_uint = false;
                                if (cell->type == "$add") primop = "add";
-                               if (cell->type == "$sub") primop = "sub";
-                               if (cell->type == "$mul") primop = "mul";
-                               if (cell->type == "$div") primop = "div";
-                               if (cell->type == "$mod") primop = "rem";
-                               if (cell->type == "$and") {
+                               else if (cell->type == "$sub") primop = "sub";
+                               else if (cell->type == "$mul") primop = "mul";
+                               else if (cell->type == "$div") primop = "div";
+                               else if (cell->type == "$mod") primop = "rem";
+                               else if (cell->type == "$and") {
                                         primop = "and";
                                         always_uint = true;
                                 }
-                               if (cell->type == "$or" ) {
+                               else if (cell->type == "$or" ) {
                                         primop =  "or";
                                         always_uint = true;
                                 }
-                               if (cell->type == "$xor") {
+                               else if (cell->type == "$xor") {
                                         primop = "xor";
                                         always_uint = true;
                                 }
-                               if ((cell->type == "$eq") | (cell->type == "$eqx")) {
+                               else if ((cell->type == "$eq") | (cell->type == "$eqx")) {
                                         primop = "eq";
                                         always_uint = true;
                                 }
-                               if ((cell->type == "$ne") | (cell->type == "$nex")) {
+                               else if ((cell->type == "$ne") | (cell->type == "$nex")) {
                                         primop = "neq";
                                         always_uint = true;
                                 }
-                               if (cell->type == "$gt") {
+                               else if (cell->type == "$gt") {
                                         primop = "gt";
                                         always_uint = true;
                                 }
-                               if (cell->type == "$ge") {
+                               else if (cell->type == "$ge") {
                                         primop = "geq";
                                         always_uint = true;
                                 }
-                               if (cell->type == "$lt") {
+                               else if (cell->type == "$lt") {
                                         primop = "lt";
                                         always_uint = true;
                                 }
-                               if (cell->type == "$le") {
+                               else if (cell->type == "$le") {
                                         primop = "leq";
                                         always_uint = true;
                                 }
-                               if ((cell->type == "$shl") | (cell->type == "$sshl")) primop = "dshl";
-                               if ((cell->type == "$shr") | (cell->type == "$sshr")) primop = "dshr";
-                               if ((cell->type == "$logic_and")) {
+                               else if ((cell->type == "$shl") | (cell->type == "$sshl")) {
+                                       // FIRRTL will widen the result (y) by the amount of the shift.
+                                       // We'll need to offset this by extracting the un-widened portion as Verilog would do.
+                                       extract_y_bits = true;
+                                       // Is the shift amount constant?
+                                       auto b_sig = cell->getPort("\\B");
+                                       if (b_sig.is_fully_const()) {
+                                               primop = "shl";
+                                       } else {
+                                               primop = "dshl";
+                                               // Convert from FIRRTL left shift semantics.
+                                               b_expr = gen_dshl(b_expr, b_padded_width);
+                                       }
+                               }
+                               else if ((cell->type == "$shr") | (cell->type == "$sshr")) {
+                                       // We don't need to extract a specific range of bits.
+                                       extract_y_bits = false;
+                                       // Is the shift amount constant?
+                                       auto b_sig = cell->getPort("\\B");
+                                       if (b_sig.is_fully_const()) {
+                                               primop = "shr";
+                                       } else {
+                                               primop = "dshr";
+                                       }
+                               }
+                               else if ((cell->type == "$logic_and")) {
                                         primop = "and";
                                         a_expr = "neq(" + a_expr + ", UInt(0))";
                                         b_expr = "neq(" + b_expr + ", UInt(0))";
                                         always_uint = true;
                                 }
-                               if ((cell->type == "$logic_or")) {
+                               else if ((cell->type == "$logic_or")) {
                                         primop = "or";
                                         a_expr = "neq(" + a_expr + ", UInt(0))";
                                         b_expr = "neq(" + b_expr + ", UInt(0))";
@@ -388,6 +485,11 @@ struct FirrtlWorker
 
                                string expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str());
 
+                               // Deal with FIRRTL's "shift widens" semantics
+                               if (extract_y_bits) {
+                                       expr = stringf("bits(%s, %d, 0)", expr.c_str(), y_width - 1);
+                               }
+
                                if ((is_signed && !always_uint) || cell->type.in("$sub"))
                                        expr = stringf("asUInt(%s)", expr.c_str());
 
@@ -513,7 +615,67 @@ struct FirrtlWorker
                                continue;
                        }
 
-                       log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
+                       // This may be a parameterized module - paramod.
+                       if (cell->type.substr(0, 8) == "$paramod")
+                       {
+                               auto paramod_module = log_id(module);
+                               auto paramod_instance = log_id(cell);
+                               process_instance(cell, wire_exprs);
+                               continue;
+                       }
+                       if (cell->type == "$shiftx") {
+                               // assign y = a[b +: y_width];
+                               // We'll extract the correct bits as part of the primop.
+
+                               string y_id = make_id(cell->name);
+                               int y_width =  cell->parameters.at("\\Y_WIDTH").as_int();
+                               string a_expr = make_expr(cell->getPort("\\A"));
+                               // Get the initial bit selector
+                               string b_expr = make_expr(cell->getPort("\\B"));
+                               wire_decls.push_back(stringf("    wire %s: UInt<%d>\n", y_id.c_str(), y_width));
+
+                               if (cell->getParam("\\B_SIGNED").as_bool()) {
+                                       // Use validif to constrain the selection (test the sign bit)
+                                       auto b_string = b_expr.c_str();
+                                       int b_sign = cell->parameters.at("\\B_WIDTH").as_int() - 1;
+                                       b_expr = stringf("validif(not(bits(%s, %d, %d)), %s)", b_string, b_sign, b_sign, b_string);
+                               }
+                               string expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_expr.c_str());
+
+                               cell_exprs.push_back(stringf("    %s <= %s\n", y_id.c_str(), expr.c_str()));
+                               register_reverse_wire_map(y_id, cell->getPort("\\Y"));
+                               continue;
+                       }
+                       if (cell->type == "$shift") {
+                               // assign y = a >> b;
+                               //  where b may be negative
+
+                               string y_id = make_id(cell->name);
+                               int y_width =  cell->parameters.at("\\Y_WIDTH").as_int();
+                               string a_expr = make_expr(cell->getPort("\\A"));
+                               string b_expr = make_expr(cell->getPort("\\B"));
+                               auto b_string = b_expr.c_str();
+                               int b_padded_width = cell->parameters.at("\\B_WIDTH").as_int();
+                               string expr;
+                               wire_decls.push_back(stringf("    wire %s: UInt<%d>\n", y_id.c_str(), y_width));
+
+                               if (cell->getParam("\\B_SIGNED").as_bool()) {
+                                       // We generate a left or right shift based on the sign of b.
+                                       std::string dshl = stringf("bits(dshl(%s, %s), 0, %d)", a_expr.c_str(), gen_dshl(b_expr, b_padded_width).c_str(), y_width);
+                                       std::string dshr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string);
+                                       expr = stringf("mux(%s < 0, %s, %s)",
+                                                                        b_string,
+                                                                        dshl.c_str(),
+                                                                        dshr.c_str()
+                                                                        );
+                               } else {
+                                       expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string);
+                               }
+                               cell_exprs.push_back(stringf("    %s <= %s\n", y_id.c_str(), expr.c_str()));
+                               register_reverse_wire_map(y_id, cell->getPort("\\Y"));
+                               continue;
+                       }
+                       log_warning("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
                }
 
                for (auto conn : module->connections())
@@ -629,38 +791,53 @@ struct FirrtlBackend : public Backend {
                log("    write_firrtl [options] [filename]\n");
                log("\n");
                log("Write a FIRRTL netlist of the current design.\n");
+               log("The following commands are executed by this command:\n");
+               log("        pmuxtree\n");
                log("\n");
        }
        void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
        {
-               size_t argidx;
-               for (argidx = 1; argidx < args.size(); argidx++)
-               {
-                       // if (args[argidx] == "-aig") {
-                       //      aig_mode = true;
-                       //      continue;
-                       // }
-                       break;
+               size_t argidx = args.size();    // We aren't expecting any arguments.
+
+               // If we weren't explicitly passed a filename, use the last argument (if it isn't a flag).
+               if (filename == "") {
+                       if (argidx > 0 && args[argidx - 1][0] != '-') {
+                               // extra_args and friends need to see this argument.
+                               argidx -= 1;
+                               filename = args[argidx];
+                       }
                }
                extra_args(f, filename, args, argidx);
 
-               log_header(design, "Executing FIRRTL backend.\n");
+               if (!design->full_selection())
+                       log_cmd_error("This command only operates on fully selected designs!\n");
 
-               Module *top = design->top_module();
+               log_header(design, "Executing FIRRTL backend.\n");
+               log_push();
 
-               if (top == nullptr)
-                       log_error("No top module found!\n");
+               Pass::call(design, stringf("pmuxtree"));
 
                namecache.clear();
                autoid_counter = 0;
 
+               // Get the top module, or a reasonable facsimile - we need something for the circuit name.
+               Module *top = design->top_module();
+               Module *last = nullptr;
+               // Generate module and wire names.
                for (auto module : design->modules()) {
                        make_id(module->name);
+                       last = module;
+                       if (top == nullptr && module->get_bool_attribute("\\top")) {
+                               top = module;
+                       }
                        for (auto wire : module->wires())
                                if (wire->port_id)
                                        make_id(wire->name);
                }
 
+               if (top == nullptr)
+                       top = last;
+
                *f << stringf("circuit %s:\n", make_id(top->name));
 
                for (auto module : design->modules())
diff --git a/tests/asicworld/xfirrtl b/tests/asicworld/xfirrtl
new file mode 100644 (file)
index 0000000..c782a2b
--- /dev/null
@@ -0,0 +1,24 @@
+# This file contains the names of verilog files to exclude from verilog to FIRRTL regression tests due to known failures.
+code_hdl_models_arbiter.v      error: reg rst; cannot be driven by primitives or continuous assignment.
+code_hdl_models_clk_div_45.v   yosys issue: 2nd PMUXTREE pass yields: ERROR: Negative edge clock on FF clk_div_45.$procdff$49.
+code_hdl_models_d_ff_gates.v   combinational loop
+code_hdl_models_d_latch_gates.v        combinational loop
+code_hdl_models_dff_async_reset.v      $adff
+code_hdl_models_tff_async_reset.v      $adff
+code_hdl_models_uart.v $adff
+code_specman_switch_fabric.v   subfield assignment (bits() <= ...)
+code_tidbits_asyn_reset.v      $adff
+code_tidbits_reg_seq_example.v $adff
+code_verilog_tutorial_always_example.v empty module
+code_verilog_tutorial_escape_id.v      make_id issues (name begins with a digit)
+code_verilog_tutorial_explicit.v       firrtl backend bug (empty module)
+code_verilog_tutorial_first_counter.v  error: reg rst; cannot be driven by primitives or continuous assignment.
+code_verilog_tutorial_fsm_full.v       error: reg reset; cannot be driven by primitives or continuous assignment.
+code_verilog_tutorial_if_else.v        empty module (everything is under 'always @ (posedge clk)')
+[code_verilog_tutorial_n_out_primitive.v       empty module
+code_verilog_tutorial_parallel_if.v    empty module (everything is under 'always @ (posedge clk)')
+code_verilog_tutorial_simple_function.v        empty module (no hardware)
+code_verilog_tutorial_simple_if.v      empty module (everything is under 'always @ (posedge clk)')
+code_verilog_tutorial_task_global.v    empty module (everything is under 'always @ (posedge clk)')
+code_verilog_tutorial_v2k_reg.v                empty module
+code_verilog_tutorial_which_clock.v     $adff
diff --git a/tests/simple/xfirrtl b/tests/simple/xfirrtl
new file mode 100644 (file)
index 0000000..00e89b3
--- /dev/null
@@ -0,0 +1,26 @@
+# This file contains the names of verilog files to exclude from verilog to FIRRTL regression tests due to known failures.
+arraycells.v   inst id[0] of
+dff_different_styles.v
+generate.v     combinational loop
+hierdefparam.v inst id[0] of
+i2c_master_tests.v   $adff
+macros.v       drops modules
+mem2reg.v      drops modules
+mem_arst.v     $adff
+memory.v       $adff
+multiplier.v   inst id[0] of
+muxtree.v      drops modules
+omsp_dbg_uart.v        $adff
+operators.v    $pow
+paramods.v     subfield assignment (bits() <= ...)
+partsel.v      drops modules
+process.v      drops modules
+realexpr.v     drops modules
+scopes.v       original verilog issues ( -x where x isn't declared signed)
+sincos.v       $adff
+specify.v      no code (empty module generates error
+subbytes.v     $adff
+task_func.v    drops modules
+values.v       combinational loop
+vloghammer.v   combinational loop
+wreduce.v      original verilog issues ( -x where x isn't declared signed)
index c68678929fa703845a192a50716f9b6956bb0f1d..e0f2bcdc1b6e6d47f58c895dcaa2bd111c27a090 100644 (file)
@@ -1,7 +1,7 @@
 
-EXTRA_FLAGS=
-SEED=
-
+# Don't bother defining default values for SEED and EXTRA_FLAGS.
+# Their "natural" default values should be sufficient,
+#   and they may be overridden in the environment.
 ifneq ($(strip $(SEED)),)
 SEEDOPT=-S$(SEED)
 endif
index d6216244fbfb59ad60024368c848d122f4a8c005..218edf931786f71fa4157308c1f902550a640f24 100755 (executable)
@@ -17,12 +17,18 @@ scriptfiles=""
 scriptopt=""
 toolsdir="$(cd $(dirname $0); pwd)"
 warn_iverilog_git=false
+# The following are used in verilog to firrtl regression tests.
+# Typically these will be passed as environment variables:
+#EXTRA_FLAGS="--firrtl2verilog 'java -cp /.../firrtl/utils/bin/firrtl.jar firrtl.Driver'"
+# The tests are skipped if firrtl2verilog is the empty string (the default).
+firrtl2verilog=""
+xfirrtl="../xfirrtl"
 
 if [ ! -f $toolsdir/cmp_tbdata -o $toolsdir/cmp_tbdata.c -nt $toolsdir/cmp_tbdata ]; then
        ( set -ex; ${CC:-gcc} -Wall -o $toolsdir/cmp_tbdata $toolsdir/cmp_tbdata.c; ) || exit 1
 fi
 
-while getopts xmGl:wkjvref:s:p:n:S:I: opt; do
+while getopts xmGl:wkjvref:s:p:n:S:I:-: opt; do
        case "$opt" in
                x)
                        use_xsim=true ;;
@@ -59,8 +65,24 @@ while getopts xmGl:wkjvref:s:p:n:S:I: opt; do
                        include_opts="$include_opts -I $OPTARG"
                        xinclude_opts="$xinclude_opts -i $OPTARG"
                        minclude_opts="$minclude_opts +incdir+$OPTARG" ;;
+               -)
+                       case "${OPTARG}" in
+                           xfirrtl)
+                               xfirrtl="${!OPTIND}"
+                               OPTIND=$(( $OPTIND + 1 ))
+                               ;;
+                           firrtl2verilog)
+                               firrtl2verilog="${!OPTIND}"
+                               OPTIND=$(( $OPTIND + 1 ))
+                               ;;
+                           *)
+                               if [ "$OPTERR" == 1 ] && [ "${optspec:0:1}" != ":" ]; then
+                                   echo "Unknown option --${OPTARG}" >&2
+                               fi
+                               ;;
+                       esac;;
                *)
-                       echo "Usage: $0 [-x|-m] [-G] [-w] [-k] [-j] [-v] [-r] [-e] [-l libs] [-f frontend] [-s script] [-p cmdstring] [-n iters] [-S seed] [-I incdir] verilog-files\n" >&2
+                       echo "Usage: $0 [-x|-m] [-G] [-w] [-k] [-j] [-v] [-r] [-e] [-l libs] [-f frontend] [-s script] [-p cmdstring] [-n iters] [-S seed] [-I incdir] [--xfirrtl FIRRTL test exclude file] [--firrtl2verilog command to generate verilog from firrtl] verilog-files\n" >&2
                        exit 1
        esac
 done
@@ -109,6 +131,8 @@ do
                fn=$(basename $fn)
                bn=$(basename $bn)
 
+               rm -f ${bn}_ref.fir
+
                egrep -v '^\s*`timescale' ../$fn > ${bn}_ref.v
 
                if [ ! -f ../${bn}_tb.v ]; then
@@ -148,6 +172,13 @@ do
                else
                        test_passes -f "$frontend $include_opts" -p "hierarchy; proc; opt; memory; opt; fsm; opt -full -fine" ${bn}_ref.v
                        test_passes -f "$frontend $include_opts" -p "hierarchy; synth -run coarse; techmap; opt; abc -dff" ${bn}_ref.v
+                       if [ -n "$firrtl2verilog" ]; then
+                           if test -z "$xfirrtl" || ! grep "$fn" "$xfirrtl" ; then
+                               "$toolsdir"/../../yosys -b "firrtl" -o ${bn}_ref.fir -f "$frontend $include_opts" -p "prep -nordff; proc; opt; memory; opt; fsm; opt -full -fine; pmuxtree" ${bn}_ref.v
+                               $firrtl2verilog -i ${bn}_ref.fir -o ${bn}_ref.fir.v  -X verilog
+                               test_passes -f "$frontend $include_opts" -p "hierarchy; proc; opt; memory; opt; fsm; opt -full -fine" ${bn}_ref.fir.v
+                           fi
+                       fi
                fi
                touch ../${bn}.log
        }
@@ -160,14 +191,18 @@ do
                ( set -ex; body; ) > ${bn}.err 2>&1
        fi
 
+       did_firrtl=""
+       if [ -f ${bn}.out/${bn}_ref.fir ]; then
+           did_firrtl="+FIRRTL "
+       fi
        if [ -f ${bn}.log ]; then
                mv ${bn}.err ${bn}.log
-               echo "${status_prefix}-> ok"
+               echo "${status_prefix}${did_firrtl}-> ok"
        elif [ -f ${bn}.skip ]; then
                mv ${bn}.err ${bn}.skip
                echo "${status_prefix}-> skip"
        else
-               echo "${status_prefix}-> ERROR!"
+               echo "${status_prefix}${did_firrtl}-> ERROR!"
                if $warn_iverilog_git; then
                        echo "Note: Make sure that 'iverilog' is an up-to-date git checkout of Icarus Verilog."
                fi