diff options
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;  | 
