diff options
-rw-r--r-- | frontends/ast/ast.cc | 1 | ||||
-rw-r--r-- | frontends/ast/ast.h | 3 | ||||
-rw-r--r-- | frontends/ast/genrtlil.cc | 5 | ||||
-rw-r--r-- | frontends/verilog/verilog_lexer.l | 1 | ||||
-rw-r--r-- | frontends/verilog/verilog_parser.y | 77 | ||||
-rw-r--r-- | tests/bind/.gitignore | 2 | ||||
-rw-r--r-- | tests/bind/basic.sv | 20 | ||||
-rw-r--r-- | tests/bind/basic.ys | 1 | ||||
-rw-r--r-- | tests/bind/cell_list.sv | 26 | ||||
-rw-r--r-- | tests/bind/cell_list.ys | 1 | ||||
-rw-r--r-- | tests/bind/hier.sv | 20 | ||||
-rw-r--r-- | tests/bind/hier.ys | 1 | ||||
-rw-r--r-- | tests/bind/inst_list.sv | 24 | ||||
-rw-r--r-- | tests/bind/inst_list.ys | 1 | ||||
-rw-r--r-- | tests/bind/param.sv | 26 | ||||
-rw-r--r-- | tests/bind/param.ys | 1 | ||||
-rwxr-xr-x | tests/bind/run-test.sh | 20 | ||||
-rw-r--r-- | tests/bind/toplevel.sv | 20 | ||||
-rw-r--r-- | tests/bind/toplevel.ys | 1 |
19 files changed, 247 insertions, 4 deletions
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index f33b76785..e42cf0348 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -177,6 +177,7 @@ std::string AST::type2str(AstNodeType type) X(AST_STRUCT) X(AST_UNION) X(AST_STRUCT_ITEM) + X(AST_BIND) #undef X default: log_abort(); diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 60c7de32d..57ce5605f 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -160,7 +160,8 @@ namespace AST AST_TYPEDEF, AST_STRUCT, AST_UNION, - AST_STRUCT_ITEM + AST_STRUCT_ITEM, + AST_BIND }; struct AstSrcLocType { diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 29d0bddef..7fa751e24 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -1963,6 +1963,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } } break; + case AST_BIND: { + // The bind construct. Currently unimplemented: just ignore it. + break; + } + case AST_FCALL: { if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") { diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index 55e8b48b9..54fb65240 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -260,6 +260,7 @@ static bool isUserType(std::string &s) "const" { if (formal_mode) return TOK_CONST; SV_KEYWORD(TOK_CONST); } "checker" { if (formal_mode) return TOK_CHECKER; SV_KEYWORD(TOK_CHECKER); } "endchecker" { if (formal_mode) return TOK_ENDCHECKER; SV_KEYWORD(TOK_ENDCHECKER); } +"bind" { if (formal_mode) return TOK_BIND; SV_KEYWORD(TOK_BIND); } "final" { SV_KEYWORD(TOK_FINAL); } "logic" { SV_KEYWORD(TOK_LOGIC); } "var" { SV_KEYWORD(TOK_VAR); } diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 9558b0e87..de463b47d 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -299,6 +299,7 @@ static void checkLabelsMatch(const char *element, const std::string *before, con %token TOK_BIT_OR_ASSIGN TOK_BIT_AND_ASSIGN TOK_BIT_XOR_ASSIGN TOK_ADD_ASSIGN %token TOK_SUB_ASSIGN TOK_DIV_ASSIGN TOK_MOD_ASSIGN TOK_MUL_ASSIGN %token TOK_SHL_ASSIGN TOK_SHR_ASSIGN TOK_SSHL_ASSIGN TOK_SSHR_ASSIGN +%token TOK_BIND %type <ast> range range_or_multirange non_opt_range non_opt_multirange %type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list non_io_wire_type io_wire_type @@ -364,6 +365,7 @@ design: typedef_decl design | package design | interface design | + bind_directive design | %empty; attr: @@ -636,7 +638,67 @@ interface_body: interface_body_stmt: param_decl | localparam_decl | typedef_decl | defparam_decl | wire_decl | always_stmt | assign_stmt | - modport_stmt; + modport_stmt | bind_directive; + +bind_directive: + TOK_BIND { + AstNode *bnode = new AstNode(AST_BIND); + ast_stack.back()->children.push_back(bnode); + ast_stack.push_back(bnode); + } + bind_target { + // bind_target should have added at least one child + log_assert(ast_stack.back()->children.size() >= 1); + } + TOK_ID { + // The single_cell parser in cell_list_no_array uses astbuf1 as + // a sort of template for constructing cells. + astbuf1 = new AstNode(AST_CELL); + astbuf1->children.push_back(new AstNode(AST_CELLTYPE)); + astbuf1->children[0]->str = *$5; + delete $5; + } + cell_parameter_list_opt cell_list_no_array ';' { + // cell_list should have added at least one more child + log_assert(ast_stack.back()->children.size() >= 2); + delete astbuf1; + ast_stack.pop_back(); + }; + +// bind_target matches the target of the bind (everything before +// bind_instantiation in the IEEE 1800 spec). +// +// We can't use the BNF from the spec directly because it's ambiguous: +// something like "bind foo bar_i (.*)" can either be interpreted with "foo" as +// a module or interface identifier (matching bind_target_scope in the spec) or +// by considering foo as a degenerate hierarchical identifier with no '.' +// characters, followed by no bit select (which matches bind_target_instance in +// the spec). +// +// Instead, we resolve everything as an instance name and then deal with the +// ambiguity when converting to RTLIL / in the hierarchy pass. +bind_target: + bind_target_instance opt_bind_target_instance_list; + +// An optional list of target instances for a bind statement, introduced by a +// colon. +opt_bind_target_instance_list: + ':' bind_target_instance_list | + %empty; + +bind_target_instance_list: + bind_target_instance | + bind_target_instance_list ',' bind_target_instance; + +// A single target instance for a bind statement. The top of ast_stack will be +// the bind node where we should add it. +bind_target_instance: + hierarchical_id { + auto *node = new AstNode(AST_IDENTIFIER); + node->str = *$1; + delete $1; + ast_stack.back()->children.push_back(node); + }; mintypmax_expr: expr { delete $1; } | @@ -813,7 +875,7 @@ module_body: module_body_stmt: task_func_decl | specify_block | param_decl | localparam_decl | typedef_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt | - enum_decl | struct_decl | + enum_decl | struct_decl | bind_directive | always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block; checker_decl: @@ -1975,6 +2037,9 @@ cell_list: cell_list ',' single_cell; single_cell: + single_cell_no_array | single_cell_arraylist; + +single_cell_no_array: TOK_ID { astbuf2 = astbuf1->clone(); if (astbuf2->type != AST_PRIMITIVE) @@ -1983,7 +2048,9 @@ single_cell: ast_stack.back()->children.push_back(astbuf2); } '(' cell_port_list ')' { SET_AST_NODE_LOC(astbuf2, @1, @$); - } | + } + +single_cell_arraylist: TOK_ID non_opt_range { astbuf2 = astbuf1->clone(); if (astbuf2->type != AST_PRIMITIVE) @@ -1994,6 +2061,10 @@ single_cell: SET_AST_NODE_LOC(astbuf2, @1, @$); }; +cell_list_no_array: + single_cell_no_array | + cell_list_no_array ',' single_cell_no_array; + prim_list: single_prim | prim_list ',' single_prim; diff --git a/tests/bind/.gitignore b/tests/bind/.gitignore new file mode 100644 index 000000000..8355de9dc --- /dev/null +++ b/tests/bind/.gitignore @@ -0,0 +1,2 @@ +*.log +run-test.mk diff --git a/tests/bind/basic.sv b/tests/bind/basic.sv new file mode 100644 index 000000000..ce0d04c48 --- /dev/null +++ b/tests/bind/basic.sv @@ -0,0 +1,20 @@ +// A basic example of the bind construct + +module foo (input logic a, input logic b, output logic c); + // Magic happens here... +endmodule + +module bar (input a, input b, output c); + assign c = a ^ b; +endmodule + +module top (); + logic u, v, w; + foo foo_i (.a (u), .b (v), .c (w)); + + bind foo bar bound_i (.*); + + always_comb begin + assert(w == u ^ v); + end +endmodule diff --git a/tests/bind/basic.ys b/tests/bind/basic.ys new file mode 100644 index 000000000..266fa4e48 --- /dev/null +++ b/tests/bind/basic.ys @@ -0,0 +1 @@ +read_verilog -sv basic.sv diff --git a/tests/bind/cell_list.sv b/tests/bind/cell_list.sv new file mode 100644 index 000000000..c0da13d29 --- /dev/null +++ b/tests/bind/cell_list.sv @@ -0,0 +1,26 @@ +// An example of specifying multiple bind instances in a single directive. This +// also uses explicit bound names. + +module foo (input logic a0, input logic b0, output logic c0, + input logic a1, input logic b1, output logic c1); + // Magic happens here... +endmodule + +module bar (input a, input b, output c); + assign c = a ^ b; +endmodule + +module top (); + logic u0, v0, w0; + logic u1, v1, w1; + + foo foo0 (.a0 (u0), .b0 (v0), .c0 (w0), + .a1 (u1), .b1 (v1), .c1 (w1)); + + bind foo bar bar0 (.a(a0), .b(b0), .c(c0)), bar1 (.a(a1), .b(b1), .c(c1)); + + always_comb begin + assert(w0 == u0 ^ v0); + assert(w1 == u1 ^ v1); + end +endmodule diff --git a/tests/bind/cell_list.ys b/tests/bind/cell_list.ys new file mode 100644 index 000000000..9afd9a941 --- /dev/null +++ b/tests/bind/cell_list.ys @@ -0,0 +1 @@ +read_verilog -sv cell_list.sv diff --git a/tests/bind/hier.sv b/tests/bind/hier.sv new file mode 100644 index 000000000..fd3bc62b8 --- /dev/null +++ b/tests/bind/hier.sv @@ -0,0 +1,20 @@ +// An example of the bind construct using a hierarchical reference starting with $root + +module foo (input logic a, input logic b, output logic c); + // Magic happens here... +endmodule + +module bar (input a, input b, output c); + assign c = a ^ b; +endmodule + +module top (); + logic u, v, w; + foo foo_i (.a (u), .b (v), .c (w)); + + always_comb begin + assert(w == u ^ v); + end +endmodule + +bind $root.top.foo_i bar bound_i (.*); diff --git a/tests/bind/hier.ys b/tests/bind/hier.ys new file mode 100644 index 000000000..c19fba100 --- /dev/null +++ b/tests/bind/hier.ys @@ -0,0 +1 @@ +read_verilog -sv hier.sv diff --git a/tests/bind/inst_list.sv b/tests/bind/inst_list.sv new file mode 100644 index 000000000..e0077caec --- /dev/null +++ b/tests/bind/inst_list.sv @@ -0,0 +1,24 @@ +// An example of specifying multiple bind targets with an instance list + +module foo (input logic a, input logic b, output logic c); + // Magic happens here... +endmodule + +module bar (input a, input b, output c); + assign c = a ^ b; +endmodule + +module top (); + logic u0, v0, w0; + logic u1, v1, w1; + + foo foo0 (.a (u0), .b (v0), .c (w0)); + foo foo1 (.a (u1), .b (v1), .c (w1)); + + bind foo : foo0, foo1 bar bound_i (.*); + + always_comb begin + assert(w0 == u0 ^ v0); + assert(w1 == u1 ^ v1); + end +endmodule diff --git a/tests/bind/inst_list.ys b/tests/bind/inst_list.ys new file mode 100644 index 000000000..ac1385b83 --- /dev/null +++ b/tests/bind/inst_list.ys @@ -0,0 +1 @@ +read_verilog -sv inst_list.sv diff --git a/tests/bind/param.sv b/tests/bind/param.sv new file mode 100644 index 000000000..c7793527a --- /dev/null +++ b/tests/bind/param.sv @@ -0,0 +1,26 @@ +// An example showing how parameters get inferred when binding + +module foo (input logic a, input logic b, output logic c); + parameter doit = 1; + + // Magic happens here... +endmodule + +module bar (input a, input b, output c); + parameter doit = 1; + + assign c = doit ? a ^ b : 0; +endmodule + +module top (input u0, input v0, output w0, + input u1, input v1, output w1); + foo #(.doit (0)) foo0 (.a (u0), .b (v0), .c (w0)); + foo #(.doit (1)) foo1 (.a (u1), .b (v1), .c (w1)); + + bind foo bar #(.doit (doit)) bound_i (.*); + + always_comb begin + assert (w0 == '0); + assert (w1 == u1 ^ v1); + end +endmodule diff --git a/tests/bind/param.ys b/tests/bind/param.ys new file mode 100644 index 000000000..a43d05759 --- /dev/null +++ b/tests/bind/param.ys @@ -0,0 +1 @@ +read_verilog -sv param.sv diff --git a/tests/bind/run-test.sh b/tests/bind/run-test.sh new file mode 100755 index 000000000..ea56b70f0 --- /dev/null +++ b/tests/bind/run-test.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -e +{ +echo "all::" +for x in *.ys; do + echo "all:: run-$x" + echo "run-$x:" + echo " @echo 'Running $x..'" + echo " @../../yosys -ql ${x%.ys}.log $x" +done +for s in *.sh; do + if [ "$s" != "run-test.sh" ]; then + echo "all:: run-$s" + echo "run-$s:" + echo " @echo 'Running $s..'" + echo " @bash $s" + fi +done +} > run-test.mk +exec ${MAKE:-make} -f run-test.mk diff --git a/tests/bind/toplevel.sv b/tests/bind/toplevel.sv new file mode 100644 index 000000000..328edcf67 --- /dev/null +++ b/tests/bind/toplevel.sv @@ -0,0 +1,20 @@ +// The bind construct occurring at top-level in the script + +module foo (input logic a, input logic b, output logic c); + // Magic happens here... +endmodule + +module bar (input a, input b, output c); + assign c = a ^ b; +endmodule + +module top (); + logic u, v, w; + foo foo_i (.a (u), .b (v), .c (w)); + + always_comb begin + assert(w == u ^ v); + end +endmodule + +bind top.foo_i bar bound_i (.*); diff --git a/tests/bind/toplevel.ys b/tests/bind/toplevel.ys new file mode 100644 index 000000000..11c0ada19 --- /dev/null +++ b/tests/bind/toplevel.ys @@ -0,0 +1 @@ +read_verilog -sv toplevel.sv |