diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ece06649..48686f1b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,5 +72,5 @@ add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src include_directories(${CMAKE_SOURCE_DIR}/include) enable_testing() add_executable(test_verilog tests/gtest/test_verilog.cpp) -target_link_libraries(test_verilog gtest_main coreir) +target_link_libraries(test_verilog gtest_main coreir coreir-commonlib) add_test(NAME test_verilog COMMAND test_verilog WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/gtest) diff --git a/include/coreir/definitions/coreVerilog.hpp b/include/coreir/definitions/coreVerilog.hpp index fadedea33..22c33d08b 100644 --- a/include/coreir/definitions/coreVerilog.hpp +++ b/include/coreir/definitions/coreVerilog.hpp @@ -1,55 +1,229 @@ +#include "verilogAST.hpp" +namespace vAST = verilogAST; + +std::vector> + make_args(std::vector args) { + std::vector> arg_ptrs; + for (auto a : args) { + arg_ptrs.push_back(vAST::make_id(a)); + } + return arg_ptrs; +} + +std::vector> + make_ext_args(std::vector> args) { + return args; +} + +std::unique_ptr make_signed_call(const char *id) { + std::vector> args; + args.push_back(vAST::make_id(std::string(id))); + return std::make_unique("$signed", std::move(args)); +} using namespace CoreIR; using namespace std; void CoreIRLoadVerilog_coreir(Context* c) { - std::map> coreVMap({ + std::map()>>>> + coreVMap({ {"unary",{ - {"wire","in"}, - {"not","~in"}, - {"neg","-in"} + {"wire",{ + "in", + []() {return vAST::make_id("in");} + }}, + {"not",{ + "~in", + []() {return std::make_unique(vAST::make_id("in"), vAST::UnOp::INVERT);} + }}, + {"neg",{ + "-in", + []() {return std::make_unique(vAST::make_id("in"), vAST::UnOp::MINUS);} + }} }}, {"unaryReduce",{ - {"andr","&in"}, - {"orr","|in"}, - {"xorr","^in"} + {"andr",{"&in", + []() {return std::make_unique(vAST::make_id("in"), vAST::UnOp::AND);} + }}, + {"orr",{"|in", + []() {return std::make_unique(vAST::make_id("in"), vAST::UnOp::OR);} + }}, + {"xorr",{"^in", + []() {return std::make_unique(vAST::make_id("in"), vAST::UnOp::XOR);} + }} }}, {"binary",{ - {"and","in0 & in1"}, - {"or","in0 | in1"}, - {"xor","in0 ^ in1"}, - {"shl","in0 << in1"}, - {"lshr","in0 >> in1"}, - {"ashr","$signed(in0) >>> in1"}, - {"add","in0 + in1"}, - {"sub","in0 - in1"}, - {"mul","in0 * in1"}, - {"udiv","in0 / in1"}, - {"sdiv","$signed(in0) / $signed(in1)"} + {"and",{"in0 & in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::AND, vAST::make_id("in1")); + }}}, + {"or",{"in0 | in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::OR, vAST::make_id("in1")); + }}}, + {"xor",{"in0 ^ in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::XOR, vAST::make_id("in1")); + }}}, + {"shl",{"in0 << in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::LSHIFT, vAST::make_id("in1")); + }}}, + {"lshr",{"in0 >> in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::RSHIFT, vAST::make_id("in1")); + }}}, + {"ashr",{"$signed(in0) >>> in1", + []() {return std::make_unique(make_signed_call("in0"), + vAST::BinOp::ARSHIFT, vAST::make_id("in1")); + }}}, + {"add",{"in0 + in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::ADD, vAST::make_id("in1")); + }}}, + {"sub",{"in0 - in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::SUB, vAST::make_id("in1")); + }}}, + {"mul",{"in0 * in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::MUL, vAST::make_id("in1")); + }}}, + {"udiv",{"in0 / in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::DIV, vAST::make_id("in1")); + }}}, + {"sdiv",{"$signed(in0) / $signed(in1)", + []() {return std::make_unique(make_signed_call("in0"), + vAST::BinOp::DIV, make_signed_call("in1")); + }}} }}, {"binaryReduce",{ - {"eq","in0 == in1"}, - {"neq","in0 != in1"}, - {"slt","$signed(in0) < $signed(in1)"}, - {"sgt","$signed(in0) > $signed(in1)"}, - {"sle","$signed(in0) <= $signed(in1)"}, - {"sge","$signed(in0) >= $signed(in1)"}, - {"ult","in0 < in1"}, - {"ugt","in0 > in1"}, - {"ule","in0 <= in1"}, - {"uge","in0 >= in1"} + {"eq",{"in0 == in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::EQ, vAST::make_id("in1")); + }}}, + {"neq",{"in0 != in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::NEQ, vAST::make_id("in1")); + }}}, + {"slt",{"$signed(in0) < $signed(in1)", + []() {return std::make_unique(make_signed_call("in0"), + vAST::BinOp::LT, make_signed_call("in1")); + }}}, + {"sgt",{"$signed(in0) > $signed(in1)", + []() {return std::make_unique(make_signed_call("in0"), + vAST::BinOp::GT, make_signed_call("in1")); + }}}, + {"sle",{"$signed(in0) <= $signed(in1)", + []() {return std::make_unique(make_signed_call("in0"), + vAST::BinOp::LTE, make_signed_call("in1")); + }}}, + {"sge",{"$signed(in0) >= $signed(in1)", + []() {return std::make_unique(make_signed_call("in0"), + vAST::BinOp::GTE, make_signed_call("in1")); + }}}, + {"ult",{"in0 < in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::LT, vAST::make_id("in1")); + }}}, + {"ugt",{"in0 > in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::GT, vAST::make_id("in1")); + }}}, + {"ule",{"in0 <= in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::LTE, vAST::make_id("in1")); + }}}, + {"uge",{"in0 >= in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::GTE, vAST::make_id("in1")); + }}} }}, {"other",{ - {"mux","sel ? in1 : in0"}, - {"slice","in[hi-1:lo]"}, - {"concat","{in1,in0}"}, - {"zext","{{(width_out-width_in){1'b0}},in}"}, - {"sext","{{(width_out-width_in){in[width_in-1]}},in}"}, - {"strip","in"}, - {"wrap","in"}, - {"const","value"}, - {"tribuf","en ? in : 'hz"}, - {"ibuf","in"}, + {"mux",{"sel ? in1 : in0", + [](){ return std::make_unique( + vAST::make_id("sel"), + vAST::make_id("in1"), + vAST::make_id("in0")); + }}}, + {"slice",{"in[hi-1:lo]", + [](){ return std::make_unique( + vAST::make_id("in"), + vAST::make_binop( + vAST::make_id("hi"), + vAST::BinOp::SUB, + vAST::make_num("1") + ), + vAST::make_id("lo")); + }}}, + {"concat",{"{in1,in0}", + [](){ return std::make_unique(make_args({"in1", "in0"})); + }}}, + {"zext",{"{{(width_out-width_in){1'b0}},in}", + [](){ + // Can't use initializer list of vector of unique ptrs, so we + // explicitly construct it + std::vector> zext_args; + zext_args.push_back( + std::make_unique( + vAST::make_binop( + vAST::make_id("width_out"), + vAST::BinOp::SUB, + vAST::make_id("width_in") + ), + std::make_unique("0", 1, false, + vAST::Radix::BINARY) + ) + ); + zext_args.push_back( + vAST::make_id("in") + ); + return std::make_unique(std::move(zext_args)); + }}}, + {"sext",{"{{(width_out-width_in){in[width_in-1]}},in}", + [](){ + // Can't use initializer list of vector of unique ptrs, so we + // explicitly construct it + std::vector> sext_args; + sext_args.push_back( + std::make_unique( + vAST::make_binop( + vAST::make_id("width_out"), + vAST::BinOp::SUB, + vAST::make_id("width_in") + ), + std::make_unique( + vAST::make_id("in"), + vAST::make_binop( + vAST::make_id("width_in"), + vAST::BinOp::SUB, + vAST::make_num("1") + ) + ) + ) + ); + sext_args.push_back(vAST::make_id("in")); + return std::make_unique(std::move(sext_args)); + }}}, + {"strip",{"in", + [](){ return vAST::make_id("in"); + }}}, + {"wrap",{"in", + [](){ return vAST::make_id("in"); + }}}, + {"const",{"value", + [](){ return vAST::make_id("value"); + }}}, + {"tribuf",{"en ? in : 'hz", + [](){ return std::make_unique( + vAST::make_id("en"), + vAST::make_id("in"), + std::make_unique("z", vAST::Radix::HEX)); + }}}, + {"ibuf",{"in", + [](){ return vAST::make_id("in"); + }}} //{"term",""} //{"reg",""}, //{"mem",""}, @@ -143,7 +317,7 @@ void CoreIRLoadVerilog_coreir(Context* c) { for (auto it0 : coreVMap) { for (auto it1 : it0.second) { string op = it1.first; - string vbody = it1.second; + string vbody = it1.second.first; json vjson; vjson["prefix"] = "coreir_"; vjson["definition"] = " assign out = " + vbody + ";"; @@ -156,7 +330,9 @@ void CoreIRLoadVerilog_coreir(Context* c) { ASSERT(coreIMap.count(it1.first),"missing" + it1.first); vjson["interface"] = coreIMap.at(it1.first); } + vjson["primitive_type"] = it0.first; core->getGenerator(op)->getMetaData()["verilog"] = vjson; + core->getGenerator(op)->setPrimitiveExpressionLambda(it1.second.second); } } diff --git a/include/coreir/definitions/corebitVerilog.hpp b/include/coreir/definitions/corebitVerilog.hpp index a53eecc1e..34c2df736 100644 --- a/include/coreir/definitions/corebitVerilog.hpp +++ b/include/coreir/definitions/corebitVerilog.hpp @@ -3,23 +3,55 @@ using namespace CoreIR; using namespace std; void CoreIRLoadVerilog_corebit(Context* c) { - std::map> bitVMap({ + std::map()>>>> + bitVMap({ {"unary",{ - {"wire","in"}, - {"not","~in"}, + {"wire",{ + "in", + []() {return vAST::make_id("in");} + }}, + {"not",{ + "~in", + []() {return std::make_unique(vAST::make_id("in"), vAST::UnOp::INVERT);} + }} }}, {"binary",{ - {"and","in0 & in1"}, - {"or","in0 | in1"}, - {"xor","in0 ^ in1"}, + {"and",{"in0 & in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::AND, vAST::make_id("in1")); + }}}, + {"or",{"in0 | in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::OR, vAST::make_id("in1")); + }}}, + {"xor",{"in0 ^ in1", + []() {return std::make_unique(vAST::make_id("in0"), + vAST::BinOp::XOR, vAST::make_id("in1")); + }}} }}, {"other",{ - {"mux","sel ? in1 : in0"}, - {"concat","{in0, in1}"}, - {"const","value"}, - {"term",""}, - {"tribuf","en ? in : 1'bz"}, - {"ibuf","in"}, + {"mux",{"sel ? in1 : in0", + [](){ return std::make_unique( + vAST::make_id("sel"), + vAST::make_id("in1"), + vAST::make_id("in0")); + }}}, + {"concat",{"{in1,in0}", + [](){ return std::make_unique(make_args({"in1", "in0"})); + }}}, + {"const",{"value", + [](){ return vAST::make_id("value"); + }}}, + {"term",{"", [](){ return nullptr; }}}, + {"tribuf",{"en ? in : 'hz", + [](){ return std::make_unique( + vAST::make_id("en"), + vAST::make_id("in"), + std::make_unique("z", vAST::Radix::HEX)); + }}}, + {"ibuf",{"in", + [](){ return vAST::make_id("in"); + }}} }} }); @@ -73,7 +105,7 @@ void CoreIRLoadVerilog_corebit(Context* c) { for (auto it0 : bitVMap) { for (auto it1 : it0.second) { string op = it1.first; - string vbody = it1.second; + string vbody = it1.second.first; json vjson; vjson["prefix"] = "corebit_"; vjson["definition"] = " assign out = " + vbody + ";"; @@ -86,6 +118,8 @@ void CoreIRLoadVerilog_corebit(Context* c) { ASSERT(bitIMap.count(it1.first),"missing" + it1.first); vjson["interface"] = bitIMap.at(it1.first); } + vjson["primitive_type"] = it0.first; + bit->getModule(op)->setPrimitiveExpressionLambda(it1.second.second); bit->getModule(op)->getMetaData()["verilog"] = vjson; } } diff --git a/include/coreir/ir/generator.h b/include/coreir/ir/generator.h index 8bb563e9a..c9ebeb455 100644 --- a/include/coreir/ir/generator.h +++ b/include/coreir/ir/generator.h @@ -4,10 +4,11 @@ #include "fwd_declare.h" #include "globalvalue.h" +#include "coreir/primitive.h" namespace CoreIR { -class Generator : public GlobalValue { +class Generator : public GlobalValue, public VerilogPrimitive { TypeGen* typegen; diff --git a/include/coreir/ir/module.h b/include/coreir/ir/module.h index 5db661ccf..2c3e70cb1 100644 --- a/include/coreir/ir/module.h +++ b/include/coreir/ir/module.h @@ -5,10 +5,11 @@ #include "fwd_declare.h" #include "args.h" #include "globalvalue.h" +#include "coreir/primitive.h" namespace CoreIR { -class Module : public GlobalValue, public Args { +class Module : public GlobalValue, public Args, public VerilogPrimitive { RecordType* type; ModuleDef* def = nullptr; diff --git a/include/coreir/primitive.h b/include/coreir/primitive.h new file mode 100644 index 000000000..ac1ef1d9a --- /dev/null +++ b/include/coreir/primitive.h @@ -0,0 +1,23 @@ +#ifndef COREIR_PRIMITIVE_HPP_ +#define COREIR_PRIMITIVE_HPP_ +#include "verilogAST.hpp" +namespace vAST = verilogAST; +namespace CoreIR { +class VerilogPrimitive { + std::function()> primitiveExpressionLambda = nullptr; + public: + + void setPrimitiveExpressionLambda(std::function()> lambda) { + this->primitiveExpressionLambda = lambda; + }; + + bool hasPrimitiveExpressionLambda() { + return this->primitiveExpressionLambda != nullptr; + } + + std::function()> getPrimitiveExpressionLambda() { + return this->primitiveExpressionLambda; + } +}; +} +#endif diff --git a/src/passes/analysis/verilog.cpp b/src/passes/analysis/verilog.cpp index 3d190a293..e0b86cbc7 100644 --- a/src/passes/analysis/verilog.cpp +++ b/src/passes/analysis/verilog.cpp @@ -1,8 +1,193 @@ #include "coreir/passes/analysis/verilog.h" #include "coreir.h" #include "coreir/tools/cxxopts.h" +#include "verilogAST/transformer.hpp" +#include "verilogAST/assign_inliner.hpp" #include +namespace vAST = verilogAST; + +class UnaryOpReplacer : public vAST::Transformer { + std::unique_ptr in; + public: + UnaryOpReplacer(std::unique_ptr in) : + in(std::move(in)){}; + + virtual std::unique_ptr visit( + std::unique_ptr node) { + if (auto ptr = dynamic_cast(node.get())) { + node.release(); + std::unique_ptr id(ptr); + if (id->value == "in") { + return std::move(this->in); + } + return vAST::Transformer::visit(std::move(id)); + } + return vAST::Transformer::visit(std::move(node)); + }; +}; + +class BinaryOpReplacer : public vAST::Transformer { + std::unique_ptr in0; + std::unique_ptr in1; + public: + BinaryOpReplacer(std::unique_ptr in0, + std::unique_ptr in1) : + in0(std::move(in0)), in1(std::move(in1)){}; + + virtual std::unique_ptr visit( + std::unique_ptr node) { + if (auto ptr = dynamic_cast(node.get())) { + node.release(); + std::unique_ptr id(ptr); + if (id->value == "in0") { + return std::move(this->in0); + } else if (id->value == "in1") { + return std::move(this->in1); + } + return vAST::Transformer::visit(std::move(id)); + } + return vAST::Transformer::visit(std::move(node)); + }; +}; + +class MuxReplacer : public vAST::Transformer { + std::unique_ptr in0; + std::unique_ptr in1; + std::unique_ptr sel; + public: + MuxReplacer(std::unique_ptr in0, + std::unique_ptr in1, + std::unique_ptr sel) : + in0(std::move(in0)), in1(std::move(in1)), sel(std::move(sel)){}; + + virtual std::unique_ptr visit( + std::unique_ptr node) { + if (auto ptr = dynamic_cast(node.get())) { + node.release(); + std::unique_ptr id(ptr); + if (id->value == "in0") { + return std::move(this->in0); + } else if (id->value == "in1") { + return std::move(this->in1); + } else if (id->value == "sel") { + return std::move(this->sel); + } + return vAST::Transformer::visit(std::move(id)); + } + return vAST::Transformer::visit(std::move(node)); + }; +}; + +bool is_inlined(std::string primitive_type, std::string name) { + return primitive_type == "binary" || primitive_type == "unary" || + primitive_type == "unaryReduce" || primitive_type == "binaryReduce" || + (primitive_type == "other" && + (name == "const" || name == "mux" || name == "slice")); +} + +bool can_inline_binary_op(CoreIR::Module *module, bool _inline) { + if (module->isGenerated() && + module->getGenerator()->getMetaData().count("verilog") > 0) { + json verilog_json = + module->getGenerator()->getMetaData()["verilog"]; + return module->getGenerator()->hasPrimitiveExpressionLambda() && + (verilog_json["primitive_type"] == "binary" || + verilog_json["primitive_type"] == "binaryReduce") + && _inline; + } + return false; +} + +std::unique_ptr inline_binary_op( + std::pair instance, + std::map> verilog_connections + ) { + BinaryOpReplacer transformer( + std::move(verilog_connections["in0"]), + std::move(verilog_connections["in1"])); + return std::make_unique( + std::make_unique(instance.first + "_out"), + transformer.visit(instance.second->getModuleRef()->getGenerator()->getPrimitiveExpressionLambda()())); +} + +bool can_inline_unary_op(CoreIR::Module *module, bool _inline) { + if (module->isGenerated() && + module->getGenerator()->getMetaData().count("verilog") > 0) { + json verilog_json = + module->getGenerator()->getMetaData()["verilog"]; + return module->getGenerator()->hasPrimitiveExpressionLambda() && + (verilog_json["primitive_type"] == "unary" || + verilog_json["primitive_type"] == "unaryReduce") + && _inline; + } + return false; +} + +std::unique_ptr inline_unary_op( + std::pair instance, + std::map> verilog_connections + ) { + UnaryOpReplacer transformer(std::move(verilog_connections["in"])); + return std::make_unique( + std::make_unique(instance.first + "_out"), + transformer.visit(instance.second->getModuleRef()->getGenerator()->getPrimitiveExpressionLambda()())); +} + +bool can_inline_const_op(CoreIR::Module *module, bool _inline) { + if (module->isGenerated() && + module->getGenerator()->getMetaData().count("verilog") > 0) { + json verilog_json = + module->getGenerator()->getMetaData()["verilog"]; + return module->getGenerator()->hasPrimitiveExpressionLambda() && + verilog_json["primitive_type"] == "other" && + module->getName() == "const" && _inline; + } + if (module->getMetaData().count("verilog") > 0) { + json verilog_json = module->getMetaData()["verilog"]; + return module->hasPrimitiveExpressionLambda() && + verilog_json["primitive_type"] == "other" && + module->getName() == "const" && _inline; + } + return false; +} + +bool can_inline_mux_op(CoreIR::Module *module, bool _inline) { + if (module->isGenerated() && + module->getGenerator()->getMetaData().count("verilog") > 0) { + json verilog_json = + module->getGenerator()->getMetaData()["verilog"]; + return module->getGenerator()->hasPrimitiveExpressionLambda() && + verilog_json["primitive_type"] == "other" && + module->getName() == "mux" && _inline; + } + return false; +} + +std::unique_ptr inline_mux_op( + std::pair instance, + std::map> verilog_connections + ) { + MuxReplacer transformer(std::move(verilog_connections["in0"]), + std::move(verilog_connections["in1"]), + std::move(verilog_connections["sel"])); + return std::make_unique( + std::make_unique(instance.first + "_out"), + transformer.visit(instance.second->getModuleRef()->getGenerator()->getPrimitiveExpressionLambda()())); +} + +bool can_inline_slice_op(CoreIR::Module *module, bool _inline) { + if (module->isGenerated() && + module->getGenerator()->getMetaData().count("verilog") > 0) { + json verilog_json = + module->getGenerator()->getMetaData()["verilog"]; + return module->getGenerator()->hasPrimitiveExpressionLambda() && + verilog_json["primitive_type"] == "other" && + module->getName() == "slice" && _inline; + } + return false; +} + // Unpack variant type and convert to parent type Expression std::unique_ptr convert_to_expression(std::variant, @@ -60,7 +245,7 @@ std::unique_ptr convert_value(Value *value) { return std::make_unique(int_value->toString()); } else if (auto bool_value = dyn_cast(value)) { return std::make_unique( - std::to_string(uint(bool_value->get()))); + std::to_string(uint(bool_value->get())), 1, false, vAST::BINARY); } else if (auto bit_vector_value = dyn_cast(value)) { BitVector bit_vector = bit_vector_value->get(); return std::make_unique( @@ -431,7 +616,8 @@ std::vector, std::unique_ptr>> compile_module_body(RecordType *module_type, std::vector connections, - std::map instances) { + std::map instances, + bool _inline) { std::vector, std::unique_ptr>> body = declare_connections(instances); @@ -487,8 +673,7 @@ compile_module_body(RecordType *module_type, } else { verilog_connections.insert(std::make_pair( port.first, - convert_to_expression(convert_to_verilog_connection(entries[0].source)) - )); + convert_to_expression(convert_to_verilog_connection(entries[0].source)))); } } // Handle module arguments @@ -510,10 +695,40 @@ compile_module_body(RecordType *module_type, convert_value(parameter.second))); } } - std::unique_ptr statement = - std::make_unique( + std::unique_ptr statement; + if (can_inline_binary_op(instance_module, _inline)) { + statement = inline_binary_op(instance, std::move(verilog_connections)); + } else if (can_inline_unary_op(instance_module, _inline)) { + statement = inline_unary_op(instance, std::move(verilog_connections)); + } else if (can_inline_mux_op(instance_module, _inline)) { + statement = inline_mux_op(instance, std::move(verilog_connections)); + } else if (can_inline_const_op(instance_module, _inline)) { + ASSERT(instance_parameters[0].first->value == "value", + "expected first param to be const value"); + statement = std::make_unique( + std::make_unique(instance.first + "_out"), + std::move(instance_parameters[0].second)); + } else if (can_inline_slice_op(instance_module, _inline)) { + ASSERT(instance_parameters[0].first->value == "hi", + "expected first param to be hi"); + ASSERT(instance_parameters[1].first->value == "lo", + "expected second param to be lo"); + statement = std::make_unique( + std::make_unique(instance.first + "_out"), + std::make_unique( + std::move(verilog_connections["in"]), + vAST::make_binop( + std::move(instance_parameters[0].second), + vAST::BinOp::SUB, + vAST::make_num("1") + ), + std::move(instance_parameters[1].second) + )); + } else { + statement = std::make_unique( module_name, std::move(instance_parameters), instance_name, std::move(verilog_connections)); + } body.push_back(std::move(statement)); } // Wire the outputs of the module and inout connections @@ -549,6 +764,12 @@ vAST::Parameters compile_params(Module *module) { void Passes::Verilog::compileModule(Module *module) { if (module->getMetaData().count("verilog") > 0) { json verilog_json = module->getMetaData()["verilog"]; + if (module->hasPrimitiveExpressionLambda() && + is_inlined(verilog_json["primitive_type"], module->getName()) && + this->_inline) { + // Module is inlined + return; + } if (verilog_json.count("verilog_string") > 0) { // module is defined by a verilog string, we just emit the entire // string @@ -566,6 +787,12 @@ void Passes::Verilog::compileModule(Module *module) { // This module is an instance of generator defined as a parametrized // verilog module json verilog_json = module->getGenerator()->getMetaData()["verilog"]; + if (module->getGenerator()->hasPrimitiveExpressionLambda() && + is_inlined(verilog_json["primitive_type"], module->getName()) && + this->_inline) { + // Module is inlined + return; + } std::string name = make_name(module->getName(), verilog_json); modules.push_back(std::make_pair( @@ -590,7 +817,9 @@ void Passes::Verilog::compileModule(Module *module) { std::unique_ptr>> body = compile_module_body(module->getType(), definition->getSortedConnections(), - definition->getInstances()); + definition->getInstances(), + this->_inline); + // Temporary support for inline verilog // See https://github.com/rdaly525/coreir/pull/823 for context if (module->getMetaData().count("inline_verilog") > 0) { @@ -604,6 +833,11 @@ void Passes::Verilog::compileModule(Module *module) { std::unique_ptr verilog_module = std::make_unique(name, std::move(ports), std::move(body), std::move(parameters)); + + if (this->_inline) { + vAST::AssignInliner transformer; + verilog_module = transformer.visit(std::move(verilog_module)); + } modules.push_back(std::make_pair(name, std::move(verilog_module))); } diff --git a/tests/gtest/add.json b/tests/gtest/add.json new file mode 100644 index 000000000..8ad5d0f33 --- /dev/null +++ b/tests/gtest/add.json @@ -0,0 +1,26 @@ +{"top":"global.Top", +"namespaces":{ + "global":{ + "modules":{ + "Top":{ + "type":["Record",[ + ["I0",["Array",8,"BitIn"]], + ["I1",["Array",8,"BitIn"]], + ["O",["Array",8,"Bit"]] + ]], + "instances":{ + "inst0":{ + "genref":"coreir.add", + "genargs":{"width":["Int",8]} + } + }, + "connections":[ + ["self.I0","inst0.in0"], + ["self.I1","inst0.in1"], + ["self.O","inst0.out"] + ] + } + } + } +} +} diff --git a/tests/gtest/add_golden.v b/tests/gtest/add_golden.v new file mode 100644 index 000000000..d0d44bc84 --- /dev/null +++ b/tests/gtest/add_golden.v @@ -0,0 +1,4 @@ +module Top (input [7:0] I0, input [7:0] I1, output [7:0] O); +assign O = I0 + I1; +endmodule + diff --git a/tests/gtest/blackbox_verilog_golden.v b/tests/gtest/blackbox_verilog_golden.v index 1a73f59a1..464c2024f 100644 --- a/tests/gtest/blackbox_verilog_golden.v +++ b/tests/gtest/blackbox_verilog_golden.v @@ -3,8 +3,6 @@ module foo (input I, output O); assign O = I; endmodule module top (input I, output O); -wire inst0_O; -foo inst0(.I(I), .O(inst0_O)); -assign O = inst0_O; +foo inst0(.I(I), .O(O)); endmodule diff --git a/tests/gtest/inline_verilog_golden.v b/tests/gtest/inline_verilog_golden.v index aae93bd79..317cdd73a 100644 --- a/tests/gtest/inline_verilog_golden.v +++ b/tests/gtest/inline_verilog_golden.v @@ -4,9 +4,7 @@ always @(posedge CLK) begin end endmodule module Main (input CLK, input I, output O); -wire FF_inst0_O; -FF FF_inst0(.CLK(CLK), .I(I), .O(FF_inst0_O)); -assign O = FF_inst0_O; +FF FF_inst0(.CLK(CLK), .I(I), .O(O)); assert property { @(posedge CLK) I |-> ##1 O }; diff --git a/tests/gtest/intermediate_connection_golden.v b/tests/gtest/intermediate_connection_golden.v index 4a3bc3f81..e0bb1a310 100644 --- a/tests/gtest/intermediate_connection_golden.v +++ b/tests/gtest/intermediate_connection_golden.v @@ -7,10 +7,8 @@ module top (input I, inout IO0, inout IO1, output O); wire inst0_IO; wire inst0_O; wire inst1_IO; -wire inst1_O; foo inst0(.I(I), .IO(inst0_IO), .O(inst0_O)); -foo inst1(.I(inst0_O), .IO(inst1_IO), .O(inst1_O)); -assign O = inst1_O; +foo inst1(.I(inst0_O), .IO(inst1_IO), .O(O)); assign inst0_IO = inst1_IO; assign IO0 = inst0_IO; assign inst0_IO = IO1; diff --git a/tests/gtest/mux.json b/tests/gtest/mux.json new file mode 100644 index 000000000..a1c0c9b32 --- /dev/null +++ b/tests/gtest/mux.json @@ -0,0 +1,40 @@ +{"top":"global.Mux8x8", +"namespaces":{ + "global":{ + "modules":{ + "Mux8x8":{ + "type":["Record",[ + ["I0",["Array",8,"BitIn"]], + ["I1",["Array",8,"BitIn"]], + ["I2",["Array",8,"BitIn"]], + ["I3",["Array",8,"BitIn"]], + ["I4",["Array",8,"BitIn"]], + ["I5",["Array",8,"BitIn"]], + ["I6",["Array",8,"BitIn"]], + ["I7",["Array",8,"BitIn"]], + ["S",["Array",3,"BitIn"]], + ["O",["Array",8,"Bit"]] + ]], + "instances":{ + "coreir_commonlib_mux8x8_inst0":{ + "genref":"commonlib.muxn", + "genargs":{"N":["Int",8], "width":["Int",8]} + } + }, + "connections":[ + ["self.I0","coreir_commonlib_mux8x8_inst0.in.data.0"], + ["self.I1","coreir_commonlib_mux8x8_inst0.in.data.1"], + ["self.I2","coreir_commonlib_mux8x8_inst0.in.data.2"], + ["self.I3","coreir_commonlib_mux8x8_inst0.in.data.3"], + ["self.I4","coreir_commonlib_mux8x8_inst0.in.data.4"], + ["self.I5","coreir_commonlib_mux8x8_inst0.in.data.5"], + ["self.I6","coreir_commonlib_mux8x8_inst0.in.data.6"], + ["self.I7","coreir_commonlib_mux8x8_inst0.in.data.7"], + ["self.S","coreir_commonlib_mux8x8_inst0.in.sel"], + ["self.O","coreir_commonlib_mux8x8_inst0.out"] + ] + } + } + } +} +} diff --git a/tests/gtest/mux_golden.v b/tests/gtest/mux_golden.v new file mode 100644 index 000000000..441223b35 --- /dev/null +++ b/tests/gtest/mux_golden.v @@ -0,0 +1,24 @@ +module commonlib_muxn__N2__width8 (input [7:0] in_data_0, input [7:0] in_data_1, input [0:0] in_sel, output [7:0] out); +assign out = in_sel[0] ? in_data_1 : in_data_0; +endmodule + +module commonlib_muxn__N4__width8 (input [7:0] in_data_0, input [7:0] in_data_1, input [7:0] in_data_2, input [7:0] in_data_3, input [1:0] in_sel, output [7:0] out); +wire [7:0] muxN_0_out; +wire [7:0] muxN_1_out; +commonlib_muxn__N2__width8 muxN_0(.in_data_0(in_data_0), .in_data_1(in_data_1), .in_sel(in_sel[1 - 1:0]), .out(muxN_0_out)); +commonlib_muxn__N2__width8 muxN_1(.in_data_0(in_data_2), .in_data_1(in_data_3), .in_sel(in_sel[1 - 1:0]), .out(muxN_1_out)); +assign out = in_sel[1] ? muxN_1_out : muxN_0_out; +endmodule + +module commonlib_muxn__N8__width8 (input [7:0] in_data_0, input [7:0] in_data_1, input [7:0] in_data_2, input [7:0] in_data_3, input [7:0] in_data_4, input [7:0] in_data_5, input [7:0] in_data_6, input [7:0] in_data_7, input [2:0] in_sel, output [7:0] out); +wire [7:0] muxN_0_out; +wire [7:0] muxN_1_out; +commonlib_muxn__N4__width8 muxN_0(.in_data_0(in_data_0), .in_data_1(in_data_1), .in_data_2(in_data_2), .in_data_3(in_data_3), .in_sel(in_sel[2 - 1:0]), .out(muxN_0_out)); +commonlib_muxn__N4__width8 muxN_1(.in_data_0(in_data_4), .in_data_1(in_data_5), .in_data_2(in_data_6), .in_data_3(in_data_7), .in_sel(in_sel[2 - 1:0]), .out(muxN_1_out)); +assign out = in_sel[2] ? muxN_1_out : muxN_0_out; +endmodule + +module Mux8x8 (input [7:0] I0, input [7:0] I1, input [7:0] I2, input [7:0] I3, input [7:0] I4, input [7:0] I5, input [7:0] I6, input [7:0] I7, output [7:0] O, input [2:0] S); +commonlib_muxn__N8__width8 coreir_commonlib_mux8x8_inst0(.in_data_0(I0), .in_data_1(I1), .in_data_2(I2), .in_data_3(I3), .in_data_4(I4), .in_data_5(I5), .in_data_6(I6), .in_data_7(I7), .in_sel(S), .out(O)); +endmodule + diff --git a/tests/gtest/test_verilog.cpp b/tests/gtest/test_verilog.cpp index 67d061adb..5885523b6 100644 --- a/tests/gtest/test_verilog.cpp +++ b/tests/gtest/test_verilog.cpp @@ -1,5 +1,8 @@ #include "gtest/gtest.h" #include "coreir.h" +#include "coreir/definitions/coreVerilog.hpp" +#include "coreir/definitions/corebitVerilog.hpp" +#include "coreir/libs/commonlib.h" #include "assert_pass.h" using namespace CoreIR; @@ -69,6 +72,76 @@ TEST(VerilogTests, TestArraySelect) { deleteContext(c); } +TEST(VerilogTests, TestAddInline) { + Context* c = newContext(); + CoreIRLoadVerilog_coreir(c); + Module* top; + + if (!loadFromFile(c, "add.json", &top)) { + c->die(); + } + assert(top != nullptr); + c->setTop(top->getRefName()); + + const std::vector passes = { + "rungenerators", + "removebulkconnections", + "flattentypes", + "verilog --inline" + }; + c->runPasses(passes, {}); + assertPassEq(c, "add_golden.v"); + deleteContext(c); +} + + +TEST(VerilogTests, TestTwoInline) { + Context* c = newContext(); + CoreIRLoadVerilog_coreir(c); + CoreIRLoadVerilog_corebit(c); + Module* top; + + if (!loadFromFile(c, "two_ops.json", &top)) { + c->die(); + } + assert(top != nullptr); + c->setTop(top->getRefName()); + + const std::vector passes = { + "rungenerators", + "removebulkconnections", + "flattentypes", + "verilog --inline" + }; + c->runPasses(passes, {}); + assertPassEq(c, "two_ops_golden.v"); + deleteContext(c); +} + +TEST(VerilogTests, TestMuxInline) { + Context* c = newContext(); + CoreIRLoadVerilog_coreir(c); + CoreIRLoadVerilog_corebit(c); + CoreIRLoadLibrary_commonlib(c); + Module* top; + + if (!loadFromFile(c, "mux.json", &top)) { + c->die(); + } + assert(top != nullptr); + c->setTop(top->getRefName()); + + const std::vector passes = { + "rungenerators", + "removebulkconnections", + "flattentypes", + "verilog --inline" + }; + c->runPasses(passes, {}); + assertPassEq(c, "mux_golden.v"); + deleteContext(c); +} + TEST(VerilogTests, TestInlineVerilogMetadata) { Context* c = newContext(); Module* top; diff --git a/tests/gtest/two_ops.json b/tests/gtest/two_ops.json new file mode 100644 index 000000000..c8dc36cb2 --- /dev/null +++ b/tests/gtest/two_ops.json @@ -0,0 +1,94 @@ +{"top":"global.test_two_ops", +"namespaces":{ + "global":{ + "modules":{ + "Add8_cin":{ + "type":["Record",[ + ["I0",["Array",8,"BitIn"]], + ["I1",["Array",8,"BitIn"]], + ["O",["Array",8,"Bit"]], + ["CIN","BitIn"] + ]], + "instances":{ + "bit_const_0_None":{ + "modref":"corebit.const", + "modargs":{"value":["Bool",false]} + }, + "inst0":{ + "genref":"coreir.add", + "genargs":{"width":["Int",8]} + }, + "inst1":{ + "genref":"coreir.add", + "genargs":{"width":["Int",8]} + } + }, + "connections":[ + ["inst1.in0.1","bit_const_0_None.out"], + ["inst1.in0.2","bit_const_0_None.out"], + ["inst1.in0.3","bit_const_0_None.out"], + ["inst1.in0.4","bit_const_0_None.out"], + ["inst1.in0.5","bit_const_0_None.out"], + ["inst1.in0.6","bit_const_0_None.out"], + ["inst1.in0.7","bit_const_0_None.out"], + ["inst1.out","inst0.in0"], + ["self.I1","inst0.in1"], + ["self.O","inst0.out"], + ["self.CIN","inst1.in0.0"], + ["self.I0","inst1.in1"] + ] + }, + "Sub8":{ + "type":["Record",[ + ["I0",["Array",8,"BitIn"]], + ["I1",["Array",8,"BitIn"]], + ["O",["Array",8,"Bit"]] + ]], + "instances":{ + "bit_const_1_None":{ + "modref":"corebit.const", + "modargs":{"value":["Bool",true]} + }, + "inst0":{ + "genref":"coreir.not", + "genargs":{"width":["Int",8]} + }, + "inst1":{ + "modref":"global.Add8_cin" + } + }, + "connections":[ + ["inst1.CIN","bit_const_1_None.out"], + ["self.I1","inst0.in"], + ["inst1.I1","inst0.out"], + ["self.I0","inst1.I0"], + ["self.O","inst1.O"] + ] + }, + "test_two_ops":{ + "type":["Record",[ + ["I0",["Array",8,"BitIn"]], + ["I1",["Array",8,"BitIn"]], + ["O",["Array",8,"Bit"]] + ]], + "instances":{ + "inst0":{ + "genref":"coreir.add", + "genargs":{"width":["Int",8]} + }, + "inst1":{ + "modref":"global.Sub8" + } + }, + "connections":[ + ["self.I0","inst0.in0"], + ["self.I1","inst0.in1"], + ["inst1.I0","inst0.out"], + ["self.I0","inst1.I1"], + ["self.O","inst1.O"] + ] + } + } + } +} +} \ No newline at end of file diff --git a/tests/gtest/two_ops_golden.v b/tests/gtest/two_ops_golden.v new file mode 100644 index 000000000..8a9cfdd4e --- /dev/null +++ b/tests/gtest/two_ops_golden.v @@ -0,0 +1,12 @@ +module Add8_cin (input CIN, input [7:0] I0, input [7:0] I1, output [7:0] O); +assign O = (({1'b0,1'b0,1'b0,1'b0,1'b0,1'b0,1'b0,CIN}) + I0) + I1; +endmodule + +module Sub8 (input [7:0] I0, input [7:0] I1, output [7:0] O); +Add8_cin inst1(.CIN(1'b1), .I0(I0), .I1(~ I1), .O(O)); +endmodule + +module test_two_ops (input [7:0] I0, input [7:0] I1, output [7:0] O); +Sub8 inst1(.I0(I0 + I1), .I1(I0), .O(O)); +endmodule + diff --git a/tests/simulator/Makefile b/tests/simulator/Makefile index 4dc520906..5228b216d 100644 --- a/tests/simulator/Makefile +++ b/tests/simulator/Makefile @@ -3,7 +3,7 @@ HOME = ../.. INCS = -I$(HOME)/include -I. LPATH = -L$(HOME)/lib -LIBS = -Wl,-rpath,$(HOME)/lib -lcoreir -lcoreirsim -lcoreir-commonlib -lcoreir-aetherlinglib -lcoreir-float +LIBS = -Wl,-rpath,$(HOME)/lib -lverilogAST -lcoreir -lcoreirsim -lcoreir-commonlib -lcoreir-aetherlinglib -lcoreir-float SRCFILES = $(wildcard [^_]*.cpp) OBJS = $(patsubst %.cpp,build/%.o,$(SRCFILES)) EXES = $(patsubst %.cpp,build/%,$(SRCFILES)) diff --git a/tests/unit/Makefile b/tests/unit/Makefile index a8071a633..0441c83e0 100755 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -3,7 +3,7 @@ HOME = ../.. INCS = -I$(HOME)/include -I. LPATH = -L$(HOME)/lib -LIBS = -Wl,-rpath,$(HOME)/lib -lcoreir -lcoreirsim -lcoreir-aetherlinglib -lcoreir-commonlib +LIBS = -Wl,-rpath,$(HOME)/lib -lverilogAST -lcoreir -lcoreirsim -lcoreir-aetherlinglib -lcoreir-commonlib #LIBS = -lcoreir SRCFILES = $(wildcard [^_]*.cpp) OBJS = $(patsubst %.cpp,build/%.o,$(SRCFILES)) diff --git a/tutorial/hellocounter/Makefile b/tutorial/hellocounter/Makefile index bed11bbed..306b34860 100755 --- a/tutorial/hellocounter/Makefile +++ b/tutorial/hellocounter/Makefile @@ -1,5 +1,5 @@ CXX ?= g++ -CXXFLAGS = -std=c++11 -Wall -fPIC -Werror +CXXFLAGS = -std=c++17 -Wall -fPIC -Werror HOME = ../.. INCS = -I$(HOME)/include -I.