diff --git a/Makefile b/Makefile index 93f5bc846a6..1e2709a1799 100644 --- a/Makefile +++ b/Makefile @@ -649,6 +649,7 @@ SEEDOPT="" endif test: $(TARGETS) $(EXTRA_TARGETS) + +cd tests/reference-out && bash run-test.sh +cd tests/round-trip && bash run-test.sh +cd tests/simple && bash run-test.sh $(SEEDOPT) +cd tests/hana && bash run-test.sh $(SEEDOPT) diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc index 44696e38f79..b31076a0702 100644 --- a/backends/ilang/ilang_backend.cc +++ b/backends/ilang/ilang_backend.cc @@ -168,6 +168,11 @@ void ILANG_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL:: f << stringf("\n"); } for (auto &it : cell->connections()) { + for (auto &it2 : it.second.attributes) { + f << stringf("%s attribute %s ", indent.c_str(), it2.first.c_str()); + dump_const(f, it2.second); + f << stringf("\n"); + } f << stringf("%s connect %s ", indent.c_str(), it.first.c_str()); dump_sigspec(f, it.second); f << stringf("\n"); diff --git a/backends/json/json.cc b/backends/json/json.cc index 56c05b82892..b3f0c922f31 100644 --- a/backends/json/json.cc +++ b/backends/json/json.cc @@ -137,6 +137,24 @@ struct JsonWriter } } + void write_connection_attributes(const dict& attributes) + { + bool first = true; + for (auto &attr : attributes) { + f << stringf("%s\n", first ? "" : ","); + f << stringf(" %s: ", get_name(attr.first).c_str()); + if ((attr.second.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) != 0) + f << get_string(attr.second.decode_string()); + else if (GetSize(attr.second.bits) > 32) + f << get_string(attr.second.as_string()); + else if ((attr.second.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) != 0) + f << stringf("%d", attr.second.as_int()); + else + f << stringf("%u", attr.second.as_int()); + first = false; + } + } + void write_module(Module *module_) { module = module_; @@ -211,7 +229,12 @@ struct JsonWriter bool first2 = true; for (auto &conn : c->connections()) { f << stringf("%s\n", first2 ? "" : ","); - f << stringf(" %s: %s", get_name(conn.first).c_str(), get_bits(conn.second).c_str()); + f << stringf(" %s: {\n", get_name(conn.first).c_str()); + f << stringf(" \"bits\": %s,\n", get_bits(conn.second).c_str()); + f << stringf(" \"attributes\": {"); + write_connection_attributes(conn.second.attributes); + f << stringf("\n }\n"); + f << stringf(" }"); first2 = false; } f << stringf("\n }\n"); diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index e2b5bbe0c52..d54bbf24478 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -345,8 +345,27 @@ void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decima } } +void dump_attributes(std::ostream &f, std::string indent, const dict &attributes, char term = '\n', bool modattr = false) +{ + if (noattr) + return; + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + f << stringf("%s" "%s %s", indent.c_str(), attr2comment ? "/*" : "(*", id(it->first).c_str()); + f << stringf(" = "); + if (modattr && (it->second == Const(0, 1) || it->second == Const(0))) + f << stringf(" 0 "); + else if (modattr && (it->second == Const(1, 1) || it->second == Const(1))) + f << stringf(" 1 "); + else + dump_const(f, it->second, -1, 0, false, attr2comment); + f << stringf(" %s%c", attr2comment ? "*/" : "*)", term); + } +} + void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) { + dump_attributes(f, "", sig.attributes, ' '); + if (GetSize(sig) == 0) { f << "\"\""; return; @@ -364,23 +383,6 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) } } -void dump_attributes(std::ostream &f, std::string indent, dict &attributes, char term = '\n', bool modattr = false) -{ - if (noattr) - return; - for (auto it = attributes.begin(); it != attributes.end(); ++it) { - f << stringf("%s" "%s %s", indent.c_str(), attr2comment ? "/*" : "(*", id(it->first).c_str()); - f << stringf(" = "); - if (modattr && (it->second == Const(0, 1) || it->second == Const(0))) - f << stringf(" 0 "); - else if (modattr && (it->second == Const(1, 1) || it->second == Const(1))) - f << stringf(" 1 "); - else - dump_const(f, it->second, -1, 0, false, attr2comment); - f << stringf(" %s%c", attr2comment ? "*/" : "*)", term); - } -} - void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) { dump_attributes(f, indent, wire->attributes); diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index b6f77d3cb05..058b18b88bf 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -1554,6 +1554,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) RTLIL::SigSpec sig; if (child->children.size() > 0) sig = child->children[0]->genRTLIL(); + for (auto &attr : child->attributes) { + if (attr.second->type != AST_CONSTANT) + log_file_error(filename, linenum, "Attribute `%s' with non-constant value.\n", attr.first.c_str()); + sig.attributes[attr.first] = attr.second->asAttrConst(); + } if (child->str.size() == 0) { char buf[100]; snprintf(buf, 100, "$%d", ++port_counter); diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y index 84c8ad11979..67f7c4194e4 100644 --- a/frontends/ilang/ilang_parser.y +++ b/frontends/ilang/ilang_parser.y @@ -255,6 +255,7 @@ cell_stmt: } cell_body TOK_END EOL; cell_body: + cell_body attr_stmt | cell_body TOK_PARAMETER TOK_ID constant EOL { current_cell->parameters[$3] = *$4; free($3); @@ -275,9 +276,11 @@ cell_body: cell_body TOK_CONNECT TOK_ID sigspec EOL { if (current_cell->hasPort($3)) rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of cell port %s.", $3).c_str()); + $4->attributes = attrbuf; current_cell->setPort($3, *$4); delete $4; free($3); + attrbuf.clear(); } | /* empty */; diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index b91c1c227f1..6bce5b9480e 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -1528,27 +1528,31 @@ cell_port_list_rules: cell_port | cell_port_list_rules ',' cell_port; cell_port: - /* empty */ { + attr { AstNode *node = new AstNode(AST_ARGUMENT); astbuf2->children.push_back(node); + append_attr(node, $1); } | - expr { + attr expr { AstNode *node = new AstNode(AST_ARGUMENT); astbuf2->children.push_back(node); - node->children.push_back($1); + node->children.push_back($2); + append_attr(node, $1); } | - '.' TOK_ID '(' expr ')' { + attr '.' TOK_ID '(' expr ')' { AstNode *node = new AstNode(AST_ARGUMENT); - node->str = *$2; + node->str = *$3; astbuf2->children.push_back(node); - node->children.push_back($4); - delete $2; + node->children.push_back($5); + append_attr(node, $1); + delete $3; } | - '.' TOK_ID '(' ')' { + attr '.' TOK_ID '(' ')' { AstNode *node = new AstNode(AST_ARGUMENT); - node->str = *$2; + node->str = *$3; astbuf2->children.push_back(node); - delete $2; + append_attr(node, $1); + delete $3; }; always_stmt: diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index b75119633ca..a933f105f6f 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -2795,6 +2795,8 @@ const RTLIL::SigSpec &RTLIL::SigSpec::operator=(const RTLIL::SigSpec &other) check(); } + attributes = other.attributes; + return *this; } @@ -3021,6 +3023,7 @@ void RTLIL::SigSpec::sort() unpack(); cover("kernel.rtlil.sigspec.sort"); std::sort(bits_.begin(), bits_.end()); + attributes.sort(); } void RTLIL::SigSpec::sort_and_unify() diff --git a/kernel/rtlil.h b/kernel/rtlil.h index f6545079c0b..2267aa49083 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -655,7 +655,7 @@ struct RTLIL::SigSpecConstIterator : public std::iterator + +INP_FILE=$1 + +BASE_NAME=$(basename ${INP_FILE}) +BASE_NAME=${BASE_NAME%.*} + +mkdir -p ${BASE_NAME} + +# List of backends +BACKEND=( "verilog" "ilang" "json" ) +EXT=( "v" "il" "json" ) + +# Generate +for i in "${!BACKEND[@]}"; do + REF_FILE=${BASE_NAME}/${BASE_NAME%.*}.ref.${EXT[i]} + ${YOSYS} -p "read_verilog $1; write_${BACKEND[i]} ${REF_FILE}" >${REF_FILE}.out.log 2>${REF_FILE}.err.log +done + diff --git a/tests/reference-out/run-test.sh b/tests/reference-out/run-test.sh new file mode 100755 index 00000000000..d2f36d70e09 --- /dev/null +++ b/tests/reference-out/run-test.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# This script loops over all verilog files in the current directory. Each one +# is read by the Yosys and then written back using specified backends. Then +# each backend output is compared with a reference output. + +set +e +YOSYS=../../yosys + +# List of backends +BACKEND=( "verilog" "ilang" "json" ) +EXT=( "v" "il" "json" ) + +# Loop over all verilog files in this folder +for INP_FILE in *.v; do + + BASE_NAME=$(basename ${INP_FILE}) + BASE_NAME=${BASE_NAME%.*} + + # Test each backend + for i in "${!BACKEND[@]}"; do + echo "Checking "${BACKEND[i]}" against "${INP_FILE}" ..." + + REF_FILE=${BASE_NAME}/${BASE_NAME%.*}.ref.${EXT[i]} + OUT_FILE=${BASE_NAME}/${BASE_NAME%.*}.out.${EXT[i]} + + # Check if the reference file exists + if [ ! -f "${REF_FILE}" ]; then + echo "ERROR: Reference file "${REF_FILE}" not found!" + exit -1 + fi + + # Generate output + set -e + ${YOSYS} -p "read_verilog ${INP_FILE}; write_${BACKEND[i]} ${OUT_FILE}" >${OUT_FILE}.out.log 2>${OUT_FILE}.err.log + + # Compare files + set +e + diff -q -I "Yosys" ${OUT_FILE} ${REF_FILE} + + # Fail if there is a difference + if [ $? -ne 0 ]; then + echo "ERROR: output and reference differ!" + diff -y --color=auto ${OUT_FILE} ${REF_FILE} + exit -1 + fi + done +done + diff --git a/tests/simple/attrib01_module.v b/tests/simple/attrib01_module.v new file mode 100644 index 00000000000..adef34f5b76 --- /dev/null +++ b/tests/simple/attrib01_module.v @@ -0,0 +1,21 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output reg out; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= ~inp; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output wire out; + + bar bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/attrib02_port_decl.v b/tests/simple/attrib02_port_decl.v new file mode 100644 index 00000000000..3505e726535 --- /dev/null +++ b/tests/simple/attrib02_port_decl.v @@ -0,0 +1,25 @@ +module bar(clk, rst, inp, out); + (* this_is_clock = 1 *) + input wire clk; + (* this_is_reset = 1 *) + input wire rst; + input wire inp; + (* an_output_register = 1*) + output reg out; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= ~inp; + +endmodule + +module foo(clk, rst, inp, out); + (* this_is_the_master_clock *) + input wire clk; + input wire rst; + input wire inp; + output wire out; + + bar bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/attrib03_parameter.v b/tests/simple/attrib03_parameter.v new file mode 100644 index 00000000000..562d225cd5b --- /dev/null +++ b/tests/simple/attrib03_parameter.v @@ -0,0 +1,28 @@ +module bar(clk, rst, inp, out); + + (* bus_width *) + parameter WIDTH = 2; + + (* an_attribute_on_localparam = 55 *) + localparam INCREMENT = 5; + + input wire clk; + input wire rst; + input wire [WIDTH-1:0] inp; + output reg [WIDTH-1:0] out; + + always @(posedge clk) + if (rst) out <= 0; + else out <= inp + INCREMENT; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire [7:0] inp; + output wire [7:0] out; + + bar # (.WIDTH(8)) bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/attrib04_net_var.v b/tests/simple/attrib04_net_var.v new file mode 100644 index 00000000000..8b552340651 --- /dev/null +++ b/tests/simple/attrib04_net_var.v @@ -0,0 +1,32 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output reg out; + + (* this_is_a_prescaler *) + reg [7:0] counter; + + (* temp_wire *) + wire out_val; + + always @(posedge clk) + counter <= counter + 1; + + assign out_val = inp ^ counter[4]; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= out_val; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output wire out; + + bar bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/attrib05_port_conn.v.DISABLED b/tests/simple/attrib05_port_conn.v.DISABLED new file mode 100644 index 00000000000..e20e6631950 --- /dev/null +++ b/tests/simple/attrib05_port_conn.v.DISABLED @@ -0,0 +1,21 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output reg out; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= ~inp; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output wire out; + + bar bar_instance ( (* clock_connected *) clk, rst, (* this_is_the_input *) inp, out); +endmodule + diff --git a/tests/simple/attrib06_operator_suffix.v b/tests/simple/attrib06_operator_suffix.v new file mode 100644 index 00000000000..e21173c58f4 --- /dev/null +++ b/tests/simple/attrib06_operator_suffix.v @@ -0,0 +1,23 @@ +module bar(clk, rst, inp_a, inp_b, out); + input wire clk; + input wire rst; + input wire [7:0] inp_a; + input wire [7:0] inp_b; + output reg [7:0] out; + + always @(posedge clk) + if (rst) out <= 0; + else out <= inp_a + (* ripple_adder *) inp_b; + +endmodule + +module foo(clk, rst, inp_a, inp_b, out); + input wire clk; + input wire rst; + input wire [7:0] inp_a; + input wire [7:0] inp_b; + output wire [7:0] out; + + bar bar_instance (clk, rst, inp_a, inp_b, out); +endmodule + diff --git a/tests/simple/attrib07_func_call.v.DISABLED b/tests/simple/attrib07_func_call.v.DISABLED new file mode 100644 index 00000000000..f55ef231609 --- /dev/null +++ b/tests/simple/attrib07_func_call.v.DISABLED @@ -0,0 +1,21 @@ +function [7:0] do_add; + input [7:0] inp_a; + input [7:0] inp_b; + + do_add = inp_a + inp_b; + +endfunction + +module foo(clk, rst, inp_a, inp_b, out); + input wire clk; + input wire rst; + input wire [7:0] inp_a; + input wire [7:0] inp_b; + output wire [7:0] out; + + always @(posedge clk) + if (rst) out <= 0; + else out <= do_add (* combinational_adder *) (inp_a, inp_b); + +endmodule + diff --git a/tests/simple/attrib08_mod_inst.v b/tests/simple/attrib08_mod_inst.v new file mode 100644 index 00000000000..c5a32234eed --- /dev/null +++ b/tests/simple/attrib08_mod_inst.v @@ -0,0 +1,22 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output reg out; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= ~inp; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output wire out; + + (* my_module_instance = 99 *) + bar bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/attrib09_case.v b/tests/simple/attrib09_case.v new file mode 100644 index 00000000000..8551bf9d0a4 --- /dev/null +++ b/tests/simple/attrib09_case.v @@ -0,0 +1,26 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire [1:0] inp; + output reg [1:0] out; + + always @(inp) + (* full_case, parallel_case *) + case(inp) + 2'd0: out <= 2'd3; + 2'd1: out <= 2'd2; + 2'd2: out <= 2'd1; + 2'd3: out <= 2'd0; + endcase + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire [1:0] inp; + output wire [1:0] out; + + bar bar_instance (clk, rst, inp, out); +endmodule +