From 7764d0ba1dcf064ae487ee985c43083a0909e7f4 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 5 Jan 2013 11:13:26 +0100 Subject: initial import --- frontends/verilog/Makefile.inc | 19 + frontends/verilog/const2ast.cc | 197 ++++++ frontends/verilog/lexer.l | 264 ++++++++ frontends/verilog/parser.y | 1074 +++++++++++++++++++++++++++++++++ frontends/verilog/preproc.cc | 360 +++++++++++ frontends/verilog/verilog_frontend.cc | 148 +++++ frontends/verilog/verilog_frontend.h | 62 ++ 7 files changed, 2124 insertions(+) create mode 100644 frontends/verilog/Makefile.inc create mode 100644 frontends/verilog/const2ast.cc create mode 100644 frontends/verilog/lexer.l create mode 100644 frontends/verilog/parser.y create mode 100644 frontends/verilog/preproc.cc create mode 100644 frontends/verilog/verilog_frontend.cc create mode 100644 frontends/verilog/verilog_frontend.h (limited to 'frontends/verilog') diff --git a/frontends/verilog/Makefile.inc b/frontends/verilog/Makefile.inc new file mode 100644 index 00000000..6693f2d1 --- /dev/null +++ b/frontends/verilog/Makefile.inc @@ -0,0 +1,19 @@ + +GENFILES += frontends/verilog/parser.tab.cc +GENFILES += frontends/verilog/parser.tab.h +GENFILES += frontends/verilog/parser.output +GENFILES += frontends/verilog/lexer.cc + +frontends/verilog/parser.tab.cc frontends/verilog/parser.tab.h: 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 + +frontends/verilog/lexer.cc: frontends/verilog/lexer.l + flex -o frontends/verilog/lexer.cc frontends/verilog/lexer.l + +OBJS += frontends/verilog/parser.tab.o +OBJS += frontends/verilog/lexer.o +OBJS += frontends/verilog/preproc.o +OBJS += frontends/verilog/verilog_frontend.o +OBJS += frontends/verilog/const2ast.o + diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc new file mode 100644 index 00000000..e5beaead --- /dev/null +++ b/frontends/verilog/const2ast.cc @@ -0,0 +1,197 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The Verilog frontend. + * + * This frontend is using the AST frontend library (see frontends/ast/). + * Thus this frontend does not generate RTLIL code directly but creates an + * AST directly from the Verilog parse tree and then passes this AST to + * the AST frontend library. + * + * --- + * + * This file contains an ad-hoc parser for Verilog constants. The Verilog + * lexer does only recognize a constant but does not actually split it to its + * components. I.e. it just passes the Verilog code for the constant to the + * bison parser. The parser then uses the function const2ast() from this file + * to create an AST node for the constant. + * + */ + +#include "verilog_frontend.h" +#include "kernel/log.h" +#include +#include +#include + +using namespace AST; + +// divide an arbitrary length decimal number by two and return the rest +static int my_decimal_div_by_two(std::vector &digits) +{ + int carry = 0; + for (size_t i = 0; i < digits.size(); i++) { + assert(digits[i] < 10); + digits[i] += carry * 10; + carry = digits[i] % 2; + digits[i] /= 2; + } + return carry; +} + +// find the number of significant bits in a binary number (not including the sign bit) +static int my_ilog2(int x) +{ + int ret = 0; + while (x != 0 && x != -1) { + x = x >> 1; + ret++; + } + return ret; +} + +// parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?') +static void my_strtobin(std::vector &data, const char *str, int len_in_bits, int base, char case_type) +{ + // all digits in string (MSB at index 0) + std::vector digits; + + while (*str) { + if ('0' <= *str && *str <= '9') + digits.push_back(*str - '0'); + else if ('a' <= *str && *str <= 'f') + digits.push_back(10 + *str - 'a'); + else if ('A' <= *str && *str <= 'F') + digits.push_back(10 + *str - 'A'); + else if (*str == 'x' || *str == 'X') + digits.push_back(0xf0); + else if (*str == 'z' || *str == 'Z') + digits.push_back(0xf1); + else if (*str == '?') + digits.push_back(0xf2); + str++; + } + + 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); + return; + } + + int bits_per_digit = my_ilog2(base-1); + if (len_in_bits < 0) + len_in_bits = digits.size() * bits_per_digit; + + data.clear(); + data.resize(len_in_bits); + + for (int i = 0; i < len_in_bits; i++) { + int bitmask = 1 << (i % bits_per_digit); + int digitidx = digits.size() - (i / bits_per_digit) - 1; + if (digitidx < 0) { + if (i > 0 && (data[i-1] == RTLIL::Sz || data[i-1] == RTLIL::Sx || data[i-1] == RTLIL::Sa)) + data[i] = data[i-1]; + else + data[i] = RTLIL::S0; + } else if (digits[digitidx] == 0xf0) + data[i] = case_type == 'x' ? RTLIL::Sa : RTLIL::Sx; + else if (digits[digitidx] == 0xf1) + data[i] = case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz; + else if (digits[digitidx] == 0xf2) + data[i] = RTLIL::Sa; + else + data[i] = (digits[digitidx] & bitmask) ? RTLIL::S1 : RTLIL::S0; + } +} + +// convert the verilog code for a constant to an AST node +AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type) +{ + const char *str = code.c_str(); + + // Strings + if (*str == '"') { + int len = strlen(str) - 2; + std::vector data; + data.reserve(len * 8); + for (int i = 0; i < len; i++) { + unsigned char ch = str[len - i]; + for (int j = 0; j < 8; j++) { + data.push_back((ch & 1) ? RTLIL::S1 : RTLIL::S0); + ch = ch >> 1; + } + } + AstNode *ast = AstNode::mkconst_bits(data, false); + ast->str = code; + return ast; + } + + for (size_t i = 0; i < code.size(); i++) + if (code[i] == '_' || code[i] == ' ' || code[i] == '\t' || code[i] == '\r' || code[i] == '\n') + code.erase(code.begin()+(i--)); + str = code.c_str(); + + char *endptr; + long intval = strtol(str, &endptr, 10); + + // Simple 32 bit integer + if (*endptr == 0) + return AstNode::mkconst_int(intval, true); + + // variable length constant + if (str == endptr) + intval = -1; + + // The "'[bodh]" syntax + if (*endptr == '\'') + { + int len_in_bits = intval; + std::vector data; + bool is_signed = false; + if (*(endptr+1) == 's') { + is_signed = true; + endptr++; + } + switch (*(endptr+1)) + { + case 'b': + my_strtobin(data, endptr+2, len_in_bits, 2, case_type); + break; + case 'o': + my_strtobin(data, endptr+2, len_in_bits, 8, case_type); + break; + case 'd': + my_strtobin(data, endptr+2, len_in_bits, 10, case_type); + break; + case 'h': + my_strtobin(data, endptr+2, len_in_bits, 16, case_type); + break; + default: + goto error; + } + return AstNode::mkconst_bits(data, is_signed); + } + +error: + log_error("Value conversion failed: `%s'\n", code.c_str()); +} + diff --git a/frontends/verilog/lexer.l b/frontends/verilog/lexer.l new file mode 100644 index 00000000..a269c072 --- /dev/null +++ b/frontends/verilog/lexer.l @@ -0,0 +1,264 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The Verilog frontend. + * + * This frontend is using the AST frontend library (see frontends/ast/). + * Thus this frontend does not generate RTLIL code directly but creates an + * AST directly from the Verilog parse tree and then passes this AST to + * the AST frontend library. + * + * --- + * + * A simple lexer for Verilog code. Non-preprocessor compiler directives are + * handled here. The preprocessor stuff is handled in preproc.cc. Everything + * else is left to the bison parser (see parser.y). + * + */ + +%{ + +#include "kernel/log.h" +#include "verilog_frontend.h" +#include "frontends/ast/ast.h" +#include "parser.tab.h" + +using namespace AST; +using namespace VERILOG_FRONTEND; + +namespace VERILOG_FRONTEND { + std::vector fn_stack; + std::vector ln_stack; + bool lexer_feature_defattr; +} + +%} + +%option yylineno +%option noyywrap +%option nounput +%option prefix="frontend_verilog_yy" + +%x COMMENT +%x STRING +%x SYNOPSYS_TRANSLATE_OFF +%x SYNOPSYS_FLAGS + +%% + +"`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 { + current_filename = fn_stack.back(); + frontend_verilog_yyset_lineno(ln_stack.back()); +} + +"`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 */ + +"`yosys_enable_defattr" lexer_feature_defattr = true; +"`yosys_disable_defattr" lexer_feature_defattr = false; + +"`"[a-zA-Z_$][a-zA-Z0-9_$]* { + frontend_verilog_yyerror("Unimplemented compiler directive or undefined macro %s.", yytext); +} + +"module" { return TOK_MODULE; } +"endmodule" { return TOK_ENDMODULE; } +"function" { return TOK_FUNCTION; } +"endfunction" { return TOK_ENDFUNCTION; } +"task" { return TOK_TASK; } +"endtask" { return TOK_ENDTASK; } +"parameter" { return TOK_PARAMETER; } +"localparam" { return TOK_LOCALPARAM; } +"assign" { return TOK_ASSIGN; } +"always" { return TOK_ALWAYS; } +"initial" { return TOK_INITIAL; } +"begin" { return TOK_BEGIN; } +"end" { return TOK_END; } +"if" { return TOK_IF; } +"else" { return TOK_ELSE; } +"for" { return TOK_FOR; } +"posedge" { return TOK_POSEDGE; } +"negedge" { return TOK_NEGEDGE; } +"or" { return TOK_OR; } +"case" { return TOK_CASE; } +"casex" { return TOK_CASEX; } +"casez" { return TOK_CASEZ; } +"endcase" { return TOK_ENDCASE; } +"default" { return TOK_DEFAULT; } +"generate" { return TOK_GENERATE; } +"endgenerate" { return TOK_ENDGENERATE; } + +"input" { return TOK_INPUT; } +"output" { return TOK_OUTPUT; } +"inout" { return TOK_INOUT; } +"wire" { return TOK_WIRE; } +"reg" { return TOK_REG; } +"integer" { return TOK_INTEGER; } +"signed" { return TOK_SIGNED; } +"genvar" { return TOK_GENVAR; } + +[0-9]+ { + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_CONST; +} + +[0-9]*[ \t]*\'s?[bodh][ \t\r\n]*[0-9a-fA-FzxZX?_]+ { + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_CONST; +} + +\" { BEGIN(STRING); } +\\. { yymore(); } +\" { + BEGIN(0); + char *yystr = strdup(yytext); + yystr[strlen(yytext) - 1] = 0; + int i = 0, j = 0; + while (yystr[i]) { + if (yystr[i] == '\\' && yystr[i + 1]) { + i++; + if (yystr[i] == 'n') + yystr[i] = '\n'; + else if (yystr[i] == 't') + yystr[i] = '\t'; + else if ('0' <= yystr[i] && yystr[i] <= '7') { + yystr[i] = yystr[i] - '0'; + if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { + yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0'; + i++; + } + if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { + yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0'; + i++; + } + } + } + yystr[j++] = yystr[i++]; + } + yystr[j] = 0; + frontend_verilog_yylval.string = new std::string(yystr); + free(yystr); + return TOK_STRING; +} +. { yymore(); } + +and|nand|or|nor|xor|xnor|not|buf { + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_PRIMITIVE; +} + +supply0 { return TOK_SUPPLY0; } +supply1 { return TOK_SUPPLY1; } + +"$"(display|time|stop|finish) { + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_ID; +} + +"$signed" { return TOK_TO_SIGNED; } +"$unsigned" { return TOK_TO_UNSIGNED; } + +[a-zA-Z_$][a-zA-Z0-9_$]* { + frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); + return TOK_ID; +} + +"/*"[ \t]*synopsys[ \t]*translate_off[ \t]*"*/" { + log("Warning: Found one of those horrible `synopsys translate_off' comments.\n"); + log("It is strongly suggested to use `ifdef constructs instead!\n"); + BEGIN(SYNOPSYS_TRANSLATE_OFF); +} +. /* ignore synopsys translate_off body */ +\n /* ignore synopsys translate_off body */ +"/*"[ \t]*"synopsys"[ \t]*"translate_on"[ \t]*"*/" { BEGIN(0); } + +"/*"[ \t]*"synopsys"[ \t]+ { + BEGIN(SYNOPSYS_FLAGS); +} +full_case { + log("Warning: Found one of those horrible `synopsys full_case' comments.\n"); + log("It is strongly suggested to use verilog x-values and default branches instead!\n"); + return TOK_SYNOPSYS_FULL_CASE; +} +parallel_case { + log("Warning: Found one of those horrible `synopsys parallel_case' comments.\n"); + log("It is strongly suggested to use verilog `parallel_case' attributes instead!\n"); + return TOK_SYNOPSYS_PARALLEL_CASE; +} +. /* ignore everything else */ +"*/" { BEGIN(0); } + +"\\"[^ \t\r\n]+ { + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_ID; +} + +"(*" { return ATTR_BEGIN; } +"*)" { return ATTR_END; } + +"{*" { if (lexer_feature_defattr) return DEFATTR_BEGIN; else REJECT; } +"*}" { if (lexer_feature_defattr) return DEFATTR_END; else REJECT; } + +"**" { return OP_POW; } +"||" { return OP_LOR; } +"&&" { return OP_LAND; } +"==" { return OP_EQ; } +"!=" { return OP_NE; } +"<=" { return OP_LE; } +">=" { return OP_GE; } + + /* "~&" { return OP_NAND; } */ + /* "~|" { return OP_NOR; } */ +"~^" { return OP_XNOR; } +"^~" { return OP_XNOR; } + +"<<" { return OP_SHL; } +">>" { return OP_SHR; } +"<<<" { return OP_SSHL; } +">>>" { return OP_SSHR; } + +"/*" { BEGIN(COMMENT); } +. /* ignore comment body */ +\n /* ignore comment body */ +"*/" { BEGIN(0); } + +[ \t\r\n] /* ignore whitespaces */ +\\[\r\n] /* ignore continuation sequence */ +"//"[^\r\n]* /* ignore one-line comments */ +"#"[$a-zA-Z_0-9\.]+ /* ignore simulation timings */ + +. { return *yytext; } + +%% + +// this is a hack to avoid the 'yyinput defined but not used' error msgs +void *frontend_verilog_avoid_input_warnings() { + return (void*)&yyinput; +} + diff --git a/frontends/verilog/parser.y b/frontends/verilog/parser.y new file mode 100644 index 00000000..7c12bd56 --- /dev/null +++ b/frontends/verilog/parser.y @@ -0,0 +1,1074 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The Verilog frontend. + * + * This frontend is using the AST frontend library (see frontends/ast/). + * Thus this frontend does not generate RTLIL code directly but creates an + * AST directly from the Verilog parse tree and then passes this AST to + * the AST frontend library. + * + * --- + * + * This is the actual bison parser for Verilog code. The AST ist created directly + * from the bison reduce functions here. Note that this code uses a few global + * variables to hold the state of the AST generator and therefore this parser is + * not reentrant. + * + */ + +%{ +#include +#include +#include "verilog_frontend.h" +#include "kernel/log.h" + +using namespace AST; +using namespace VERILOG_FRONTEND; + +namespace VERILOG_FRONTEND { + int port_counter; + std::map port_stubs; + std::map attr_list, default_attr_list; + std::map *albuf; + std::vector ast_stack; + struct AstNode *astbuf1, *astbuf2, *astbuf3; + struct AstNode *current_function_or_task; + struct AstNode *current_ast, *current_ast_mod; + int current_function_or_task_port_id; + std::vector case_type_stack; +} + +static void append_attr(AstNode *ast, std::map *al) +{ + for (auto &it : *al) { + if (ast->attributes.count(it.first) > 0) + delete ast->attributes[it.first]; + ast->attributes[it.first] = it.second; + } + delete al; +} + +static void append_attr_clone(AstNode *ast, std::map *al) +{ + for (auto &it : *al) { + if (ast->attributes.count(it.first) > 0) + delete ast->attributes[it.first]; + ast->attributes[it.first] = it.second->clone(); + } +} + +static void free_attr(std::map *al) +{ + for (auto &it : *al) + delete it.second; + delete al; +} + +%} + +%name-prefix="frontend_verilog_yy" + +%union { + std::string *string; + struct AstNode *ast; + std::map *al; + bool boolean; +} + +%token TOK_STRING TOK_ID TOK_CONST TOK_PRIMITIVE +%token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END +%token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM +%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_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_SYNOPSYS_FULL_CASE TOK_SYNOPSYS_PARALLEL_CASE +%token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED + +%type wire_type range expr basic_expr concat_list lvalue lvalue_concat_list +%type opt_label tok_prim_wrapper +%type opt_signed +%type attr + +// operator precedence from low to high +%left OP_LOR +%left OP_LAND +%left '|' +%left '^' OP_XNOR +%left '&' +%left OP_EQ OP_NE +%left '<' OP_LE OP_GE '>' +%left OP_SHL OP_SHR OP_SSHL OP_SSHR +%left '+' '-' +%left '*' '/' '%' +%left OP_POW +%right UNARY_OPS + +%expect 2 +%debug + +%% + +input: + module input | + defattr input | + /* empty */ { + for (auto &it : default_attr_list) + delete it.second; + default_attr_list.clear(); + }; + +attr: + { + for (auto &it : attr_list) + delete it.second; + attr_list.clear(); + for (auto &it : default_attr_list) + attr_list[it.first] = it.second->clone(); + } attr_opt { + std::map *al = new std::map; + al->swap(attr_list); + $$ = al; + }; + +attr_opt: + attr_opt ATTR_BEGIN opt_attr_list ATTR_END | + /* empty */; + +defattr: + DEFATTR_BEGIN { + for (auto &it : default_attr_list) + delete it.second; + default_attr_list.clear(); + for (auto &it : attr_list) + delete it.second; + attr_list.clear(); + } opt_attr_list { + default_attr_list = attr_list; + attr_list.clear(); + } DEFATTR_END; + +opt_attr_list: + attr_list | /* empty */; + +attr_list: + attr_assign | + attr_list ',' attr_assign; + +attr_assign: + TOK_ID { + if (attr_list.count(*$1) != 0) + delete attr_list[*$1]; + attr_list[*$1] = AstNode::mkconst_int(0, false, 0); + delete $1; + } | + TOK_ID '=' expr { + if (attr_list.count(*$1) != 0) + delete attr_list[*$1]; + attr_list[*$1] = $3; + delete $1; + }; + +module: + attr TOK_MODULE TOK_ID { + AstNode *mod = new AstNode(AST_MODULE); + current_ast->children.push_back(mod); + current_ast_mod = mod; + ast_stack.push_back(mod); + port_stubs.clear(); + port_counter = 0; + mod->str = *$3; + append_attr(mod, $1); + delete $3; + } module_para_opt module_args_opt ';' module_body TOK_ENDMODULE { + if (port_stubs.size() != 0) + frontend_verilog_yyerror("Missing details for module port `%s'.", + port_stubs.begin()->first.c_str()); + ast_stack.pop_back(); + assert(ast_stack.size() == 0); + }; + +module_para_opt: + '#' '(' TOK_PARAMETER param_decl_list optional_comma ')' | /* empty */; + +module_args_opt: + '(' ')' | /* empty */ | '(' module_args optional_comma ')'; + +module_args: + module_arg | module_args ',' module_arg; + +optional_comma: + ',' | /* empty */; + +module_arg: + TOK_ID range { + if (port_stubs.count(*$1) != 0) + frontend_verilog_yyerror("Duplicate module port `%s'.", $1->c_str()); + port_stubs[*$1] = ++port_counter; + if ($2 != NULL) + delete $2; + delete $1; + } | + attr wire_type range TOK_ID { + AstNode *node = $2; + node->str = *$4; + node->port_id = ++port_counter; + if ($3 != NULL) + node->children.push_back($3); + if (!node->is_input && !node->is_output) + frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $4->c_str()); + if (node->is_reg && node->is_input && !node->is_output) + frontend_verilog_yyerror("Input port `%s' is declared as register.", $4->c_str()); + ast_stack.back()->children.push_back(node); + append_attr(node, $1); + delete $4; + }; + +wire_type: + { + astbuf3 = new AstNode(AST_WIRE); + } wire_type_token_list { + $$ = astbuf3; + }; + +wire_type_token_list: + wire_type_token | wire_type_token_list wire_type_token; + +wire_type_token: + TOK_INPUT { + astbuf3->is_input = true; + } | + TOK_OUTPUT { + astbuf3->is_output = true; + } | + TOK_INOUT { + astbuf3->is_input = true; + astbuf3->is_output = true; + } | + TOK_WIRE { + } | + TOK_REG { + astbuf3->is_reg = true; + } | + TOK_INTEGER { + astbuf3->is_reg = true; + astbuf3->range_left = 31; + astbuf3->range_right = 0; + } | + TOK_GENVAR { + astbuf3->type = AST_GENVAR; + astbuf3->is_reg = true; + astbuf3->range_left = 31; + astbuf3->range_right = 0; + } | + TOK_SIGNED { + astbuf3->is_signed = true; + }; + +range: + '[' expr ':' expr ']' { + $$ = new AstNode(AST_RANGE); + $$->children.push_back($2); + $$->children.push_back($4); + } | + '[' expr ']' { + $$ = new AstNode(AST_RANGE); + $$->children.push_back($2); + } | + /* empty */ { + $$ = NULL; + }; + +module_body: + module_body module_body_stmt | + /* empty */; + +module_body_stmt: + task_func_decl | param_decl | localparam_decl | wire_decl | assign_stmt | cell_stmt | + always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr; + +task_func_decl: + TOK_TASK TOK_ID ';' { + current_function_or_task = new AstNode(AST_TASK); + current_function_or_task->str = *$2; + 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; + } task_func_body TOK_ENDTASK { + current_function_or_task = NULL; + ast_stack.pop_back(); + } | + TOK_FUNCTION opt_signed range TOK_ID ';' { + current_function_or_task = new AstNode(AST_FUNCTION); + current_function_or_task->str = *$4; + 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; + current_function_or_task->children.push_back(outreg); + current_function_or_task_port_id = 1; + delete $4; + } task_func_body TOK_ENDFUNCTION { + current_function_or_task = NULL; + ast_stack.pop_back(); + }; + +opt_signed: + TOK_SIGNED { + $$ = true; + } | + /* empty */ { + $$ = false; + }; + +task_func_body: + task_func_body wire_decl | + task_func_body behavioral_stmt | + /* empty */; + +param_decl: + TOK_PARAMETER param_decl_list ';'; + +param_decl_list: + single_param_decl | param_decl_list ',' single_param_decl; + +single_param_decl: + range TOK_ID '=' expr { + AstNode *node = new AstNode(AST_PARAMETER); + node->str = *$2; + node->children.push_back($4); + if ($1 != NULL) + node->children.push_back($1); + ast_stack.back()->children.push_back(node); + delete $2; + }; + +localparam_decl: + TOK_LOCALPARAM localparam_decl_list ';'; + +localparam_decl_list: + single_localparam_decl | localparam_decl_list ',' single_localparam_decl; + +single_localparam_decl: + range TOK_ID '=' expr { + AstNode *node = new AstNode(AST_LOCALPARAM); + node->str = *$2; + node->children.push_back($4); + if ($1 != NULL) + node->children.push_back($1); + ast_stack.back()->children.push_back(node); + delete $2; + }; + +wire_decl: + attr wire_type range { + albuf = $1; + astbuf1 = $2; + astbuf2 = $3; + if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) { + if (astbuf2) { + frontend_verilog_yyerror("Syntax error."); + } else { + astbuf2 = new AstNode(AST_RANGE); + astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true)); + astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_right, true)); + } + } + if (astbuf2 && astbuf2->children.size() != 2) + frontend_verilog_yyerror("Syntax error."); + } wire_name_list ';' { + delete astbuf1; + if (astbuf2 != NULL) + delete astbuf2; + free_attr(albuf); + } | + attr TOK_SUPPLY0 TOK_ID ';' { + ast_stack.back()->children.push_back(new AstNode(AST_WIRE)); + ast_stack.back()->children.back()->str = *$3; + append_attr(ast_stack.back()->children.back(), $1); + ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1))); + ast_stack.back()->children.back()->children[0]->str = *$3; + delete $3; + } | + attr TOK_SUPPLY1 TOK_ID ';' { + ast_stack.back()->children.push_back(new AstNode(AST_WIRE)); + ast_stack.back()->children.back()->str = *$3; + append_attr(ast_stack.back()->children.back(), $1); + ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(1, false, 1))); + ast_stack.back()->children.back()->children[0]->str = *$3; + delete $3; + }; + +wire_name_list: + wire_name_and_opt_assign | wire_name_list ',' wire_name_and_opt_assign; + +wire_name_and_opt_assign: + wire_name | + wire_name '=' expr { + if (!astbuf1->is_reg) { + AstNode *wire = new AstNode(AST_IDENTIFIER); + wire->str = ast_stack.back()->children.back()->str; + ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $3)); + } + }; + +wire_name: + TOK_ID range { + AstNode *node = astbuf1->clone(); + node->str = *$1; + append_attr_clone(node, albuf); + if (astbuf2 != NULL) + node->children.push_back(astbuf2->clone()); + if ($2 != NULL) { + if (node->is_input || node->is_output) + frontend_verilog_yyerror("Syntax error."); + if (!astbuf2) { + AstNode *rng = new AstNode(AST_RANGE); + rng->children.push_back(AstNode::mkconst_int(0, true)); + rng->children.push_back(AstNode::mkconst_int(0, true)); + node->children.push_back(rng); + } + node->type = AST_MEMORY; + node->children.push_back($2); + } + if (current_function_or_task == NULL) { + 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()); + if (node->is_reg && node->is_input && !node->is_output) + frontend_verilog_yyerror("Input port `%s' is declared as register.", $1->c_str()); + node->port_id = port_stubs[*$1]; + port_stubs.erase(*$1); + } else { + 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); + } + delete $1; + }; + +assign_stmt: + TOK_ASSIGN assign_expr_list ';'; + +assign_expr_list: + assign_expr | assign_expr_list ',' assign_expr; + +assign_expr: + expr '=' expr { + ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, $1, $3)); + }; + +cell_stmt: + attr TOK_ID { + astbuf1 = new AstNode(AST_CELL); + append_attr(astbuf1, $1); + astbuf1->children.push_back(new AstNode(AST_CELLTYPE)); + astbuf1->children[0]->str = *$2; + delete $2; + } cell_parameter_list_opt cell_list ';' { + delete astbuf1; + } | + attr tok_prim_wrapper { + astbuf1 = new AstNode(AST_PRIMITIVE); + astbuf1->str = *$2; + append_attr(astbuf1, $1); + delete $2; + } prim_list ';' { + delete astbuf1; + }; + +tok_prim_wrapper: + TOK_PRIMITIVE { + $$ = $1; + } | + TOK_OR { + $$ = new std::string("or"); + }; + +cell_list: + single_cell | + cell_list ',' single_cell; + +single_cell: + TOK_ID { + astbuf2 = astbuf1->clone(); + if (astbuf2->type != AST_PRIMITIVE) + astbuf2->str = *$1; + delete $1; + ast_stack.back()->children.push_back(astbuf2); + } '(' cell_port_list ')'; + +prim_list: + single_prim | + prim_list ',' single_prim; + +single_prim: + single_cell | + /* no name */ { + astbuf2 = astbuf1->clone(); + ast_stack.back()->children.push_back(astbuf2); + } '(' cell_port_list ')'; + +cell_parameter_list_opt: + '#' '(' cell_parameter_list ')' | /* empty */; + +cell_parameter_list: + /* empty */ | cell_parameter | + cell_parameter ',' cell_parameter_list; + +cell_parameter: + expr { + AstNode *node = new AstNode(AST_PARASET); + astbuf1->children.push_back(node); + node->children.push_back($1); + } | + '.' TOK_ID '(' expr ')' { + AstNode *node = new AstNode(AST_PARASET); + node->str = *$2; + astbuf1->children.push_back(node); + node->children.push_back($4); + delete $2; + }; + +cell_port_list: + /* empty */ | cell_port | + cell_port ',' cell_port_list | + /* empty */ ',' { + AstNode *node = new AstNode(AST_ARGUMENT); + astbuf2->children.push_back(node); + } cell_port_list; + +cell_port: + expr { + AstNode *node = new AstNode(AST_ARGUMENT); + astbuf2->children.push_back(node); + node->children.push_back($1); + } | + '.' TOK_ID '(' expr ')' { + AstNode *node = new AstNode(AST_ARGUMENT); + node->str = *$2; + astbuf2->children.push_back(node); + node->children.push_back($4); + delete $2; + } | + '.' TOK_ID '(' ')' { + AstNode *node = new AstNode(AST_ARGUMENT); + node->str = *$2; + astbuf2->children.push_back(node); + delete $2; + }; + +always_stmt: + attr TOK_ALWAYS { + AstNode *node = new AstNode(AST_ALWAYS); + append_attr(node, $1); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + } always_cond { + AstNode *block = new AstNode(AST_BLOCK); + ast_stack.back()->children.push_back(block); + ast_stack.push_back(block); + } behavioral_stmt { + ast_stack.pop_back(); + ast_stack.pop_back(); + } | + attr TOK_INITIAL { + AstNode *node = new AstNode(AST_ALWAYS); + append_attr(node, $1); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + AstNode *block = new AstNode(AST_BLOCK); + ast_stack.back()->children.push_back(block); + ast_stack.push_back(block); + } behavioral_stmt { + ast_stack.pop_back(); + ast_stack.pop_back(); + }; + +always_cond: + '@' '(' always_events ')' | + '@' '*' | + /* empty */; + +always_events: + always_event | + always_events TOK_OR always_event | + always_events ',' always_event; + +always_event: + TOK_POSEDGE expr { + AstNode *node = new AstNode(AST_POSEDGE); + ast_stack.back()->children.push_back(node); + node->children.push_back($2); + } | + TOK_NEGEDGE expr { + AstNode *node = new AstNode(AST_NEGEDGE); + ast_stack.back()->children.push_back(node); + node->children.push_back($2); + } | + expr { + AstNode *node = new AstNode(AST_EDGE); + ast_stack.back()->children.push_back(node); + node->children.push_back($1); + }; + +opt_label: + ':' TOK_ID { + $$ = $2; + } | + /* empty */ { + $$ = NULL; + }; + +simple_behavioral_stmt: + lvalue '=' expr { + AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $3); + ast_stack.back()->children.push_back(node); + } | + lvalue OP_LE expr { + AstNode *node = new AstNode(AST_ASSIGN_LE, $1, $3); + ast_stack.back()->children.push_back(node); + }; + +// this production creates the obligatory if-else shift/reduce conflict +behavioral_stmt: + defattr | + simple_behavioral_stmt ';' | + TOK_ID attr { + AstNode *node = new AstNode(AST_TCALL); + node->str = *$1; + delete $1; + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + append_attr(node, $2); + } opt_arg_list ';'{ + ast_stack.pop_back(); + } | + attr TOK_BEGIN opt_label { + AstNode *node = new AstNode(AST_BLOCK); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + append_attr(node, $1); + } behavioral_stmt_list TOK_END opt_label { + if ($3 != NULL) + delete $3; + if ($7 != NULL) + delete $7; + ast_stack.pop_back(); + } | + attr TOK_FOR '(' { + AstNode *node = new AstNode(AST_FOR); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + append_attr(node, $1); + } simple_behavioral_stmt ';' expr { + ast_stack.back()->children.push_back($7); + } ';' simple_behavioral_stmt ')' { + AstNode *block = new AstNode(AST_BLOCK); + 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); + AstNode *cond = new AstNode(AST_COND, AstNode::mkconst_int(1, false, 1), block); + ast_stack.back()->children.push_back(node); + node->children.push_back(new AstNode(AST_REDUCE_BOOL, $4)); + node->children.push_back(cond); + ast_stack.push_back(node); + ast_stack.push_back(block); + append_attr(node, $1); + } behavioral_stmt optional_else { + ast_stack.pop_back(); + ast_stack.pop_back(); + } | + attr case_type '(' expr ')' { + AstNode *node = new AstNode(AST_CASE, $4); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + append_attr(node, $1); + } opt_synopsys_attr case_body TOK_ENDCASE { + case_type_stack.pop_back(); + ast_stack.pop_back(); + }; + +case_type: + TOK_CASE { + case_type_stack.push_back(0); + } | + TOK_CASEX { + case_type_stack.push_back('x'); + } | + TOK_CASEZ { + case_type_stack.push_back('z'); + }; + +opt_synopsys_attr: + opt_synopsys_attr TOK_SYNOPSYS_FULL_CASE { + if (ast_stack.back()->attributes.count("\\full_case") == 0) + ast_stack.back()->attributes["\\full_case"] = AstNode::mkconst_int(0, false, 0); + } | + opt_synopsys_attr TOK_SYNOPSYS_PARALLEL_CASE { + if (ast_stack.back()->attributes.count("\\parallel_case") == 0) + ast_stack.back()->attributes["\\parallel_case"] = AstNode::mkconst_int(0, false, 0); + } | + /* empty */; + +behavioral_stmt_opt: + behavioral_stmt | + ';' ; + +behavioral_stmt_list: + behavioral_stmt_list behavioral_stmt | + /* empty */; + +optional_else: + TOK_ELSE { + AstNode *block = new AstNode(AST_BLOCK); + AstNode *cond = new AstNode(AST_COND, new AstNode(AST_DEFAULT), block); + ast_stack.pop_back(); + ast_stack.back()->children.push_back(cond); + ast_stack.push_back(block); + } behavioral_stmt | + /* empty */; + +case_body: + case_body case_item | + /* empty */; + +case_item: + { + AstNode *node = new AstNode(AST_COND); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + } case_select { + AstNode *block = new AstNode(AST_BLOCK); + ast_stack.back()->children.push_back(block); + ast_stack.push_back(block); + case_type_stack.push_back(0); + } behavioral_stmt_opt { + case_type_stack.pop_back(); + ast_stack.pop_back(); + ast_stack.pop_back(); + }; + +case_select: + case_expr_list ':' | + TOK_DEFAULT; + +case_expr_list: + TOK_DEFAULT { + ast_stack.back()->children.push_back(new AstNode(AST_DEFAULT)); + } | + expr { + ast_stack.back()->children.push_back($1); + } | + case_expr_list ',' expr { + ast_stack.back()->children.push_back($3); + }; + +lvalue: + TOK_ID range { + $$ = new AstNode(AST_IDENTIFIER); + $$->str = *$1; + if ($2) + $$->children.push_back($2); + delete $1; + } | + '{' lvalue_concat_list '}' { + $$ = $2; + }; + +lvalue_concat_list: + expr { + $$ = new AstNode(AST_CONCAT); + $$->children.push_back($1); + } | + expr ',' lvalue_concat_list { + $$ = $3; + $$->children.push_back($1); + }; + +opt_arg_list: + '(' arg_list optional_comma ')' | + /* empty */; + +arg_list: + arg_list2 | + /* empty */; + +arg_list2: + single_arg | + arg_list ',' single_arg; + +single_arg: + expr { + ast_stack.back()->children.push_back($1); + }; + +module_gen_body: + module_gen_body gen_stmt | + module_gen_body module_body_stmt | + /* empty */; + +// this production creates the obligatory if-else shift/reduce conflict +gen_stmt: + TOK_FOR '(' { + AstNode *node = new AstNode(AST_GENFOR); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + } simple_behavioral_stmt ';' expr { + ast_stack.back()->children.push_back($6); + } ';' simple_behavioral_stmt ')' gen_stmt { + ast_stack.pop_back(); + } | + TOK_IF '(' expr ')' { + AstNode *node = new AstNode(AST_GENIF); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + ast_stack.back()->children.push_back($3); + } gen_stmt opt_gen_else { + ast_stack.pop_back(); + } | + TOK_BEGIN opt_label { + AstNode *node = new AstNode(AST_GENBLOCK); + node->str = $2 ? *$2 : std::string(); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + } module_gen_body TOK_END opt_label { + if ($2 != NULL) + delete $2; + if ($6 != NULL) + delete $6; + ast_stack.pop_back(); + }; + +opt_gen_else: + TOK_ELSE gen_stmt | /* empty */; + +expr: + basic_expr { + $$ = $1; + } | + basic_expr '?' attr expr ':' expr { + $$ = new AstNode(AST_TERNARY); + $$->children.push_back($1); + $$->children.push_back($4); + $$->children.push_back($6); + append_attr($$, $3); + }; + +basic_expr: + TOK_CONST { + $$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back()); + delete $1; + } | + TOK_STRING { + std::string str = *$1; + std::vector data; + data.reserve(str.size() * 8); + for (size_t i = 0; i < str.size(); i++) { + unsigned char ch = str[str.size() - i - 1]; + for (int j = 0; j < 8; j++) { + data.push_back((ch & 1) ? RTLIL::S1 : RTLIL::S0); + ch = ch >> 1; + } + } + $$ = AstNode::mkconst_bits(data, false); + $$->str = str; + delete $1; + } | + TOK_ID range { + $$ = new AstNode(AST_IDENTIFIER, $2); + $$->str = *$1; + delete $1; + } | + TOK_ID attr { + AstNode *node = new AstNode(AST_FCALL); + node->str = *$1; + delete $1; + ast_stack.push_back(node); + append_attr(node, $2); + } '(' arg_list optional_comma ')' { + $$ = ast_stack.back(); + ast_stack.pop_back(); + } | + TOK_TO_SIGNED attr '(' expr ')' { + $$ = new AstNode(AST_TO_SIGNED, $4); + append_attr($$, $2); + } | + TOK_TO_UNSIGNED attr '(' expr ')' { + $$ = new AstNode(AST_TO_UNSIGNED, $4); + append_attr($$, $2); + } | + '(' expr ')' { + $$ = $2; + } | + '{' concat_list '}' { + $$ = $2; + } | + '{' expr '{' expr '}' '}' { + $$ = new AstNode(AST_REPLICATE, $2, $4); + } | + '~' attr basic_expr %prec UNARY_OPS { + $$ = new AstNode(AST_BIT_NOT, $3); + append_attr($$, $2); + } | + basic_expr '&' attr basic_expr { + $$ = new AstNode(AST_BIT_AND, $1, $4); + append_attr($$, $3); + } | + basic_expr '|' attr basic_expr { + $$ = new AstNode(AST_BIT_OR, $1, $4); + append_attr($$, $3); + } | + basic_expr '^' attr basic_expr { + $$ = new AstNode(AST_BIT_XOR, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_XNOR attr basic_expr { + $$ = new AstNode(AST_BIT_XNOR, $1, $4); + append_attr($$, $3); + } | + '&' attr basic_expr %prec UNARY_OPS { + $$ = new AstNode(AST_REDUCE_AND, $3); + append_attr($$, $2); + } | + '|' attr basic_expr %prec UNARY_OPS { + $$ = new AstNode(AST_REDUCE_OR, $3); + append_attr($$, $2); + } | + '^' attr basic_expr %prec UNARY_OPS { + $$ = new AstNode(AST_REDUCE_XOR, $3); + append_attr($$, $2); + } | + OP_XNOR attr basic_expr %prec UNARY_OPS { + $$ = new AstNode(AST_REDUCE_XNOR, $3); + append_attr($$, $2); + } | + basic_expr OP_SHL attr basic_expr { + $$ = new AstNode(AST_SHIFT_LEFT, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_SHR attr basic_expr { + $$ = new AstNode(AST_SHIFT_RIGHT, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_SSHL attr basic_expr { + $$ = new AstNode(AST_SHIFT_SLEFT, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_SSHR attr basic_expr { + $$ = new AstNode(AST_SHIFT_SRIGHT, $1, $4); + append_attr($$, $3); + } | + basic_expr '<' attr basic_expr { + $$ = new AstNode(AST_LT, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_LE attr basic_expr { + $$ = new AstNode(AST_LE, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_EQ attr basic_expr { + $$ = new AstNode(AST_EQ, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_NE attr basic_expr { + $$ = new AstNode(AST_NE, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_GE attr basic_expr { + $$ = new AstNode(AST_GE, $1, $4); + append_attr($$, $3); + } | + basic_expr '>' attr basic_expr { + $$ = new AstNode(AST_GT, $1, $4); + append_attr($$, $3); + } | + basic_expr '+' attr basic_expr { + $$ = new AstNode(AST_ADD, $1, $4); + append_attr($$, $3); + } | + basic_expr '-' attr basic_expr { + $$ = new AstNode(AST_SUB, $1, $4); + append_attr($$, $3); + } | + basic_expr '*' attr basic_expr { + $$ = new AstNode(AST_MUL, $1, $4); + append_attr($$, $3); + } | + basic_expr '/' attr basic_expr { + $$ = new AstNode(AST_DIV, $1, $4); + append_attr($$, $3); + } | + basic_expr '%' attr basic_expr { + $$ = new AstNode(AST_MOD, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_POW attr basic_expr { + $$ = new AstNode(AST_POW, $1, $4); + append_attr($$, $3); + } | + '+' attr basic_expr %prec UNARY_OPS { + $$ = new AstNode(AST_POS, $3); + append_attr($$, $2); + } | + '-' attr basic_expr %prec UNARY_OPS { + $$ = new AstNode(AST_NEG, $3); + append_attr($$, $2); + } | + basic_expr OP_LAND attr basic_expr { + $$ = new AstNode(AST_LOGIC_AND, $1, $4); + append_attr($$, $3); + } | + basic_expr OP_LOR attr basic_expr { + $$ = new AstNode(AST_LOGIC_OR, $1, $4); + append_attr($$, $3); + } | + '!' attr basic_expr %prec UNARY_OPS { + $$ = new AstNode(AST_LOGIC_NOT, $3); + append_attr($$, $2); + }; + +concat_list: + expr { + $$ = new AstNode(AST_CONCAT, $1); + } | + expr ',' concat_list { + $$ = $3; + $$->children.push_back($1); + }; + diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc new file mode 100644 index 00000000..e6fdc1ff --- /dev/null +++ b/frontends/verilog/preproc.cc @@ -0,0 +1,360 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The Verilog frontend. + * + * This frontend is using the AST frontend library (see frontends/ast/). + * Thus this frontend does not generate RTLIL code directly but creates an + * AST directly from the Verilog parse tree and then passes this AST to + * the AST frontend library. + * + * --- + * + * Ad-hoc implementation of a Verilog preprocessor. The directives `define, + * `include, `ifdef, `ifndef, `else and `endif are handled here. All other + * directives are handled by the lexer (see lexer.l). + * + */ + +#include "verilog_frontend.h" +#include "kernel/log.h" +#include +#include +#include +#include +#include + +static std::list output_code; +static std::list input_buffer; +static size_t input_buffer_charp; + +static void return_char(char ch) +{ + if (input_buffer_charp == 0) + input_buffer.push_front(std::string() + ch); + else + input_buffer.front()[--input_buffer_charp] = ch; +} + +static void insert_input(std::string str) +{ + if (input_buffer_charp != 0) { + input_buffer.front() = input_buffer.front().substr(input_buffer_charp); + input_buffer_charp = 0; + } + input_buffer.push_front(str); +} + +static char next_char() +{ + if (input_buffer.size() == 0) + return 0; + + assert(input_buffer_charp <= input_buffer.front().size()); + if (input_buffer_charp == input_buffer.front().size()) { + input_buffer_charp = 0; + input_buffer.pop_front(); + return next_char(); + } + + char ch = input_buffer.front()[input_buffer_charp++]; + return ch == '\r' ? next_char() : ch; +} + +static void skip_spaces() +{ + while (1) { + char ch = next_char(); + if (ch == 0) + break; + if (ch != ' ' && ch != '\t') { + return_char(ch); + break; + } + } +} + +static std::string next_token(bool pass_newline = false) +{ + std::string token; + + char ch = next_char(); + if (ch == 0) + return token; + + token += ch; + if (ch == '\n') { + if (pass_newline) { + output_code.push_back(token); + return ""; + } + return token; + } + + if (ch == ' ' || ch == '\t') + { + while ((ch = next_char()) != 0) { + if (ch != ' ' && ch != '\t') { + return_char(ch); + break; + } + token += ch; + } + } + else if (ch == '"') + { + while ((ch = next_char()) != 0) { + token += ch; + if (ch == '"') + break; + if (ch == '\\') { + if ((ch = next_char()) != 0) + token += ch; + } + } + } + else if (ch == '/') + { + if ((ch = next_char()) != 0) { + if (ch == '/') { + token += '*'; + char last_ch = 0; + while ((ch = next_char()) != 0) { + if (ch == '\n') { + return_char(ch); + break; + } + if (last_ch != '*' || ch != '/') { + token += ch; + last_ch = ch; + } + } + token += " */"; + } + else if (ch == '*') { + token += '*'; + int newline_count = 0; + char last_ch = 0; + while ((ch = next_char()) != 0) { + if (ch == '\n') { + newline_count++; + token += ' '; + } else + token += ch; + if (last_ch == '*' && ch == '/') + break; + last_ch = ch; + } + while (newline_count-- > 0) + return_char('\n'); + } + else + return_char(ch); + } + } + else + { + const char *ok = "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789"; + while ((ch = next_char()) != 0) { + if (strchr(ok, ch) == NULL) { + return_char(ch); + break; + } + token += ch; + } + } + + return token; +} + +static void input_file(FILE *f, std::string filename) +{ + char buffer[513]; + int rc; + + insert_input(""); + auto it = input_buffer.begin(); + + input_buffer.insert(it, "`file_push " + filename + "\n"); + while ((rc = fread(buffer, 1, sizeof(buffer)-1, f)) > 0) { + buffer[rc] = 0; + input_buffer.insert(it, buffer); + } + input_buffer.insert(it, "`file_pop\n"); +} + +static std::string define_to_feature(std::string defname) +{ + if (defname == "__YOSYS_ENABLE_DEFATTR__") + return "defattr"; + return std::string(); +} + +std::string frontend_verilog_preproc(FILE *f, std::string filename) +{ + std::map defines_map; + int ifdef_fail_level = 0; + + output_code.clear(); + input_buffer.clear(); + input_buffer_charp = 0; + + input_file(f, filename); + defines_map["__YOSYS__"] = "1"; + + while (!input_buffer.empty()) + { + std::string tok = next_token(); + // printf("token: >>%s<<\n", tok != "\n" ? tok.c_str() : "NEWLINE"); + + if (tok == "`endif") { + if (ifdef_fail_level > 0) + ifdef_fail_level--; + continue; + } + + if (tok == "`else") { + if (ifdef_fail_level == 0) + ifdef_fail_level = 1; + else if (ifdef_fail_level == 1) + ifdef_fail_level = 0; + continue; + } + + if (tok == "`ifdef") { + skip_spaces(); + std::string name = next_token(true); + if (ifdef_fail_level > 0 || defines_map.count(name) == 0) + ifdef_fail_level++; + continue; + } + + if (tok == "`ifndef") { + skip_spaces(); + std::string name = next_token(true); + if (ifdef_fail_level > 0 || defines_map.count(name) != 0) + ifdef_fail_level++; + continue; + } + + if (ifdef_fail_level > 0) { + if (tok == "\n") + output_code.push_back(tok); + continue; + } + + if (tok == "`include") { + skip_spaces(); + std::string fn = next_token(true); + while (1) { + size_t pos = fn.find('"'); + if (pos == std::string::npos) + break; + if (pos == 0) + fn = fn.substr(1); + 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::string fn2 = filename.substr(0, filename.rfind('/')+1) + fn; + fp = fopen(fn2.c_str(), "r"); + } + if (fp != NULL) { + input_file(fp, fn); + fclose(fp); + } else + output_code.push_back("`file_notfound " + fn + "\n"); + continue; + } + + if (tok == "`define") { + std::string name, value; + skip_spaces(); + name = next_token(true); + if (!define_to_feature(name).empty()) + output_code.push_back("`yosys_enable_" + define_to_feature(name)); + skip_spaces(); + int newline_count = 0; + while (!tok.empty()) { + tok = next_token(); + if (tok == "\n") { + return_char('\n'); + break; + } + if (tok == "\\") { + char ch = next_char(); + if (ch == '\n') { + value += " "; + newline_count++; + } else { + value += std::string("\\"); + return_char(ch); + } + } else + value += tok; + } + while (newline_count-- > 0) + return_char('\n'); + // printf("define: >>%s<< -> >>%s<<\n", name.c_str(), value.c_str()); + defines_map[name] = value; + continue; + } + + if (tok == "`undef") { + std::string name; + skip_spaces(); + name = next_token(true); + if (!define_to_feature(name).empty()) + output_code.push_back("`yosys_disable_" + define_to_feature(name)); + // printf("undef: >>%s<<\n", name.c_str()); + defines_map.erase(name); + continue; + } + + if (tok == "`timescale") { + std::string name; + skip_spaces(); + while (!tok.empty() && tok != "\n") + tok = next_token(true); + if (tok == "\n") + return_char('\n'); + continue; + } + + if (tok.size() > 1 && tok[0] == '`' && defines_map.count(tok.substr(1)) > 0) { + // printf("expand: >>%s<< -> >>%s<<\n", tok.c_str(), defines_map[tok.substr(1)].c_str()); + insert_input(defines_map[tok.substr(1)]); + continue; + } + + output_code.push_back(tok); + } + + std::string output; + for (auto &str : output_code) + output += str; + + output_code.clear(); + input_buffer.clear(); + input_buffer_charp = 0; + + return output; +} + diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc new file mode 100644 index 00000000..c1823379 --- /dev/null +++ b/frontends/verilog/verilog_frontend.cc @@ -0,0 +1,148 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The Verilog frontend. + * + * This frontend is using the AST frontend library (see frontends/ast/). + * Thus this frontend does not generate RTLIL code directly but creates an + * AST directly from the Verilog parse tree and then passes this AST to + * the AST frontend library. + * + */ + +#include "verilog_frontend.h" +#include "kernel/register.h" +#include "kernel/log.h" +#include "kernel/sha1.h" +#include +#include +#include + +using namespace VERILOG_FRONTEND; + +// use the Verilog bison/flex parser to generate an AST and use AST::process() to convert it to RTLIL + +struct VerilogFrontend : public Frontend { + VerilogFrontend() : Frontend("verilog") { } + virtual void execute(FILE *&f, std::string filename, std::vector args, RTLIL::Design *design) + { + bool flag_dump_ast = false; + bool flag_dump_ast_diff = false; + bool flag_dump_vlog = false; + bool flag_nolatches = false; + bool flag_nomem2reg = false; + bool flag_ppdump = false; + bool flag_nopp = false; + frontend_verilog_yydebug = false; + + log_header("Executing Verilog-2005 frontend.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-dump_ast") { + flag_dump_ast = true; + continue; + } + if (arg == "-dump_ast_diff") { + flag_dump_ast = true; + flag_dump_ast_diff = true; + continue; + } + if (arg == "-dump_vlog") { + flag_dump_vlog = true; + continue; + } + if (arg == "-yydebug") { + frontend_verilog_yydebug = true; + continue; + } + if (arg == "-nolatches") { + flag_nolatches = true; + continue; + } + if (arg == "-nomem2reg") { + flag_nomem2reg = true; + continue; + } + if (arg == "-ppdump") { + flag_ppdump = true; + continue; + } + if (arg == "-nopp") { + flag_nopp = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + + log("Parsing Verilog input from `%s' to AST representation.\n", filename.c_str()); + + AST::current_filename = filename; + AST::set_line_num = &frontend_verilog_yyset_lineno; + AST::get_line_num = &frontend_verilog_yyget_lineno; + + current_ast = new AST::AstNode(AST::AST_DESIGN); + + FILE *fp = f; + std::string code_after_preproc; + + if (!flag_nopp) { + code_after_preproc = frontend_verilog_preproc(f, filename); + 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"); + } + + lexer_feature_defattr = false; + + frontend_verilog_yyset_lineno(1); + frontend_verilog_yyrestart(fp); + frontend_verilog_yyparse(); + frontend_verilog_yylex_destroy(); + + AST::process(design, current_ast, flag_dump_ast, flag_dump_ast_diff, flag_dump_vlog, flag_nolatches, flag_nomem2reg); + + if (!flag_nopp) + fclose(fp); + + delete current_ast; + current_ast = NULL; + + log("Successfully finished Verilog frontend.\n"); + } +} VerilogFrontend; + +// the yyerror function used by bison to report parser errors +void frontend_verilog_yyerror(char const *fmt, ...) +{ + va_list ap; + char buffer[1024]; + char *p = buffer; + p += snprintf(p, buffer + sizeof(buffer) - p, "Parser error in line %s:%d: ", + AST::current_filename.c_str(), frontend_verilog_yyget_lineno()); + va_start(ap, fmt); + p += vsnprintf(p, buffer + sizeof(buffer) - p, fmt, ap); + va_end(ap); + p += snprintf(p, buffer + sizeof(buffer) - p, "\n"); + log_error("%s", buffer); + exit(1); +} + diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h new file mode 100644 index 00000000..808edfc7 --- /dev/null +++ b/frontends/verilog/verilog_frontend.h @@ -0,0 +1,62 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * The Verilog frontend. + * + * This frontend is using the AST frontend library (see frontends/ast/). + * Thus this frontend does not generate RTLIL code directly but creates an + * AST directly from the Verilog parse tree and then passes this AST to + * the AST frontend library. + * + */ + +#ifndef VERILOG_FRONTEND_H +#define VERILOG_FRONTEND_H + +#include "kernel/rtlil.h" +#include "frontends/ast/ast.h" +#include +#include + +namespace VERILOG_FRONTEND +{ + // this variable is set to a new AST_DESIGN node and then filled with the AST by the bison parser + extern struct AST::AstNode *current_ast; + + // this function converts a Verilog constant to an AST_CONSTANT node + AST::AstNode *const2ast(std::string code, char case_type = 0); + + // lexer state variables + extern bool lexer_feature_defattr; +} + +// the pre-processor +std::string frontend_verilog_preproc(FILE *f, std::string filename); + +// the usual bison/flex stuff +extern int frontend_verilog_yydebug; +int frontend_verilog_yylex(void); +void frontend_verilog_yyerror(char const *fmt, ...); +void frontend_verilog_yyrestart(FILE *f); +int frontend_verilog_yyparse(void); +int frontend_verilog_yylex_destroy(void); +int frontend_verilog_yyget_lineno(void); +void frontend_verilog_yyset_lineno (int); + +#endif -- cgit v1.2.3