Skip to content

Commit ec082d9

Browse files
kroeningclaude
andcommitted
hw-cbmc: re-enable --vcd
The --vcd option was parsed but had no effect: show_unwind_trace() was only reachable via CBMC's old error_trace() callback mechanism, which no longer exists since hw-cbmc moved to the goto_verifiert pipeline. The option now enables trace storage in the verifier, and on VERIFICATION FAILED the stored goto trace is converted into a hardware trans_tracet by collecting the values of the timeframe-instantiated signals (identifier@t), which is then written using the existing show_trans_trace_vcd(). This is independent of the front-end's module naming. The dead do_unwind_module()/error_trace() code is removed. Fixes: #1955 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 7b05b8d commit ec082d9

5 files changed

Lines changed: 188 additions & 47 deletions

File tree

regression/hw-cbmc/VCD1/main.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* unwinding bound */
2+
3+
void next_timeframe();
4+
extern const unsigned int bound;
5+
6+
/*
7+
Module verilog::top
8+
*/
9+
10+
struct top_module
11+
{
12+
unsigned long counter;
13+
};
14+
15+
extern struct top_module top;
16+
17+
int main()
18+
{
19+
unsigned cycle;
20+
unsigned val;
21+
22+
for(cycle = 0; cycle < bound; cycle++)
23+
{
24+
val = top.counter;
25+
assert(val != 5);
26+
next_timeframe();
27+
}
28+
}

regression/hw-cbmc/VCD1/main.v

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module top(input clk);
2+
3+
reg [63:0] counter;
4+
5+
initial counter=0;
6+
7+
always @(posedge clk)
8+
counter=counter+1;
9+
10+
endmodule

regression/hw-cbmc/VCD1/test.desc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
--module top --bound 10 main.v --vcd -
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
^\$scope module top \$end$
8+
^ \$var reg 64 top\.counter counter \[63:0\] \$end$
9+
^b0+101 top\.counter$
10+
--
11+
^warning: ignoring

src/hw-cbmc/hw_cbmc_parse_options.cpp

Lines changed: 134 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ Author: Daniel Kroening, kroening@kroening.com
3333
#include <ebmc/show_modules.h>
3434
#include <ebmc/transition_system.h>
3535
#include <goto-checker/all_properties_verifier_with_trace_storage.h>
36+
#include <goto-checker/goto_trace_storage.h>
3637
#include <goto-checker/goto_verifier.h>
3738
#include <goto-checker/multi_path_symex_checker.h>
3839
#include <langapi/language_file.h>
3940
#include <langapi/mode.h>
4041
#include <linking/static_lifetime_init.h>
42+
#include <trans-netlist/trans_trace.h>
4143
#include <trans-word-level/instantiate_word_level.h>
42-
#include <trans-word-level/trans_trace_word_level.h>
43-
#include <trans-word-level/unwind.h>
4444
#include <verilog/verilog_ebmc_language.h>
4545

4646
#include "gen_interface.h"
@@ -422,8 +422,14 @@ int hw_cbmc_parse_optionst::doit()
422422
messaget::M_STATISTICS, ui_message_handler);
423423

424424
if(cmdline.isset("vcd"))
425+
{
425426
options.set_option("vcd", cmdline.get_value("vcd"));
426427

428+
// the verifier only stores the traces needed for the
429+
// VCD output when the trace option is set
430+
options.set_option("trace", true);
431+
}
432+
427433
//
428434
// Print a banner
429435
//
@@ -567,14 +573,17 @@ int hw_cbmc_parse_optionst::doit()
567573
if(set_properties())
568574
return 7;
569575

570-
std::unique_ptr<goto_verifiert> verifier = std::make_unique<
576+
auto verifier = std::make_unique<
571577
all_properties_verifier_with_trace_storaget<multi_path_symex_checkert>>(
572578
options, ui_message_handler, goto_model);
573579

574580
// do actual BMC
575581
const resultt result = (*verifier)();
576582
verifier->report();
577583

584+
if(result == resultt::FAIL && options.get_option("vcd") != "")
585+
show_unwind_trace(options, verifier->get_traces());
586+
578587
return result_to_exit_code(result);
579588
}
580589

@@ -728,54 +737,142 @@ void hw_cbmc_parse_optionst::help()
728737
"\n";
729738
}
730739

731-
void hw_cbmc_parse_optionst::do_unwind_module(prop_convt &prop_conv) {
732-
if (unwind_module == "" || unwind_no_timeframes == 0)
733-
return;
740+
/*******************************************************************\
741+
742+
Function: compute_trans_trace
743+
744+
Inputs:
745+
746+
Outputs:
747+
748+
Purpose: build a hardware trace from a goto trace, using the
749+
values of the timeframe-instantiated hardware symbols
750+
751+
\*******************************************************************/
752+
753+
static trans_tracet compute_trans_trace(
754+
const goto_tracet &goto_trace,
755+
std::size_t no_timeframes,
756+
const namespacet &ns,
757+
const irep_idt &module)
758+
{
759+
// The hardware signals show up in the goto trace as assignments
760+
// to the timeframe-instantiated symbols (identifier@t). Collect
761+
// their values, per signal and timeframe.
762+
std::map<irep_idt, std::vector<exprt>> values;
763+
764+
for(const auto &step : goto_trace.steps)
765+
{
766+
if(!step.is_assignment() && !step.is_decl())
767+
continue;
768+
769+
if(step.full_lhs.id() != ID_symbol)
770+
continue;
771+
772+
const std::string identifier =
773+
id2string(to_symbol_expr(step.full_lhs).get_identifier());
774+
775+
const auto separator_pos = identifier.rfind('@');
776+
if(separator_pos == std::string::npos)
777+
continue;
778+
779+
const auto timeframe_string = identifier.substr(separator_pos + 1);
780+
if(
781+
timeframe_string.empty() ||
782+
timeframe_string.find_first_not_of("0123456789") != std::string::npos)
783+
{
784+
continue;
785+
}
786+
787+
const auto timeframe = unsafe_string2size_t(timeframe_string);
788+
if(timeframe >= no_timeframes)
789+
continue;
790+
791+
// the base identifier must be a hardware signal
792+
const irep_idt base_identifier = identifier.substr(0, separator_pos);
793+
const symbolt *symbol;
794+
if(ns.lookup(base_identifier, symbol))
795+
continue;
734796

735-
namespacet ns{goto_model.symbol_table};
736-
const symbolt &symbol = ns.lookup(unwind_module);
797+
if(symbol->is_type || symbol->is_property || symbol->is_macro)
798+
continue;
737799

738-
log.status() << "Unwinding transition system `" << symbol.name << "' with "
739-
<< unwind_no_timeframes << " time frames" << messaget::eom;
800+
auto &signal_values = values[base_identifier];
801+
signal_values.resize(no_timeframes, nil_exprt());
802+
signal_values[timeframe] = step.full_lhs_value;
803+
}
740804

741-
// auto dp= get_decision_procedure();
805+
trans_tracet dest;
742806

743-
::unwind(to_trans_expr(symbol.value), ui_message_handler, prop_conv,
744-
unwind_no_timeframes, ns, true);
807+
dest.mode = id2string(ns.lookup(module).mode);
808+
dest.states.resize(no_timeframes);
809+
810+
for(std::size_t t = 0; t < no_timeframes; t++)
811+
{
812+
trans_tracet::statet &state = dest.states[t];
813+
814+
for(const auto &[identifier, signal_values] : values)
815+
{
816+
const symbolt &symbol = ns.lookup(identifier);
817+
state.assignments.emplace_back(symbol.symbol_expr(), signal_values[t]);
818+
}
819+
}
745820

746-
log.status() << "Unwinding transition system done" << messaget::eom;
821+
return dest;
747822
}
748823

749-
void hw_cbmc_parse_optionst::show_unwind_trace(const optionst &options,
750-
const prop_convt &prop_conv) {
751-
if (unwind_module == "" || unwind_no_timeframes == 0)
824+
/*******************************************************************\
825+
826+
Function: hw_cbmc_parse_optionst::show_unwind_trace
827+
828+
Inputs:
829+
830+
Outputs:
831+
832+
Purpose: write the hardware signals of the counterexample
833+
as a VCD file
834+
835+
\*******************************************************************/
836+
837+
void hw_cbmc_parse_optionst::show_unwind_trace(
838+
const optionst &options,
839+
const goto_trace_storaget &traces)
840+
{
841+
if(unwind_module.empty() || unwind_no_timeframes == 0)
752842
return;
753843

754-
namespacet ns{goto_model.symbol_table};
755-
auto trans_trace =
756-
compute_trans_trace(prop_conv, unwind_no_timeframes, ns, unwind_module);
844+
if(traces.all().empty())
845+
return;
757846

758-
if (options.get_option("vcd") != "") {
759-
if (options.get_option("vcd") == "-")
760-
show_trans_trace_vcd(trans_trace, log, ns, std::cout);
761-
else {
762-
std::ofstream out(widen_if_needed(options.get_option("vcd")));
763-
show_trans_trace_vcd(trans_trace, log, ns, out);
764-
}
847+
const namespacet ns{goto_model.symbol_table};
848+
849+
auto trans_trace = compute_trans_trace(
850+
traces.all().front(), unwind_no_timeframes, ns, unwind_module);
851+
852+
if(
853+
trans_trace.states.empty() ||
854+
trans_trace.states.front().assignments.empty())
855+
{
856+
log.warning() << "no hardware signals in counterexample, "
857+
<< "not generating VCD" << messaget::eom;
858+
return;
765859
}
766860

767-
switch(ui_message_handler.get_ui())
861+
const auto &vcd_file = options.get_option("vcd");
862+
863+
if(vcd_file == "-")
864+
show_trans_trace_vcd(trans_trace, log, ns, std::cout);
865+
else
768866
{
769-
case ui_message_handlert::uit::PLAIN:
770-
show_trans_trace(trans_trace, log, ns, std::cout);
771-
break;
867+
std::ofstream out(widen_if_needed(vcd_file));
772868

773-
case ui_message_handlert::uit::XML_UI:
774-
show_trans_trace_xml(trans_trace, log, ns, std::cout);
775-
break;
869+
if(!out)
870+
{
871+
log.error() << "failed to open VCD file `" << vcd_file << "'"
872+
<< messaget::eom;
873+
return;
874+
}
776875

777-
case ui_message_handlert::uit::JSON_UI:
778-
show_trans_trace_json(trans_trace, log, ns, std::cout);
779-
break;
876+
show_trans_trace_vcd(trans_trace, log, ns, out);
780877
}
781878
}

src/hw-cbmc/hw_cbmc_parse_options.h

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Author: Daniel Kroening, kroening@kroening.com
1111

1212
#include <cbmc/cbmc_parse_options.h>
1313

14+
class goto_trace_storaget;
15+
1416
#define HW_CBMC_OPTIONS \
1517
"(showvarmap)(bound):(module):(top):" \
1618
"(show-modules)(gen-interface)(vcd):"
@@ -20,7 +22,7 @@ class hw_cbmc_parse_optionst:public cbmc_parse_optionst
2022
public:
2123
virtual int doit();
2224
virtual void help();
23-
25+
2426
hw_cbmc_parse_optionst(int argc, const char **argv):
2527
cbmc_parse_optionst(argc, argv, HW_CBMC_OPTIONS)
2628
{
@@ -29,15 +31,8 @@ class hw_cbmc_parse_optionst:public cbmc_parse_optionst
2931
irep_idt unwind_module;
3032
unsigned unwind_no_timeframes;
3133

32-
virtual void do_unwind_module(prop_convt &prop_conv);
33-
34-
virtual void show_unwind_trace(const optionst &options,
35-
const prop_convt &prop_conv);
36-
37-
virtual void error_trace(const optionst &options,
38-
const prop_convt &prop_conv) {
39-
show_unwind_trace(options, prop_conv);
40-
}
34+
void
35+
show_unwind_trace(const optionst &options, const goto_trace_storaget &traces);
4136

4237
protected:
4338
virtual int get_modules(std::list<exprt> &bmc_constraints);

0 commit comments

Comments
 (0)