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;
123 for (
const auto& o_net : output_nets)
126 if (input_nets_res.is_error())
129 "cannot generate functional candidates for " + sc->
m_gates.front()->get_name() +
": failed to get subgrapg inputs for net " + o_net->get_name() +
" with ID "
130 + std::to_string(o_net->get_id()));
132 const auto input_net_count = input_nets_res.get().size();
134 if (input_net_count > max_inputs)
136 found_oversized_boolean_function =
true;
141 if (found_oversized_boolean_function)
149 return ERR(
"cannot generate functional candidates: candiate and context gates are not identical!");
155 return ERR_APPEND(res.get_error(),
"cannot generate functional candidates: failed to populate Boolean context cache");
168 const auto start_pre_processing = std::chrono::steady_clock::now();
170 const u64 duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_pre_processing).count();
172 if (new_candidates_res.is_error())
174 return ERR(new_candidates_res.get_error().get());
176 auto new_candidates = new_candidates_res.get();
178 std::map<std::string, std::map<std::string, std::map<std::string, std::map<std::string, u64>>>> new_entries = {};
179 new_entries[
"PRE_PROCESSING"][
enum_to_string(
type)][
"CANDIDATE_CREATION"][
"DURATION"] += duration;
180 new_entries[
"PRE_PROCESSING"][
enum_to_string(
type)][
"CANDIDATE_CREATION"][
"AMOUNT"] += new_candidates.size();
183 for (
auto& nc : new_candidates)
185 result.push_back(nc);
195 void work(std::vector<StructuralCandidate*>& structural_candidates_work_queque,
196 std::vector<std::unique_ptr<StructuralCandidate>>& structural_candidates,
197 std::vector<std::unique_ptr<StructuralCandidate>>& done_structural_candidates,
198 std::atomic<u32>& structural_workload,
199 std::deque<std::pair<std::unique_ptr<StructuralCandidate>, std::vector<FunctionalCandidate>>>& functional_candidates,
200 std::map<BaseCandidate*, std::vector<VerifiedCandidate>>& verified_candidates,
201 const Configuration& config,
203 std::vector<std::string>& status,
204 std::vector<std::string>& candidate_info,
205 std::mutex& thread_sync,
206 const u32 thread_idx)
208 std::map<BaseCandidate*, std::vector<VerifiedCandidate>> verified_candidates_cache;
210 const auto work_structural =
211 [&config, &structural_candidates_work_queque, &structural_candidates, &structural_workload, &functional_candidates, &stats, &
status, &candidate_info, &thread_sync, &thread_idx]()
214 if (!structural_candidates_work_queque.empty())
217 structural_candidates_work_queque.pop_back();
219 auto unique_sc = std::move(structural_candidates.back());
220 structural_candidates.pop_back();
222 thread_sync.unlock();
224 candidate_info.at(thread_idx) =
"S_" + unique_sc->base_candidate->m_gates.front()->get_name();
227 if (new_functional_candidates_res.is_error())
230 "failed to generate functional candidates for carry chain {}: {}",
231 unique_sc.get()->m_gates.front()->get_name(),
232 new_functional_candidates_res.get_error().get());
235 std::vector<FunctionalCandidate> new_functional_candidates = new_functional_candidates_res.get();
237 for (
const auto& fc : new_functional_candidates)
239 const auto _bfs = unique_sc.get()->ctx.get_boolean_functions(fc.m_output_nets, fc.m_control_mapping);
242 structural_workload -= 1;
243 if (!new_functional_candidates.empty())
246 status.at(thread_idx) =
"LOCKING STRUCTUAL at " + std::to_string(thread_idx);
247 functional_candidates.push_back({std::move(unique_sc), new_functional_candidates});
248 thread_sync.unlock();
257 const auto work_functional = [&config, &functional_candidates, &done_structural_candidates, &verified_candidates_cache, &candidate_info, &thread_sync, &thread_idx, &stats]() ->
bool {
259 if (!functional_candidates.empty())
261 auto& [sc, f_candidates] = functional_candidates.front();
263 auto sc_ptr = functional_candidates.front().first.get();
264 auto bc = sc->base_candidate;
265 auto fc = f_candidates.back();
267 if (f_candidates.size() == 1)
270 done_structural_candidates.push_back(std::move(sc));
271 sc_ptr = done_structural_candidates.back().get();
273 functional_candidates.pop_front();
278 f_candidates.pop_back();
280 thread_sync.unlock();
282 candidate_info.at(thread_idx) =
"F_" + fc.m_base_gates.front()->get_name();
284 const auto output_functions_res = sc_ptr->ctx.get_boolean_functions_const(fc.m_output_nets, fc.m_control_mapping);
285 if (output_functions_res.is_error())
287 log_error(
"module_identification",
"cannot check candidate: failed to get Boolean output functions before check.\n {}", output_functions_res.get_error().get());
289 const auto output_functions = output_functions_res.get();
291 const auto res = fc.check(output_functions, config.m_known_registers);
295 "failed to check current overaching candidate at carry chain {} of type {}:\n{}",
296 fc.m_gates.front()->get_name(),
298 res.get_error().get());
301 stats.add_stat(bc, fc);
304 vc.m_base_gates = bc->m_gates;
306 if (vc.is_verified())
308 verified_candidates_cache[bc].push_back(vc);
316 status.at(thread_idx) =
"INIT";
320 candidate_info.at(thread_idx) =
"NONE";
321 status.at(thread_idx) =
"LOCKED";
325 if ((structural_workload == 0) && functional_candidates.empty())
327 for (
auto [bc, vc] : verified_candidates_cache)
329 verified_candidates.at(bc).insert(verified_candidates.at(bc).end(), vc.begin(), vc.end());
331 thread_sync.unlock();
333 status.at(thread_idx) =
"FINISHED";
339 status.at(thread_idx) =
"RUNNING STRUCTURAL";
340 if (work_structural())
345 status.at(thread_idx) =
"RUNNING FUNCTIONAL";
346 if (work_functional())
353 status.at(thread_idx) =
"RUNNING FUNCTIONAL";
354 if (work_functional())
359 status.at(thread_idx) =
"RUNNING STRUCTURAL";
360 if (work_structural())
366 thread_sync.unlock();
368 status.at(thread_idx) =
"WAITING";
369 std::this_thread::sleep_for(std::chrono::milliseconds(200));
374 execute_on_structural_candidates(std::vector<std::pair<std::unique_ptr<BaseCandidate>, std::vector<std::unique_ptr<StructuralCandidate>>>>& base_to_structural_candidates,
375 const Configuration& config)
377 Netlist* nl = config.m_netlist;
380 std::map<BaseCandidate*, std::vector<VerifiedCandidate>> verified_candidates;
382 std::vector<StructuralCandidate*> structural_candidates_work_queque;
383 std::vector<std::unique_ptr<StructuralCandidate>> structural_candidates;
384 std::vector<std::unique_ptr<StructuralCandidate>> done_structural_candidates;
385 std::deque<std::pair<std::unique_ptr<StructuralCandidate>, std::vector<FunctionalCandidate>>> functional_candidates;
387 for (
auto& [base_cand, struct_cands] : base_to_structural_candidates)
390 verified_candidates.insert(std::make_pair(base_cand.get(), std::vector<VerifiedCandidate>()));
393 for (
auto& sc : struct_cands)
395 structural_candidates_work_queque.push_back(sc.get());
396 structural_candidates.push_back(std::move(sc));
400 std::atomic<u32> structural_workload = structural_candidates.size();
402 if (structural_candidates.empty())
404 Result(nl, std::vector<std::pair<BaseCandidate, VerifiedCandidate>>());
407 const u32 num_threads = std::min(config.m_max_thread_count, std::thread::hardware_concurrency() - 1);
409 log_info(
"module_identification",
"running with {} threads and {} multithreading priority", num_threads,
static_cast<int>(config.m_multithreading_priority));
411 auto stats = Statistics();
414 const auto gnd_res = NetlistModificationDecorator(*nl).create_gnd_net();
415 if (gnd_res.is_error())
417 log_error(
"module_identification",
"failed to create GND net: {}", gnd_res.get_error().get());
420 const auto vcc_res = NetlistModificationDecorator(*nl).create_vcc_net();
421 if (vcc_res.is_error())
423 log_error(
"module_identification",
"failed to create VCC net: {}", vcc_res.get_error().get());
427 log_info(
"module_identification",
"running checks for {} possible candidates...", structural_candidates.size());
428 std::vector<std::thread> workers;
429 std::vector<std::string>
status = {num_threads + 1,
"NOT STARTED"};
430 std::vector<std::string> candidate_info = {num_threads + 1,
"NONE"};
432 std::mutex thread_sync;
434 for (
u32 i = 0; i < num_threads; ++i)
436 workers.emplace_back([&, i]() {
437 return work(structural_candidates_work_queque,
438 structural_candidates,
439 done_structural_candidates,
441 functional_candidates,
452 #ifdef PRINT_THREAD_INFO
453 while ((structural_workload != 0) || !functional_candidates.empty())
455 std::cout <<
"WORKLOAD: " << structural_workload << std::endl;
457 std::map<std::string, u32> status_collection;
458 for (
u32 i = 0; i < num_threads; i++)
460 status_collection[
status.at(i)] += 1;
463 std::cout <<
"Thread status: " << std::endl;
464 for (
const auto& [sn, sc] : status_collection)
466 std::cout <<
"\t" << sn <<
": " << sc << std::endl;
469 std::map<std::string, u32> candidate_collection;
470 for (
u32 i = 0; i < num_threads; i++)
472 candidate_collection[candidate_info.at(i)] += 1;
475 if (candidate_collection.size() < 10)
477 std::cout <<
"Candidate Info: " << std::endl;
478 for (
const auto& [sn, sc] : candidate_collection)
480 std::cout <<
"\t" << sn <<
": " << sc << std::endl;
484 std::this_thread::sleep_for(std::chrono::milliseconds(500));
489 for (
auto& worker : workers)
501 std::map<std::string, u32> type_counter;
502 std::vector<std::vector<hal::Gate*>> known_registers = config.m_known_registers;
503 std::vector<std::pair<BaseCandidate, VerifiedCandidate>> result_input;
505 log_info(
"module_identification",
"Done with module identification, now doing post processing");
507 const auto start_post_processing = std::chrono::steady_clock::now();
508 for (
auto& [bc, vc] : verified_candidates)
510 auto post_processing_result =
post_processing(vc, nl, known_registers);
512 if (!post_processing_result.is_verified())
514 post_processing_result = VerifiedCandidate({}, {}, {}, {}, {}, {}, bc->m_gates, {}, {});
517 result_input.push_back(std::make_pair(*bc, post_processing_result));
519 const auto duration_post_processing = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_post_processing);
520 log_info(
"module_identification",
"Post processing took {} ms", duration_post_processing.count());
522 return OK(Result(nl, result_input, stats.to_json()));
531 "Executing on netlist {} / {} / {} and gatelib {}",
540 return ERR_APPEND(res.get_error(),
"cannot execute plugin: failed structural candidate generation");
543 auto base_to_structural_candidates = res.get();
544 return execute_on_structural_candidates(base_to_structural_candidates, config);
551 return ERR(
"cannot execute plugin: provided gates are empty");
554 std::unique_ptr<BaseCandidate> bc = std::make_unique<BaseCandidate>(gates);
556 std::vector<std::unique_ptr<StructuralCandidate>> structural_vector;
557 structural_vector.emplace_back(std::make_unique<StructuralCandidate>(bc.get(), gates));
558 std::vector<std::pair<std::unique_ptr<BaseCandidate>, std::vector<std::unique_ptr<StructuralCandidate>>>> base_to_structural_candidates;
559 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)));
561 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
Result< std::set< const Net * > > get_subgraph_function_inputs(const std::vector< const Gate * > &subgraph_gates, const Net *subgraph_output) 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.