Skip to content

Commit 17e7175

Browse files
committed
Implement parameterised macros
There was support for macros before but non-parameterised version. Now we can also take parameters. See the new test for a simple example.
1 parent 7dc2753 commit 17e7175

File tree

4 files changed

+187
-32
lines changed

4 files changed

+187
-32
lines changed

regression/verilog/assert3/main.v

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module main();
2+
`define SIZE 31
3+
`define plus_one(a) a+1
4+
5+
reg [`SIZE:0] x;
6+
wire clk;
7+
8+
initial x=1;
9+
10+
always @(posedge clk) x<=`plus_one(x);
11+
12+
always assert property1: x!=10;
13+
14+
endmodule

regression/verilog/assert3/test.desc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.v
3+
--module main --bound 20 --trace
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^Counterexample:$
7+
\[main.property.property1\] .* FAILURE$
8+
--
9+
^warning: ignoring

src/verilog/verilog_preprocessor.cpp

Lines changed: 141 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,74 @@ Author: Daniel Kroening, [email protected]
66
77
\*******************************************************************/
88

9+
#include <algorithm>
910
#include <fstream>
1011

1112
#include <util/config.h>
1213

1314
#include "verilog_preprocessor.h"
1415

16+
void verilog_preprocessort::definet::replace_substring(
17+
std::string &source, const std::string &orig_sub,
18+
const std::string &new_sub) const {
19+
PRECONDITION(!orig_sub.empty());
20+
PRECONDITION(!new_sub.empty());
21+
22+
std::size_t index = 0;
23+
auto const orig_sub_size = orig_sub.size();
24+
auto const new_sub_size = new_sub.size();
25+
26+
while (true) {
27+
index = source.find(orig_sub, index);
28+
if (index == std::string::npos)
29+
break;
30+
31+
source.replace(index, orig_sub_size, new_sub);
32+
index += new_sub_size;
33+
}
34+
}
35+
36+
std::string verilog_preprocessort::definet::replace_macro(
37+
const std::string &arg_string) const {
38+
std::vector<std::string> arguments =
39+
split_string(arg_string, ',', true, true);
40+
PRECONDITION(arguments.size() == parameters.size());
41+
42+
if (parameters.size() == 1 && parameters.back().empty())
43+
return value;
44+
45+
auto longer_first = [](const std::string &left, const std::string &right) {
46+
if (left.size() > right.size())
47+
return true;
48+
return std::lexicographical_compare(left.begin(), left.end(), right.begin(),
49+
right.end());
50+
};
51+
52+
std::map<std::string, std::string, decltype(longer_first)> param_to_arg{
53+
longer_first};
54+
55+
for (std::size_t i = 0; i < parameters.size(); ++i) {
56+
param_to_arg.emplace(parameters[i], arguments[i]);
57+
}
58+
59+
std::string result_value = value;
60+
for (auto const &param_arg_pair : param_to_arg) {
61+
replace_substring(result_value, param_arg_pair.first,
62+
param_arg_pair.second);
63+
}
64+
return result_value;
65+
}
66+
67+
optionalt<std::size_t>
68+
verilog_preprocessort::find_define(const std::string &name) const {
69+
std::size_t define_index = 0;
70+
for (; define_index != defines.size(); ++define_index) {
71+
if (defines[define_index].identifier == name)
72+
return define_index;
73+
}
74+
return {};
75+
}
76+
1577
/*******************************************************************\
1678
1779
Function: verilog_preprocessort::getline
@@ -301,18 +363,42 @@ void verilog_preprocessort::replace_macros(std::string &s)
301363
i++;
302364

303365
std::string text(s, start, i-start);
366+
std::string arg_string;
367+
368+
unsigned i_before_whitespace_skip = i;
369+
// skip whitespace
370+
while (s[i] == ' ' || s[i] == '\t' || s[i] == '\n')
371+
++i;
372+
373+
// maybe read the arguments
374+
if (s[i] == '(') {
375+
std::size_t level = 0;
376+
377+
// find the matching parenthesis, everything between is the argument
378+
// list
379+
while (s[++i] != ')' || level != 0) {
380+
arg_string += s[i];
381+
382+
if (s[i] == '(')
383+
++level;
384+
else if (s[i] == ')')
385+
--level;
386+
}
387+
// now s[i]==')' -> let's move one more
388+
++i;
389+
} else {
390+
// just in case the whitespace was important
391+
i = i_before_whitespace_skip;
392+
}
304393

305-
definest::const_iterator it=defines.find(text);
306-
307-
if(it==defines.end())
308-
{
394+
auto maybe_define_index = find_define(text);
395+
if (!maybe_define_index.has_value()) {
309396
error() << "unknown preprocessor macro \"" << text << "\"" << eom;
310397
throw 0;
311398
}
312399

313400
// found it! replace it!
314-
315-
dest+=it->second;
401+
dest += defines[*maybe_define_index].replace_macro(arg_string);
316402
}
317403
else
318404
{
@@ -353,6 +439,40 @@ void verilog_preprocessort::directive()
353439
}
354440
}
355441

442+
std::string arg_string;
443+
444+
size_t unget_counter = 0;
445+
// skip whitespace
446+
while (files.back().in->get(ch)) {
447+
++unget_counter;
448+
if (ch == ' ' || ch == '\t' || ch == '\n')
449+
;
450+
else
451+
break;
452+
}
453+
454+
// maybe read the arguments
455+
if (ch == '(') {
456+
std::size_t level = 0;
457+
458+
// find the matching parenthesis, everything between is the argument
459+
// list
460+
while (files.back().in->get(ch)) {
461+
if (level == 0 && ch == ')') {
462+
break;
463+
}
464+
arg_string += ch;
465+
if (ch == '(')
466+
++level;
467+
else if (ch == ')')
468+
--level;
469+
}
470+
} else {
471+
// just in case the whitespace was important
472+
while (unget_counter-- > 0)
473+
files.back().in->unget();
474+
}
475+
356476
std::string line;
357477

358478
if(text=="define")
@@ -367,7 +487,7 @@ void verilog_preprocessort::directive()
367487
// skip whitespace
368488
while(*tptr==' ' || *tptr=='\t') tptr++;
369489

370-
std::string identifier, value;
490+
std::string identifier, param_string, value;
371491

372492
// copy identifier
373493
while(isalnum(*tptr) || *tptr=='$' || *tptr=='_')
@@ -379,8 +499,9 @@ void verilog_preprocessort::directive()
379499
// is there a parameter list?
380500
if(*tptr=='(')
381501
{
382-
error() << "`define with parameters not yet supported" << eom;
383-
throw 0;
502+
while (*(++tptr) != ')')
503+
param_string.push_back(*tptr);
504+
++tptr; // get past the closing parenthesis
384505
}
385506

386507
// skip whitespace
@@ -401,7 +522,7 @@ void verilog_preprocessort::directive()
401522
<< "< = >" << value << "<" << std::endl;
402523
#endif
403524

404-
defines[identifier]=value;
525+
defines.emplace_back(identifier, param_string, value);
405526
}
406527
else if(text=="undef")
407528
{
@@ -424,13 +545,9 @@ void verilog_preprocessort::directive()
424545
tptr++;
425546
}
426547

427-
definest::iterator it=defines.find(identifier);
428-
429-
if(it!=defines.end())
430-
{
431-
// found it! remove it!
432-
433-
defines.erase(it);
548+
auto maybe_define_index = find_define(identifier);
549+
if (maybe_define_index.has_value()) {
550+
defines.erase(defines.begin() + *maybe_define_index);
434551
}
435552
}
436553
else if(text=="ifdef" || text=="ifndef")
@@ -451,15 +568,15 @@ void verilog_preprocessort::directive()
451568
tptr++;
452569
}
453570

454-
definest::iterator it=defines.find(identifier);
571+
auto maybe_define_index = find_define(identifier);
455572

456573
conditionalt conditional;
457574

458575
if(text=="ifdef")
459-
conditional.condition=(it!=defines.end());
576+
conditional.condition = maybe_define_index.has_value();
460577
else
461-
conditional.condition=(it==defines.end());
462-
578+
conditional.condition = !maybe_define_index.has_value();
579+
463580
conditional.previous_condition=condition;
464581
conditionals.push_back(conditional);
465582
condition=conditional.get_cond();
@@ -562,17 +679,15 @@ void verilog_preprocessort::directive()
562679

563680
if(condition)
564681
{
565-
definest::const_iterator it=defines.find(text);
566-
567-
if(it==defines.end())
568-
{
682+
auto maybe_define_index = find_define(text);
683+
if (!maybe_define_index.has_value()) {
569684
error() << "unknown preprocessor directive \"" << text << "\"" << eom;
570685
throw 0;
571686
}
572687

573688
// found it! replace it!
574689

575-
out << it->second;
690+
out << defines[*maybe_define_index].replace_macro(arg_string);
576691
}
577692
}
578693
}

src/verilog/verilog_preprocessor.h

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
#include <list>
55

66
#include <util/irep.h>
7-
#include <util/string_hash.h>
87
#include <util/preprocessor.h>
8+
#include <util/string_hash.h>
9+
#include <util/string_utils.h>
910

1011
class verilog_preprocessort:public preprocessort
1112
{
@@ -25,11 +26,27 @@ class verilog_preprocessort:public preprocessort
2526
virtual ~verilog_preprocessort() { }
2627

2728
protected:
28-
typedef std::unordered_map<std::string, std::string, string_hash>
29-
definest;
30-
31-
definest defines;
32-
29+
struct definet {
30+
std::string identifier;
31+
std::vector<std::string> parameters;
32+
std::string value;
33+
34+
definet(const std::string &identifier, const std::string &param_string,
35+
const std::string &value)
36+
: identifier(identifier),
37+
parameters(split_string(param_string, ',', true, true)),
38+
value(value) {}
39+
40+
void replace_substring(std::string &source, const std::string &orig_sub,
41+
const std::string &new_sub) const;
42+
43+
std::string replace_macro(const std::string &arg_string) const;
44+
};
45+
46+
std::vector<definet> defines;
47+
48+
optionalt<std::size_t> find_define(const std::string &name) const;
49+
3350
virtual void directive();
3451
virtual void replace_macros(std::string &s);
3552
virtual void include(const std::string &filename);

0 commit comments

Comments
 (0)