24 namespace module_identification
30 std::vector<std::pair<std::unique_ptr<BaseCandidate>, std::vector<std::unique_ptr<StructuralCandidate>>>> candidates;
32 if (gl_name ==
"ICE40ULTRA" || gl_name ==
"ICE40ULTRA_iPhone" || gl_name ==
"ICE40ULTRA_WITH_HAL_TYPES")
34 log_info(
"module_identification",
"generate arithmetic structures for {}", gl_name);
37 else if (gl_name ==
"XILINX_UNISIM_WITH_HAL_TYPES" || gl_name ==
"XILINX_UNISIM")
39 log_info(
"module_identification",
"generate arithmetic structures for {}", gl_name);
44 return ERR(
"arithmetic structure generation not available for gate_lib: " + gl_name);
48 std::vector<u32> filtered_indices;
49 std::vector<std::pair<std::unique_ptr<BaseCandidate>, std::vector<std::unique_ptr<StructuralCandidate>>>> filtered_candidates;
51 for (
u32 idx = 0; idx < candidates.size(); idx++)
53 auto& [base_candidate, structural_candidates] = candidates.at(idx);
54 bool filtered_out =
false;
58 const std::set<Gate*> base_candidate_set = {base_candidate->m_gates.begin(), base_candidate->m_gates.end()};
60 if (base_candidate_set == blocked_base_candidate)
69 const std::set<Gate*> already_classified_candidates_set = {already_classified_candidates.begin(), already_classified_candidates.end()};
71 for (
const auto& g : base_candidate->m_gates)
73 if (already_classified_candidates_set.find(g) != already_classified_candidates_set.end())
88 filtered_indices.push_back(idx);
92 log_info(
"module_identification",
"Filtered out already classified candidates. Left with {} / {} base candidates", filtered_indices.size(), candidates.size());
94 for (
const auto& idx : filtered_indices)
96 auto& [base_candidate, structural_candidates] = candidates.at(idx);
97 filtered_candidates.push_back({std::move(base_candidate), std::move(structural_candidates)});
100 for (
const auto& [bc, sc_vec] : filtered_candidates)
102 for (
const auto& sc : sc_vec)
105 sc->ctx.m_gates = sc->m_gates;
109 return OK(std::move(filtered_candidates));
114 std::vector<FunctionalCandidate> result;
120 const u32 max_inputs = 130;
121 bool found_oversized_boolean_function =
false;
125 auto cache_res = subgraph_dec.make_subgraph_function_cache(sc->
m_gates);
126 if (cache_res.is_error())
128 return ERR_APPEND(cache_res.get_error(),
"cannot generate functional candidates for " + sc->
m_gates.front()->get_name() +
": failed to create subgraph function cache");
130 auto cache = cache_res.get();
132 for (
const auto& o_net : output_nets)
134 const auto input_nets_res = subgraph_dec.get_subgraph_function_inputs(o_net, cache);
135 if (input_nets_res.is_error())
138 "cannot generate functional candidates for " + sc->
m_gates.front()->get_name() +
": failed to get subgrapg inputs for net " + o_net->get_name() +
" with ID "
139 + std::to_string(o_net->get_id()));
141 const auto input_net_count = input_nets_res.get().size();
143 if (input_net_count > max_inputs)
145 found_oversized_boolean_function =
true;
150 if (found_oversized_boolean_function)
158 return ERR(
"cannot generate functional candidates: candiate and context gates are not identical!");
164 return ERR_APPEND(res.get_error(),
"cannot generate functional candidates: failed to populate Boolean context cache");
177 const auto start_pre_processing = std::chrono::steady_clock::now();
179 const u64 duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_pre_processing).count();
181 if (new_candidates_res.is_error())
183 return ERR(new_candidates_res.get_error().get());
185 auto new_candidates = new_candidates_res.get();
187 std::map<std::string, std::map<std::string, std::map<std::string, std::map<std::string, u64>>>> new_entries = {};
188 new_entries[
"PRE_PROCESSING"][
enum_to_string(
type)][
"CANDIDATE_CREATION"][
"DURATION"] += duration;
189 new_entries[
"PRE_PROCESSING"][
enum_to_string(
type)][
"CANDIDATE_CREATION"][
"AMOUNT"] += new_candidates.size();
192 for (
auto& nc : new_candidates)
194 result.push_back(nc);
204 void work(std::vector<StructuralCandidate*>& structural_candidates_work_queque,
205 std::vector<std::unique_ptr<StructuralCandidate>>& structural_candidates,
206 std::vector<std::unique_ptr<StructuralCandidate>>& done_structural_candidates,
207 std::atomic<u32>& structural_workload,
208 std::deque<std::pair<std::unique_ptr<StructuralCandidate>, std::vector<FunctionalCandidate>>>& functional_candidates,
209 std::map<BaseCandidate*, std::vector<VerifiedCandidate>>& verified_candidates,
210 const Configuration& config,
212 std::vector<std::string>& status,
213 std::vector<std::string>& candidate_info,
214 std::mutex& thread_sync,
215 const u32 thread_idx)
217 std::map<BaseCandidate*, std::vector<VerifiedCandidate>> verified_candidates_cache;
219 const auto work_structural =
220 [&config, &structural_candidates_work_queque, &structural_candidates, &structural_workload, &functional_candidates, &stats, &
status, &candidate_info, &thread_sync, &thread_idx]()
223 if (!structural_candidates_work_queque.empty())
226 structural_candidates_work_queque.pop_back();
228 auto unique_sc = std::move(structural_candidates.back());
229 structural_candidates.pop_back();
231 thread_sync.unlock();
233 candidate_info.at(thread_idx) =
"S_" + unique_sc->base_candidate->m_gates.front()->get_name();
236 if (new_functional_candidates_res.is_error())
239 "failed to generate functional candidates for carry chain {}: {}",
240 unique_sc.get()->m_gates.front()->get_name(),
241 new_functional_candidates_res.get_error().get());
244 std::vector<FunctionalCandidate> new_functional_candidates = new_functional_candidates_res.get();
246 for (
const auto& fc : new_functional_candidates)
248 const auto _bfs = unique_sc.get()->ctx.get_boolean_functions(fc.m_output_nets, fc.m_control_mapping);
251 structural_workload -= 1;
252 if (!new_functional_candidates.empty())
255 status.at(thread_idx) =
"LOCKING STRUCTUAL at " + std::to_string(thread_idx);
256 functional_candidates.push_back({std::move(unique_sc), new_functional_candidates});
257 thread_sync.unlock();
266 const auto work_functional = [&config, &functional_candidates, &done_structural_candidates, &verified_candidates_cache, &candidate_info, &thread_sync, &thread_idx, &stats]() ->
bool {
268 if (!functional_candidates.empty())
270 auto& [sc, f_candidates] = functional_candidates.front();
272 auto sc_ptr = functional_candidates.front().first.get();
273 auto bc = sc->base_candidate;
274 auto fc = f_candidates.back();
276 if (f_candidates.size() == 1)
279 done_structural_candidates.push_back(std::move(sc));
280 sc_ptr = done_structural_candidates.back().get();
282 functional_candidates.pop_front();
287 f_candidates.pop_back();
289 thread_sync.unlock();
291 candidate_info.at(thread_idx) =
"F_" + fc.m_base_gates.front()->get_name();
293 const auto output_functions_res = sc_ptr->ctx.get_boolean_functions_const(fc.m_output_nets, fc.m_control_mapping);
294 if (output_functions_res.is_error())
296 log_error(
"module_identification",
"cannot check candidate: failed to get Boolean output functions before check.\n {}", output_functions_res.get_error().get());
298 const auto output_functions = output_functions_res.get();
300 const auto res = fc.check(output_functions, config.m_known_registers);
304 "failed to check current overaching candidate at carry chain {} of type {}:\n{}",
305 fc.m_gates.front()->get_name(),
307 res.get_error().get());
310 stats.add_stat(bc, fc);
313 vc.m_base_gates = bc->m_gates;
315 if (vc.is_verified())
317 verified_candidates_cache[bc].push_back(vc);
325 status.at(thread_idx) =
"INIT";
329 candidate_info.at(thread_idx) =
"NONE";
330 status.at(thread_idx) =
"LOCKED";
334 if ((structural_workload == 0) && functional_candidates.empty())
336 for (
auto [bc, vc] : verified_candidates_cache)
338 verified_candidates.at(bc).insert(verified_candidates.at(bc).end(), vc.begin(), vc.end());
340 thread_sync.unlock();
342 status.at(thread_idx) =
"FINISHED";
348 status.at(thread_idx) =
"RUNNING STRUCTURAL";
349 if (work_structural())
354 status.at(thread_idx) =
"RUNNING FUNCTIONAL";
355 if (work_functional())
362 status.at(thread_idx) =
"RUNNING FUNCTIONAL";
363 if (work_functional())
368 status.at(thread_idx) =
"RUNNING STRUCTURAL";
369 if (work_structural())
375 thread_sync.unlock();
377 status.at(thread_idx) =
"WAITING";
378 std::this_thread::sleep_for(std::chrono::milliseconds(200));
383 execute_on_structural_candidates(std::vector<std::pair<std::unique_ptr<BaseCandidate>, std::vector<std::unique_ptr<StructuralCandidate>>>>& base_to_structural_candidates,
384 const Configuration& config)
386 Netlist* nl = config.m_netlist;
389 std::map<BaseCandidate*, std::vector<VerifiedCandidate>> verified_candidates;
391 std::vector<StructuralCandidate*> structural_candidates_work_queque;
392 std::vector<std::unique_ptr<StructuralCandidate>> structural_candidates;
393 std::vector<std::unique_ptr<StructuralCandidate>> done_structural_candidates;
394 std::deque<std::pair<std::unique_ptr<StructuralCandidate>, std::vector<FunctionalCandidate>>> functional_candidates;
396 for (
auto& [base_cand, struct_cands] : base_to_structural_candidates)
399 verified_candidates.insert(std::make_pair(base_cand.get(), std::vector<VerifiedCandidate>()));
402 for (
auto& sc : struct_cands)
404 structural_candidates_work_queque.push_back(sc.get());
405 structural_candidates.push_back(std::move(sc));
409 std::atomic<u32> structural_workload = structural_candidates.size();
411 if (structural_candidates.empty())
413 Result(nl, std::vector<std::pair<BaseCandidate, VerifiedCandidate>>());
416 const u32 num_threads = std::min(config.m_max_thread_count, std::thread::hardware_concurrency() - 1);
418 log_info(
"module_identification",
"running with {} threads and {} multithreading priority", num_threads,
static_cast<int>(config.m_multithreading_priority));
420 auto stats = Statistics();
423 const auto gnd_res = NetlistModificationDecorator(*nl).create_gnd_net();
424 if (gnd_res.is_error())
426 log_error(
"module_identification",
"failed to create GND net: {}", gnd_res.get_error().get());
429 const auto vcc_res = NetlistModificationDecorator(*nl).create_vcc_net();
430 if (vcc_res.is_error())
432 log_error(
"module_identification",
"failed to create VCC net: {}", vcc_res.get_error().get());
436 log_info(
"module_identification",
"running checks for {} possible candidates...", structural_candidates.size());
437 std::vector<std::thread> workers;
438 std::vector<std::string>
status = {num_threads + 1,
"NOT STARTED"};
439 std::vector<std::string> candidate_info = {num_threads + 1,
"NONE"};
441 std::mutex thread_sync;
443 for (
u32 i = 0; i < num_threads; ++i)
445 workers.emplace_back([&, i]() {
446 return work(structural_candidates_work_queque,
447 structural_candidates,
448 done_structural_candidates,
450 functional_candidates,
461 #ifdef PRINT_THREAD_INFO
462 while ((structural_workload != 0) || !functional_candidates.empty())
464 std::cout <<
"WORKLOAD: " << structural_workload << std::endl;
466 std::map<std::string, u32> status_collection;
467 for (
u32 i = 0; i < num_threads; i++)
469 status_collection[
status.at(i)] += 1;
472 std::cout <<
"Thread status: " << std::endl;
473 for (
const auto& [sn, sc] : status_collection)
475 std::cout <<
"\t" << sn <<
": " << sc << std::endl;
478 std::map<std::string, u32> candidate_collection;
479 for (
u32 i = 0; i < num_threads; i++)
481 candidate_collection[candidate_info.at(i)] += 1;
484 if (candidate_collection.size() < 10)
486 std::cout <<
"Candidate Info: " << std::endl;
487 for (
const auto& [sn, sc] : candidate_collection)
489 std::cout <<
"\t" << sn <<
": " << sc << std::endl;
493 std::this_thread::sleep_for(std::chrono::milliseconds(500));
498 for (
auto& worker : workers)
510 std::map<std::string, u32> type_counter;
511 std::vector<std::vector<hal::Gate*>> known_registers = config.m_known_registers;
512 std::vector<std::pair<BaseCandidate, VerifiedCandidate>> result_input;
514 log_info(
"module_identification",
"Done with module identification, now doing post processing");
516 const auto start_post_processing = std::chrono::steady_clock::now();
517 for (
auto& [bc, vc] : verified_candidates)
519 auto post_processing_result =
post_processing(vc, nl, known_registers);
521 if (!post_processing_result.is_verified())
523 post_processing_result = VerifiedCandidate({}, {}, {}, {}, {}, {}, bc->m_gates, {}, {});
526 result_input.push_back(std::make_pair(*bc, post_processing_result));
528 const auto duration_post_processing = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_post_processing);
529 log_info(
"module_identification",
"Post processing took {} ms", duration_post_processing.count());
531 return OK(Result(nl, result_input, stats.to_json()));
540 "Executing on netlist {} / {} / {} and gatelib {}",
549 return ERR_APPEND(res.get_error(),
"cannot execute plugin: failed structural candidate generation");
552 auto base_to_structural_candidates = res.get();
553 return execute_on_structural_candidates(base_to_structural_candidates, config);
560 return ERR(
"cannot execute plugin: provided gates are empty");
563 std::unique_ptr<BaseCandidate> bc = std::make_unique<BaseCandidate>(gates);
565 std::vector<std::unique_ptr<StructuralCandidate>> structural_vector;
566 structural_vector.emplace_back(std::make_unique<StructuralCandidate>(bc.get(), gates));
567 std::vector<std::pair<std::unique_ptr<BaseCandidate>, std::vector<std::unique_ptr<StructuralCandidate>>>> base_to_structural_candidates;
568 base_to_structural_candidates.emplace_back(std::make_pair<std::unique_ptr<BaseCandidate>, std::vector<std::unique_ptr<StructuralCandidate>>>(std::move(bc), std::move(structural_vector)));
570 return execute_on_structural_candidates(base_to_structural_candidates, config);
This file contains the definition of the BaseCandidate class, which represents a base candidate in th...
This file contains the enumeration and constants for the candidate types used in the module identific...
std::string get_name() const
std::string get_name() const
Module * get_top_module() const
const std::string & get_device_name() const
const std::string & get_design_name() const
const GateLibrary * get_gate_library() const
static hal::Result< std::vector< FunctionalCandidate > > create_candidates(StructuralCandidate *sc, u32 max_control_signal, CandidateContext &ctx, module_identification::CandidateType candidate_type, const std::vector< std::vector< Gate * >> ®isters)
Create functional candidates from a structural candidate.
A class representing a structural candidate for module identification.
BaseCandidate * base_candidate
Pointer to the base candidate.
std::vector< Gate * > m_gates
Vector of gates that form the structural candidate.
CandidateContext ctx
Candidate context for the structural candidate.
This file contains the class and functions for handling functional candidates within the module ident...
#define log_error(channel,...)
#define log_info(channel,...)
#define ERR_APPEND(prev_error, message)
This file contains the function to generate structural candidates for Lattice iCE40 FPGAs.
This file contains the struct Configuration for module identification analysis.
This file contains the function declarations for the Module Identification plugin in hal.
std::vector< std::pair< std::unique_ptr< BaseCandidate >, std::vector< std::unique_ptr< StructuralCandidate > > > > generate_structural_candidates(const Netlist *nl)
Generate structural candidates for a given netlist.
std::vector< std::pair< std::unique_ptr< BaseCandidate >, std::vector< std::unique_ptr< StructuralCandidate > > > > generate_structural_candidates(const Netlist *nl)
Generate structural candidates for a given netlist.
hal::Result< std::vector< FunctionalCandidate > > generate_functional_candidates(StructuralCandidate *sc, const Configuration &config, Statistics &stats)
const std::vector< CandidateType > all_checkable_candidate_types
A list of all candidate types that are selectable to be checked.
hal::Result< std::vector< std::pair< std::unique_ptr< BaseCandidate >, std::vector< std::unique_ptr< StructuralCandidate > > > > > generate_structural_candidates(const Netlist *nl, const Configuration &config)
hal::Result< Result > execute(const Configuration &config)
Perform a full run of the module identification process on the given netlist with the provided config...
VerifiedCandidate post_processing(const std::vector< VerifiedCandidate > &verified_candidates, const Netlist *nl, const std::vector< std::vector< Gate * >> &dana_cache)
Performs post-processing on a set of verified candidates to identify the best candidate for module id...
hal::Result< Result > execute_on_gates(const std::vector< Gate * > &gates, const Configuration &config)
Perform a module identification run on the specified gates with the provided configuration.
std::vector< Net * > get_output_nets(const std::vector< Gate * > &gates, bool only_external_destinations=true)
Get output nets from a list of gates.
@ time_priority
Prioritize time efficiency in multithreading.
std::string enum_to_string(T e)
This file contains the structures and functions related to module identification results.
This file contains helper functions for module identification in the HAL framework.
This file contains the function to perform post-processing on verified candidates to identify the bes...
std::vector< Gate * > m_gates
The gates of the corresponding structural candidate.
hal::Result< std::monostate > populate_boolean_function_cache(const std::vector< Net * > nets)
Populates the boolean function cache for a set of nets.
Configuration for the module identification analysis.
Netlist * m_netlist
The netlist to be analyzed.
std::vector< std::set< Gate * > > m_blocked_base_candidates
Base candidates to block during analysis.
std::vector< module_identification::CandidateType > m_types_to_check
CandidateTypes that shall be checked. Defaults to all checkable candidate types.
std::vector< std::vector< Gate * > > m_known_registers
A vector handling possibly known registers.
u32 m_max_control_signals
Maximum number of control signals to be tested. Defaults to 3.
std::vector< std::vector< Gate * > > m_already_classified_candidates
Gates to ignore during processing.
void add_stat(const BaseCandidate *bc, const FunctionalCandidate &oc)
This file contains the class for defining and managing structural candidates within the module identi...
This file contains the function to generate structural candidates for Xilinx Unisim libraries.