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