11 #include "nlohmann/json.hpp"
23 using GateFunctionCache = std::map<std::pair<u32, const GatePin*>, BooleanFunction>;
25 Result<z3::expr> get_prefixed_function_of_net(
const std::vector<Gate*>& subgraph_gates,
27 const std::string& variable_prefix,
29 std::map<u32, z3::expr>& net_cache,
30 GateFunctionCache& gate_cache)
32 if (
const auto it = net_cache.find(
net->get_id()); it != net_cache.end())
34 return OK(it->second);
37 const std::vector<Endpoint*> sources =
net->get_sources();
40 if (sources.size() > 1)
42 return ERR(
"cannot get Boolean z3 function of net " +
net->get_name() +
" with ID " + std::to_string(
net->get_id()) +
": net is multi driven.");
48 const std::string var_name = variable_prefix + BooleanFunctionNetDecorator(*net).get_boolean_variable_name();
49 z3::expr ret = ctx.bv_const(var_name.c_str(), 1);
50 net_cache.insert({
net->get_id(), ret});
54 const Endpoint* src_ep = sources.front();
56 if (src_ep->get_gate() ==
nullptr)
58 return ERR(
"cannot get Boolean z3 function of net " +
net->get_name() +
" with ID " + std::to_string(
net->get_id()) +
": net source is null.");
61 const Gate* src = src_ep->get_gate();
64 if (std::find(subgraph_gates.begin(), subgraph_gates.end(), src) == subgraph_gates.end())
66 const std::string var_name = variable_prefix + BooleanFunctionNetDecorator(*net).get_boolean_variable_name();
67 z3::expr ret = ctx.bv_const(var_name.c_str(), 1);
68 net_cache.insert({
net->get_id(), ret});
73 if (
const auto it = gate_cache.find({src->get_id(), src_ep->get_pin()}); it == gate_cache.end())
75 const auto bf_res = src->get_boolean_function(src_ep->get_pin(),
true,
true);
76 if (bf_res.is_error())
79 "cannot get Boolean z3 function of net " +
net->get_name() +
" with ID " + std::to_string(
net->get_id()) +
": failed to get function of gate.");
83 gate_cache.insert({{src->get_id(), src_ep->get_pin()}, bf});
90 std::map<std::string, z3::expr> input_to_expr;
92 for (
const std::string& in_net_str : bf.get_variable_names())
95 if (in_net_res.is_error())
98 "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 "
101 const auto in_net = in_net_res.get();
103 const auto in_bf_res = get_prefixed_function_of_net(subgraph_gates, in_net, variable_prefix, ctx, net_cache, gate_cache);
104 if (in_bf_res.is_error())
109 const auto in_bf = in_bf_res.get();
111 input_to_expr.insert({in_net_str, in_bf});
115 net_cache.insert({
net->get_id(), ret});
120 Result<z3::expr> get_prefixed_subgraph_z3_function_internal(
const std::vector<Gate*>& subgraph_gates,
122 const std::string& variable_prefix,
124 std::map<u32, z3::expr>& net_cache,
125 GateFunctionCache& gate_cache)
128 if (subgraph_gates.empty())
130 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");
132 else if (std::any_of(subgraph_gates.begin(), subgraph_gates.end(), [](
const Gate* g) { return g == nullptr; }))
134 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'");
136 else if (
net ==
nullptr)
138 return ERR(
"could not get subgraph z3 function: net is a 'nullptr'");
140 else if (
net->get_num_of_sources() > 1)
142 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");
145 return get_prefixed_function_of_net(subgraph_gates,
net, variable_prefix, ctx, net_cache, gate_cache);
148 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)
150 std::map<u32, z3::expr> net_cache;
151 GateFunctionCache gate_cache;
153 return get_prefixed_subgraph_z3_function_internal(subgraph_gates, subgraph_output, variable_prefix, ctx, net_cache, gate_cache);
159 std::unordered_map<Gate*, std::vector<std::string>> restore_ff_replacements(
const Netlist* nl)
161 std::unordered_map<Gate*, std::vector<std::string>> replacements;
163 for (
auto& g : nl->get_gates())
165 if (g->has_data(
"preprocessing_information",
"replaced_gates"))
167 const auto& [_, s] = g->get_data(
"preprocessing_information",
"replaced_gates");
169 replacements.insert({g, replaced_gate_names});
179 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)
182 for (
const auto&
net : nets)
184 if (
net->get_sources().size() > 1)
186 return ERR(
"cannot replace net id for net " +
net->get_name() +
" with ID " + std::to_string(
net->get_id()) +
" because it is multi driven!");
189 const std::string var_name = variable_prefix + BooleanFunctionNetDecorator(*net).get_boolean_variable_name();
190 const auto sources =
net->get_sources([](
const Endpoint* ep) {
return (ep->get_gate() !=
nullptr) && (ep->get_gate()->get_type()->has_property(
GateTypeProperty::sequential)); });
191 if (
net->is_global_input_net())
193 const auto pin = nl->get_top_module()->get_pin_by_net(
net);
197 return ERR(
"cannot replace net id for net " +
net->get_name() +
" with ID " + std::to_string(
net->get_id())
198 +
": net is global input but unable to find pin at top module!");
201 const z3::expr new_expr = ctx.bv_const((
"GLOBAL_IN_" + pin->get_name()).c_str(), 1);
202 const z3::expr net_expr = ctx.bv_const(var_name.c_str(), 1);
204 s.add(net_expr == new_expr);
211 log_debug(
"z3_utils",
"No source found for net {}. Cannot replace net and will rename with net name {}!",
net->get_id(),
net->get_name());
213 const z3::expr new_expr = ctx.bv_const(
net->get_name().c_str(), 1);
214 const z3::expr net_expr = ctx.bv_const(var_name.c_str(), 1);
216 s.add(net_expr == new_expr);
220 const Endpoint* src = sources.front();
222 if (src->get_gate() ==
nullptr)
224 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());
226 const z3::expr new_expr = ctx.bv_const(
net->get_name().c_str(), 1);
227 const z3::expr net_expr = ctx.bv_const(var_name.c_str(), 1);
229 s.add(net_expr == new_expr);
233 const std::string src_gate_name = src->get_gate()->get_name();
234 const std::string src_pin = src->get_pin()->get_name();
235 const std::string
name = src_gate_name +
"_" + src_pin;
236 const z3::expr new_expr = ctx.bv_const(
name.c_str(), 1);
237 const z3::expr net_expr = ctx.bv_const(var_name.c_str(), 1);
239 s.add(net_expr == new_expr);
245 void add_replacements_equal_constraints(z3::context& ctx, z3::solver& s,
const std::unordered_map<Gate*, std::vector<std::string>>& replacements)
247 for (
const auto& [g, replaced_names] : replacements)
249 for (
const auto& ep : g->get_fan_out_endpoints())
251 const std::string g_name = g->get_name() +
"_" + ep->get_pin()->get_name();
252 const z3::expr g_expr = ctx.bv_const(g_name.c_str(), 1);
254 for (
const auto&
name : replaced_names)
256 const std::string replaced_name =
name +
"_" + ep->get_pin()->get_name();
257 const z3::expr replaced_expr = ctx.bv_const(replaced_name.c_str(), 1);
259 s.add(g_expr == replaced_expr);
267 Result<std::monostate> setup_solver(z3::context& ctx,
269 const Netlist* netlist_a,
270 const Netlist* netlist_b,
271 const std::unordered_map<
hal::Gate*, std::vector<std::string>>& ff_replacements_a,
272 const std::unordered_map<
hal::Gate*, std::vector<std::string>>& ff_replacements_b)
274 std::vector<Net*> sub_nets_a;
275 for (
const auto& n : netlist_a->get_nets())
277 const auto comb_sources =
278 n->get_sources([](
const Endpoint* ep) {
return (ep->get_gate() !=
nullptr) && (ep->get_gate()->get_type()->has_property(
GateTypeProperty::combinational)); });
279 if (comb_sources.empty())
281 sub_nets_a.push_back(n);
284 std::vector<Net*> sub_nets_b;
285 for (
const auto& n : netlist_b->get_nets())
287 const auto comb_sources =
288 n->get_sources([](
const Endpoint* ep) {
return (ep->get_gate() !=
nullptr) && (ep->get_gate()->get_type()->has_property(
GateTypeProperty::combinational)); });
289 if (comb_sources.empty())
291 sub_nets_b.push_back(n);
295 auto sub_a_res = substitute_net_ids(ctx, s, sub_nets_a,
"netlist_a_", netlist_a);
296 if (sub_a_res.is_error())
298 return ERR_APPEND(sub_a_res.get_error(),
"cannot compare netA" + std::to_string(netlist_a->get_id()));
300 auto sub_b_res = substitute_net_ids(ctx, s, sub_nets_b,
"netlist_b_", netlist_b);
301 if (sub_b_res.is_error())
303 return ERR_APPEND(sub_b_res.get_error(),
"cannot compare net A " + std::to_string(netlist_b->get_id()));
306 add_replacements_equal_constraints(ctx, s, ff_replacements_a);
307 add_replacements_equal_constraints(ctx, s, ff_replacements_b);
312 Result<bool> compare_nets_internal(z3::context& ctx,
316 const std::vector<Gate*>& gates_a,
317 const std::vector<Gate*>& gates_b,
318 const bool fail_on_unknown,
319 const u32 solver_timeout)
321 const auto bf_res_a = z3_utils::get_prefixed_subgraph_z3_function(gates_a, net_a,
"netlist_a_", ctx);
322 if (bf_res_a.is_error())
325 "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 "
326 + std::to_string(net_b->get_id()) +
": failed to build subgraph function for net a");
328 const auto bf_a = bf_res_a.get();
330 const auto bf_res_b = z3_utils::get_prefixed_subgraph_z3_function(gates_b, net_b,
"netlist_b_", ctx);
331 if (bf_res_b.is_error())
334 "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 "
335 + std::to_string(net_b->get_id()) +
": failed to build subgraph function for net b");
337 const auto bf_b = bf_res_b.get();
342 auto smt2_str = s.to_smt2();
344 if (query_res.is_error())
347 "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 "
348 + std::to_string(net_b->get_id()) +
": failed solver_check");
350 const auto check_result = query_res.get();
352 if (check_result.is_unsat())
357 if (check_result.is_unknown())
359 return OK(!fail_on_unknown);
368 if (netlist_a ==
nullptr)
370 return ERR(
"cannot compare nets: netlist_a is a nullptr!");
373 if (netlist_b ==
nullptr)
375 return ERR(
"cannot compare nets: netlist_b is a nullptr!");
378 if (net_a ==
nullptr)
380 return ERR(
"cannot compare nets: net_a is a nullptr!");
383 if (net_b ==
nullptr)
385 return ERR(
"cannot compare nets: net_b is a nullptr!");
391 const auto ff_replacements_a = restore_ff_replacements(netlist_a);
392 const auto ff_replacements_b = restore_ff_replacements(netlist_b);
397 auto setup_res = setup_solver(ctx, s, netlist_a, netlist_b, ff_replacements_a, ff_replacements_b);
398 if (setup_res.is_error())
401 "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");
404 return compare_nets_internal(ctx, s, net_a, net_b, comb_gates_a, comb_gates_b, fail_on_unknown, solver_timeout);
409 if (netlist_a ==
nullptr)
411 return ERR(
"cannot compare nets: netlist_a is a nullptr!");
414 if (netlist_b ==
nullptr)
416 return ERR(
"cannot compare nets: netlist_b is a nullptr!");
422 const auto ff_replacements_a = restore_ff_replacements(netlist_a);
423 const auto ff_replacements_b = restore_ff_replacements(netlist_b);
428 auto setup_res = setup_solver(ctx, s, netlist_a, netlist_b, ff_replacements_a, ff_replacements_b);
429 if (setup_res.is_error())
432 "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");
435 for (
const auto& [net_a, net_b] : nets)
437 if ((net_a ==
nullptr) && (net_b ==
nullptr))
442 if ((net_a ==
nullptr) || (net_b ==
nullptr))
448 auto comp_res = compare_nets_internal(ctx, s, net_a, net_b, comb_gates_a, comb_gates_b, fail_on_unknown, solver_timeout);
451 if (comp_res.is_error())
454 "cannot compare netlist a with ID " + std::to_string(netlist_a->
get_id()) +
" netlist b with ID " + std::to_string(netlist_b->
get_id())
455 +
": failed net comparison");
457 const auto eq = comp_res.get();
461 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());
477 const auto ff_replacements_a = restore_ff_replacements(netlist_a);
478 const auto ff_replacements_b = restore_ff_replacements(netlist_b);
481 std::unordered_map<std::string, Gate*> gate_name_to_gate_a;
482 std::unordered_map<std::string, Gate*> gate_name_to_gate_b;
484 for (
const auto& gate_a : seq_gates_a)
486 gate_name_to_gate_a[gate_a->get_name()] = gate_a;
488 if (
const auto& it = ff_replacements_a.find(gate_a); it != ff_replacements_a.end())
490 for (
const auto& s : it->second)
492 gate_name_to_gate_a[s] = gate_a;
496 for (
const auto& gate_b : seq_gates_b)
498 gate_name_to_gate_b[gate_b->get_name()] = gate_b;
500 if (
const auto& it = ff_replacements_b.find(gate_b); it != ff_replacements_b.end())
502 for (
const auto& s : it->second)
504 gate_name_to_gate_b[s] = gate_b;
509 for (
const auto& [gate_a_name, gate_a] : gate_name_to_gate_a)
511 if (
const auto gate_b_it = gate_name_to_gate_b.find(gate_a_name); gate_b_it == gate_name_to_gate_b.end())
514 "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!",
522 for (
const auto& [gate_b_name, gate_b] : gate_name_to_gate_b)
524 if (
const auto gate_a_it = gate_name_to_gate_a.find(gate_b_name); gate_a_it == gate_name_to_gate_a.end())
527 "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!",
536 log_info(
"z3_utils",
"Checking {} sequential gates for equality.", seq_gates_a.size());
541 auto setup_res = setup_solver(ctx, s, netlist_a, netlist_b, ff_replacements_a, ff_replacements_b);
542 if (setup_res.is_error())
545 "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");
548 std::set<std::pair<Net*, Net*>> to_compare;
554 auto all_out_pins = out_pins_a;
555 all_out_pins.insert(all_out_pins.end(), out_pins_b.begin(), out_pins_b.end());
557 for (
const auto& pin : all_out_pins)
559 auto it_a = std::find(out_pins_a.begin(), out_pins_a.end(), pin);
560 if (it_a == out_pins_a.end())
563 "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!",
570 auto it_b = std::find(out_pins_b.begin(), out_pins_b.end(), pin);
571 if (it_b == out_pins_b.end())
574 "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!",
584 to_compare.insert({net_a, net_b});
588 for (
const Gate* gate_a : seq_gates_a)
590 const Gate* gate_b = gate_name_to_gate_b.at(gate_a->get_name());
592 if (gate_a->get_type() != gate_b->
get_type())
595 "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. {}",
602 gate_a->get_type()->get_name(),
609 Net* net_a = gate_a->get_fan_in_net(pin);
612 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)