verilog: fix handling of nested ifdef directives

- track depth so we know whether to consider higher-level elsifs
- error on unmatched endif/elsif/else
This commit is contained in:
Zachary Snow 2021-02-25 15:53:55 -05:00 committed by Zachary Snow
parent b6904a8e53
commit 1ec5994100
8 changed files with 197 additions and 11 deletions

View File

@ -727,7 +727,8 @@ frontend_verilog_preproc(std::istream &f,
std::vector<std::string> filename_stack; std::vector<std::string> filename_stack;
int ifdef_fail_level = 0; int ifdef_fail_level = 0;
bool in_elseif = false; int ifdef_pass_level = 0;
bool ifdef_already_satisfied = false;
output_code.clear(); output_code.clear();
input_buffer.clear(); input_buffer.clear();
@ -743,42 +744,68 @@ frontend_verilog_preproc(std::istream &f,
if (tok == "`endif") { if (tok == "`endif") {
if (ifdef_fail_level > 0) if (ifdef_fail_level > 0)
ifdef_fail_level--; ifdef_fail_level--;
if (ifdef_fail_level == 0) else if (ifdef_pass_level > 0)
in_elseif = false; ifdef_already_satisfied = --ifdef_pass_level;
else
log_error("Found %s outside of macro conditional branch!\n", tok.c_str());
continue; continue;
} }
if (tok == "`else") { if (tok == "`else") {
if (ifdef_fail_level == 0) if (ifdef_fail_level == 0) {
if (ifdef_pass_level == 0)
log_error("Found %s outside of macro conditional branch!\n", tok.c_str());
log_assert(ifdef_already_satisfied);
ifdef_fail_level = 1; ifdef_fail_level = 1;
else if (ifdef_fail_level == 1 && !in_elseif) } else if (ifdef_fail_level == 1 && !ifdef_already_satisfied) {
ifdef_fail_level = 0; ifdef_fail_level = 0;
ifdef_pass_level++;
ifdef_already_satisfied = true;
}
continue; continue;
} }
if (tok == "`elsif") { if (tok == "`elsif") {
skip_spaces(); skip_spaces();
std::string name = next_token(true); std::string name = next_token(true);
if (ifdef_fail_level == 0) if (ifdef_fail_level == 0) {
ifdef_fail_level = 1, in_elseif = true; if (ifdef_pass_level == 0)
else if (ifdef_fail_level == 1 && defines.find(name)) log_error("Found %s outside of macro conditional branch!\n", tok.c_str());
ifdef_fail_level = 0, in_elseif = true; log_assert(ifdef_already_satisfied);
ifdef_fail_level = 1;
} else if (ifdef_fail_level == 1 && !ifdef_already_satisfied && defines.find(name)) {
ifdef_fail_level = 0;
ifdef_pass_level++;
ifdef_already_satisfied = true;
}
continue; continue;
} }
if (tok == "`ifdef") { if (tok == "`ifdef") {
skip_spaces(); skip_spaces();
std::string name = next_token(true); std::string name = next_token(true);
if (ifdef_fail_level > 0 || !defines.find(name)) if (ifdef_fail_level > 0 || !defines.find(name)) {
ifdef_fail_level++; ifdef_fail_level++;
} else {
ifdef_pass_level++;
ifdef_already_satisfied = true;
}
if (ifdef_fail_level == 1)
ifdef_already_satisfied = false;
continue; continue;
} }
if (tok == "`ifndef") { if (tok == "`ifndef") {
skip_spaces(); skip_spaces();
std::string name = next_token(true); std::string name = next_token(true);
if (ifdef_fail_level > 0 || defines.find(name)) if (ifdef_fail_level > 0 || defines.find(name)) {
ifdef_fail_level++; ifdef_fail_level++;
} else {
ifdef_pass_level++;
ifdef_already_satisfied = true;
}
if (ifdef_fail_level == 1)
ifdef_already_satisfied = false;
continue; continue;
} }

88
tests/simple/ifdef_1.v Normal file
View File

@ -0,0 +1,88 @@
module top(o1, o2, o3, o4);
`define FAIL input wire not_a_port;
`ifdef COND_1
`FAIL
`elsif COND_2
`FAIL
`elsif COND_3
`FAIL
`elsif COND_4
`FAIL
`else
`define COND_4
output wire o4;
`ifdef COND_1
`FAIL
`elsif COND_2
`FAIL
`elsif COND_3
`FAIL
`elsif COND_4
`define COND_3
output wire o3;
`ifdef COND_1
`FAIL
`elsif COND_2
`FAIL
`elsif COND_3
`define COND_2
output wire o2;
`ifdef COND_1
`FAIL
`elsif COND_2
`define COND_1
output wire o1;
`ifdef COND_1
`ifdef COND_1
`elsif COND_2
`FAIL
`elsif COND_3
`FAIL
`elsif COND_4
`FAIL
`else
`FAIL
`endif
`elsif COND_2
`FAIL
`elsif COND_3
`FAIL
`elsif COND_4
`FAIL
`else
`FAIL
`endif
`elsif COND_3
`FAIL
`elsif COND_4
`FAIL
`else
`FAIL
`endif
`elsif COND_4
`FAIL
`else
`FAIL
`endif
`else
`FAIL
`endif
`endif
endmodule

21
tests/simple/ifdef_2.v Normal file
View File

@ -0,0 +1,21 @@
module top(o1, o2, o3);
output wire o1;
`define COND_1
`define COND_2
`define COND_3
`ifdef COND_1
output wire o2;
`elsif COND_2
input wire dne1;
`elsif COND_3
input wire dne2;
`else
input wire dne3;
`endif
output wire o3;
endmodule

View File

@ -0,0 +1,30 @@
`ifdef GUARD_5
module top;
wire x;
endmodule
`elsif GUARD_4
`define GUARD_5
`include "include_self.v"
`elsif GUARD_3
`define GUARD_4
`include "include_self.v"
`elsif GUARD_2
`define GUARD_3
`include "include_self.v"
`elsif GUARD_1
`define GUARD_2
`include "include_self.v"
`elsif GUARD_0
`define GUARD_1
`include "include_self.v"
`else
`define GUARD_0
`include "include_self.v"
`endif

View File

@ -0,0 +1,2 @@
read_verilog include_self.v
select -assert-count 1 top/x

View File

@ -0,0 +1,6 @@
logger -expect error "Found `else outside of macro conditional branch!" 1
read_verilog <<EOT
module top;
`else
endmodule
EOT

View File

@ -0,0 +1,6 @@
logger -expect error "Found `elsif outside of macro conditional branch!" 1
read_verilog <<EOT
module top;
`elsif
endmodule
EOT

View File

@ -0,0 +1,6 @@
logger -expect error "Found `endif outside of macro conditional branch!" 1
read_verilog <<EOT
module top;
`endif
endmodule
EOT