Support free-form `timescale directives with interspersed comments (issue #782)

Verilog compiler directives are free-form and, subject to semantic rules,
can appear anywhere in the source code. Whilst it is common practice to
write them on a separate line, we should handle all legal syntax.
This commit is contained in:
Martin Whitaker 2022-12-20 11:57:42 +00:00
parent 46e1a21d7b
commit 59d70cad45
4 changed files with 160 additions and 133 deletions

View File

@ -0,0 +1,10 @@
File ./ivltests/br_gh782b.v line 10
Time scale of (t1) is 1s / 100ms
File ./ivltests/br_gh782b.v line 19
Time scale of (t2) is 100ms / 10ms
File ./ivltests/br_gh782b.v line 28
Time scale of (t3) is 10us / 1us
File ./ivltests/br_gh782b.v line 38
Time scale of (t4) is 1ns / 1ps
File ./ivltests/br_gh782b.v line 48
Time scale of (t5) is 1ps / 1fs

View File

@ -0,0 +1,51 @@
/* comment */ `timescale /* comment */ // comment
/* comment */ 1 /* comment */ // comment
/* comment */ s /* comment */ // comment
/* comment */ / /* comment */ // comment
/* comment */ 100 /* comment */ // comment
/* comment */ ms /* comment */ // comment
module t1;
initial begin
$display("File %s line %0d", `__FILE__, `__LINE__);
$printtimescale;
end
endmodule
`timescale 100 ms / 10 ms // single line comment
module t2;
initial begin
$display("File %s line %0d", `__FILE__, `__LINE__);
$printtimescale;
end
endmodule
`timescale 10us/1us /* another single line comment */
module t3;
initial begin
$display("File %s line %0d", `__FILE__, `__LINE__);
$printtimescale;
end
endmodule
`timescale 1ns/1ps /* multi-line
comment */
module t4;
initial begin
$display("File %s line %0d", `__FILE__, `__LINE__);
$printtimescale;
end
endmodule
`timescale 1ps/1fs /* single */ /* and
multi-line comment */
module t5;
initial begin
$display("File %s line %0d", `__FILE__, `__LINE__);
$printtimescale;
end
endmodule

View File

@ -224,6 +224,7 @@ br_gh672 normal,-g2009 ivltests
br_gh699 CE,-g2009 ivltests
br_gh756 normal,-g2009 ivltests
br_gh782a normal,-g2009 ivltests gold=br_gh782a.gold
br_gh782b normal,-g2009 ivltests gold=br_gh782b.gold
br_gh801 normal,-g2009 ivltests
br_gh801b normal,-g2009 ivltests
br_ml20171017 normal,-g2009 ivltests

231
lexor.lex
View File

@ -4,7 +4,7 @@
%{
/*
* Copyright (c) 1998-2021 Stephen Williams (steve@icarus.com)
* Copyright (c) 1998-2022 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
@ -109,7 +109,9 @@ verinum*make_unsized_hex(const char*txt);
static int dec_buf_div2(char *buf);
static void process_timescale(const char*txt);
static int get_timescale_scale(const char*cp);
static int get_timescale_const(int scale, const char*units);
static void process_ucdrive(const char*txt);
static list<int> keyword_mask_stack;
@ -119,6 +121,10 @@ static bool in_module = false;
static bool in_UDP = false;
bool in_celldefine = false;
UCDriveType uc_drive = UCD_NONE;
static int ts_state = 0;
static int ts_scale = 0;
static int ts_unit = 0;
static int ts_prec = 0;
/*
* The parser sometimes needs to indicate to the lexor that the next
@ -139,7 +145,10 @@ void lex_in_package_scope(PPackage*pkg)
%x LCOMMENT
%x CSTRING
%s UDPTABLE
%x PPTIMESCALE
%x PPTIMESCALE_SCALE
%x PPTIMESCALE_UNITS
%x PPTIMESCALE_SLASH
%x PPTIMESCALE_ERROR
%x PPUCDRIVE
%x PPDEFAULT_NETTYPE
%x PPBEGIN_KEYWORDS
@ -578,16 +587,54 @@ TU [munpf]
/* Notice and handle the `timescale directive. */
^{W}?`timescale { BEGIN(PPTIMESCALE); }
<PPTIMESCALE>.* { process_timescale(yytext); }
<PPTIMESCALE>\n {
if (in_module) {
cerr << yylloc.text << ":" << yylloc.first_line << ": error: "
"`timescale directive can not be inside a module "
"definition." << endl;
error_count += 1;
}
yylloc.first_line += 1;
`timescale { ts_state = 0; BEGIN(PPTIMESCALE_SCALE); }
<PPTIMESCALE_SCALE>10?0? {
ts_scale = get_timescale_scale(yytext);
BEGIN(PPTIMESCALE_UNITS); }
<PPTIMESCALE_SCALE>"//" { comment_enter = PPTIMESCALE_SCALE; BEGIN(LCOMMENT); }
<PPTIMESCALE_SCALE>"/*" { comment_enter = PPTIMESCALE_SCALE; BEGIN(CCOMMENT); }
<PPTIMESCALE_SCALE>"\n" { yylloc.first_line += 1; }
<PPTIMESCALE_SCALE>{W} { ; }
<PPTIMESCALE_SCALE>. { BEGIN(PPTIMESCALE_ERROR); }
<PPTIMESCALE_UNITS>[munpf]?s {
if (++ts_state == 1) {
ts_unit = get_timescale_const(ts_scale, yytext);
BEGIN(PPTIMESCALE_SLASH);
} else {
ts_prec = get_timescale_const(ts_scale, yytext);
if (in_module) {
VLerror(yylloc, "error: timescale directive cannot be inside "
"a module definition.");
}
if (ts_unit < ts_prec) {
VLerror(yylloc, "error: timescale unit must not be less than "
"the precision.");
} else {
pform_set_timescale(ts_unit, ts_prec, yylloc.text, yylloc.first_line);
}
BEGIN(0);
} }
<PPTIMESCALE_UNITS>"//" { comment_enter = PPTIMESCALE_UNITS; BEGIN(LCOMMENT); }
<PPTIMESCALE_UNITS>"/*" { comment_enter = PPTIMESCALE_UNITS; BEGIN(CCOMMENT); }
<PPTIMESCALE_UNITS>"\n" { yylloc.first_line += 1; }
<PPTIMESCALE_UNITS>{W} { ; }
<PPTIMESCALE_UNITS>. { BEGIN(PPTIMESCALE_ERROR); }
<PPTIMESCALE_SLASH>"/" { BEGIN(PPTIMESCALE_SCALE); }
<PPTIMESCALE_SLASH>"//" { comment_enter = PPTIMESCALE_SLASH; BEGIN(LCOMMENT); }
<PPTIMESCALE_SLASH>"/*" { comment_enter = PPTIMESCALE_SLASH; BEGIN(CCOMMENT); }
<PPTIMESCALE_SLASH>"\n" { yylloc.first_line += 1; }
<PPTIMESCALE_SLASH>{W} { ; }
<PPTIMESCALE_SLASH>. { BEGIN(PPTIMESCALE_ERROR); }
/* On error, try to recover by skipping to the end of the line. */
<PPTIMESCALE_ERROR>[^\n]+ {
VLerror(yylloc, "error: Invalid `timescale directive.");
BEGIN(0); }
/* Notice and handle the `celldefine and `endcelldefine directives. */
@ -1302,82 +1349,52 @@ verinum*make_unsized_dec(const char*ptr)
}
/*
* Convert the string to a time unit or precision.
* Returns true on failure.
* Convert a string to a scale value ("1" -> 0, "10" -> 1, "100" -> 2).
* We have already checked the string is valid.
*/
static bool get_timescale_const(const char *&cp, int &res, bool is_unit)
static int get_timescale_scale(const char *cp)
{
/* Check for the 1 digit. */
if (*cp != '1') {
if (is_unit) {
VLerror(yylloc, "Invalid `timescale unit constant "
"(1st digit)");
} else {
VLerror(yylloc, "Invalid `timescale precision constant "
"(1st digit)");
}
return true;
}
/* Skip the 1 digit. */
assert(*cp == '1');
cp += 1;
/* Check the number of zeros after the 1. */
res = strspn(cp, "0");
if (res > 2) {
if (is_unit) {
VLerror(yylloc, "Invalid `timescale unit constant "
"(number of zeros)");
} else {
VLerror(yylloc, "Invalid `timescale precision constant "
"(number of zeros)");
}
return true;
}
cp += res;
int scale = strspn(cp, "0");
assert(scale < 3);
cp += scale;
/* Skip any space between the digits and the scaling string. */
cp += strspn(cp, " \t");
/* Now process the scaling string. */
if (strncmp("s", cp, 1) == 0) {
res -= 0;
cp += 1;
return false;
} else if (strncmp("ms", cp, 2) == 0) {
res -= 3;
cp += 2;
return false;
} else if (strncmp("us", cp, 2) == 0) {
res -= 6;
cp += 2;
return false;
} else if (strncmp("ns", cp, 2) == 0) {
res -= 9;
cp += 2;
return false;
} else if (strncmp("ps", cp, 2) == 0) {
res -= 12;
cp += 2;
return false;
} else if (strncmp("fs", cp, 2) == 0) {
res -= 15;
cp += 2;
return false;
}
if (is_unit) {
VLerror(yylloc, "Invalid `timescale unit scale");
} else {
VLerror(yylloc, "Invalid `timescale precision scale");
}
return true;
assert(*cp == '\0');
return scale;
}
/*
* Convert a scale and a units string to a time unit or precision.
* We have already checked the string is valid.
*/
static int get_timescale_const(int scale, const char *units)
{
if (strncmp("s", units, 1) == 0) {
return scale;
} else if (strncmp("ms", units, 2) == 0) {
return scale - 3;
} else if (strncmp("us", units, 2) == 0) {
return scale - 6;
} else if (strncmp("ns", units, 2) == 0) {
return scale - 9;
} else if (strncmp("ps", units, 2) == 0) {
return scale - 12;
} else if (strncmp("fs", units, 2) == 0) {
return scale - 15;
}
assert(0);
return 0;
}
/*
* process either a pull0 or a pull1.
@ -1424,58 +1441,6 @@ static void process_ucdrive(const char*txt)
uc_drive = ucd;
}
/*
* The timescale parameter has the form:
* " <num> xs / <num> xs"
*/
static void process_timescale(const char*txt)
{
const char*cp = txt + strspn(txt, " \t");
/* Skip the space after the `timescale directive. */
if (cp == txt) {
VLerror(yylloc, "Space required after `timescale directive.");
return;
}
int unit = 0;
int prec = 0;
/* Get the time units. */
if (get_timescale_const(cp, unit, true)) return;
/* Skip any space after the time units, the '/' and any
* space after the '/'. */
cp += strspn(cp, " \t");
if (*cp != '/') {
VLerror(yylloc, "`timescale separator '/' appears to be missing.");
return;
}
cp += 1;
cp += strspn(cp, " \t");
/* Get the time precision. */
if (get_timescale_const(cp, prec, false)) return;
/* Verify that only space and/or a single line comment is left. */
cp += strspn(cp, " \t");
if (strncmp(cp, "//", 2) != 0 &&
(size_t)(cp-yytext) != strlen(yytext)) {
VLerror(yylloc, "Invalid `timescale directive (extra garbage "
"after precision).");
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);
}
int yywrap()
{
return 1;