From 6116078d9f111d2e61c483ed0eb95be9d4f8b4eb Mon Sep 17 00:00:00 2001 From: Cary R Date: Thu, 9 Jul 2009 14:41:41 -0700 Subject: [PATCH 01/14] Real constants are always signed. Set the signed_flag_ for real constants. --- net_expr.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net_expr.cc b/net_expr.cc index dee99b45d..786895237 100644 --- a/net_expr.cc +++ b/net_expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2008 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2009 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -438,6 +438,7 @@ NetECReal::NetECReal(const verireal&val) : value_(val) { expr_width(1); + cast_signed(true); } NetECReal::~NetECReal() From e191cf7f5560b1793edc263f06946504b6652fd5 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 10 Jul 2009 18:12:09 -0700 Subject: [PATCH 02/14] Add check for prec > unit in `timescale directive. --- lexor.lex | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lexor.lex b/lexor.lex index 954a2ab61..c3937fc64 100644 --- a/lexor.lex +++ b/lexor.lex @@ -1205,6 +1205,13 @@ static void process_timescale(const char*txt) return; } + /* The time unit must be greater than or equal to the precision. */ + if (unit < prec) { + VLerror(yylloc, "error: `timescale unit must not be less than " + "the precision."); + return; + } + pform_set_timescale(unit, prec, yylloc.text, yylloc.first_line); } From c2feeb03df0a7ed91059e178f6dcef53dcb54311 Mon Sep 17 00:00:00 2001 From: Sreeraj R Date: Sat, 20 Jun 2009 09:28:46 +0530 Subject: [PATCH 03/14] System Verilog timeunit and timeprecision addition This patch adds timeunit and timeprecision keywords.Use -gsystem-verilog generation flag to use this feature --- compiler.h | 2 + lexor.lex | 21 ++++++ lexor_keyword.gperf | 2 + parse.y | 71 ++++++++++++++++++- pform.cc | 168 ++++++++++++++++++++++++++++++++++++++++++++ pform.h | 6 ++ 6 files changed, 267 insertions(+), 3 deletions(-) diff --git a/compiler.h b/compiler.h index 934344b74..1dcc050a7 100644 --- a/compiler.h +++ b/compiler.h @@ -134,6 +134,8 @@ extern bool gn_specify_blocks_flag; /* If this flag is true, then support/elaborate Verilog-AMS. */ extern bool gn_verilog_ams_flag; +extern bool gn_system_verilog_flag; + /* If this flag is false a warning is printed when the port declaration is scalar and the net/register definition is vectored. */ extern bool gn_io_range_error_flag; diff --git a/lexor.lex b/lexor.lex index c3937fc64..0ca016179 100644 --- a/lexor.lex +++ b/lexor.lex @@ -107,6 +107,8 @@ W [ \t\b\f\r]+ S [afpnumkKMGT] +TU [munpf] + %% /* Recognize the various line directives. */ @@ -323,6 +325,25 @@ S [afpnumkKMGT] based_size = yylval.number->as_ulong(); return DEC_NUMBER; } +[0-9]+{TU}?s { + if(gn_system_verilog_flag) + { + yylval.text = strdupnew(yytext); + return TIME_LITERAL; + } + else + REJECT; + } + +[0-9]*\.[0-9]+{TU}?s { + if(gn_system_verilog_flag) + { + yylval.text = strdupnew(yytext); + return TIME_LITERAL; + } + else + REJECT; + } /* These rules handle the scaled real literals from Verilog-AMS. The value is a number with a single letter scale factor. If verilog-ams is not enabled, then reject this rule. If it is diff --git a/lexor_keyword.gperf b/lexor_keyword.gperf index 5ba577a71..e07c0bdc5 100644 --- a/lexor_keyword.gperf +++ b/lexor_keyword.gperf @@ -162,6 +162,8 @@ tan, GN_KEYWORDS_VAMS_2_3, K_tan tanh, GN_KEYWORDS_VAMS_2_3, K_tanh task, GN_KEYWORDS_1364_1995, K_task time, GN_KEYWORDS_1364_1995, K_time +timeprecision, GN_KEYWORDS_1800_2005, K_timeprecision +timeunit, GN_KEYWORDS_1800_2005, K_timeunit tran, GN_KEYWORDS_1364_1995, K_tran tranif0, GN_KEYWORDS_1364_1995, K_tranif0 tranif1, GN_KEYWORDS_1364_1995, K_tranif1 diff --git a/parse.y b/parse.y index 44f09da35..eb32ddd55 100644 --- a/parse.y +++ b/parse.y @@ -218,7 +218,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2) list *dimensions; }; -%token IDENTIFIER SYSTEM_IDENTIFIER STRING +%token IDENTIFIER SYSTEM_IDENTIFIER STRING TIME_LITERAL %token DISCIPLINE_IDENTIFIER %token PATHPULSE_IDENTIFIER %token BASED_NUMBER DEC_NUMBER @@ -269,7 +269,8 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2) %token K_wone K_uwire /* The new tokens from 1800-2005. */ -%token K_always_comb K_always_ff K_always_latch K_assert +%token K_always_comb K_always_ff K_always_latch K_assert +%token K_timeprecision K_timeunit /* The new tokens for Verilog-AMS 2.3. */ %token K_abs K_abstol K_access K_acos K_acosh K_analog K_asin K_asinh @@ -728,6 +729,7 @@ description delete[] $3; delete[] $5; } + | timeunits_declaration ; /* The discipline and nature declarations used to take no ';' after @@ -736,6 +738,67 @@ description choose to make the ';' optional in this context. */ optional_semicolon : ';' | ; +timeunits_declaration_opt + :local_timeunits_declaration + | + ; + +timeunits_declaration + : K_timeprecision TIME_LITERAL ';' + K_timeunit TIME_LITERAL ';' + { + pform_set_timeprecision($2,false,false); + pform_set_timeunit($5,false,false); + } + | K_timeunit TIME_LITERAL ';' + K_timeprecision TIME_LITERAL ';' + { + pform_set_timeunit($2,false,false); + pform_set_timeprecision($5,false,false); + } + | K_timeunit TIME_LITERAL ';' + { + pform_set_timeunit($2,false,false); + } + | K_timeprecision TIME_LITERAL ';' + { + pform_set_timeprecision($2,false,false); + } + + ; + +local_timeunits_declaration + : K_timeprecision TIME_LITERAL ';' + K_timeunit TIME_LITERAL ';' + { + pform_set_timeprecision($2,true,false); + pform_set_timeunit($5,true,false); + } + | K_timeunit TIME_LITERAL ';' + K_timeprecision TIME_LITERAL ';' + { + pform_set_timeunit($2,true,false); + pform_set_timeprecision($5,true,false); + } + | K_timeunit TIME_LITERAL ';' + { + pform_set_timeunit($2,true,false); + } + | K_timeprecision TIME_LITERAL ';' + { + pform_set_timeprecision($2,true,false); + } + +timeunits_declaration_check + : K_timeunit TIME_LITERAL ';' + { + pform_set_timeunit($2,true,true); + } + | K_timeprecision TIME_LITERAL ';' + { + pform_set_timeprecision($2,true,true); + } + ; discipline_declaration : K_discipline IDENTIFIER optional_semicolon { pform_start_discipline($2); } @@ -1986,6 +2049,7 @@ module : attribute_list_opt module_start IDENTIFIER module_port_list_opt module_attribute_foreign ';' { pform_module_set_ports($6); } + timeunits_declaration_opt module_item_list_opt K_endmodule { Module::UCDriveType ucd; @@ -2463,7 +2527,8 @@ module_item } | KK_attribute '(' error ')' ';' { yyerror(@1, "error: Malformed $attribute parameter list."); } - ; + | timeunits_declaration_check + ; automatic_opt : K_automatic { $$ = true; } diff --git a/pform.cc b/pform.cc index 1e881c80f..f824fb0ec 100644 --- a/pform.cc +++ b/pform.cc @@ -73,6 +73,19 @@ static NetNet::Type pform_default_nettype = NetNet::WIRE; static int pform_time_unit; static int pform_time_prec; +/* These two flags check the initial timeprecison and timeunit + * declaration inside a module + */ +static bool tp_decl_flag = false; +static bool tu_decl_flag = false; + +/* + * Flags used to set time_from_timescale based on timeunit and + * timeprecision + */ +static bool tu_valid_flag = false; +static bool tp_valid_flag = false; + static char*pform_timescale_file = 0; static unsigned pform_timescale_line; @@ -339,6 +352,156 @@ void pform_set_timescale(int unit, int prec, } } +void pform_set_timeunit(const char*txt,bool in_module,bool only_check) +{ + unsigned num; + const char*cp = txt + strspn(txt, " \t"); + char*tmp; + const char*ctmp; + + int val = 0; + num = strtoul(cp, &tmp, 10); + if (num == 0) { + VLerror(yylloc, "Invalid timeunit string."); + return; + } + + while (num >= 10) { + val += 1; + num /= 10; + } + if (num != 1) { + VLerror(yylloc, "Invalid timeunit number."); + return; + } + + cp = tmp; + cp += strspn(cp, " \t"); + ctmp = cp + strcspn(cp, " \t/"); + + if (strncmp("s", cp, ctmp-cp) == 0) { + val -= 0; + + } else if (strncmp("ms", cp, ctmp-cp) == 0) { + val -= 3; + + } else if (strncmp("us", cp, ctmp-cp) == 0) { + val -= 6; + + } else if (strncmp("ns", cp, ctmp-cp) == 0) { + val -= 9; + + } else if (strncmp("ps", cp, ctmp-cp) == 0) { + val -= 12; + + } else if (strncmp("fs", cp, ctmp-cp) == 0) { + val -= 15; + + } else { + VLerror(yylloc, "Invalid unit of measurement"); + return; + } + if(in_module ) + { + if(!only_check) + { + pform_cur_module->time_unit = val; + tu_decl_flag=true; + tu_valid_flag=true; + } + else if (!tu_decl_flag) + { + VLerror(yylloc, "Timeunit declaration not found after module declaration"); + return; + } + else if(pform_cur_module->time_unit!= val) + { + VLerror(yylloc, "Timeliteral do not match the initial timeunit declaration"); + return; + } + + } + else + { + tu_valid_flag=true; + pform_time_unit = val; + } +} + +void pform_set_timeprecision(const char*txt,bool in_module,bool only_check) +{ + unsigned num; + const char*cp = txt + strspn(txt, " \t"); + char*tmp; + const char*ctmp; + + int val = 0; + num = strtoul(cp, &tmp, 10); + if (num == 0) { + VLerror(yylloc, "Invalid timeprecision string."); + return; + } + + while (num >= 10) { + val += 1; + num /= 10; + } + if (num != 1) { + VLerror(yylloc, "Invalid timeprecision number."); + return; + } + + cp = tmp; + cp += strspn(cp, " \t"); + ctmp = cp + strcspn(cp, " \t/"); + + if (strncmp("s", cp, ctmp-cp) == 0) { + val -= 0; + + } else if (strncmp("ms", cp, ctmp-cp) == 0) { + val -= 3; + + } else if (strncmp("us", cp, ctmp-cp) == 0) { + val -= 6; + + } else if (strncmp("ns", cp, ctmp-cp) == 0) { + val -= 9; + + } else if (strncmp("ps", cp, ctmp-cp) == 0) { + val -= 12; + + } else if (strncmp("fs", cp, ctmp-cp) == 0) { + val -= 15; + + } else { + VLerror(yylloc, "Invalid unit of measurement"); + return; + } + if(in_module) + { + if(!only_check) + { + pform_cur_module->time_precision = val; + tp_decl_flag=true; + tp_valid_flag=true; + } + else if (!tp_decl_flag) + { + VLerror(yylloc, "Timeprecision declaration not found after module declaration"); + return; + } + else if(pform_cur_module->time_precision!= val) + { + VLerror(yylloc, "Timeliteral do not match the initial timeprecision declaration"); + return; + } + } + else + { + pform_time_prec = val; + tp_valid_flag=true; + } +} verinum* pform_verinum_with_size(verinum*siz, verinum*val, const char*file, unsigned lineno) @@ -471,6 +634,7 @@ void pform_endmodule(const char*name, bool in_celldefine, Module::UCDriveType uc_drive) { assert(pform_cur_module); + pform_cur_module->time_from_timescale = (tp_valid_flag && tu_valid_flag)|| pform_timescale_file != 0; perm_string mod_name = pform_cur_module->mod_name(); assert(strcmp(name, mod_name) == 0); pform_cur_module->is_cell = in_celldefine; @@ -495,6 +659,10 @@ void pform_endmodule(const char*name, bool in_celldefine, ivl_assert(*pform_cur_module, lexical_scope == 0); pform_cur_module = 0; + tp_decl_flag=false; + tu_decl_flag=false; + tu_valid_flag=false; + tp_valid_flag=false; } void pform_genvars(const struct vlltype&li, list*names) diff --git a/pform.h b/pform.h index 53e56f38e..13e93bb83 100644 --- a/pform.h +++ b/pform.h @@ -409,4 +409,10 @@ extern PExpr* pform_make_branch_probe_expression(const struct vlltype&loc, extern PExpr* pform_make_branch_probe_expression(const struct vlltype&loc, char*name, char*branch); +/* + * Timeunit and precision setting function + */ +extern void pform_set_timeunit(const char*txt,bool in_module,bool only_check); +extern void pform_set_timeprecision(const char*txt,bool in_module,bool only_check); + #endif From 9dce6496fb1b0eb30c869f3da2c4e0ac57cee21d Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 10 Jul 2009 18:19:59 -0700 Subject: [PATCH 04/14] Fix up original SV timeunit/timeprecision patch. This patch modifies the original SystemVerilog timeunit/timeprecision patch in the following way: Removed trailing space. Reworked some code to use standard spacing rules. Added some comments. Combined some code. Major rework of local/global timeunit/timeprecision logic. Major rework of timeunit/timeprecision declaration/check code. This was needed to remove the shift/reduce warnings. Add a number of checks for invalid combinations. --- compiler.h | 1 + lexor.lex | 35 +++--- lexor_keyword.gperf | 4 +- parse.y | 111 +++++++---------- parse_misc.h | 6 + pform.cc | 292 +++++++++++++++++++++++--------------------- pform.h | 8 +- 7 files changed, 224 insertions(+), 233 deletions(-) diff --git a/compiler.h b/compiler.h index 1dcc050a7..94bf37c2c 100644 --- a/compiler.h +++ b/compiler.h @@ -134,6 +134,7 @@ extern bool gn_specify_blocks_flag; /* If this flag is true, then support/elaborate Verilog-AMS. */ extern bool gn_verilog_ams_flag; +/* If this flag is true, then support/elaborate SystemVerilog. */ extern bool gn_system_verilog_flag; /* If this flag is false a warning is printed when the port declaration diff --git a/lexor.lex b/lexor.lex index 0ca016179..9e8504c66 100644 --- a/lexor.lex +++ b/lexor.lex @@ -108,7 +108,7 @@ W [ \t\b\f\r]+ S [afpnumkKMGT] TU [munpf] - + %% /* Recognize the various line directives. */ @@ -250,6 +250,15 @@ TU [munpf] in_UDP = false; break; + /* Translate these to checks if we already have or are + * outside the declaration region. */ + case K_timeunit: + if (have_timeunit_decl) rc = K_timeunit_check; + break; + case K_timeprecision: + if (have_timeprec_decl) rc = K_timeprecision_check; + break; + default: yylval.text = 0; break; @@ -325,25 +334,13 @@ TU [munpf] based_size = yylval.number->as_ulong(); return DEC_NUMBER; } -[0-9]+{TU}?s { - if(gn_system_verilog_flag) - { - yylval.text = strdupnew(yytext); - return TIME_LITERAL; - } - else - REJECT; - } + /* This rule handles scaled time values for SystemVerilog. */ +[0-9][0-9_]*(\.[0-9][0-9_]*)?{TU}?s { + if(gn_system_verilog_flag) { + yylval.text = strdupnew(yytext); + return TIME_LITERAL; + } else REJECT; } -[0-9]*\.[0-9]+{TU}?s { - if(gn_system_verilog_flag) - { - yylval.text = strdupnew(yytext); - return TIME_LITERAL; - } - else - REJECT; - } /* These rules handle the scaled real literals from Verilog-AMS. The value is a number with a single letter scale factor. If verilog-ams is not enabled, then reject this rule. If it is diff --git a/lexor_keyword.gperf b/lexor_keyword.gperf index e07c0bdc5..9602f952a 100644 --- a/lexor_keyword.gperf +++ b/lexor_keyword.gperf @@ -162,8 +162,8 @@ tan, GN_KEYWORDS_VAMS_2_3, K_tan tanh, GN_KEYWORDS_VAMS_2_3, K_tanh task, GN_KEYWORDS_1364_1995, K_task time, GN_KEYWORDS_1364_1995, K_time -timeprecision, GN_KEYWORDS_1800_2005, K_timeprecision -timeunit, GN_KEYWORDS_1800_2005, K_timeunit +timeprecision, GN_KEYWORDS_1364_1995, K_timeprecision +timeunit, GN_KEYWORDS_1364_1995, K_timeunit tran, GN_KEYWORDS_1364_1995, K_tran tranif0, GN_KEYWORDS_1364_1995, K_tranif0 tranif1, GN_KEYWORDS_1364_1995, K_tranif1 diff --git a/parse.y b/parse.y index eb32ddd55..592115727 100644 --- a/parse.y +++ b/parse.y @@ -35,6 +35,9 @@ class PSpecPath; extern void lex_start_table(); extern void lex_end_table(); +bool have_timeunit_decl = false; +bool have_timeprec_decl = false; + static svector* param_active_range = 0; static bool param_active_signed = false; static ivl_variable_type_t param_active_type = IVL_VT_LOGIC; @@ -270,7 +273,9 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2) /* The new tokens from 1800-2005. */ %token K_always_comb K_always_ff K_always_latch K_assert -%token K_timeprecision K_timeunit +%token K_timeprecision K_timeunit + /* Fake tokens that are passed once we have an initial token. */ +%token K_timeprecision_check K_timeunit_check /* The new tokens for Verilog-AMS 2.3. */ %token K_abs K_abstol K_access K_acos K_acosh K_analog K_asin K_asinh @@ -729,7 +734,10 @@ description delete[] $3; delete[] $5; } - | timeunits_declaration + | K_timeunit TIME_LITERAL ';' + { pform_set_timeunit($2, false, false); } + | K_timeprecision TIME_LITERAL ';' + { pform_set_timeprecision($2, false, false); } ; /* The discipline and nature declarations used to take no ';' after @@ -738,67 +746,6 @@ description choose to make the ';' optional in this context. */ optional_semicolon : ';' | ; -timeunits_declaration_opt - :local_timeunits_declaration - | - ; - -timeunits_declaration - : K_timeprecision TIME_LITERAL ';' - K_timeunit TIME_LITERAL ';' - { - pform_set_timeprecision($2,false,false); - pform_set_timeunit($5,false,false); - } - | K_timeunit TIME_LITERAL ';' - K_timeprecision TIME_LITERAL ';' - { - pform_set_timeunit($2,false,false); - pform_set_timeprecision($5,false,false); - } - | K_timeunit TIME_LITERAL ';' - { - pform_set_timeunit($2,false,false); - } - | K_timeprecision TIME_LITERAL ';' - { - pform_set_timeprecision($2,false,false); - } - - ; - -local_timeunits_declaration - : K_timeprecision TIME_LITERAL ';' - K_timeunit TIME_LITERAL ';' - { - pform_set_timeprecision($2,true,false); - pform_set_timeunit($5,true,false); - } - | K_timeunit TIME_LITERAL ';' - K_timeprecision TIME_LITERAL ';' - { - pform_set_timeunit($2,true,false); - pform_set_timeprecision($5,true,false); - } - | K_timeunit TIME_LITERAL ';' - { - pform_set_timeunit($2,true,false); - } - | K_timeprecision TIME_LITERAL ';' - { - pform_set_timeprecision($2,true,false); - } - -timeunits_declaration_check - : K_timeunit TIME_LITERAL ';' - { - pform_set_timeunit($2,true,true); - } - | K_timeprecision TIME_LITERAL ';' - { - pform_set_timeprecision($2,true,true); - } - ; discipline_declaration : K_discipline IDENTIFIER optional_semicolon { pform_start_discipline($2); } @@ -2038,6 +1985,25 @@ cont_assign_list { $$ = $1; } ; + /* We allow zero, one or two unique declarations. */ +local_timeunit_prec_decl_opt + : /* Empty */ + | local_timeunit_prec_decl + | local_timeunit_prec_decl local_timeunit_prec_decl + ; + + /* By setting the appropriate have_time???_decl we allow only + one declaration of each type in this module. */ +local_timeunit_prec_decl + : K_timeunit TIME_LITERAL ';' + { pform_set_timeunit($2, true, false); + have_timeunit_decl = true; + } + | K_timeprecision TIME_LITERAL ';' + { pform_set_timeprecision($2, true, false); + have_timeprec_decl = true; + } + ; /* This is the global structure of a module. A module in a start section, with optional ports, then an optional list of module @@ -2049,8 +2015,12 @@ module : attribute_list_opt module_start IDENTIFIER module_port_list_opt module_attribute_foreign ';' { pform_module_set_ports($6); } - timeunits_declaration_opt - module_item_list_opt + local_timeunit_prec_decl_opt + { have_timeunit_decl = true; // Every thing past here is + have_timeprec_decl = true; // a check! + pform_check_timeunit_prec(); + } + module_item_list_opt K_endmodule { Module::UCDriveType ucd; switch (uc_drive) { @@ -2067,8 +2037,9 @@ module : attribute_list_opt module_start IDENTIFIER } pform_endmodule($3, in_celldefine, ucd); delete[]$3; + have_timeunit_decl = false; // We will allow decls again. + have_timeprec_decl = false; } - ; module_start : K_module | K_macromodule ; @@ -2527,8 +2498,12 @@ module_item } | KK_attribute '(' error ')' ';' { yyerror(@1, "error: Malformed $attribute parameter list."); } - | timeunits_declaration_check - ; + + | K_timeunit_check TIME_LITERAL ';' + { pform_set_timeunit($2, true, true); } + | K_timeprecision_check TIME_LITERAL ';' + { pform_set_timeprecision($2, true, true); } + ; automatic_opt : K_automatic { $$ = true; } diff --git a/parse_misc.h b/parse_misc.h index 8b58aa4e8..c49cb1652 100644 --- a/parse_misc.h +++ b/parse_misc.h @@ -70,5 +70,11 @@ extern unsigned long based_size; extern bool in_celldefine; enum UCDriveType { UCD_NONE, UCD_PULL0, UCD_PULL1 }; extern UCDriveType uc_drive; +/* + * Flags to control if we are declaring or checking a timeunit or + * timeprecision statement. + */ +extern bool have_timeunit_decl; +extern bool have_timeprec_decl; #endif diff --git a/pform.cc b/pform.cc index f824fb0ec..eaaa18c9a 100644 --- a/pform.cc +++ b/pform.cc @@ -73,18 +73,20 @@ static NetNet::Type pform_default_nettype = NetNet::WIRE; static int pform_time_unit; static int pform_time_prec; -/* These two flags check the initial timeprecison and timeunit - * declaration inside a module +/* These two flags check the initial timeprecision and timeunit + * declaration inside a module. */ static bool tp_decl_flag = false; static bool tu_decl_flag = false; /* - * Flags used to set time_from_timescale based on timeunit and - * timeprecision + * Flags used to set time_from_timescale based on timeunit and + * timeprecision. */ -static bool tu_valid_flag = false; -static bool tp_valid_flag = false; +static bool tu_global_flag = false; +static bool tp_global_flag = false; +static bool tu_local_flag = false; +static bool tp_local_flag = false; static char*pform_timescale_file = 0; static unsigned pform_timescale_line; @@ -305,6 +307,9 @@ void pform_set_timescale(int unit, int prec, assert(unit >= prec); pform_time_unit = unit; pform_time_prec = prec; + /* A `timescale clears the timeunit/timeprecision state. */ + tu_global_flag = false; + tp_global_flag = false; if (pform_timescale_file) { free(pform_timescale_file); @@ -352,154 +357,137 @@ void pform_set_timescale(int unit, int prec, } } -void pform_set_timeunit(const char*txt,bool in_module,bool only_check) +/* + * Get a timeunit or timeprecision value from a string. This is + * similar to the code in lexor.lex for the `timescale directive. + */ +static bool get_time_unit_prec(const char*cp, int &res, bool is_unit) { - unsigned num; - const char*cp = txt + strspn(txt, " \t"); - char*tmp; - const char*ctmp; - - int val = 0; - num = strtoul(cp, &tmp, 10); - if (num == 0) { - VLerror(yylloc, "Invalid timeunit string."); - return; + /* We do not support a '_' in these time constants. */ + if (strchr(cp, '_')) { + if (is_unit) { + VLerror(yylloc, "Invalid timeunit constant ('_' is not " + "supported)."); + } else { + VLerror(yylloc, "Invalid timeprecision constant ('_' is not " + "supported)."); + } + return true; } - while (num >= 10) { - val += 1; - num /= 10; + /* Check for the 1 digit. */ + if (*cp != '1') { + if (is_unit) { + VLerror(yylloc, "Invalid timeunit constant (1st digit)."); + } else { + VLerror(yylloc, "Invalid timeprecision constant (1st digit)."); + } + return true; } - if (num != 1) { - VLerror(yylloc, "Invalid timeunit number."); - return; + cp += 1; + + /* Check the number of zeros after the 1. */ + res = strspn(cp, "0"); + if (res > 2) { + if (is_unit) { + VLerror(yylloc, "Invalid timeunit constant (number of " + "zeros)."); + } else { + VLerror(yylloc, "Invalid timeprecision constant (number of " + "zeros)."); + } + return true; + } + cp += res; + + /* Now process the scaling string. */ + if (strncmp("s", cp, 1) == 0) { + res -= 0; + return false; + + } else if (strncmp("ms", cp, 2) == 0) { + res -= 3; + return false; + + } else if (strncmp("us", cp, 2) == 0) { + res -= 6; + return false; + + } else if (strncmp("ns", cp, 2) == 0) { + res -= 9; + return false; + + } else if (strncmp("ps", cp, 2) == 0) { + res -= 12; + return false; + + } else if (strncmp("fs", cp, 2) == 0) { + res -= 15; + return false; + } - cp = tmp; - cp += strspn(cp, " \t"); - ctmp = cp + strcspn(cp, " \t/"); + ostringstream msg; + msg << "Invalid "; + if (is_unit) msg << "timeunit"; + else msg << "timeprecision"; + msg << " scale '" << cp << "'."; + VLerror(msg.str().c_str()); + return true; +} - if (strncmp("s", cp, ctmp-cp) == 0) { - val -= 0; +void pform_set_timeunit(const char*txt, bool in_module, bool only_check) +{ + int val; - } else if (strncmp("ms", cp, ctmp-cp) == 0) { - val -= 3; + if (get_time_unit_prec(txt, val, true)) return; - } else if (strncmp("us", cp, ctmp-cp) == 0) { - val -= 6; - - } else if (strncmp("ns", cp, ctmp-cp) == 0) { - val -= 9; - - } else if (strncmp("ps", cp, ctmp-cp) == 0) { - val -= 12; - - } else if (strncmp("fs", cp, ctmp-cp) == 0) { - val -= 15; + if (in_module) { + if (!only_check) { + pform_cur_module->time_unit = val; + tu_decl_flag = true; + tu_local_flag = true; + } else if (!tu_decl_flag) { + VLerror(yylloc, "error: repeat timeunit found and the " + "initial module timeunit is missing."); + return; + } else if (pform_cur_module->time_unit != val) { + VLerror(yylloc, "error: repeat timeunit does not match " + "the initial module timeunit " + "declaration."); + return; + } } else { - VLerror(yylloc, "Invalid unit of measurement"); - return; - } - if(in_module ) - { - if(!only_check) - { - pform_cur_module->time_unit = val; - tu_decl_flag=true; - tu_valid_flag=true; - } - else if (!tu_decl_flag) - { - VLerror(yylloc, "Timeunit declaration not found after module declaration"); - return; - } - else if(pform_cur_module->time_unit!= val) - { - VLerror(yylloc, "Timeliteral do not match the initial timeunit declaration"); - return; - } - - } - else - { - tu_valid_flag=true; - pform_time_unit = val; + tu_global_flag = true; + pform_time_unit = val; } } -void pform_set_timeprecision(const char*txt,bool in_module,bool only_check) +void pform_set_timeprecision(const char*txt, bool in_module, bool only_check) { - unsigned num; - const char*cp = txt + strspn(txt, " \t"); - char*tmp; - const char*ctmp; + int val; - int val = 0; - num = strtoul(cp, &tmp, 10); - if (num == 0) { - VLerror(yylloc, "Invalid timeprecision string."); - return; - } - - while (num >= 10) { - val += 1; - num /= 10; - } - if (num != 1) { - VLerror(yylloc, "Invalid timeprecision number."); - return; - } - - cp = tmp; - cp += strspn(cp, " \t"); - ctmp = cp + strcspn(cp, " \t/"); - - if (strncmp("s", cp, ctmp-cp) == 0) { - val -= 0; - - } else if (strncmp("ms", cp, ctmp-cp) == 0) { - val -= 3; - - } else if (strncmp("us", cp, ctmp-cp) == 0) { - val -= 6; - - } else if (strncmp("ns", cp, ctmp-cp) == 0) { - val -= 9; - - } else if (strncmp("ps", cp, ctmp-cp) == 0) { - val -= 12; - - } else if (strncmp("fs", cp, ctmp-cp) == 0) { - val -= 15; + if (get_time_unit_prec(txt, val, false)) return; + if (in_module) { + if (!only_check) { + pform_cur_module->time_precision = val; + tp_decl_flag = true; + tp_local_flag = true; + } else if (!tp_decl_flag) { + VLerror(yylloc, "error: repeat timeprecision found and the " + "initial module timeprecision is missing."); + return; + } else if (pform_cur_module->time_precision != val) { + VLerror(yylloc, "error: repeat timeprecision does not match " + "the initial module timeprecision " + "declaration."); + return; + } } else { - VLerror(yylloc, "Invalid unit of measurement"); - return; - } - if(in_module) - { - if(!only_check) - { - pform_cur_module->time_precision = val; - tp_decl_flag=true; - tp_valid_flag=true; - } - else if (!tp_decl_flag) - { - VLerror(yylloc, "Timeprecision declaration not found after module declaration"); - return; - } - else if(pform_cur_module->time_precision!= val) - { - VLerror(yylloc, "Timeliteral do not match the initial timeprecision declaration"); - return; - } - } - else - { - pform_time_prec = val; - tp_valid_flag=true; + pform_time_prec = val; + tp_global_flag=true; } } @@ -560,8 +548,12 @@ void pform_startmodule(const char*name, const char*file, unsigned lineno, perm_string lex_name = lex_strings.make(name); pform_cur_module = new Module(lex_name); + /* Set the local time unit/precision to the global value. */ pform_cur_module->time_unit = pform_time_unit; pform_cur_module->time_precision = pform_time_prec; + tu_local_flag = tu_global_flag; + tp_local_flag = tp_global_flag; + /* If we have a timescale file then the time information is from * a timescale directive. */ pform_cur_module->time_from_timescale = pform_timescale_file != 0; @@ -594,6 +586,22 @@ void pform_startmodule(const char*name, const char*file, unsigned lineno, } } +/* + * In SystemVerilog we can have separate timeunit and timeprecision + * declarations. We need to have the values worked out by time this + * task is called. + */ +void pform_check_timeunit_prec() +{ + assert(pform_cur_module); + if (gn_system_verilog_flag && (pform_cur_module->time_unit < + pform_cur_module->time_precision)) { + VLerror("error: a timeprecision is missing or is too " + "large!"); + } else assert(pform_cur_module->time_unit >= + pform_cur_module->time_precision); +} + /* * This function is called by the parser to make a simple port * reference. This is a name without a .X(...), so the internal name @@ -634,7 +642,9 @@ void pform_endmodule(const char*name, bool in_celldefine, Module::UCDriveType uc_drive) { assert(pform_cur_module); - pform_cur_module->time_from_timescale = (tp_valid_flag && tu_valid_flag)|| pform_timescale_file != 0; + pform_cur_module->time_from_timescale = (tu_local_flag && + tp_local_flag) || + (pform_timescale_file != 0); perm_string mod_name = pform_cur_module->mod_name(); assert(strcmp(name, mod_name) == 0); pform_cur_module->is_cell = in_celldefine; @@ -659,10 +669,10 @@ void pform_endmodule(const char*name, bool in_celldefine, ivl_assert(*pform_cur_module, lexical_scope == 0); pform_cur_module = 0; - tp_decl_flag=false; - tu_decl_flag=false; - tu_valid_flag=false; - tp_valid_flag=false; + tp_decl_flag = false; + tu_decl_flag = false; + tu_local_flag = false; + tp_local_flag = false; } void pform_genvars(const struct vlltype&li, list*names) diff --git a/pform.h b/pform.h index 13e93bb83..778d748d3 100644 --- a/pform.h +++ b/pform.h @@ -143,6 +143,7 @@ extern PWire* pform_get_wire_in_scope(perm_string name); */ extern void pform_startmodule(const char*, const char*file, unsigned lineno, svector*attr); +extern void pform_check_timeunit_prec(); extern void pform_module_set_ports(vector*); /* This function is used to support the port definition in a @@ -410,9 +411,10 @@ extern PExpr* pform_make_branch_probe_expression(const struct vlltype&loc, extern PExpr* pform_make_branch_probe_expression(const struct vlltype&loc, char*name, char*branch); /* - * Timeunit and precision setting function + * Tasks to set the timeunit or timeprecision for SystemVerilog. */ -extern void pform_set_timeunit(const char*txt,bool in_module,bool only_check); -extern void pform_set_timeprecision(const char*txt,bool in_module,bool only_check); +extern void pform_set_timeunit(const char*txt, bool in_module, bool only_check); +extern void pform_set_timeprecision(const char*txt, bool in_module, + bool only_check); #endif From ddfdd492b39533c4646251f0db6bf156e8482993 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 10 Jul 2009 19:15:50 -0700 Subject: [PATCH 05/14] base if for ivl pbase is for ivlpp --- driver/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/main.c b/driver/main.c index bf322d77d..f7a7574d0 100644 --- a/driver/main.c +++ b/driver/main.c @@ -281,7 +281,7 @@ static int t_version_only(void) } fflush(0); - snprintf(tmp, sizeof tmp, "%s%civl -V -C%s -C%s", pbase, sep, + snprintf(tmp, sizeof tmp, "%s%civl -V -C%s -C%s", base, sep, iconfig_path, iconfig_common_path); rc = system(tmp); if (rc != 0) { From 212bd4134a581edcaad9d6a556e6b31c61fbe696 Mon Sep 17 00:00:00 2001 From: Cary R Date: Thu, 23 Jul 2009 11:27:41 -0700 Subject: [PATCH 06/14] Find the width of net arrays correctly. Add code to the generic get_array_word_size() function to correctly find the width of net arrays and use this routine in the _vthr_ code as needed. --- vvp/array.cc | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/vvp/array.cc b/vvp/array.cc index f7678acca..ec99cd09b 100644 --- a/vvp/array.cc +++ b/vvp/array.cc @@ -151,11 +151,25 @@ struct __vpiArrayVthrA { } }; -/* Get the array word size. This has only been checked for reg arrays. */ +/* Get the array word size. */ unsigned get_array_word_size(vvp_array_t array) { - assert(array->vals4); - return array->vals_width; + unsigned width; + + assert(array->array_count > 0); + /* For a net array we need to get the width from the first element. */ + if (array->nets) { + assert(array->vals4 == 0 && array->valsr == 0); + struct __vpiSignal*vsig = vpip_signal_from_handle(array->nets[0]); + assert(vsig); + width = vpip_size(vsig); + /* For a variable array we can get the width from vals_width. */ + } else { + assert(array->vals4 || array->valsr); + width = array->vals_width; + } + + return width; } bool is_net_array(vpiHandle obj) @@ -664,7 +678,7 @@ static int vpi_array_vthr_A_get(int code, vpiHandle ref) return 0; // Not implemented for now! case vpiSize: - return parent->vals_width; + return get_array_word_size(parent); case vpiLeftRange: return parent->msb.value; @@ -745,8 +759,8 @@ static void vpi_array_vthr_A_get_value(vpiHandle ref, p_vpi_value value) vpip_real_get_value(tmp, value); } else { vvp_vector4_t tmp = array_get_word(parent, index); - vpip_vec4_get_value(tmp, parent->vals_width, parent->signed_flag, - value); + unsigned width = get_array_word_size(parent); + vpip_vec4_get_value(tmp, width, parent->signed_flag, value); } } @@ -765,7 +779,8 @@ static vpiHandle vpi_array_vthr_A_put_value(vpiHandle ref, p_vpi_value vp, int) double val = real_from_vpi_value(vp); array_set_word(parent, index, val); } else { - vvp_vector4_t val = vec4_from_vpi_value(vp, parent->vals_width); + unsigned width = get_array_word_size(parent); + vvp_vector4_t val = vec4_from_vpi_value(vp, width); array_set_word(parent, index, 0, val); } From e932c415876443816233383e22761cbb6b1511c1 Mon Sep 17 00:00:00 2001 From: Cary R Date: Thu, 23 Jul 2009 13:16:55 -0700 Subject: [PATCH 07/14] When skipping C-style comments in a skipped `ifdef include the '\n' When a C-style comment /* */ is being skipped because it is in a skipped `ifdef, `ifndef, etc. directive then we need to output a '\n' at any comment end of line. --- ivlpp/lexor.lex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivlpp/lexor.lex b/ivlpp/lexor.lex index e96ecf3e7..2a34f8c74 100644 --- a/ivlpp/lexor.lex +++ b/ivlpp/lexor.lex @@ -457,7 +457,7 @@ keywords (include|define|undef|ifdef|ifndef|else|elseif|endif) \n\r | \r\n | \n | -\r { istack->lineno += 1; } +\r { istack->lineno += 1; fputc('\n', yyout); } "*/" { BEGIN(comment_enter); } [^\r\n] { } From c5ee1fdbf55d3a371950e45ed78a20a1013a877b Mon Sep 17 00:00:00 2001 From: Cary R Date: Thu, 23 Jul 2009 14:34:54 -0700 Subject: [PATCH 08/14] The scanf functions must return EOF if source starts at the end of input. The $fscanf() and $sscanf() functions are defined to return EOF when the end of input is reached before any matches or match failures have occurred. --- vpi/sys_scanf.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vpi/sys_scanf.c b/vpi/sys_scanf.c index f500aa585..b523d2d60 100644 --- a/vpi/sys_scanf.c +++ b/vpi/sys_scanf.c @@ -240,6 +240,14 @@ static int scan_format(vpiHandle callh, struct byte_source*src, vpiHandle argv) vpi_get_value(item, &val); fmtp = fmt = strdup(val.value.str); + /* See if we are at EOF before we even start. */ + ch = byte_getc(src); + if (ch == EOF) { + rc = EOF; + match_fail = 1; + } + byte_ungetc(src, ch); + while ( fmtp && *fmtp != 0 && !match_fail) { if (isspace(*fmtp)) { From e362a86b167ae294c6e0a363e3b711edbe6509f0 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 24 Jul 2009 09:52:48 -0700 Subject: [PATCH 09/14] Fix procedural concatenation/repetition problems This patch evaluates the whole concatenation expression and makes the concatenation padding sign aware. This is needed when $signed({...}) is passed as an argument. A repetition is just N copies of the base expression not N evaluations of the base expression. This is only a problem when functions have side effects. It's also faster to copy. The evaluation must also be done when the replication count is zero (see 1364-2005). --- eval_tree.cc | 10 ---- t-dll-expr.cc | 2 +- tgt-vvp/eval_expr.c | 131 ++++++++++++++++++++++++++++---------------- 3 files changed, 85 insertions(+), 58 deletions(-) diff --git a/eval_tree.cc b/eval_tree.cc index 668213d62..deb313f34 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -1161,16 +1161,6 @@ NetEConst* NetEConcat::eval_tree(int prune_to_width) if (local_errors > 0) return 0; - // Handle the special case that the repeat expression is - // zero. In this case, just return a 0 value with the expected - // width. - if (repeat_val == 0) { - verinum val (verinum::V0, expr_width()); - NetEConst*res = new NetEConst(val); - res->set_width(val.len()); - return res; - } - // At this point, the "gap" is the width of a single repeat of // the concatenation. The total width of the result is the gap // times the repeat count. diff --git a/t-dll-expr.cc b/t-dll-expr.cc index 303449a07..128830a44 100644 --- a/t-dll-expr.cc +++ b/t-dll-expr.cc @@ -199,7 +199,7 @@ void dll_target::expr_concat(const NetEConcat*net) cur->type_ = IVL_EX_CONCAT; cur->value_ = IVL_VT_VECTOR; cur->width_ = net->expr_width(); - cur->signed_ = 0; + cur->signed_ = net->has_sign() ? 1 : 0; cur->u_.concat_.rept = net->repeat(); cur->u_.concat_.parms = net->nparms(); diff --git a/tgt-vvp/eval_expr.c b/tgt-vvp/eval_expr.c index cd8bb004a..3e6074794 100644 --- a/tgt-vvp/eval_expr.c +++ b/tgt-vvp/eval_expr.c @@ -1619,82 +1619,118 @@ static struct vector_info draw_binary_expr(ivl_expr_t exp, static struct vector_info draw_concat_expr(ivl_expr_t exp, unsigned wid, int stuff_ok_flag) { - unsigned off, rep; + unsigned off, rep, expr_wid, concat_wid, num_sube, idx; struct vector_info res; int alloc_exclusive = (stuff_ok_flag&STUFF_OK_RO) ? 0 : 1; - /* Allocate a vector to hold the result. */ - res.base = allocate_vector(wid); - res.wid = wid; - if (res.base == 0) { - fprintf(stderr, "%s:%u: vvp.tgt error: " - "Unable to allocate %u thread bits " - "for result of concatenation.\n", - ivl_expr_file(exp), ivl_expr_lineno(exp), wid); - vvp_errors += 1; + /* Find out how wide the base concatenation expression is. */ + num_sube = ivl_expr_parms(exp); + expr_wid = 0; + for (idx = 0 ; idx < num_sube; idx += 1) { + expr_wid += ivl_expr_width(ivl_expr_parm(exp, idx)); } /* Get the repeat count. This must be a constant that has been evaluated at compile time. The operands will be repeated to form the result. */ rep = ivl_expr_repeat(exp); - off = 0; - while (rep > 0) { + /* Allocate a vector to hold the result. */ + if (rep == 0) { + /* If the replication is zero we need to allocate temporary + * space to build the concatenation. */ + res.base = allocate_vector(expr_wid); + res.wid = expr_wid; + } else { + res.base = allocate_vector(wid); + res.wid = wid; + } + if (res.base == 0) { + fprintf(stderr, "%s:%u: vvp.tgt error: " + "Unable to allocate %u thread bits " + "for result of concatenation.\n", + ivl_expr_file(exp), ivl_expr_lineno(exp), + rep ? wid : expr_wid); + vvp_errors += 1; + } - /* Each repeat, evaluate the sub-expressions, from lsb - to msb, and copy each into the result vector. The - expressions are arranged in the concatenation from - MSB to LSB, to go through them backwards. - - Abort the loop if the result vector gets filled up. */ - - unsigned idx = ivl_expr_parms(exp); - while ((idx > 0) && (off < wid)) { + /* If the result is the right size we can just build this in place. */ + concat_wid = rep*expr_wid; + if (concat_wid <= wid) { + off = 0; + /* Evaluate the base expression. */ + for (idx = num_sube; idx > 0; idx -= 1) { ivl_expr_t arg = ivl_expr_parm(exp, idx-1); unsigned awid = ivl_expr_width(arg); - - unsigned trans; struct vector_info avec; + assert(awid+off <= expr_wid); + /* Try to locate the subexpression in the - lookaside map. */ + * lookaside map and use it when available. */ avec.base = allocate_vector_exp(arg, awid, alloc_exclusive); avec.wid = awid; - trans = awid; - if ((off + awid) > wid) - trans = wid - off; - if (avec.base != 0) { assert(awid == avec.wid); - - fprintf(vvp_out, " %%mov %u, %u, %u; Reuse calculated expression\n", - res.base+off, - avec.base, trans); - clr_vector(avec); - + fprintf(vvp_out, " %%mov %u, %u, %u; Reuse " + "calculated expression.\n", + res.base+off, avec.base, awid); + clr_vector(avec); } else { struct vector_info dest; dest.base = res.base+off; - dest.wid = trans; + dest.wid = awid; draw_eval_expr_dest(arg, dest, 0); } - - idx -= 1; - off += trans; - assert(off <= wid); + off += awid; } - rep -= 1; - } - /* Pad the result with 0, if necessary. */ - if (off < wid) { - fprintf(vvp_out, " %%mov %u, 0, %u;\n", - res.base+off, wid-off); + /* Now repeat the expression as needed. */ + if (rep != 0) { + rep -= 1; + } else { + /* Clear the temporary space and return nothing. + * This will be caught in draw_eval_expr_dest() + * and dropped. */ + clr_vector(res); + res.base = 0; + res.wid = 0; + } + while (rep > 0) { + fprintf(vvp_out, " %%mov %u, %u, %u; Repetition %u\n", + res.base+expr_wid*rep, res.base, expr_wid, + rep+1); + rep -= 1; + } + + /* Pad the expression when needed. */ + if (wid > concat_wid) { + /* We can get a signed concatenation with $signed({...}). */ + if (ivl_expr_signed(exp)) { + unsigned base = res.base+concat_wid-1; + for (idx = 1; idx <= wid-concat_wid; idx += 1) { + fprintf(vvp_out, " %%mov %u, %u, 1;\n", + base+idx, base); + } + } else { + fprintf(vvp_out, " %%mov %u, 0, %u;\n", + res.base+concat_wid, wid-concat_wid); + } + } + } else { + /* The concatenation is too big for the result so draw it + * at full width and then copy the bits that are needed. */ + struct vector_info full_res; + full_res = draw_concat_expr(exp, concat_wid, stuff_ok_flag); + assert(full_res.base); + + fprintf(vvp_out, " %%mov %u, %u, %u;\n", res.base, + full_res.base, wid); + clr_vector(full_res); } /* Save the accumulated result in the lookaside map. */ @@ -2843,8 +2879,9 @@ static void draw_eval_expr_dest(ivl_expr_t exp, struct vector_info dest, tmp = draw_eval_expr_wid(exp, dest.wid, stuff_ok_flag); assert(tmp.wid == dest.wid); - fprintf(vvp_out, " %%mov %u, %u, %u;\n", - dest.base, tmp.base, dest.wid); + /* If the replication is 0 we can have a zero width, so skip it. */ + if (dest.wid) fprintf(vvp_out, " %%mov %u, %u, %u;\n", + dest.base, tmp.base, dest.wid); if (tmp.base >= 8) save_expression_lookaside(tmp.base, exp, tmp.wid); From b629d913d254d47477e37f080c0a2b6ae3b05d73 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 24 Jul 2009 11:59:10 -0700 Subject: [PATCH 10/14] Fix CA replication of zero issues. This patch fixes a CA to evaluate and ignore a replication of zero. It also fixes a minor glitch in the data type calculation code. This was causing a problem if a zero width replication was the first element in a concatenation/replication since the data type was hard coded to the first element of the concatenation/replication. It now uses the first defined element in the concatenation/replication to determine the data type. --- expr_synth.cc | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/expr_synth.cc b/expr_synth.cc index 3affe9086..70b5dff76 100644 --- a/expr_synth.cc +++ b/expr_synth.cc @@ -722,25 +722,35 @@ NetNet* NetEConcat::synthesize(Design*des, NetScope*scope, NetExpr*root) unsigned nparms = parms_.count(); NetNet**tmp = new NetNet*[parms_.count()]; bool flag = true; + ivl_variable_type_t data_type = IVL_VT_NO_TYPE; for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) { if (parms_[idx]->expr_width() == 0) { - tmp[idx] = 0; + /* We need to synthesize a replication of zero. */ + tmp[idx] = parms_[idx]->synthesize(des, scope, root); + assert(tmp[idx] == 0); nparms -= 1; } else { tmp[idx] = parms_[idx]->synthesize(des, scope, root); if (tmp[idx] == 0) flag = false; + /* Set the data type to the first one found. */ + if (data_type == IVL_VT_NO_TYPE) { + data_type = tmp[idx]->data_type(); + } } } if (flag == false) return 0; - ivl_assert(*this, tmp[0]); + ivl_assert(*this, data_type != IVL_VT_NO_TYPE); + + /* If this is a replication of zero just return 0. */ + if (expr_width() == 0) return 0; /* Make a NetNet object to carry the output vector. */ perm_string path = scope->local_symbol(); NetNet*osig = new NetNet(scope, path, NetNet::IMPLICIT, expr_width()); osig->local_flag(true); - osig->data_type(tmp[0]->data_type()); + osig->data_type(data_type); NetConcat*concat = new NetConcat(scope, scope->local_symbol(), osig->vector_width(), From e98000426e814f68e1c62076600d9de952d94d77 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 24 Jul 2009 16:54:58 -0700 Subject: [PATCH 11/14] Fix power operator width in self-determined context. In a self determined context the width of the power operator is defined to be the left argument width. --- net_expr.cc | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/net_expr.cc b/net_expr.cc index 786895237..3b5890caa 100644 --- a/net_expr.cc +++ b/net_expr.cc @@ -257,15 +257,13 @@ ivl_variable_type_t NetEBMinMax::expr_type() const NetEBMult::NetEBMult(char op__, NetExpr*l, NetExpr*r) : NetEBinary(op__, l, r) { - if (expr_type() == IVL_VT_REAL) + if (expr_type() == IVL_VT_REAL) { expr_width(1); - else - expr_width(l->expr_width() + r->expr_width()); - - if (expr_type() == IVL_VT_REAL) cast_signed(true); - else + } else { + expr_width(l->expr_width() + r->expr_width()); cast_signed(l->has_sign() && r->has_sign()); + } } NetEBMult::~NetEBMult() @@ -294,8 +292,8 @@ NetEBPow::NetEBPow(char op__, NetExpr*l, NetExpr*r) : NetEBinary(op__, l, r) { assert(op__ == 'p'); - /* This is incorrect! a * (2^b - 1) is close. */ - expr_width(l->expr_width()+r->expr_width()); + /* You could need up to a * (2^b - 1) bits. */ + expr_width(l->expr_width()); cast_signed(l->has_sign() || r->has_sign()); } From 1453c5b0bbb4180e767ce8e323293a205e0ec64c Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Sat, 25 Jul 2009 21:05:03 +0100 Subject: [PATCH 12/14] Suppress unnecessary update of part select functor output. Currently a part select functor will send updates to nodes connected to its output whenever any part of its input vector changes. This patch ensures updates are only sent when the selected part of the input vector changes. --- vvp/part.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/vvp/part.cc b/vvp/part.cc index f3d98836e..1dcc38735 100644 --- a/vvp/part.cc +++ b/vvp/part.cc @@ -58,10 +58,15 @@ void vvp_fun_part_sa::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, { assert(port.port() == 0); - if (val_ .eeq( bit )) + vvp_vector4_t tmp (wid_, BIT4_X); + for (unsigned idx = 0 ; idx < wid_ ; idx += 1) { + if (idx + base_ < bit.size()) + tmp.set_bit(idx, bit.value(base_+idx)); + } + if (val_ .eeq( tmp )) return; - val_ = bit; + val_ = tmp; if (net_ == 0) { net_ = port.ptr(); @@ -95,12 +100,7 @@ void vvp_fun_part_sa::run_run() vvp_net_t*ptr = net_; net_ = 0; - vvp_vector4_t res (wid_, BIT4_X); - for (unsigned idx = 0 ; idx < wid_ ; idx += 1) { - if (idx + base_ < val_.size()) - res.set_bit(idx, val_.value(base_+idx)); - } - vvp_send_vec4(ptr->out, res, 0); + vvp_send_vec4(ptr->out, val_, 0); } vvp_fun_part_aa::vvp_fun_part_aa(unsigned base, unsigned wid) From d7fe9c613cb08a1661465b2d59ece7d23ef600fc Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Sun, 26 Jul 2009 23:43:38 +0100 Subject: [PATCH 13/14] Fix for pr2821633. Currently, edge event functors declared in automatically allocated scopes that are used to detect edges on signals declared in static scopes are unable to correctly determine edge information for the first signal delta they receive because they do not know the old state of the signal. This patch causes the state of static signals received by these event functors to be recorded as static state in the functors, so the old state of the signals can be initialised to the correct value when a new automatic context is created. --- vvp/event.cc | 19 ++++++++++++------- vvp/event.h | 10 ++++++---- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/vvp/event.cc b/vvp/event.cc index 7bb4aed4d..3a6f95a67 100644 --- a/vvp/event.cc +++ b/vvp/event.cc @@ -195,6 +195,8 @@ struct vvp_fun_edge_state_s : public waitable_state_s { vvp_fun_edge::vvp_fun_edge(edge_t e) : edge_(e) { + for (unsigned idx = 0 ; idx < 4 ; idx += 1) + bits_[idx] = BIT4_X; } vvp_fun_edge::~vvp_fun_edge() @@ -220,8 +222,6 @@ bool vvp_fun_edge::recv_vec4_(vvp_net_ptr_t port, const vvp_vector4_t&bit, vvp_fun_edge_sa::vvp_fun_edge_sa(edge_t e) : vvp_fun_edge(e), threads_(0) { - for (unsigned idx = 0 ; idx < 4 ; idx += 1) - bits_[idx] = BIT4_X; } vvp_fun_edge_sa::~vvp_fun_edge_sa() @@ -259,6 +259,7 @@ vvp_fun_edge_aa::~vvp_fun_edge_aa() void vvp_fun_edge_aa::alloc_instance(vvp_context_t context) { vvp_set_context_item(context, context_idx_, new vvp_fun_edge_state_s); + reset_instance(context); } void vvp_fun_edge_aa::reset_instance(vvp_context_t context) @@ -268,7 +269,7 @@ void vvp_fun_edge_aa::reset_instance(vvp_context_t context) state->threads = 0; for (unsigned idx = 0 ; idx < 4 ; idx += 1) - state->bits[idx] = BIT4_X; + state->bits[idx] = bits_[idx]; } #ifdef CHECK_WITH_VALGRIND @@ -308,6 +309,7 @@ void vvp_fun_edge_aa::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, recv_vec4(port, bit, context); context = vvp_get_next_context(context); } + bits_[port.port()] = bit.value(0); } } @@ -324,6 +326,8 @@ struct vvp_fun_anyedge_state_s : public waitable_state_s { vvp_fun_anyedge::vvp_fun_anyedge() { + for (unsigned idx = 0 ; idx < 4 ; idx += 1) + bitsr_[idx] = 0.0; } vvp_fun_anyedge::~vvp_fun_anyedge() @@ -369,8 +373,6 @@ bool vvp_fun_anyedge::recv_real_(vvp_net_ptr_t port, double bit, vvp_fun_anyedge_sa::vvp_fun_anyedge_sa() : threads_(0) { - for (unsigned idx = 0 ; idx < 4 ; idx += 1) - bitsr_[idx] = 0.0; } vvp_fun_anyedge_sa::~vvp_fun_anyedge_sa() @@ -416,6 +418,7 @@ vvp_fun_anyedge_aa::~vvp_fun_anyedge_aa() void vvp_fun_anyedge_aa::alloc_instance(vvp_context_t context) { vvp_set_context_item(context, context_idx_, new vvp_fun_anyedge_state_s); + reset_instance(context); } void vvp_fun_anyedge_aa::reset_instance(vvp_context_t context) @@ -425,8 +428,8 @@ void vvp_fun_anyedge_aa::reset_instance(vvp_context_t context) state->threads = 0; for (unsigned idx = 0 ; idx < 4 ; idx += 1) { - state->bits[idx].set_to_x(); - state->bitsr[idx] = 0.0; + state->bits[idx] = bits_[idx]; + state->bitsr[idx] = bitsr_[idx]; } } @@ -467,6 +470,7 @@ void vvp_fun_anyedge_aa::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, recv_vec4(port, bit, context); context = vvp_get_next_context(context); } + bits_[port.port()] = bit; } } @@ -487,6 +491,7 @@ void vvp_fun_anyedge_aa::recv_real(vvp_net_ptr_t port, double bit, recv_real(port, bit, context); context = vvp_get_next_context(context); } + bitsr_[port.port()] = bit; } } diff --git a/vvp/event.h b/vvp/event.h index 7700f4329..fd7ac81ae 100644 --- a/vvp/event.h +++ b/vvp/event.h @@ -141,6 +141,8 @@ class vvp_fun_edge : public vvp_net_fun_t, public waitable_hooks_s { bool recv_vec4_(vvp_net_ptr_t port, const vvp_vector4_t&bit, vvp_bit4_t&old_bit, vthread_t&threads); + vvp_bit4_t bits_[4]; + private: edge_t edge_; }; @@ -165,7 +167,6 @@ class vvp_fun_edge_sa : public vvp_fun_edge { private: vthread_t threads_; - vvp_bit4_t bits_[4]; }; /* @@ -214,6 +215,10 @@ class vvp_fun_anyedge : public vvp_net_fun_t, public waitable_hooks_s { vvp_vector4_t&old_bits, vthread_t&threads); bool recv_real_(vvp_net_ptr_t port, double bit, double&old_bits, vthread_t&threads); + + vvp_vector4_t bits_[4]; + // In case I'm a real-valued event. + double bitsr_[4]; }; /* @@ -235,9 +240,6 @@ class vvp_fun_anyedge_sa : public vvp_fun_anyedge { private: vthread_t threads_; - vvp_vector4_t bits_[4]; - // In case I'm a real-valued event. - double bitsr_[4]; }; /* From 72a98e85cba5233b90a058451d478421a3fd99db Mon Sep 17 00:00:00 2001 From: Cary R Date: Tue, 28 Jul 2009 11:42:07 -0700 Subject: [PATCH 14/14] Add full genvar support and name space checking. This patch adds genvars to the elaboration process. It adds checks that a genvar is defined for a generate loop and that a genvar does not conflict with any other items in its name space. --- elab_scope.cc | 133 ++++++++++++++++++++++++++++++++++++++++++-------- elab_sig.cc | 8 +++ net_scope.cc | 24 +++++++++ netlist.h | 5 ++ pform.cc | 11 ++++- 5 files changed, 160 insertions(+), 21 deletions(-) diff --git a/elab_scope.cc b/elab_scope.cc index afc438573..5929b03b2 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -174,6 +174,15 @@ static void elaborate_scope_parameters_(Design*des, NetScope*scope, for (mparm_it_t cur = parameters.begin() ; cur != parameters.end() ; cur ++) { + // A parameter can not have the same name as a genvar. + if (scope->find_genvar((*cur).first)) { + cerr << cur->second.get_fileline() + << ": error: parameter and genvar in '" + << scope->fullname() << "' have the same name '" + << (*cur).first << "'." << endl; + des->errors += 1; + } + elaborate_parm_item_((*cur).first, (*cur).second, des, scope); } } @@ -184,6 +193,15 @@ static void elaborate_scope_localparams_(Design*des, NetScope*scope, for (mparm_it_t cur = localparams.begin() ; cur != localparams.end() ; cur ++) { + // A localparam can not have the same name as a genvar. + if (scope->find_genvar((*cur).first)) { + cerr << cur->second.get_fileline() + << ": error: localparam and genvar in '" + << scope->fullname() << "' have the same name '" + << (*cur).first << "'." << endl; + des->errors += 1; + } + elaborate_parm_item_((*cur).first, (*cur).second, des, scope); } } @@ -249,6 +267,15 @@ static void elaborate_scope_tasks(Design*des, NetScope*scope, continue; } + // A task can not have the same name as a genvar. + if (scope->find_genvar((*cur).first)) { + cerr << cur->second->get_fileline() + << ": error: task and genvar in '" + << scope->fullname() << "' have the same name '" + << (*cur).first << "'." << endl; + des->errors += 1; + } + // A task can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = scope->get_parameter((*cur).first, ex_msb, @@ -296,6 +323,15 @@ static void elaborate_scope_funcs(Design*des, NetScope*scope, continue; } + // A function can not have the same name as a genvar. + if (scope->find_genvar((*cur).first)) { + cerr << cur->second->get_fileline() + << ": error: function and genvar in '" + << scope->fullname() << "' have the same name '" + << (*cur).first << "'." << endl; + des->errors += 1; + } + // A function can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = scope->get_parameter((*cur).first, ex_msb, @@ -361,6 +397,12 @@ bool Module::elaborate_scope(Design*des, NetScope*scope, << scope_path(scope) << "." << endl; } + // Add the genvars to the scope. + typedef map::const_iterator genvar_it_t; + for (genvar_it_t cur = genvars.begin(); cur != genvars.end(); cur++ ) { + scope->add_genvar((*cur).first, (*cur).second); + } + // Generate all the parameters that this instance of this // module introduces to the design. This loop elaborates the // parameters, but doesn't evaluate references to @@ -521,12 +563,13 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) { // Check that the loop_index variable was declared in a // genvar statement. - - // MISSING CODE! - // - // Also the genvar checks below need to be moved/changed - // when this is implemented. They currently work, but do - // not reference the genvar statement. + if (container->find_genvar(loop_index) == 0) { + cerr << get_fileline() << ": error: genvar is missing for " + "generate \"loop\" variable '" << loop_index << "'." + << endl; + des->errors += 1; + return false; + } // We're going to need a genvar... int genvar; @@ -558,6 +601,15 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) des->errors += 1; return false; } + + // A generate "loop" can not have the same name as a genvar. + if (container->find_genvar(scope_name)) { + cerr << get_fileline() << ": error: generate \"loop\" and " + "genvar in '" << container->fullname() + << "' have the same name '" << scope_name << "'." << endl; + des->errors += 1; + } + // A generate "loop" can not have the same name as a named event. const NetEvent *event = container->find_event(scope_name); if (event) { @@ -578,17 +630,11 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) des->errors += 1; } - // Since we will be adding the genvar value as a local parameter - // to each instances scope. We need to make sure a parameter does - // not already exist. - texpr = container->get_parameter(loop_index, tmsb, tlsb); - if (texpr != 0) { - cerr << get_fileline() << ": error: genvar and " - "parameter in '" << container->fullname() - << "' have the same name '" << loop_index << "'." << endl; - des->errors += 1; - return false; - } + // These have all been checked so we just need to skip the actual + // generation for these name conflicts. Not skipping these two will + // cause the compiler to have problems (assert, inf. loop, etc.). + if (container->get_parameter(loop_index, tmsb, tlsb)) return false; + if (container->find_event(loop_index)) return false; genvar = init->value().as_long(); delete init_ex; @@ -672,7 +718,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) } // Clear the genvar_tmp field in the scope to reflect that the - // genvar is no longer value for evaluating expressions. + // genvar is no longer valid for evaluating expressions. container->genvar_tmp = perm_string(); return true; @@ -717,6 +763,14 @@ bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else return false; } + // A generate "if" can not have the same name as a genvar. + if (container->find_genvar(scope_name)) { + cerr << get_fileline() << ": error: generate \"if\" and " + "genvar in '" << container->fullname() + << "' have the same name '" << scope_name << "'." << endl; + des->errors += 1; + } + // A generate "if" can not have the same name as a named event. const NetEvent *event = container->find_event(scope_name); if (event) { @@ -855,6 +909,14 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container) return false; } + // A generate "case" can not have the same name as a genvar. + if (container->find_genvar(item->scope_name)) { + cerr << get_fileline() << ": error: generate \"case\" and " + "genvar in '" << container->fullname() + << "' have the same name '" << use_name << "'." << endl; + des->errors += 1; + } + // A generate "case" can not have the same name as a named event. const NetEvent *event = container->find_event(item->scope_name); if (event) { @@ -895,7 +957,8 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container) bool PGenerate::generate_scope_nblock_(Design*des, NetScope*container) { hname_t use_name (scope_name); - // A generate "case" can not have the same name as another scope object. + // A generate "block" can not have the same name as another scope + // object. const NetScope *child = container->child(use_name); if (child) { cerr << get_fileline() << ": error: generate \"block\" and "; @@ -906,6 +969,14 @@ bool PGenerate::generate_scope_nblock_(Design*des, NetScope*container) return false; } + // A generate "block" can not have the same name as a genvar. + if (container->find_genvar(scope_name)) { + cerr << get_fileline() << ": error: generate \"block\" and " + "genvar in '" << container->fullname() + << "' have the same name '" << scope_name << "'." << endl; + des->errors += 1; + } + // A generate "block" can not have the same name as a named event. const NetEvent *event = container->find_event(scope_name); if (event) { @@ -1057,6 +1128,14 @@ void PGModule::elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const return; } + // A module instance can not have the same name as a genvar. + if (sc->find_genvar(get_name())) { + cerr << get_fileline() << ": error: module <" << mod->mod_name() + << "> instance and genvar in '" << sc->fullname() + << "' have the same name '" << get_name() << "'." << endl; + des->errors += 1; + } + // A module instance can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = sc->get_parameter(get_name(), ex_msb, ex_lsb); @@ -1314,6 +1393,14 @@ void PEvent::elaborate_scope(Design*des, NetScope*scope) const des->errors += 1; } + // A named event can not have the same name as a genvar. + if (scope->find_genvar(name_)) { + cerr << get_fileline() << ": error: named event and " + << "genvar in '" << scope->fullname() + << "' have the same name '" << name_ << "'." << endl; + des->errors += 1; + } + // A named event can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = scope->get_parameter(name_, ex_msb, ex_lsb); @@ -1416,6 +1503,14 @@ void PBlock::elaborate_scope(Design*des, NetScope*scope) const return; } + // A named block can not have the same name as a genvar. + if (scope->find_genvar(pscope_name())) { + cerr << get_fileline() << ": error: named block and " + "genvar in '" << scope->fullname() + << "' have the same name '" << use_name << "'." << endl; + des->errors += 1; + } + // A named block can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = scope->get_parameter(pscope_name(), ex_msb, diff --git a/elab_sig.cc b/elab_sig.cc index 752b2cb77..0c69c3b96 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -878,6 +878,14 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const cerr << " in '" << scope->fullname() << "' have the same name '" << name_ << "'." << endl; des->errors += 1; + } + // A signal can not have the same name as a genvar. + const LineInfo *genvar = scope->find_genvar(name_); + if (genvar) { + cerr << get_fileline() << ": error: signal and genvar in '" + << scope->fullname() << "' have the same name '" << name_ + << "'." << endl; + des->errors += 1; } // A signal can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; diff --git a/net_scope.cc b/net_scope.cc index 3a2e6f91b..6bdcd8713 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -395,6 +395,30 @@ NetEvent* NetScope::find_event(perm_string name) return 0; } +// We only add genvars to a module scope, so we do not need to search +// for the module scope here. +void NetScope::add_genvar(perm_string name, LineInfo *li) +{ + genvars_[name] = li; +} + +LineInfo* NetScope::find_genvar(perm_string name) +{ + // genvars are only added to the module so we need to find it + // if we are in a sub scope. + NetScope *scope = this; + while (scope->type() != NetScope::MODULE) { + scope = scope->parent(); + assert(scope != NULL); + } + + if (scope->genvars_.find(name) != scope->genvars_.end()) { + return scope->genvars_[name]; + } + + return 0; +} + void NetScope::add_signal(NetNet*net) { signals_map_[net->name()]=net; diff --git a/netlist.h b/netlist.h index 05c57ebfd..d34fca4c5 100644 --- a/netlist.h +++ b/netlist.h @@ -720,6 +720,9 @@ class NetScope : public Attrib { void rem_event(NetEvent*); NetEvent*find_event(perm_string name); + /* These methods add or find a genvar that lives in this scope. */ + void add_genvar(perm_string name, LineInfo *li); + LineInfo* find_genvar(perm_string name); /* These methods manage signals. The add_ and rem_signal methods are used by the NetNet objects to make themselves @@ -899,6 +902,8 @@ class NetScope : public Attrib { NetEvent *events_; + map genvars_; + typedef std::map::const_iterator signals_map_iter_t; std::map signals_map_; perm_string module_name_; diff --git a/pform.cc b/pform.cc index eaaa18c9a..9c2660f46 100644 --- a/pform.cc +++ b/pform.cc @@ -131,7 +131,7 @@ PTask* pform_push_task_scope(const struct vlltype&loc, char*name, bool is_auto) if (pform_cur_generate->tasks.find(task->pscope_name()) != pform_cur_generate->tasks.end()) { cerr << task->get_fileline() << ": error: duplicate " - " definition for task '" << name << "' in '" + "definition for task '" << name << "' in '" << pform_cur_module->mod_name() << "' (generate)." << endl; error_count += 1; @@ -681,7 +681,14 @@ void pform_genvars(const struct vlltype&li, list*names) for (cur = names->begin(); cur != names->end() ; *cur++) { LineInfo*lni = new LineInfo(); FILE_NAME(lni, li); - pform_cur_module->genvars[*cur] = lni; + if (pform_cur_module->genvars.find(*cur) != + pform_cur_module->genvars.end()) { + cerr << lni->get_fileline() << ": error: duplicate " + "definition for genvar '" << *cur << "' in '" + << pform_cur_module->mod_name() << "'." << endl; + error_count += 1; + delete lni; + } else pform_cur_module->genvars[*cur] = lni; } delete names;