HAL  v4.5.0-133-g64838ea8d
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
preprocessing.cpp
Go to the documentation of this file.
3 #include "hal_core/netlist/net.h"
6 
7 #include <functional>
8 
9 namespace hal
10 {
11  namespace xilinx_toolbox
12  {
13  namespace
14  {
20  std::vector<Gate*> gates_in_scope(const Netlist* nl, const std::vector<Gate*>& gates, const std::function<bool(const Gate*)>& type_filter)
21  {
22  if (gates.empty())
23  {
24  return nl->get_gates(type_filter);
25  }
26 
27  std::vector<Gate*> res;
28  for (auto* g : gates)
29  {
30  if (type_filter(g))
31  {
32  res.push_back(g);
33  }
34  }
35  return res;
36  }
37  } // namespace
38 
39  Result<u32> split_luts(Netlist* nl, const std::vector<Gate*>& gates)
40  {
41  u32 deleted_gates = 0;
42  u32 new_gates = 0;
43  std::vector<Gate*> to_delete;
44 
45  const auto lut6_type = nl->get_gate_library()->get_gate_type_by_name("LUT6");
46  const auto lut5_type = nl->get_gate_library()->get_gate_type_by_name("LUT5");
47 
48  const auto lut6_2_gates = gates_in_scope(nl, gates, [](const Gate* g) { return g->get_type()->get_name() == "LUT6_2"; });
49 
50  for (const auto& g : lut6_2_gates)
51  {
52  auto* o5 = g->get_fan_out_net("O5");
53  auto* o6 = g->get_fan_out_net("O6");
54  const auto* i5 = g->get_fan_in_net("I5");
55 
56  const auto init_get_res = g->get_init_data();
57  if (init_get_res.is_error())
58  {
59  log_warning("xilinx_toolbox", "could not get INIT string of gate '{}' with ID {}, skipping this gate.", g->get_name(), g->get_id());
60  continue;
61  }
62  auto init = init_get_res.get().front();
63  if (init.length() != 16)
64  {
65  log_warning("xilinx_toolbox", "INIT string '{}' has length {}, expected 16.", init, init.length());
66  continue;
67  }
68 
69  // 'O5' or 'O6' may be entirely unconnected, which is the common case this function is meant to handle
70  if (o5 != nullptr && o5->get_num_of_destinations() > 0)
71  {
72  // create LUT5
73  auto* lut5 = nl->create_gate(lut5_type, g->get_name() + "_split_O5");
74  lut5->set_data("xilinx_preprocessing_information", "original_init", "string", init);
75 
76  auto init_O5 = init.substr(8, 8);
77  if (lut5->set_init_data({init_O5}).is_error())
78  {
79  log_warning("xilinx_toolbox", "could not set INIT string of gate '{}' with ID {}, skipping this gate.", lut5->get_name(), lut5->get_id());
80  }
81 
82  if (auto* mod = g->get_module(); !mod->is_top_module())
83  {
84  mod->assign_gate(lut5);
85  }
86 
87  for (const auto& in_ep : g->get_fan_in_endpoints([](const Endpoint* ep) { return ep->get_pin()->get_name() != "I5"; }))
88  {
89  in_ep->get_net()->add_destination(lut5, in_ep->get_pin()->get_name());
90  }
91 
92  new_gates++;
93  o5->add_source(lut5, "O");
94  }
95 
96  if (o6 != nullptr && o6->get_num_of_destinations() > 0)
97  {
98  // create LUT6
99  auto* lut6 = nl->create_gate(lut6_type, g->get_name() + "_split_O6");
100  lut6->set_data("xilinx_preprocessing_information", "original_init", "string", init);
101 
102  if (lut6->set_init_data({init}).is_error())
103  {
104  log_warning("xilinx_toolbox", "could not set INIT string of gate '{}' with ID {}, skipping this gate.", lut6->get_name(), lut6->get_id());
105  nl->delete_gate(lut6);
106  continue;
107  }
108 
109  if (auto* mod = g->get_module(); !mod->is_top_module())
110  {
111  mod->assign_gate(lut6);
112  }
113 
114  for (const auto& in_ep : g->get_fan_in_endpoints())
115  {
116  in_ep->get_net()->add_destination(lut6, in_ep->get_pin()->get_name());
117  }
118 
119  new_gates++;
120  o6->add_source(lut6, "O");
121  }
122  to_delete.push_back(g);
123  }
124 
125  for (const auto& g : to_delete)
126  {
127  if (!nl->delete_gate(g))
128  {
129  return ERR("Cannot split luts for netlist with ID " + std::to_string(nl->get_id()) + ": Failed to delete gate " + g->get_name() + " with ID " + std::to_string(g->get_id()));
130  }
131  else
132  {
133  deleted_gates++;
134  }
135  }
136 
137  log_info("xilinx_toolbox", "split {} LUT6_2 gates into {} LUT6 and LUT5 gates", deleted_gates, new_gates);
138  return OK(deleted_gates);
139  }
140 
141  Result<u32> split_shift_registers(Netlist* nl, const std::vector<Gate*>& gates)
142  {
143  u32 deleted_gates = 0;
144  u32 new_gates = 0;
145  std::vector<Gate*> to_delete;
146 
147  GateType* ff_gt = nl->get_gate_library()->get_gate_type_by_name("FDE");
148  if (ff_gt == nullptr)
149  {
150  return ERR("could not find gate type 'FDE' in gate library");
151  }
152 
153  // iterate over all shift registers of type 'SRL16E' or 'SRLC32E'
154  for (const auto& gate : gates_in_scope(nl, gates, [](const Gate* g) { return g->get_type()->get_name() == "SRL16E" || g->get_type()->get_name() == "SRLC32E"; }))
155  {
156  auto control_pins = gate->get_type()->get_pins([](const auto& pg) { return (pg->get_direction() == PinDirection::input) && (pg->get_type() == PinType::control); });
157  if (control_pins.size() != 4 && gate->get_type()->get_name() == "SRLC16E" || control_pins.size() != 5 && gate->get_type()->get_name() == "SRLC32E")
158  {
159  return ERR("invalid number of control pins");
160  }
161 
162  if (gate->get_type()->get_name() == "SRL16E")
163  {
164  std::sort(control_pins.begin(), control_pins.end(), [](const auto& p1, const auto& p2) {
165  const u32 idx1 = std::stoull(p1->get_name().substr(1)); // Ai
166  const u32 idx2 = std::stoull(p2->get_name().substr(1)); // Ai
167 
168  return idx1 < idx2;
169  });
170  }
171  else
172  {
173  std::sort(control_pins.begin(), control_pins.end(), [](const auto& p1, const auto& p2) {
174  const u32 idx1 = std::stoull(p1->get_name().substr(2)); // A(i)
175  const u32 idx2 = std::stoull(p2->get_name().substr(2)); // A(i)
176 
177  return idx1 < idx2;
178  });
179  }
180 
181  u32 select_value = 0;
182  for (u32 idx = 0; idx < control_pins.size(); idx++)
183  {
184  const Net* cn = gate->get_fan_in_net(control_pins.at(idx));
185 
186  if (cn == nullptr)
187  {
188  log_warning("xilinx_toolbox", "control net at pin '{}' of gate '{}' with ID {} is 'nullptr'", control_pins.at(idx)->get_name(), gate->get_name(), gate->get_id());
189  continue;
190  }
191 
192  if (!cn->is_gnd_net() && !cn->is_vcc_net())
193  {
194  log_warning("xilinx_toolbox", "control net at pin '{}' of gate '{}' with ID {} is not constant", control_pins.at(idx)->get_name(), gate->get_name(), gate->get_id());
195  continue;
196  }
197 
198  select_value += (cn->is_gnd_net() ? 0 : 1) << idx;
199  }
200 
201  const auto clock_pins = gate->get_type()->get_pins([](const auto& p) { return (p->get_direction() == PinDirection::input) && (p->get_type() == PinType::clock); });
202  if (clock_pins.size() != 1)
203  {
204  return ERR("invalid number of input clock pins at shift register gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id()));
205  }
206 
207  const auto enable_pins = gate->get_type()->get_pins([](const auto& p) { return (p->get_direction() == PinDirection::input) && (p->get_type() == PinType::enable); });
208  if (enable_pins.size() != 1)
209  {
210  return ERR("invalid number of input enable pins at shift register gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id()));
211  }
212 
213  const auto data_pins = gate->get_type()->get_pins([](const auto& p) { return (p->get_direction() == PinDirection::input) && (p->get_type() == PinType::data); });
214  if (data_pins.size() != 1)
215  {
216  return ERR("invalid number of input data pins at shift register gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id()));
217  }
218 
219  Net* clk_in = gate->get_fan_in_net(clock_pins.front());
220  Net* enable_in = gate->get_fan_in_net(enable_pins.front());
221  Net* data_in = gate->get_fan_in_net(data_pins.front());
222  Net* state_out = nullptr;
223  Net* max_state_out = nullptr;
224 
225  if (gate->get_type()->get_name() == "SRLC32E")
226  {
227  state_out = gate->get_fan_out_net("Q");
228  max_state_out = gate->get_fan_out_net("Q31");
229  }
230  else
231  {
232  state_out = gate->get_fan_out_net("Q");
233  }
234 
235  if (clk_in == nullptr)
236  {
237  return ERR("no clock input net connected to shift register gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id()));
238  }
239 
240  if (enable_in == nullptr)
241  {
242  return ERR("no enable input net connected to shift register gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id()));
243  }
244 
245  if (data_in == nullptr)
246  {
247  return ERR("no data input net connected to shift register gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id()));
248  }
249 
250  if (state_out == nullptr && max_state_out == nullptr)
251  {
252  return ERR("no state output net connected to shift register gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id()));
253  }
254 
255  u32 register_size = 0;
256  std::vector<Gate*> flip_flops;
257  std::vector<Net*> state_nets;
258 
259  if (max_state_out != nullptr)
260  {
261  register_size = max_state_out->get_num_of_destinations() > 0 ? 31 : select_value;
262  }
263  else
264  {
265  register_size = select_value;
266  }
267 
268  for (u32 ff_idx = 0; ff_idx <= register_size; ff_idx++)
269  {
270  const std::string ff_name = gate->get_name() + "_split_ff_" + std::to_string(ff_idx);
271  Gate* new_gate = nl->create_gate(ff_gt, ff_name);
272  new_gates++;
273 
274  // keep the replacement within the module of the gate it replaces instead of the top module
275  if (auto* mod = gate->get_module(); !mod->is_top_module())
276  {
277  mod->assign_gate(new_gate);
278  }
279 
280  clk_in->add_destination(new_gate, "C");
281  enable_in->add_destination(new_gate, "CE");
282 
283  if (ff_idx == 0)
284  {
285  data_in->add_destination(new_gate, "D");
286  }
287  else
288  {
289  state_nets.back()->add_destination(new_gate, "D");
290  }
291 
292  if (ff_idx == select_value && state_out != nullptr)
293  {
294  state_out->add_source(new_gate, "Q");
295  state_nets.push_back(state_out);
296  }
297  else if (ff_idx == register_size && max_state_out != nullptr)
298  {
299  max_state_out->add_source(new_gate, "Q");
300  state_nets.push_back(max_state_out);
301  }
302  else
303  {
304  Net* new_net = nl->create_net(ff_name + "_out");
305  new_net->add_source(new_gate, "Q");
306  state_nets.push_back(new_net);
307  }
308  }
309 
310  to_delete.push_back(gate);
311  }
312 
313  for (const auto& g : to_delete)
314  {
315  if (!nl->delete_gate(g))
316  {
317  return ERR("Cannot split shift register primitives for netlist with ID " + std::to_string(nl->get_id()) + ": Failed to delete gate " + g->get_name() + " with ID "
318  + std::to_string(g->get_id()));
319  }
320  else
321  {
322  deleted_gates++;
323  }
324  }
325 
326  log_info("xilinx_toolbox", "split {} SRL16E/SRLC32E gates into {} flip-flops", deleted_gates, new_gates);
327 
328  return OK(deleted_gates);
329  }
330 
331  Result<u32> remove_no_load_wires(Netlist* nl, const std::vector<Net*>& nets)
332  {
333  if (nl == nullptr)
334  {
335  return ERR("cannot remove no load wires: netlist is a nullptr");
336  }
337 
338  // Vivado names a net that only exists to give an unused output pin something to drive 'NLW_<instance>_<pin>_UNCONNECTED'.
339  // When the hierarchy is flattened, the instance is a path, so the prefix either starts the name or follows a '/';
340  // a multi-bit no load wire is written as a vector and its bits carry a trailing index like '(2)' or '[2]'.
341  const auto is_no_load_wire = [](const Net* n) {
342  const std::string& name = n->get_name();
343  const std::string prefix = "NLW_";
344  const std::string suffix = "_UNCONNECTED";
345 
346  if (name.compare(0, prefix.size(), prefix) != 0 && name.find("/" + prefix) == std::string::npos)
347  {
348  return false;
349  }
350 
351  std::string::size_type end = name.size();
352  if (end > 0 && (name[end - 1] == ')' || name[end - 1] == ']'))
353  {
354  const auto open = name.find_last_of("([");
355  if (open == std::string::npos || open + 1 >= end - 1 || name.find_first_not_of("0123456789", open + 1) != end - 1)
356  {
357  return false;
358  }
359  end = open;
360  }
361 
362  return end > prefix.size() + suffix.size() && name.compare(end - suffix.size(), suffix.size(), suffix) == 0;
363  };
364 
365  std::vector<Net*> to_delete;
366  for (Net* n : nets.empty() ? nl->get_nets() : nets)
367  {
368  if (n == nullptr || n->get_netlist() != nl)
369  {
370  return ERR("cannot remove no load wires: a net of the scope does not belong to netlist with ID " + std::to_string(nl->get_id()));
371  }
372 
373  if (is_no_load_wire(n) && n->get_num_of_destinations() == 0 && !n->is_global_output_net())
374  {
375  to_delete.push_back(n);
376  }
377  }
378 
379  u32 deleted_nets = 0;
380  for (Net* n : to_delete)
381  {
382  const std::string name = n->get_name();
383  const u32 id = n->get_id();
384  if (!nl->delete_net(n))
385  {
386  return ERR("cannot remove no load wires: failed to delete net '" + name + "' with ID " + std::to_string(id));
387  }
388  deleted_nets++;
389  }
390 
391  log_info("xilinx_toolbox", "removed {} no load wires", deleted_nets);
392 
393  return OK(deleted_nets);
394  }
395  } // namespace xilinx_toolbox
396 } // namespace hal
bool set_data(const std::string &category, const std::string &key, const std::string &data_type, const std::string &value, const bool log_with_info_level=false)
Definition: gate.h:58
Net * get_fan_in_net(const std::string &pin_name) const
Definition: gate.cpp:617
GateType * get_type() const
Definition: gate.cpp:125
Net * get_fan_out_net(const std::string &pin_name) const
Definition: gate.cpp:761
const std::string & get_name() const
Definition: gate.cpp:105
Module * get_module() const
Definition: gate.cpp:174
const std::vector< Endpoint * > & get_fan_in_endpoints() const
Definition: gate.cpp:653
Result< std::vector< std::string > > get_init_data() const
Definition: gate.cpp:1034
u32 get_id() const
Definition: gate.cpp:95
GateType * get_gate_type_by_name(const std::string &name) const
const std::string & get_name() const
Definition: gate_type.cpp:64
bool is_top_module() const
Definition: module.cpp:321
Definition: net.h:58
Endpoint * add_destination(Gate *gate, const std::string &pin_name)
Definition: net.cpp:300
Endpoint * add_source(Gate *gate, const std::string &pin_name)
Definition: net.cpp:127
u32 get_num_of_destinations(const std::function< bool(Endpoint *ep)> &filter=nullptr) const
Definition: net.cpp:432
bool is_vcc_net() const
Definition: net.cpp:479
bool is_gnd_net() const
Definition: net.cpp:474
bool delete_net(Net *net)
Definition: netlist.cpp:345
Net * create_net(const u32 net_id, const std::string &name)
Definition: netlist.cpp:335
bool delete_gate(Gate *gate)
Definition: netlist.cpp:185
u32 get_id() const
Definition: netlist.cpp:77
Gate * create_gate(const u32 gate_id, GateType *gate_type, const std::string &name="", i32 x=-1, i32 y=-1)
Definition: netlist.cpp:175
const std::vector< Net * > & get_nets() const
Definition: netlist.cpp:366
const GateLibrary * get_gate_library() const
Definition: netlist.cpp:134
uint32_t u32
Definition: defines.h:41
#define log_info(channel,...)
Definition: log.h:70
#define log_warning(channel,...)
Definition: log.h:76
#define ERR(message)
Definition: result.h:60
#define OK(...)
Definition: result.h:56
Result< u32 > remove_no_load_wires(Netlist *nl, const std::vector< Net * > &nets={})
Remove the "no load wire" nets that Vivado writes for unused output pins.
Result< u32 > split_luts(Netlist *nl, const std::vector< Gate * > &gates={})
Split LUTs with two outputs into two separate LUT gates.
Result< u32 > split_shift_registers(Netlist *nl, const std::vector< Gate * > &gates={})
Split shift register primitives and replaces them with equivalent flip-flops chains.
Definition: defines.h:45
std::string name
This file contains all functions related to the HAL plugin API.