11 #include "nlohmann/json.hpp"
22 Result<z3::expr> get_prefixed_function_of_net(
const std::vector<Gate*>& subgraph_gates,
24 const std::string& variable_prefix,
26 std::map<u32, z3::expr>& net_cache,
27 std::map<std::pair<u32, const GatePin*>, BooleanFunction>& gate_cache)
29 if (
const auto it = net_cache.find(
net->get_id()); it != net_cache.end())
31 return OK(it->second);
34 const std::vector<Endpoint*> sources =
net->get_sources();
37 if (sources.size() > 1)
39 return ERR(
"cannot get Boolean z3 function of net " +
net->get_name() +
" with ID " + std::to_string(
net->get_id()) +
": net is multi driven.");
45 const std::string var_name = variable_prefix + BooleanFunctionNetDecorator(*net).get_boolean_variable_name();
46 z3::expr ret = ctx.bv_const(var_name.c_str(), 1);
47 net_cache.insert({
net->get_id(), ret});
51 const Endpoint* src_ep = sources.front();
53 if (src_ep->get_gate() ==
nullptr)
55 return ERR(
"cannot get Boolean z3 function of net " +
net->get_name() +
" with ID " + std::to_string(
net->get_id()) +
": net source is null.");
58 const Gate* src = src_ep->get_gate();
61 if (std::find(subgraph_gates.begin(), subgraph_gates.end(), src) == subgraph_gates.end())
63 const std::string var_name = variable_prefix + BooleanFunctionNetDecorator(*net).get_boolean_variable_name();
64 z3::expr ret = ctx.bv_const(var_name.c_str(), 1);
65 net_cache.insert({
net->get_id(), ret});
70 if (
const auto it = gate_cache.find({src->get_id(), src_ep->get_pin()}); it == gate_cache.end())
72 const auto bf_res = src->get_resolved_boolean_function(src_ep->get_pin());
73 if (bf_res.is_error())
76 "cannot get Boolean z3 function of net " +
net->get_name() +
" with ID " + std::to_string(
net->get_id()) +
": failed to get function of gate.");
80 gate_cache.insert({{src->get_id(), src_ep->get_pin()}, bf});
87 std::map<std::string, z3::expr> input_to_expr;
89 for (
const std::string& in_net_str : bf.get_variable_names())
92 if (in_net_res.is_error())
95 "cannot get Boolean z3 function of net " +
net->get_name() +
" with ID " + std::to_string(
net->get_id()) +
": failed to reconstruct input net from variable "
98 const auto in_net = in_net_res.get();
100 const auto in_bf_res = get_prefixed_function_of_net(subgraph_gates, in_net, variable_prefix, ctx, net_cache, gate_cache);
101 if (in_bf_res.is_error())
106 const auto in_bf = in_bf_res.get();
108 input_to_expr.insert({in_net_str, in_bf});
112 net_cache.insert({
net->get_id(), ret});
117 Result<z3::expr> get_prefixed_subgraph_z3_function_internal(
const std::vector<Gate*>& subgraph_gates,
119 const std::string& variable_prefix,
121 std::map<u32, z3::expr>& net_cache,
122 std::map<std::pair<u32, const GatePin*>, BooleanFunction>& gate_cache)
125 if (subgraph_gates.empty())
127 return ERR(
"could not get subgraph z3 function of net '" +
net->get_name() +
"' with ID " + std::to_string(
net->get_id()) +
": subgraph contains no gates");
129 else if (std::any_of(subgraph_gates.begin(), subgraph_gates.end(), [](
const Gate* g) { return g == nullptr; }))
131 return ERR(
"could not get subgraph z3 function of net '" +
net->get_name() +
"' with ID " + std::to_string(
net->get_id()) +
": subgraph contains a gate that is a 'nullptr'");
133 else if (
net ==
nullptr)
135 return ERR(
"could not get subgraph z3 function: net is a 'nullptr'");
137 else if (
net->get_num_of_sources() > 1)
139 return ERR(
"could not get subgraph z3 function of net '" +
net->get_name() +
"' with ID " + std::to_string(
net->get_id()) +
": net has more than one source");
142 return get_prefixed_function_of_net(subgraph_gates,
net, variable_prefix, ctx, net_cache, gate_cache);
145 Result<z3::expr> get_prefixed_subgraph_z3_function(
const std::vector<Gate*>& subgraph_gates,
const Net* subgraph_output,
const std::string& variable_prefix, z3::context& ctx)
147 std::map<u32, z3::expr> net_cache;
148 std::map<std::pair<u32, const GatePin*>, BooleanFunction> gate_cache;
150 return get_prefixed_subgraph_z3_function_internal(subgraph_gates, subgraph_output, variable_prefix, ctx, net_cache, gate_cache);
156 std::unordered_map<Gate*, std::vector<std::string>> restore_ff_replacements(
const Netlist* nl)
158 std::unordered_map<Gate*, std::vector<std::string>> replacements;
160 for (
auto& g : nl->get_gates())
162 if (g->has_data(
"preprocessing_information",
"replaced_gates"))
164 const auto& [_, s] = g->get_data(
"preprocessing_information",
"replaced_gates");
166 replacements.insert({g, replaced_gate_names});
176 Result<std::monostate> substitute_net_ids(z3::context& ctx, z3::solver& s,
const std::vector<Net*>& nets,
const std::string& variable_prefix,
const Netlist* nl)
179 for (
const auto&
net : nets)
181 if (
net->get_sources().size() > 1)
183 return ERR(
"cannot replace net id for net " +
net->get_name() +
" with ID " + std::to_string(
net->get_id()) +
" because it is multi driven!");
186 const std::string var_name = variable_prefix + BooleanFunctionNetDecorator(*net).get_boolean_variable_name();
187 const auto sources =
net->get_sources([](
const Endpoint* ep) {
return (ep->get_gate() !=
nullptr) && (ep->get_gate()->get_type()->has_property(
GateTypeProperty::sequential)); });
188 if (
net->is_global_input_net())
190 const auto pin = nl->get_top_module()->get_pin_by_net(
net);
194 return ERR(
"cannot replace net id for net " +
net->get_name() +
" with ID " + std::to_string(
net->get_id())
195 +
": net is global input but unable to find pin at top module!");
198 const z3::expr new_expr = ctx.bv_const((
"GLOBAL_IN_" + pin->get_name()).c_str(), 1);
199 const z3::expr net_expr = ctx.bv_const(var_name.c_str(), 1);
201 s.add(net_expr == new_expr);
208 log_debug(
"z3_utils",
"No source found for net {}. Cannot replace net and will rename with net name {}!",
net->get_id(),
net->get_name());
210 const z3::expr new_expr = ctx.bv_const(
net->get_name().c_str(), 1);
211 const z3::expr net_expr = ctx.bv_const(var_name.c_str(), 1);
213 s.add(net_expr == new_expr);
217 const Endpoint* src = sources.front();
219 if (src->get_gate() ==
nullptr)
221 log_warning(
"z3_utils",
"No source gate found for net {}. Cannot replace net and will rename with net name {}!",
net->get_id(),
net->get_name());
223 const z3::expr new_expr = ctx.bv_const(
net->get_name().c_str(), 1);
224 const z3::expr net_expr = ctx.bv_const(var_name.c_str(), 1);
226 s.add(net_expr == new_expr);
230 const std::string src_gate_name = src->get_gate()->get_name();
231 const std::string src_pin = src->get_pin()->get_name();
232 const std::string
name = src_gate_name +
"_" + src_pin;
233 const z3::expr new_expr = ctx.bv_const(
name.c_str(), 1);
234 const z3::expr net_expr = ctx.bv_const(var_name.c_str(), 1);
236 s.add(net_expr == new_expr);
242 void add_replacements_equal_constraints(z3::context& ctx, z3::solver& s,
const std::unordered_map<Gate*, std::vector<std::string>>& replacements)
244 for (
const auto& [g, replaced_names] : replacements)
246 for (
const auto& ep : g->get_fan_out_endpoints())
248 const std::string g_name = g->get_name() +
"_" + ep->get_pin()->get_name();
249 const z3::expr g_expr = ctx.bv_const(g_name.c_str(), 1);
251 for (
const auto&
name : replaced_names)
253 const std::string replaced_name =
name +
"_" + ep->get_pin()->get_name();
254 const z3::expr replaced_expr = ctx.bv_const(replaced_name.c_str(), 1);
256 s.add(g_expr == replaced_expr);
264 Result<std::monostate> setup_solver(z3::context& ctx,
266 const Netlist* netlist_a,
267 const Netlist* netlist_b,
268 const std::unordered_map<
hal::Gate*, std::vector<std::string>>& ff_replacements_a,
269 const std::unordered_map<
hal::Gate*, std::vector<std::string>>& ff_replacements_b)
271 std::vector<Net*> sub_nets_a;
272 for (
const auto& n : netlist_a->get_nets())
274 const auto comb_sources =
275 n->get_sources([](
const Endpoint* ep) {
return (ep->get_gate() !=
nullptr) && (ep->get_gate()->get_type()->has_property(
GateTypeProperty::combinational)); });
276 if (comb_sources.empty())
278 sub_nets_a.push_back(n);
281 std::vector<Net*> sub_nets_b;
282 for (
const auto& n : netlist_b->get_nets())
284 const auto comb_sources =
285 n->get_sources([](
const Endpoint* ep) {
return (ep->get_gate() !=
nullptr) && (ep->get_gate()->get_type()->has_property(
GateTypeProperty::combinational)); });
286 if (comb_sources.empty())
288 sub_nets_b.push_back(n);
292 auto sub_a_res = substitute_net_ids(ctx, s, sub_nets_a,
"netlist_a_", netlist_a);
293 if (sub_a_res.is_error())
295 return ERR_APPEND(sub_a_res.get_error(),
"cannot compare netA" + std::to_string(netlist_a->get_id()));
297 auto sub_b_res = substitute_net_ids(ctx, s, sub_nets_b,
"netlist_b_", netlist_b);
298 if (sub_b_res.is_error())
300 return ERR_APPEND(sub_b_res.get_error(),
"cannot compare net A " + std::to_string(netlist_b->get_id()));
303 add_replacements_equal_constraints(ctx, s, ff_replacements_a);
304 add_replacements_equal_constraints(ctx, s, ff_replacements_b);
309 Result<bool> compare_nets_internal(z3::context& ctx,
313 const std::vector<Gate*>& gates_a,
314 const std::vector<Gate*>& gates_b,
315 const bool fail_on_unknown,
316 const u32 solver_timeout)
318 const auto bf_res_a = z3_utils::get_prefixed_subgraph_z3_function(gates_a, net_a,
"netlist_a_", ctx);
319 if (bf_res_a.is_error())
322 "cannot compare net A " + net_a->get_name() +
" with ID " + std::to_string(net_a->get_id()) +
" with net B " + net_b->get_name() +
" with ID "
323 + std::to_string(net_b->get_id()) +
": failed to build subgraph function for net a");
325 const auto bf_a = bf_res_a.get();
327 const auto bf_res_b = z3_utils::get_prefixed_subgraph_z3_function(gates_b, net_b,
"netlist_b_", ctx);
328 if (bf_res_b.is_error())
331 "cannot compare net A " + net_a->get_name() +
" with ID " + std::to_string(net_a->get_id()) +
" with net B " + net_b->get_name() +
" with ID "
332 + std::to_string(net_b->get_id()) +
": failed to build subgraph function for net b");
334 const auto bf_b = bf_res_b.get();
339 auto smt2_str = s.to_smt2();
341 if (query_res.is_error())
344 "cannot compare net A " + net_a->get_name() +
" with ID " + std::to_string(net_a->get_id()) +
" with net B " + net_b->get_name() +
" with ID "
345 + std::to_string(net_b->get_id()) +
": failed solver_check");
347 const auto check_result = query_res.get();
349 if (check_result.is_unsat())
354 if (check_result.is_unknown())
356 return OK(!fail_on_unknown);
365 if (netlist_a ==
nullptr)
367 return ERR(
"cannot compare nets: netlist_a is a nullptr!");
370 if (netlist_b ==
nullptr)
372 return ERR(
"cannot compare nets: netlist_b is a nullptr!");
375 if (net_a ==
nullptr)
377 return ERR(
"cannot compare nets: net_a is a nullptr!");
380 if (net_b ==
nullptr)
382 return ERR(
"cannot compare nets: net_b is a nullptr!");
388 const auto ff_replacements_a = restore_ff_replacements(netlist_a);
389 const auto ff_replacements_b = restore_ff_replacements(netlist_b);
394 auto setup_res = setup_solver(ctx, s, netlist_a, netlist_b, ff_replacements_a, ff_replacements_b);
395 if (setup_res.is_error())
398 "cannot compare netlist a with ID " + std::to_string(netlist_a->
get_id()) +
" netlist b with ID " + std::to_string(netlist_b->
get_id()) +
": failed to setup solver");
401 return compare_nets_internal(ctx, s, net_a, net_b, comb_gates_a, comb_gates_b, fail_on_unknown, solver_timeout);
406 if (netlist_a ==
nullptr)
408 return ERR(
"cannot compare nets: netlist_a is a nullptr!");
411 if (netlist_b ==
nullptr)
413 return ERR(
"cannot compare nets: netlist_b is a nullptr!");
419 const auto ff_replacements_a = restore_ff_replacements(netlist_a);
420 const auto ff_replacements_b = restore_ff_replacements(netlist_b);
425 auto setup_res = setup_solver(ctx, s, netlist_a, netlist_b, ff_replacements_a, ff_replacements_b);
426 if (setup_res.is_error())
429 "cannot compare netlist a with ID " + std::to_string(netlist_a->
get_id()) +
" netlist b with ID " + std::to_string(netlist_b->
get_id()) +
": failed to setup solver");
432 for (
const auto& [net_a, net_b] : nets)
434 if ((net_a ==
nullptr) && (net_b ==
nullptr))
439 if ((net_a ==
nullptr) || (net_b ==
nullptr))
445 auto comp_res = compare_nets_internal(ctx, s, net_a, net_b, comb_gates_a, comb_gates_b, fail_on_unknown, solver_timeout);
448 if (comp_res.is_error())
451 "cannot compare netlist a with ID " + std::to_string(netlist_a->
get_id()) +
" netlist b with ID " + std::to_string(netlist_b->
get_id())
452 +
": failed net comparison");
454 const auto eq = comp_res.get();
458 log_warning(
"z3_utils",
"Failed net comparison for net A {} / {} and net B {} / {}", net_a->get_id(), net_a->get_name(), net_b->get_id(), net_b->get_name());
474 const auto ff_replacements_a = restore_ff_replacements(netlist_a);
475 const auto ff_replacements_b = restore_ff_replacements(netlist_b);
478 std::unordered_map<std::string, Gate*> gate_name_to_gate_a;
479 std::unordered_map<std::string, Gate*> gate_name_to_gate_b;
481 for (
const auto& gate_a : seq_gates_a)
483 gate_name_to_gate_a[gate_a->get_name()] = gate_a;
485 if (
const auto& it = ff_replacements_a.find(gate_a); it != ff_replacements_a.end())
487 for (
const auto& s : it->second)
489 gate_name_to_gate_a[s] = gate_a;
493 for (
const auto& gate_b : seq_gates_b)
495 gate_name_to_gate_b[gate_b->get_name()] = gate_b;
497 if (
const auto& it = ff_replacements_b.find(gate_b); it != ff_replacements_b.end())
499 for (
const auto& s : it->second)
501 gate_name_to_gate_b[s] = gate_b;
506 for (
const auto& [gate_a_name, gate_a] : gate_name_to_gate_a)
508 if (
const auto gate_b_it = gate_name_to_gate_b.find(gate_a_name); gate_b_it == gate_name_to_gate_b.end())
511 "netlist a with ID {} and netlist b with ID {} are not equal: gate a {} with ID {} is included in netlist a but does not have a counter part in netlist b!",
519 for (
const auto& [gate_b_name, gate_b] : gate_name_to_gate_b)
521 if (
const auto gate_a_it = gate_name_to_gate_a.find(gate_b_name); gate_a_it == gate_name_to_gate_a.end())
524 "netlist a with ID {} and netlist b with ID {} are not equal: gate b {} with ID {} is included in netlist b but does not have a counter part in netlist a!",
533 log_info(
"z3_utils",
"Checking {} sequential gates for equality.", seq_gates_a.size());
538 auto setup_res = setup_solver(ctx, s, netlist_a, netlist_b, ff_replacements_a, ff_replacements_b);
539 if (setup_res.is_error())
542 "cannot compare netlist a with ID " + std::to_string(netlist_a->
get_id()) +
" netlist b with ID " + std::to_string(netlist_b->
get_id()) +
": failed to setup solver");
545 std::set<std::pair<Net*, Net*>> to_compare;
551 auto all_out_pins = out_pins_a;
552 all_out_pins.insert(all_out_pins.end(), out_pins_b.begin(), out_pins_b.end());
554 for (
const auto& pin : all_out_pins)
556 auto it_a = std::find(out_pins_a.begin(), out_pins_a.end(), pin);
557 if (it_a == out_pins_a.end())
560 "netlist a with ID {} and netlist b with ID {} might not be equal: netlist a has output pin {} that does not exist in netlist b!",
567 auto it_b = std::find(out_pins_b.begin(), out_pins_b.end(), pin);
568 if (it_b == out_pins_b.end())
571 "netlist a with ID {} and netlist b with ID {} might not be equal: netlist a has output pin {} that does not exist in netlist b!",
581 to_compare.insert({net_a, net_b});
585 for (
const Gate* gate_a : seq_gates_a)
587 const Gate* gate_b = gate_name_to_gate_b.at(gate_a->get_name());
589 if (gate_a->get_type() != gate_b->
get_type())
592 "netlist a with ID {} and netlist b with ID {} are not equal: gate a {} with ID {} and gate b {} with ID {} do not have the same type! {} vs. {}",
599 gate_a->get_type()->get_name(),
606 Net* net_a = gate_a->get_fan_in_net(pin);
609 to_compare.insert({net_a, net_b});
static Result< Net * > get_net_from(const Netlist *netlist, const BooleanFunction &var)
Net * get_fan_in_net(const std::string &pin_name) const
GateType * get_type() const
const std::string & get_name() const
const std::string & get_name() const
ModulePin * get_pin_by_name(const std::string &name) const
std::vector< std::string > get_output_pin_names() const
Module * get_top_module() const
const std::vector< Gate * > & get_gates() const
static Result< SolverResult > query_local_with_smt2(const QueryConfig &config, const std::string &smt2)
#define log_debug(channel,...)
#define log_info(channel,...)
#define log_warning(channel,...)
#define ERR_APPEND(prev_error, message)
std::unique_ptr< GateLibrary > parse(std::filesystem::path file_path)
std::vector< T > to_vector(const Container< T, Args... > &container)
Result< bool > compare_netlists(const Netlist *netlist_a, const Netlist *netlist_b, const bool fail_on_unknown=true, const u32 solver_timeout=10)
Compares two netlists on a functional level.
z3::expr from_bf(const BooleanFunction &bf, z3::context &ctx, const std::map< std::string, z3::expr > &var2expr={})
Result< bool > compare_nets(const Netlist *netlist_a, const Netlist *netlist_b, const Net *net_a, const Net *net_b, const bool fail_on_unknown=true, const u32 solver_timeout=10)
Compare two nets from two different netlists.
QueryConfig & without_model_generation()
QueryConfig & with_timeout(u64 seconds)