diff options
author | Ahmed Irfan <ahmedirfan1983@gmail.com> | 2014-09-22 11:35:04 +0200 |
---|---|---|
committer | Ahmed Irfan <ahmedirfan1983@gmail.com> | 2014-09-22 11:35:04 +0200 |
commit | d3c67ad9b61f602de1100cd264efd227dcacb417 (patch) | |
tree | 88c462c53bdab128cd1edbded42483772f82612a /frontends/verilog | |
parent | b783dbe148e6d246ebd107c0913de2989ab5af48 (diff) | |
parent | 13117bb346dd02d2345f716b4403239aebe3d0e2 (diff) | |
download | yosys-d3c67ad9b61f602de1100cd264efd227dcacb417.tar.gz yosys-d3c67ad9b61f602de1100cd264efd227dcacb417.tar.bz2 yosys-d3c67ad9b61f602de1100cd264efd227dcacb417.zip |
Merge branch 'master' of https://github.com/cliffordwolf/yosys into btor
added case for memwr cell that is used in muxes (same cell is used more than one time)
corrected bug for xnor and logic_not
added pmux cell translation
Conflicts:
backends/btor/btor.cc
Diffstat (limited to 'frontends/verilog')
-rw-r--r-- | frontends/verilog/Makefile.inc | 6 | ||||
-rw-r--r-- | frontends/verilog/const2ast.cc | 46 | ||||
-rw-r--r-- | frontends/verilog/lexer.l | 90 | ||||
-rw-r--r-- | frontends/verilog/parser.y | 262 | ||||
-rw-r--r-- | frontends/verilog/preproc.cc | 65 | ||||
-rw-r--r-- | frontends/verilog/verilog_frontend.cc | 58 | ||||
-rw-r--r-- | frontends/verilog/verilog_frontend.h | 17 |
7 files changed, 440 insertions, 104 deletions
diff --git a/frontends/verilog/Makefile.inc b/frontends/verilog/Makefile.inc index 5586b4cc2..49eb320ec 100644 --- a/frontends/verilog/Makefile.inc +++ b/frontends/verilog/Makefile.inc @@ -5,13 +5,13 @@ GENFILES += frontends/verilog/parser.output GENFILES += frontends/verilog/lexer.cc frontends/verilog/parser.tab.cc: frontends/verilog/parser.y - bison -d -r all -b frontends/verilog/parser frontends/verilog/parser.y - mv frontends/verilog/parser.tab.c frontends/verilog/parser.tab.cc + $(P) bison -d -r all -b frontends/verilog/parser frontends/verilog/parser.y + $(Q) mv frontends/verilog/parser.tab.c frontends/verilog/parser.tab.cc frontends/verilog/parser.tab.h: frontends/verilog/parser.tab.cc frontends/verilog/lexer.cc: frontends/verilog/lexer.l - flex -o frontends/verilog/lexer.cc frontends/verilog/lexer.l + $(P) flex -o frontends/verilog/lexer.cc frontends/verilog/lexer.l OBJS += frontends/verilog/parser.tab.o OBJS += frontends/verilog/lexer.o diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc index c95ce5dc4..a81e3010f 100644 --- a/frontends/verilog/const2ast.cc +++ b/frontends/verilog/const2ast.cc @@ -36,10 +36,11 @@ #include "verilog_frontend.h" #include "kernel/log.h" -#include <assert.h> #include <string.h> #include <math.h> +YOSYS_NAMESPACE_BEGIN + using namespace AST; // divide an arbitrary length decimal number by two and return the rest @@ -47,11 +48,13 @@ static int my_decimal_div_by_two(std::vector<uint8_t> &digits) { int carry = 0; for (size_t i = 0; i < digits.size(); i++) { - assert(digits[i] < 10); + log_assert(digits[i] < 10); digits[i] += carry * 10; carry = digits[i] % 2; digits[i] /= 2; } + while (!digits.empty() && !digits.front()) + digits.erase(digits.begin()); return carry; } @@ -90,10 +93,15 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le if (base == 10) { data.clear(); - if (len_in_bits < 0) - len_in_bits = ceil(digits.size()/log10(2)); - for (int i = 0; i < len_in_bits; i++) - data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0); + if (len_in_bits < 0) { + while (!digits.empty()) + data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0); + while (data.size() < 32) + data.push_back(RTLIL::S0); + } else { + for (int i = 0; i < len_in_bits; i++) + data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0); + } return; } @@ -151,20 +159,24 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type) str = code.c_str(); char *endptr; - long intval = strtol(str, &endptr, 10); + long len_in_bits = strtol(str, &endptr, 10); + + // Simple base-10 integer + if (*endptr == 0) { + std::vector<RTLIL::State> data; + my_strtobin(data, str, -1, 10, case_type); + if (data.back() == RTLIL::S1) + data.push_back(RTLIL::S0); + return AstNode::mkconst_bits(data, true); + } - // Simple 32 bit integer - if (*endptr == 0) - return AstNode::mkconst_int(intval, true); - // unsized constant if (str == endptr) - intval = -1; + len_in_bits = -1; // The "<bits>'s?[bodh]<digits>" syntax if (*endptr == '\'') { - int len_in_bits = intval; std::vector<RTLIL::State> data; bool is_signed = false; if (*(endptr+1) == 's') { @@ -188,9 +200,17 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type) default: return NULL; } + if (len_in_bits < 0) { + if (is_signed && data.back() == RTLIL::S1) + data.push_back(RTLIL::S0); + while (data.size() < 32) + data.push_back(RTLIL::S0); + } return AstNode::mkconst_bits(data, is_signed); } return NULL; } +YOSYS_NAMESPACE_END + diff --git a/frontends/verilog/lexer.l b/frontends/verilog/lexer.l index 81167cf4e..c9302aba8 100644 --- a/frontends/verilog/lexer.l +++ b/frontends/verilog/lexer.l @@ -34,18 +34,37 @@ %{ +#ifdef __clang__ +// bison generates code using the 'register' storage class specifier +#pragma clang diagnostic ignored "-Wdeprecated-register" +#endif + #include "kernel/log.h" #include "verilog_frontend.h" #include "frontends/ast/ast.h" #include "parser.tab.h" +USING_YOSYS_NAMESPACE using namespace AST; using namespace VERILOG_FRONTEND; +YOSYS_NAMESPACE_BEGIN namespace VERILOG_FRONTEND { std::vector<std::string> fn_stack; std::vector<int> ln_stack; } +YOSYS_NAMESPACE_END + +#define SV_KEYWORD(_tok) \ + if (sv_mode) return _tok; \ + log("Lexer warning: The SystemVerilog keyword `%s' (at %s:%d) is not "\ + "recognized unless read_verilog is called with -sv!\n", yytext, \ + AST::current_filename.c_str(), frontend_verilog_yyget_lineno()); \ + frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); \ + return TOK_ID; + +#define YY_INPUT(buf,result,max_size) \ + result = lexin->readsome(buf, max_size); %} @@ -58,29 +77,53 @@ namespace VERILOG_FRONTEND { %x STRING %x SYNOPSYS_TRANSLATE_OFF %x SYNOPSYS_FLAGS +%x IMPORT_DPI %% -"`file_push "[^\n]* { +<INITIAL,SYNOPSYS_TRANSLATE_OFF>"`file_push "[^\n]* { fn_stack.push_back(current_filename); ln_stack.push_back(frontend_verilog_yyget_lineno()); current_filename = yytext+11; frontend_verilog_yyset_lineno(0); } -"`file_pop"[^\n]*\n { +<INITIAL,SYNOPSYS_TRANSLATE_OFF>"`file_pop"[^\n]*\n { current_filename = fn_stack.back(); fn_stack.pop_back(); frontend_verilog_yyset_lineno(ln_stack.back()); ln_stack.pop_back(); } +<INITIAL,SYNOPSYS_TRANSLATE_OFF>"`line"[ \t]+[^ \t\r\n]+[ \t]+\"[^ \r\n]+\"[^\r\n]*\n { + char *p = yytext + 5; + while (*p == ' ' || *p == '\t') p++; + frontend_verilog_yyset_lineno(atoi(p)); + while (*p && *p != ' ' && *p != '\t') p++; + while (*p == ' ' || *p == '\t') p++; + char *q = *p ? p + 1 : p; + while (*q && *q != '"') q++; + current_filename = std::string(p).substr(1, q-p-1); +} + "`file_notfound "[^\n]* { log_error("Can't open include file `%s'!\n", yytext + 15); } "`timescale"[ \t]+[^ \t\r\n/]+[ \t]*"/"[ \t]*[^ \t\r\n]* /* ignore timescale directive */ +"`default_nettype"[ \t]+[^ \t\r\n/]+ { + char *p = yytext; + while (*p != 0 && *p != ' ' && *p != '\t') p++; + while (*p == ' ' || *p == '\t') p++; + if (!strcmp(p, "none")) + VERILOG_FRONTEND::default_nettype_wire = false; + else if (!strcmp(p, "wire")) + VERILOG_FRONTEND::default_nettype_wire = true; + else + frontend_verilog_yyerror("Unsupported default nettype: %s", p); +} + "`"[a-zA-Z_$][a-zA-Z0-9_$]* { frontend_verilog_yyerror("Unimplemented compiler directive or undefined macro %s.", yytext); } @@ -112,8 +155,17 @@ namespace VERILOG_FRONTEND { "default" { return TOK_DEFAULT; } "generate" { return TOK_GENERATE; } "endgenerate" { return TOK_ENDGENERATE; } +"while" { return TOK_WHILE; } +"repeat" { return TOK_REPEAT; } -"assert"([ \t\r\n]+"property")? { return TOK_ASSERT; } +"always_comb" { SV_KEYWORD(TOK_ALWAYS); } +"always_ff" { SV_KEYWORD(TOK_ALWAYS); } +"always_latch" { SV_KEYWORD(TOK_ALWAYS); } + +"assert" { SV_KEYWORD(TOK_ASSERT); } +"property" { SV_KEYWORD(TOK_PROPERTY); } +"logic" { SV_KEYWORD(TOK_REG); } +"bit" { SV_KEYWORD(TOK_REG); } "input" { return TOK_INPUT; } "output" { return TOK_OUTPUT; } @@ -123,6 +175,7 @@ namespace VERILOG_FRONTEND { "integer" { return TOK_INTEGER; } "signed" { return TOK_SIGNED; } "genvar" { return TOK_GENVAR; } +"real" { return TOK_REAL; } [0-9]+ { frontend_verilog_yylval.string = new std::string(yytext); @@ -134,6 +187,16 @@ namespace VERILOG_FRONTEND { return TOK_CONST; } +[0-9][0-9_]*\.[0-9][0-9_]*([eE][-+]?[0-9_]+)? { + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_REALVAL; +} + +[0-9][0-9_]*[eE][-+]?[0-9_]+ { + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_REALVAL; +} + \" { BEGIN(STRING); } <STRING>\\. { yymore(); } <STRING>\" { @@ -215,6 +278,27 @@ supply1 { return TOK_SUPPLY1; } <SYNOPSYS_FLAGS>. /* ignore everything else */ <SYNOPSYS_FLAGS>"*/" { BEGIN(0); } +import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { + BEGIN(IMPORT_DPI); + return TOK_DPI_FUNCTION; +} + +<IMPORT_DPI>[a-zA-Z_$][a-zA-Z0-9_$]* { + frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); + return TOK_ID; +} + +<IMPORT_DPI>[ \t\r\n] /* ignore whitespaces */ + +<IMPORT_DPI>";" { + BEGIN(0); + return *yytext; +} + +<IMPORT_DPI>. { + return *yytext; +} + "\\"[^ \t\r\n]+ { frontend_verilog_yylval.string = new std::string(yytext); return TOK_ID; diff --git a/frontends/verilog/parser.y b/frontends/verilog/parser.y index 5b6bf58c2..a9f69a49c 100644 --- a/frontends/verilog/parser.y +++ b/frontends/verilog/parser.y @@ -35,13 +35,15 @@ %{ #include <list> -#include <assert.h> +#include <string.h> #include "verilog_frontend.h" #include "kernel/log.h" +USING_YOSYS_NAMESPACE using namespace AST; using namespace VERILOG_FRONTEND; +YOSYS_NAMESPACE_BEGIN namespace VERILOG_FRONTEND { int port_counter; std::map<std::string, int> port_stubs; @@ -53,7 +55,12 @@ namespace VERILOG_FRONTEND { struct AstNode *current_ast, *current_ast_mod; int current_function_or_task_port_id; std::vector<char> case_type_stack; + bool do_not_require_port_stubs; + bool default_nettype_wire; + bool sv_mode; + std::istream *lexin; } +YOSYS_NAMESPACE_END static void append_attr(AstNode *ast, std::map<std::string, AstNode*> *al) { @@ -83,30 +90,31 @@ static void free_attr(std::map<std::string, AstNode*> *al) %} -%name-prefix="frontend_verilog_yy" +%name-prefix "frontend_verilog_yy" %union { std::string *string; - struct AstNode *ast; - std::map<std::string, AstNode*> *al; + struct YOSYS_NAMESPACE_PREFIX AST::AstNode *ast; + std::map<std::string, YOSYS_NAMESPACE_PREFIX AST::AstNode*> *al; bool boolean; } -%token <string> TOK_STRING TOK_ID TOK_CONST TOK_PRIMITIVE +%token <string> TOK_STRING TOK_ID TOK_CONST TOK_REALVAL TOK_PRIMITIVE %token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END %token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM %token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG %token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL -%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR -%token TOK_POSEDGE TOK_NEGEDGE TOK_OR +%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT +%token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR %token TOK_CASE TOK_CASEX TOK_CASEZ TOK_ENDCASE TOK_DEFAULT %token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK -%token TOK_GENERATE TOK_ENDGENERATE TOK_GENVAR +%token TOK_GENERATE TOK_ENDGENERATE TOK_GENVAR TOK_REAL %token TOK_SYNOPSYS_FULL_CASE TOK_SYNOPSYS_PARALLEL_CASE %token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED -%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_ASSERT +%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_ASSERT TOK_PROPERTY -%type <ast> wire_type range non_opt_range expr basic_expr concat_list rvalue lvalue lvalue_concat_list +%type <ast> range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int +%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list %type <string> opt_label tok_prim_wrapper hierarchical_id %type <boolean> opt_signed %type <al> attr @@ -130,14 +138,21 @@ static void free_attr(std::map<std::string, AstNode*> *al) %% -input: - module input | - defattr input | - /* empty */ { - for (auto &it : default_attr_list) - delete it.second; - default_attr_list.clear(); - }; +input: { + ast_stack.push_back(current_ast); +} design { + ast_stack.pop_back(); + log_assert(SIZE(ast_stack) == 0); + for (auto &it : default_attr_list) + delete it.second; + default_attr_list.clear(); +}; + +design: + module design | + defattr design | + task_func_decl design | + /* empty */; attr: { @@ -205,10 +220,11 @@ hierarchical_id: module: attr TOK_MODULE TOK_ID { + do_not_require_port_stubs = false; AstNode *mod = new AstNode(AST_MODULE); - current_ast->children.push_back(mod); - current_ast_mod = mod; + ast_stack.back()->children.push_back(mod); ast_stack.push_back(mod); + current_ast_mod = mod; port_stubs.clear(); port_counter = 0; mod->str = *$3; @@ -219,7 +235,8 @@ module: frontend_verilog_yyerror("Missing details for module port `%s'.", port_stubs.begin()->first.c_str()); ast_stack.pop_back(); - assert(ast_stack.size() == 0); + log_assert(ast_stack.size() == 1); + current_ast_mod = NULL; }; module_para_opt: @@ -288,7 +305,10 @@ module_arg: ast_stack.back()->children.push_back(node); append_attr(node, $1); delete $4; - } module_arg_opt_assignment; + } module_arg_opt_assignment | + '.' '.' '.' { + do_not_require_port_stubs = true; + }; wire_type: { @@ -320,6 +340,7 @@ wire_type_token: astbuf3->is_reg = true; astbuf3->range_left = 31; astbuf3->range_right = 0; + astbuf3->is_signed = true; } | TOK_GENVAR { astbuf3->type = AST_GENVAR; @@ -352,6 +373,15 @@ non_opt_range: $$->children.push_back($2); }; +non_opt_multirange: + non_opt_range non_opt_range { + $$ = new AstNode(AST_MULTIRANGE, $1, $2); + } | + non_opt_multirange non_opt_range { + $$ = $1; + $$->children.push_back($2); + }; + range: non_opt_range { $$ = $1; @@ -360,44 +390,120 @@ range: $$ = NULL; }; +range_or_multirange: + range { $$ = $1; } | + non_opt_multirange { $$ = $1; }; + +range_or_signed_int: + range { + $$ = $1; + } | + TOK_INTEGER { + $$ = new AstNode(AST_RANGE); + $$->children.push_back(AstNode::mkconst_int(31, true)); + $$->children.push_back(AstNode::mkconst_int(0, true)); + $$->is_signed = true; + }; + module_body: module_body module_body_stmt | + /* the following line makes the generate..endgenrate keywords optional */ + module_body gen_stmt | /* empty */; module_body_stmt: task_func_decl | param_decl | localparam_decl | defparam_decl | wire_decl | assign_stmt | cell_stmt | - always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert; + always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property; task_func_decl: - TOK_TASK TOK_ID ';' { + attr TOK_DPI_FUNCTION TOK_ID TOK_ID { + current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$3), AstNode::mkconst_str(*$4)); + current_function_or_task->str = *$4; + append_attr(current_function_or_task, $1); + ast_stack.back()->children.push_back(current_function_or_task); + delete $3; + delete $4; + } opt_dpi_function_args ';' { + current_function_or_task = NULL; + } | + attr TOK_DPI_FUNCTION TOK_ID '=' TOK_ID TOK_ID { + current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$5), AstNode::mkconst_str(*$3)); + current_function_or_task->str = *$6; + append_attr(current_function_or_task, $1); + ast_stack.back()->children.push_back(current_function_or_task); + delete $3; + delete $5; + delete $6; + } opt_dpi_function_args ';' { + current_function_or_task = NULL; + } | + attr TOK_DPI_FUNCTION TOK_ID ':' TOK_ID '=' TOK_ID TOK_ID { + current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$7), AstNode::mkconst_str(*$3 + ":" + RTLIL::unescape_id(*$5))); + current_function_or_task->str = *$8; + append_attr(current_function_or_task, $1); + ast_stack.back()->children.push_back(current_function_or_task); + delete $3; + delete $5; + delete $7; + delete $8; + } opt_dpi_function_args ';' { + current_function_or_task = NULL; + } | + attr TOK_TASK TOK_ID ';' { current_function_or_task = new AstNode(AST_TASK); - current_function_or_task->str = *$2; + current_function_or_task->str = *$3; + append_attr(current_function_or_task, $1); ast_stack.back()->children.push_back(current_function_or_task); ast_stack.push_back(current_function_or_task); current_function_or_task_port_id = 1; - delete $2; + delete $3; } task_func_body TOK_ENDTASK { current_function_or_task = NULL; ast_stack.pop_back(); } | - TOK_FUNCTION opt_signed range TOK_ID ';' { + attr TOK_FUNCTION opt_signed range_or_signed_int TOK_ID ';' { current_function_or_task = new AstNode(AST_FUNCTION); - current_function_or_task->str = *$4; + current_function_or_task->str = *$5; + append_attr(current_function_or_task, $1); ast_stack.back()->children.push_back(current_function_or_task); ast_stack.push_back(current_function_or_task); AstNode *outreg = new AstNode(AST_WIRE); - if ($3 != NULL) - outreg->children.push_back($3); - outreg->str = *$4; - outreg->is_signed = $2; + outreg->str = *$5; + outreg->is_signed = $3; + if ($4 != NULL) { + outreg->children.push_back($4); + outreg->is_signed = $3 || $4->is_signed; + $4->is_signed = false; + } current_function_or_task->children.push_back(outreg); current_function_or_task_port_id = 1; - delete $4; + delete $5; } task_func_body TOK_ENDFUNCTION { current_function_or_task = NULL; ast_stack.pop_back(); }; +dpi_function_arg: + TOK_ID TOK_ID { + current_function_or_task->children.push_back(AstNode::mkconst_str(*$1)); + delete $1; + delete $2; + } | + TOK_ID { + current_function_or_task->children.push_back(AstNode::mkconst_str(*$1)); + delete $1; + }; + +opt_dpi_function_args: + '(' dpi_function_args ')' | + /* empty */; + +dpi_function_args: + dpi_function_args ',' dpi_function_arg | + dpi_function_args ',' | + dpi_function_arg | + /* empty */; + opt_signed: TOK_SIGNED { $$ = true; @@ -422,6 +528,14 @@ param_integer: astbuf1->children.push_back(new AstNode(AST_RANGE)); astbuf1->children.back()->children.push_back(AstNode::mkconst_int(31, true)); astbuf1->children.back()->children.push_back(AstNode::mkconst_int(0, true)); + astbuf1->is_signed = true; + } | /* empty */; + +param_real: + TOK_REAL { + if (astbuf1->children.size() != 1) + frontend_verilog_yyerror("Syntax error."); + astbuf1->children.push_back(new AstNode(AST_REALVALUE)); } | /* empty */; param_range: @@ -437,7 +551,7 @@ param_decl: TOK_PARAMETER { astbuf1 = new AstNode(AST_PARAMETER); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); - } param_signed param_integer param_range param_decl_list ';' { + } param_signed param_integer param_real param_range param_decl_list ';' { delete astbuf1; }; @@ -445,7 +559,7 @@ localparam_decl: TOK_LOCALPARAM { astbuf1 = new AstNode(AST_LOCALPARAM); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); - } param_signed param_integer param_range param_decl_list ';' { + } param_signed param_integer param_real param_range param_decl_list ';' { delete astbuf1; }; @@ -533,7 +647,7 @@ wire_name_and_opt_assign: }; wire_name: - TOK_ID range { + TOK_ID range_or_multirange { AstNode *node = astbuf1->clone(); node->str = *$1; append_attr_clone(node, albuf); @@ -552,6 +666,9 @@ wire_name: node->children.push_back($2); } if (current_function_or_task == NULL) { + if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) { + port_stubs[*$1] = ++port_counter; + } if (port_stubs.count(*$1) != 0) { if (!node->is_input && !node->is_output) frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $1->c_str()); @@ -563,12 +680,11 @@ wire_name: if (node->is_input || node->is_output) frontend_verilog_yyerror("Module port `%s' is not declared in module header.", $1->c_str()); } - ast_stack.back()->children.push_back(node); } else { if (node->is_input || node->is_output) node->port_id = current_function_or_task_port_id++; - current_function_or_task->children.push_back(node); } + ast_stack.back()->children.push_back(node); delete $1; }; @@ -621,6 +737,13 @@ single_cell: astbuf2->str = *$1; delete $1; ast_stack.back()->children.push_back(astbuf2); + } '(' cell_port_list ')' | + TOK_ID non_opt_range { + astbuf2 = astbuf1->clone(); + if (astbuf2->type != AST_PRIMITIVE) + astbuf2->str = *$1; + delete $1; + ast_stack.back()->children.push_back(new AstNode(AST_CELLARRAY, $2, astbuf2)); } '(' cell_port_list ')'; prim_list: @@ -753,6 +876,11 @@ assert: ast_stack.back()->children.push_back(new AstNode(AST_ASSERT, $3)); }; +assert_property: + TOK_ASSERT TOK_PROPERTY '(' expr ')' ';' { + ast_stack.back()->children.push_back(new AstNode(AST_ASSERT, $4)); + }; + simple_behavioral_stmt: lvalue '=' expr { AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $3); @@ -808,6 +936,32 @@ behavioral_stmt: ast_stack.pop_back(); ast_stack.pop_back(); } | + attr TOK_WHILE '(' expr ')' { + AstNode *node = new AstNode(AST_WHILE); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + append_attr(node, $1); + AstNode *block = new AstNode(AST_BLOCK); + ast_stack.back()->children.push_back($4); + ast_stack.back()->children.push_back(block); + ast_stack.push_back(block); + } behavioral_stmt { + ast_stack.pop_back(); + ast_stack.pop_back(); + } | + attr TOK_REPEAT '(' expr ')' { + AstNode *node = new AstNode(AST_REPEAT); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + append_attr(node, $1); + AstNode *block = new AstNode(AST_BLOCK); + ast_stack.back()->children.push_back($4); + ast_stack.back()->children.push_back(block); + ast_stack.push_back(block); + } behavioral_stmt { + ast_stack.pop_back(); + ast_stack.pop_back(); + } | attr TOK_IF '(' expr ')' { AstNode *node = new AstNode(AST_CASE); AstNode *block = new AstNode(AST_BLOCK); @@ -934,8 +1088,8 @@ rvalue: $$->str = *$1; delete $1; } | - hierarchical_id non_opt_range non_opt_range { - $$ = new AstNode(AST_IDENTIFIER, $2, $3); + hierarchical_id non_opt_multirange { + $$ = new AstNode(AST_IDENTIFIER, $2); $$->str = *$1; delete $1; }; @@ -976,9 +1130,12 @@ single_arg: }; module_gen_body: - module_gen_body gen_stmt | + module_gen_body gen_stmt_or_module_body_stmt | /* empty */; +gen_stmt_or_module_body_stmt: + gen_stmt | module_body_stmt; + // this production creates the obligatory if-else shift/reduce conflict gen_stmt: TOK_FOR '(' { @@ -1017,15 +1174,14 @@ gen_stmt: if ($6 != NULL) delete $6; ast_stack.pop_back(); - } | - module_body_stmt; + }; gen_stmt_block: { AstNode *node = new AstNode(AST_GENBLOCK); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); - } gen_stmt { + } gen_stmt_or_module_body_stmt { ast_stack.pop_back(); }; @@ -1073,12 +1229,30 @@ basic_expr: delete $1; delete $2; } | + TOK_CONST TOK_CONST { + $$ = const2ast(*$1 + *$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back()); + if ($$ == NULL || (*$2)[0] != '\'') + log_error("Value conversion failed: `%s%s'\n", $1->c_str(), $2->c_str()); + delete $1; + delete $2; + } | TOK_CONST { $$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back()); if ($$ == NULL) log_error("Value conversion failed: `%s'\n", $1->c_str()); delete $1; } | + TOK_REALVAL { + $$ = new AstNode(AST_REALVALUE); + char *p = strdup($1->c_str()), *q; + for (int i = 0, j = 0; !p[j]; j++) + if (p[j] != '_') + p[i++] = p[j], p[i] = 0; + $$->realvalue = strtod(p, &q); + log_assert(*q == 0); + delete $1; + free(p); + } | TOK_STRING { $$ = AstNode::mkconst_str(*$1); delete $1; diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index db53e8c68..f83433219 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -37,7 +37,8 @@ #include <stdarg.h> #include <stdio.h> #include <string.h> -#include <assert.h> + +YOSYS_NAMESPACE_BEGIN static std::list<std::string> output_code; static std::list<std::string> input_buffer; @@ -65,7 +66,7 @@ static char next_char() if (input_buffer.empty()) return 0; - assert(input_buffer_charp <= input_buffer.front().size()); + log_assert(input_buffer_charp <= input_buffer.front().size()); if (input_buffer_charp == input_buffer.front().size()) { input_buffer_charp = 0; input_buffer.pop_front(); @@ -130,6 +131,12 @@ static std::string next_token(bool pass_newline = false) token += ch; } } + if (token == "\"\"" && (ch = next_char()) != 0) { + if (ch == '"') + token += ch; + else + return_char(ch); + } } else if (ch == '/') { @@ -186,7 +193,7 @@ static std::string next_token(bool pass_newline = false) return token; } -static void input_file(FILE *f, std::string filename) +static void input_file(std::istream &f, std::string filename) { char buffer[513]; int rc; @@ -195,14 +202,14 @@ static void input_file(FILE *f, std::string filename) auto it = input_buffer.begin(); input_buffer.insert(it, "`file_push " + filename + "\n"); - while ((rc = fread(buffer, 1, sizeof(buffer)-1, f)) > 0) { + while ((rc = f.readsome(buffer, sizeof(buffer)-1)) > 0) { buffer[rc] = 0; input_buffer.insert(it, buffer); } - input_buffer.insert(it, "`file_pop\n"); + input_buffer.insert(it, "\n`file_pop\n"); } -std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs) +std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs) { std::set<std::string> defines_with_args; std::map<std::string, std::string> defines_map(pre_defines_map); @@ -281,27 +288,28 @@ std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::m else fn = fn.substr(0, pos) + fn.substr(pos+1); } - FILE *fp = fopen(fn.c_str(), "r"); - if (fp == NULL && fn.size() > 0 && fn[0] != '/' && filename.find('/') != std::string::npos) { + std::ifstream ff; + ff.clear(); + ff.open(fn.c_str()); + if (ff.fail() && fn.size() > 0 && fn[0] != '/' && filename.find('/') != std::string::npos) { // if the include file was not found, it is not given with an absolute path, and the // currently read file is given with a path, then try again relative to its directory - std::string fn2 = filename.substr(0, filename.rfind('/')+1) + fn; - fp = fopen(fn2.c_str(), "r"); + ff.clear(); + ff.open(filename.substr(0, filename.rfind('/')+1) + fn); } - if (fp == NULL && fn.size() > 0 && fn[0] != '/') { + if (ff.fail() && fn.size() > 0 && fn[0] != '/') { // if the include file was not found and it is not given with an absolute path, then // search it in the include path for (auto incdir : include_dirs) { - std::string fn2 = incdir + '/' + fn; - fp = fopen(fn2.c_str(), "r"); - if (fp != NULL) break; + ff.clear(); + ff.open(incdir + '/' + fn); + if (!ff.fail()) break; } } - if (fp != NULL) { - input_file(fp, fn); - fclose(fp); - } else - output_code.push_back("`file_notfound " + fn + "\n"); + if (ff.fail()) + output_code.push_back("`file_notfound " + fn); + else + input_file(ff, fn); continue; } @@ -310,12 +318,17 @@ std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::m std::map<std::string, int> args; skip_spaces(); name = next_token(true); + bool here_doc_mode = false; int newline_count = 0; int state = 0; if (skip_spaces() != "") state = 3; while (!tok.empty()) { tok = next_token(); + if (tok == "\"\"\"") { + here_doc_mode = !here_doc_mode; + continue; + } if (state == 0 && tok == "(") { state = 1; skip_spaces(); @@ -332,9 +345,14 @@ std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::m if (state != 2) state = 3; if (tok == "\n") { - return_char('\n'); - break; - } + if (here_doc_mode) { + value += " "; + newline_count++; + } else { + return_char('\n'); + break; + } + } else if (tok == "\\") { char ch = next_char(); if (ch == '\n') { @@ -373,7 +391,6 @@ std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::m } if (tok == "`timescale") { - std::string name; skip_spaces(); while (!tok.empty() && tok != "\n") tok = next_token(true); @@ -429,3 +446,5 @@ std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::m return output; } +YOSYS_NAMESPACE_END + diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index c70d6f305..c6d4a0b79 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -27,13 +27,11 @@ */ #include "verilog_frontend.h" -#include "kernel/register.h" -#include "kernel/log.h" +#include "kernel/yosys.h" #include "libs/sha1/sha1.h" -#include <sstream> #include <stdarg.h> -#include <assert.h> +YOSYS_NAMESPACE_BEGIN using namespace VERILOG_FRONTEND; // use the Verilog bison/flex parser to generate an AST and use AST::process() to convert it to RTLIL @@ -47,11 +45,15 @@ struct VerilogFrontend : public Frontend { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" read_verilog [filename]\n"); + log(" read_verilog [options] [filename]\n"); log("\n"); log("Load modules from a verilog file to the current design. A large subset of\n"); log("Verilog-2005 is supported.\n"); log("\n"); + log(" -sv\n"); + log(" enable support for SystemVerilog features. (only a small subset\n"); + log(" of SystemVerilog is supported)\n"); + log("\n"); log(" -dump_ast1\n"); log(" dump abstract syntax tree (before simplification)\n"); log("\n"); @@ -106,6 +108,11 @@ struct VerilogFrontend : public Frontend { log(" ignore re-definitions of modules. (the default behavior is to\n"); log(" create an error message.)\n"); log("\n"); + log(" -defer\n"); + log(" only read the abstract syntax tree and defer actual compilation\n"); + log(" to a later 'hierarchy' command. Useful in cases where the default\n"); + log(" parameters of modules yield invalid or not synthesizable code.\n"); + log("\n"); log(" -setattr <attribute_name>\n"); log(" set the specified attribute (to the value 1) on all loaded modules\n"); log("\n"); @@ -120,8 +127,13 @@ struct VerilogFrontend : public Frontend { log("The command 'verilog_defaults' can be used to register default options for\n"); log("subsequent calls to 'read_verilog'.\n"); log("\n"); + log("Note that the Verilog frontend does a pretty good job of processing valid\n"); + log("verilog input, but has not very good error reporting. It generally is\n"); + log("recommended to use a simulator (for example icarus verilog) for checking\n"); + log("the syntax of the code, rather than to rely on read_verilog for that.\n"); + log("\n"); } - virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) { bool flag_dump_ast1 = false; bool flag_dump_ast2 = false; @@ -135,10 +147,13 @@ struct VerilogFrontend : public Frontend { bool flag_noopt = false; bool flag_icells = false; bool flag_ignore_redef = false; + bool flag_defer = false; std::map<std::string, std::string> defines_map; std::list<std::string> include_dirs; std::list<std::string> attributes; + frontend_verilog_yydebug = false; + sv_mode = false; log_header("Executing Verilog-2005 frontend.\n"); @@ -147,6 +162,10 @@ struct VerilogFrontend : public Frontend { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; + if (arg == "-sv") { + sv_mode = true; + continue; + } if (arg == "-dump_ast1") { flag_dump_ast1 = true; continue; @@ -199,6 +218,10 @@ struct VerilogFrontend : public Frontend { flag_ignore_redef = true; continue; } + if (arg == "-defer") { + flag_defer = true; + continue; + } if (arg == "-setattr" && argidx+1 < args.size()) { attributes.push_back(RTLIL::escape_id(args[++argidx])); continue; @@ -241,33 +264,34 @@ struct VerilogFrontend : public Frontend { AST::get_line_num = &frontend_verilog_yyget_lineno; current_ast = new AST::AstNode(AST::AST_DESIGN); + default_nettype_wire = true; - FILE *fp = f; + lexin = f; std::string code_after_preproc; if (!flag_nopp) { - code_after_preproc = frontend_verilog_preproc(f, filename, defines_map, include_dirs); + code_after_preproc = frontend_verilog_preproc(*f, filename, defines_map, include_dirs); if (flag_ppdump) log("-- Verilog code after preprocessor --\n%s-- END OF DUMP --\n", code_after_preproc.c_str()); - fp = fmemopen((void*)code_after_preproc.c_str(), code_after_preproc.size(), "r"); + lexin = new std::istringstream(code_after_preproc); } frontend_verilog_yyset_lineno(1); - frontend_verilog_yyrestart(fp); + frontend_verilog_yyrestart(NULL); frontend_verilog_yyparse(); frontend_verilog_yylex_destroy(); for (auto &child : current_ast->children) { - log_assert(child->type == AST::AST_MODULE); - for (auto &attr : attributes) - if (child->attributes.count(attr) == 0) - child->attributes[attr] = AST::AstNode::mkconst_int(1, false); + if (child->type == AST::AST_MODULE) + for (auto &attr : attributes) + if (child->attributes.count(attr) == 0) + child->attributes[attr] = AST::AstNode::mkconst_int(1, false); } - AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_ignore_redef); + AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_ignore_redef, flag_defer, default_nettype_wire); if (!flag_nopp) - fclose(fp); + delete lexin; delete current_ast; current_ast = NULL; @@ -350,3 +374,5 @@ struct VerilogDefaults : public Pass { } } VerilogDefaults; +YOSYS_NAMESPACE_END + diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h index 8b4fae6e9..af6495f8f 100644 --- a/frontends/verilog/verilog_frontend.h +++ b/frontends/verilog/verilog_frontend.h @@ -29,12 +29,14 @@ #ifndef VERILOG_FRONTEND_H #define VERILOG_FRONTEND_H -#include "kernel/rtlil.h" +#include "kernel/yosys.h" #include "frontends/ast/ast.h" #include <stdio.h> #include <stdint.h> #include <list> +YOSYS_NAMESPACE_BEGIN + namespace VERILOG_FRONTEND { // this variable is set to a new AST_DESIGN node and then filled with the AST by the bison parser @@ -42,10 +44,21 @@ namespace VERILOG_FRONTEND // this function converts a Verilog constant to an AST_CONSTANT node AST::AstNode *const2ast(std::string code, char case_type = 0); + + // state of `default_nettype + extern bool default_nettype_wire; + + // running in SystemVerilog mode + extern bool sv_mode; + + // lexer input stream + extern std::istream *lexin; } // the pre-processor -std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs); +std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs); + +YOSYS_NAMESPACE_END // the usual bison/flex stuff extern int frontend_verilog_yydebug; |