12 #include "nlohmann/json.hpp"
23 Result<z3::expr> get_prefixed_function_of_net(
const std::vector<Gate*>& subgraph_gates,
25 const std::string& variable_prefix,
27 std::map<u32, z3::expr>& net_cache,
30 if (
const auto it = net_cache.find(
net->get_id()); it != net_cache.end())
32 return OK(it->second);
35 const std::vector<Endpoint*> sources =
net->get_sources();
38 if (sources.size() > 1)
40 return ERR(
"cannot get Boolean z3 function of net " +
net->get_name() +
" with ID " + std::to_string(
net->get_id()) +
": net is multi driven.");
46 const std::string var_name = variable_prefix + BooleanFunctionNetDecorator(*net).get_boolean_variable_name();
47 z3::expr ret = ctx.bv_const(var_name.c_str(), 1);
48 net_cache.insert({
net->get_id(), ret});
52 const Endpoint* src_ep = sources.front();
54 if (src_ep->get_gate() ==
nullptr)
56 return ERR(
"cannot get Boolean z3 function of net " +
net->get_name() +
" with ID " + std::to_string(
net->get_id()) +
": net source is null.");
59 const Gate* src = src_ep->get_gate();
62 if (std::find(subgraph_gates.begin(), subgraph_gates.end(), src) == subgraph_gates.end())
64 const std::string var_name = variable_prefix + BooleanFunctionNetDecorator(*net).get_boolean_variable_name();
65 z3::expr ret = ctx.bv_const(var_name.c_str(), 1);
66 net_cache.insert({
net->get_id(), ret});
71 if (
const auto it = gate_cache.find({src->get_id(), src_ep->get_pin()}); it == gate_cache.end())
73 const auto bf_res = src->get_resolved_boolean_function(src_ep->get_pin());
74 if (bf_res.is_error())
77 "cannot get Boolean z3 function of net " +
net->get_name() +
" with ID " + std::to_string(
net->get_id()) +
": failed to get function of gate.");
81 gate_cache.insert({{src->get_id(), src_ep->get_pin()}, bf});
88 std::map<std::string, z3::expr> input_to_expr;
90 for (
const std::string& in_net_str : bf.get_variable_names())
93 if (in_net_res.is_error())
96 "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 "
99 const auto in_net = in_net_res.get();
101 const auto in_bf_res = get_prefixed_function_of_net(subgraph_gates, in_net, variable_prefix, ctx, net_cache, gate_cache);
102 if (in_bf_res.is_error())
107 const auto in_bf = in_bf_res.get();
109 input_to_expr.insert({in_net_str, in_bf});
113 net_cache.insert({
net->get_id(), ret});
118 Result<z3::expr> get_prefixed_subgraph_z3_function_internal(
const std::vector<Gate*>& subgraph_gates,
120 const std::string& variable_prefix,
122 std::map<u32, z3::expr>& net_cache,
126 if (subgraph_gates.empty())
128 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");
130 else if (std::any_of(subgraph_gates.begin(), subgraph_gates.end(), [](
const Gate* g) { return g == nullptr; }))
132 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'");
134 else if (
net ==
nullptr)
136 return ERR(
"could not get subgraph z3 function: net is a 'nullptr'");
138 else if (
net->get_num_of_sources() > 1)
140 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");
143 return get_prefixed_function_of_net(subgraph_gates,
net, variable_prefix, ctx, net_cache, gate_cache);
146 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)
148 std::map<u32, z3::expr> net_cache;
151 return get_prefixed_subgraph_z3_function_internal(subgraph_gates, subgraph_output, variable_prefix, ctx, net_cache, gate_cache);
157 std::unordered_map<Gate*, std::vector<std::string>> restore_ff_replacements(
const Netlist* nl)
159 std::unordered_map<Gate*, std::vector<std::string>> replacements;
161 for (
auto& g : nl->get_gates())
163 if (g->has_data(
"preprocessing_information",
"replaced_gates"))
165 const auto& [_, s] = g->get_data(
"preprocessing_information",
"replaced_gates");
167 replacements.insert({g, replaced_gate_names});
177 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)
180 for (
const auto&
net : nets)
182 if (
net->get_sources().size() > 1)
184 return ERR(
"cannot replace net id for net " +
net->get_name() +
" with ID " + std::to_string(
net->get_id()) +
" because it is multi driven!");
187 const std::string var_name = variable_prefix + BooleanFunctionNetDecorator(*net).get_boolean_variable_name();
188 const auto sources =
net->get_sources([](
const Endpoint* ep) {
return (ep->get_gate() !=
nullptr) && (ep->get_gate()->get_type()->has_property(
GateTypeProperty::sequential)); });
189 if (
net->is_global_input_net())
191 const auto pin = nl->get_top_module()->get_pin_by_net(
net);
195 return ERR(
"cannot replace net id for net " +
net->get_name() +
" with ID " + std::to_string(
net->get_id())
196 +
": net is global input but unable to find pin at top module!");
199 const z3::expr new_expr = ctx.bv_const((
"GLOBAL_IN_" + pin->get_name()).c_str(), 1);
200 const z3::expr net_expr = ctx.bv_const(var_name.c_str(), 1);
202 s.add(net_expr == new_expr);
209 log_debug(
"z3_utils",
"No source found for net {}. Cannot replace net and will rename with net name {}!",
net->get_id(),
net->get_name());
211 const z3::expr new_expr = ctx.bv_const(
net->get_name().c_str(), 1);
212 const z3::expr net_expr = ctx.bv_const(var_name.c_str(), 1);
214 s.add(net_expr == new_expr);
218 const Endpoint* src = sources.front();
220 if (src->get_gate() ==
nullptr)
222 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());
224 const z3::expr new_expr = ctx.bv_const(
net->get_name().c_str(), 1);
225 const z3::expr net_expr = ctx.bv_const(var_name.c_str(), 1);
227 s.add(net_expr == new_expr);
231 const std::string src_gate_name = src->get_gate()->get_name();
232 const std::string src_pin = src->get_pin()->get_name();
233 const std::string
name = src_gate_name +
"_" + src_pin;
234 const z3::expr new_expr = ctx.bv_const(
name.c_str(), 1);
235 const z3::expr net_expr = ctx.bv_const(var_name.c_str(), 1);
237 s.add(net_expr == new_expr);
243 void add_replacements_equal_constraints(z3::context& ctx, z3::solver& s,
const std::unordered_map<Gate*, std::vector<std::string>>& replacements)
245 for (
const auto& [g, replaced_names] : replacements)
247 for (
const auto& ep : g->get_fan_out_endpoints())
249 const std::string g_name = g->get_name() +
"_" + ep->get_pin()->get_name();
250 const z3::expr g_expr = ctx.bv_const(g_name.c_str(), 1);
252 for (
const auto&
name : replaced_names)
254 const std::string replaced_name =
name +
"_" + ep->get_pin()->get_name();
255 const z3::expr replaced_expr = ctx.bv_const(replaced_name.c_str(), 1);
257 s.add(g_expr == replaced_expr);
265 Result<std::monostate> setup_solver(z3::context& ctx,
267 const Netlist* netlist_a,
268 const Netlist* netlist_b,
269 const std::unordered_map<
hal::Gate*, std::vector<std::string>>& ff_replacements_a,
270 const std::unordered_map<
hal::Gate*, std::vector<std::string>>& ff_replacements_b)
272 std::vector<Net*> sub_nets_a;
273 for (
const auto& n : netlist_a->get_nets())
275 const auto comb_sources =
276 n->get_sources([](
const Endpoint* ep) {
return (ep->get_gate() !=
nullptr) && (ep->get_gate()->get_type()->has_property(
GateTypeProperty::combinational)); });
277 if (comb_sources.empty())
279 sub_nets_a.push_back(n);
282 std::vector<Net*> sub_nets_b;
283 for (
const auto& n : netlist_b->get_nets())
285 const auto comb_sources =
286 n->get_sources([](
const Endpoint* ep) {
return (ep->get_gate() !=
nullptr) && (ep->get_gate()->get_type()->has_property(
GateTypeProperty::combinational)); });
287 if (comb_sources.empty())
289 sub_nets_b.push_back(n);
293 auto sub_a_res = substitute_net_ids(ctx, s, sub_nets_a,
"netlist_a_", netlist_a);
294 if (sub_a_res.is_error())
296 return ERR_APPEND(sub_a_res.get_error(),
"cannot compare netA" + std::to_string(netlist_a->get_id()));
298 auto sub_b_res = substitute_net_ids(ctx, s, sub_nets_b,
"netlist_b_", netlist_b);
299 if (sub_b_res.is_error())
301 return ERR_APPEND(sub_b_res.get_error(),
"cannot compare net A " + std::to_string(netlist_b->get_id()));
304 add_replacements_equal_constraints(ctx, s, ff_replacements_a);
305 add_replacements_equal_constraints(ctx, s, ff_replacements_b);
310 Result<bool> compare_nets_internal(z3::context& ctx,
314 const std::vector<Gate*>& gates_a,
315 const std::vector<Gate*>& gates_b,
316 const bool fail_on_unknown,
317 const u32 solver_timeout)
319 const auto bf_res_a = z3_utils::get_prefixed_subgraph_z3_function(gates_a, net_a,
"netlist_a_", ctx);
320 if (bf_res_a.is_error())
323 "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 "
324 + std::to_string(net_b->get_id()) +
": failed to build subgraph function for net a");
326 const auto bf_a = bf_res_a.get();
328 const auto bf_res_b = z3_utils::get_prefixed_subgraph_z3_function(gates_b, net_b,
"netlist_b_", ctx);
329 if (bf_res_b.is_error())
332 "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 "
333 + std::to_string(net_b->get_id()) +
": failed to build subgraph function for net b");
335 const auto bf_b = bf_res_b.get();
340 auto smt2_str = s.to_smt2();
342 if (query_res.is_error())
345 "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 "
346 + std::to_string(net_b->get_id()) +
": failed solver_check");
348 const auto check_result = query_res.get();
350 if (check_result.is_unsat())
355 if (check_result.is_unknown())
357 return OK(!fail_on_unknown);
366 if (netlist_a ==
nullptr)
368 return ERR(
"cannot compare nets: netlist_a is a nullptr!");
371 if (netlist_b ==
nullptr)
373 return ERR(
"cannot compare nets: netlist_b is a nullptr!");
376 if (net_a ==
nullptr)
378 return ERR(
"cannot compare nets: net_a is a nullptr!");
381 if (net_b ==
nullptr)
383 return ERR(
"cannot compare nets: net_b is a nullptr!");
389 const auto ff_replacements_a = restore_ff_replacements(netlist_a);
390 const auto ff_replacements_b = restore_ff_replacements(netlist_b);
395 auto setup_res = setup_solver(ctx, s, netlist_a, netlist_b, ff_replacements_a, ff_replacements_b);
396 if (setup_res.is_error())
399 "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");
402 return compare_nets_internal(ctx, s, net_a, net_b, comb_gates_a, comb_gates_b, fail_on_unknown, solver_timeout);
407 if (netlist_a ==
nullptr)
409 return ERR(
"cannot compare nets: netlist_a is a nullptr!");
412 if (netlist_b ==
nullptr)
414 return ERR(
"cannot compare nets: netlist_b is a nullptr!");
420 const auto ff_replacements_a = restore_ff_replacements(netlist_a);
421 const auto ff_replacements_b = restore_ff_replacements(netlist_b);
426 auto setup_res = setup_solver(ctx, s, netlist_a, netlist_b, ff_replacements_a, ff_replacements_b);
427 if (setup_res.is_error())
430 "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");
433 for (
const auto& [net_a, net_b] : nets)
435 if ((net_a ==
nullptr) && (net_b ==
nullptr))
440 if ((net_a ==
nullptr) || (net_b ==
nullptr))
446 auto comp_res = compare_nets_internal(ctx, s, net_a, net_b, comb_gates_a, comb_gates_b, fail_on_unknown, solver_timeout);
449 if (comp_res.is_error())
452 "cannot compare netlist a with ID " + std::to_string(netlist_a->
get_id()) +
" netlist b with ID " + std::to_string(netlist_b->
get_id())
453 +
": failed net comparison");
455 const auto eq = comp_res.get();
459 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());
475 const auto ff_replacements_a = restore_ff_replacements(netlist_a);
476 const auto ff_replacements_b = restore_ff_replacements(netlist_b);
479 std::unordered_map<std::string, Gate*> gate_name_to_gate_a;
480 std::unordered_map<std::string, Gate*> gate_name_to_gate_b;
482 for (
const auto& gate_a : seq_gates_a)
484 gate_name_to_gate_a[gate_a->get_name()] = gate_a;
486 if (
const auto& it = ff_replacements_a.find(gate_a); it != ff_replacements_a.end())
488 for (
const auto& s : it->second)
490 gate_name_to_gate_a[s] = gate_a;
494 for (
const auto& gate_b : seq_gates_b)
496 gate_name_to_gate_b[gate_b->get_name()] = gate_b;
498 if (
const auto& it = ff_replacements_b.find(gate_b); it != ff_replacements_b.end())
500 for (
const auto& s : it->second)
502 gate_name_to_gate_b[s] = gate_b;
507 for (
const auto& [gate_a_name, gate_a] : gate_name_to_gate_a)
509 if (
const auto gate_b_it = gate_name_to_gate_b.find(gate_a_name); gate_b_it == gate_name_to_gate_b.end())
512 "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!",
520 for (
const auto& [gate_b_name, gate_b] : gate_name_to_gate_b)
522 if (
const auto gate_a_it = gate_name_to_gate_a.find(gate_b_name); gate_a_it == gate_name_to_gate_a.end())
525 "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!",
534 log_info(
"z3_utils",
"Checking {} sequential gates for equality.", seq_gates_a.size());
539 auto setup_res = setup_solver(ctx, s, netlist_a, netlist_b, ff_replacements_a, ff_replacements_b);
540 if (setup_res.is_error())
543 "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");
546 std::set<std::pair<Net*, Net*>> to_compare;
552 auto all_out_pins = out_pins_a;
553 all_out_pins.insert(all_out_pins.end(), out_pins_b.begin(), out_pins_b.end());
555 for (
const auto& pin : all_out_pins)
557 auto it_a = std::find(out_pins_a.begin(), out_pins_a.end(), pin);
558 if (it_a == out_pins_a.end())
561 "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!",
568 auto it_b = std::find(out_pins_b.begin(), out_pins_b.end(), pin);
569 if (it_b == out_pins_b.end())
572 "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!",
582 to_compare.insert({net_a, net_b});
586 for (
const Gate* gate_a : seq_gates_a)
588 const Gate* gate_b = gate_name_to_gate_b.at(gate_a->get_name());
590 if (gate_a->get_type() != gate_b->
get_type())
593 "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. {}",
600 gate_a->get_type()->get_name(),
607 Net* net_a = gate_a->get_fan_in_net(pin);
610 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)
std::map< std::pair< u32, const GatePin * >, BooleanFunction > SubgraphFunctionCache
#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)