HAL  v4.5.0-257-ge24279b4e
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
verilog_writer.cpp
Go to the documentation of this file.
2 
6 #include "hal_core/netlist/net.h"
9 
10 #include <fstream>
11 #include <iomanip>
12 #include <sstream>
13 
14 namespace hal
15 {
16  Result<std::monostate> VerilogWriter::write(Netlist* netlist, const std::filesystem::path& file_path)
17  {
18  std::stringstream res_stream;
19 
20  if (netlist == nullptr)
21  {
22  return ERR("could not write netlist to Verilog file '" + file_path.string() + "': netlist is a 'nullptr'");
23  }
24 
25  if (file_path.empty())
26  {
27  return ERR("could not write netlist to Verilog file '" + file_path.string() + "': file path is empty");
28  }
29 
30  // get modules in hierarchical order (bottom-up)
31  std::vector<Module*> ordered_modules;
32  {
33  const std::vector<Module*> modules = netlist->get_modules();
34  std::unordered_set<Module*> modules_set(modules.begin(), modules.end());
35 
36  while (!modules_set.empty())
37  {
38  for (auto it = modules_set.begin(); it != modules_set.end();)
39  {
40  std::vector<Module*> submodules = (*it)->get_submodules();
41  if (submodules.empty() || std::all_of(submodules.begin(), submodules.end(), [modules_set](Module* submod) { return modules_set.find(submod) == modules_set.end(); }))
42  {
43  ordered_modules.push_back(*it);
44  it = modules_set.erase(it);
45  }
46  else
47  {
48  it++;
49  }
50  }
51  }
52 
53  assert(ordered_modules.back()->is_top_module() == true);
54  }
55 
56  std::unordered_map<const Module*, std::string> module_aliases;
57  std::unordered_map<std::string, u32> module_identifier_occurrences;
58  for (Module* mod : ordered_modules)
59  {
60  if (auto res = write_module_declaration(res_stream, mod, module_aliases, module_identifier_occurrences); res.is_error())
61  {
62  return ERR_APPEND(res.get_error(), "could not write netlist to Verilog file '" + file_path.string() + "': failed to write module declaration");
63  }
64  res_stream << std::endl;
65  }
66 
67  // write to file
68  std::ofstream file;
69  file.open(file_path.string(), std::ofstream::out);
70  if (!file.is_open())
71  {
72  return ERR("could not write netlist to Verilog file '" + file_path.string() + "': failed to open file");
73  }
74  file << "`timescale 1 ps/1 ps" << std::endl;
75  file << res_stream.str();
76  file.close();
77 
78  return OK({});
79  }
80 
81  Result<std::monostate> VerilogWriter::write_module_declaration(std::stringstream& res_stream,
82  const Module* module,
83  std::unordered_map<const Module*, std::string>& module_type_aliases,
84  std::unordered_map<std::string, u32>& module_type_occurrences) const
85  {
86  // deal with empty modules
87  if (module->get_gates(nullptr, true).empty() && !module->is_top_module())
88  {
89  return OK({});
90  }
91 
92  // deal with unspecified module type
93  std::string module_type = module->get_type();
94  if (module_type.empty())
95  {
96  module_type = module->get_name();
97  if (!module->is_top_module())
98  {
99  module_type += "_type";
100  }
101  }
102  module_type_aliases[module] = get_unique_alias(module_type_occurrences, module_type);
103 
104  if (const std::string& design_name = module->get_netlist()->get_design_name(); module_type_aliases.at(module) == "top_module" && !design_name.empty())
105  {
106  res_stream << "module " << escape(design_name);
107  }
108  else
109  {
110  res_stream << "module " << escape(module_type_aliases.at(module));
111  }
112 
113  std::unordered_map<const DataContainer*, std::string> aliases;
114  std::unordered_map<std::string, u32> identifier_occurrences;
115 
116  bool first_port = true;
117  std::stringstream tmp_stream;
118 
119  res_stream << "(";
120  for (const auto* pin : module->get_pins())
121  {
122  Net* net = pin->get_net();
123  if (first_port)
124  {
125  first_port = false;
126  }
127  else
128  {
129  res_stream << ",";
130  }
131 
132  aliases[net] = escape(get_unique_alias(identifier_occurrences, pin->get_name()));
133 
134  res_stream << aliases.at(net);
135  tmp_stream << " " << enum_to_string(pin->get_direction()) << " " << aliases.at(net) << ";" << std::endl;
136  }
137 
138  res_stream << ");" << std::endl;
139  res_stream << tmp_stream.str();
140 
141  {
142  // module parameters
143  for (const auto& [name, decl_and_value] : module->get_parameters())
144  {
145  const auto& [decl, value] = decl_and_value;
146  res_stream << " parameter " << escape(name) << " = ";
147  if (auto res = write_typed_parameter_value(res_stream, decl, value); res.is_error())
148  {
149  return ERR_APPEND(res.get_error(),
150  "could not write declaration of module '" + module->get_name() + "' with ID " + std::to_string(module->get_id()) + ": failed to write parameter value");
151  }
152  res_stream << ";" << std::endl;
153  }
154  }
155 
156  std::unordered_set<Net*> port_nets = module->get_input_nets();
157  std::unordered_set<Net*> output_nets_tmp = module->get_output_nets();
158  port_nets.reserve(output_nets_tmp.size());
159  port_nets.insert(output_nets_tmp.begin(), output_nets_tmp.end());
160 
161  for (Net* net : module->get_nets())
162  {
163  if (port_nets.find(net) != port_nets.end())
164  {
165  continue;
166  }
167 
168  if (aliases.find(net) == aliases.end())
169  {
170  auto net_alias = escape(get_unique_alias(identifier_occurrences, net->get_name()));
171  aliases[net] = net_alias;
172 
173  res_stream << " wire " << net_alias;
174 
175  if (net->is_vcc_net() && net->get_num_of_sources() == 0)
176  {
177  res_stream << " = 1'b1";
178  }
179  else if (net->is_gnd_net() && net->get_num_of_sources() == 0)
180  {
181  res_stream << " = 1'b0";
182  }
183 
184  res_stream << ";" << std::endl;
185  }
186  }
187 
188  // write gate instances
189  for (const Gate* gate : module->get_gates())
190  {
191  res_stream << std::endl;
192  if (auto res = write_gate_instance(res_stream, gate, aliases, identifier_occurrences); res.is_error())
193  {
194  return ERR_APPEND(res.get_error(),
195  "could not write declaration of module '" + module->get_name() + "' with ID " + std::to_string(module->get_id()) + ": failed to write gate '" + gate->get_name()
196  + "' with ID " + std::to_string(gate->get_id()));
197  }
198  }
199 
200  // write module instances
201  for (const Module* sub_module : module->get_submodules())
202  {
203  res_stream << std::endl;
204  if (auto res = write_module_instance(res_stream, sub_module, aliases, identifier_occurrences, module_type_aliases); res.is_error())
205  {
206  return ERR_APPEND(res.get_error(),
207  "could not write declaration of module '" + module->get_name() + "' with ID " + std::to_string(module->get_id()) + ": failed to write sub-module '"
208  + sub_module->get_name() + "' with ID " + std::to_string(sub_module->get_id()));
209  }
210  }
211 
212  res_stream << "endmodule" << std::endl;
213 
214  return OK({});
215  }
216 
217  Result<std::monostate> VerilogWriter::write_gate_instance(std::stringstream& res_stream,
218  const Gate* gate,
219  std::unordered_map<const DataContainer*, std::string>& aliases,
220  std::unordered_map<std::string, u32>& identifier_occurrences) const
221  {
222  const GateType* gate_type = gate->get_type();
223 
224  res_stream << " " << escape(gate_type->get_name());
225  if (auto res = write_parameter_assignments(res_stream, gate); res.is_error())
226  {
227  return ERR_APPEND(res.get_error(), "could not write gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id()) + ": failed to write parameter assignments");
228  }
229  aliases[gate] = escape(get_unique_alias(identifier_occurrences, gate->get_name()));
230  res_stream << " " << aliases.at(gate);
231 
232  // collect all endpoints (i.e., pins that are actually in use)
233  std::unordered_map<const GatePin*, const Net*> connections;
234  for (const Endpoint* ep : gate->get_fan_in_endpoints())
235  {
236  connections[ep->get_pin()] = ep->get_net();
237  }
238 
239  for (const Endpoint* ep : gate->get_fan_out_endpoints())
240  {
241  connections[ep->get_pin()] = ep->get_net();
242  }
243 
244  // extract pin assignments (in order, respecting pin groups)
245  std::vector<std::pair<std::string, std::vector<const Net*>>> pin_assignments;
246  for (const PinGroup<GatePin>* pin_group : gate_type->get_pin_groups())
247  {
248  std::vector<const Net*> nets;
249  for (const GatePin* pin : pin_group->get_pins())
250  {
251  if (const auto ep_it = connections.find(pin); ep_it != connections.end())
252  {
253  nets.push_back(ep_it->second);
254  }
255  else
256  {
257  nets.push_back(nullptr);
258  }
259  }
260 
261  // only append if at least one pin of the group is connected
262  if (std::any_of(nets.begin(), nets.end(), [](const Net* net) { return net != nullptr; }))
263  {
264  pin_assignments.push_back(std::make_pair(pin_group->get_name(), nets));
265  }
266  }
267 
268  if (auto res = write_pin_assignments(res_stream, pin_assignments, aliases); res.is_error())
269  {
270  return ERR_APPEND(res.get_error(), "could not write gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id()) + ": failed to write pin assignments");
271  }
272 
273  res_stream << ";" << std::endl;
274 
275  return OK({});
276  }
277 
278  Result<std::monostate> VerilogWriter::write_module_instance(std::stringstream& res_stream,
279  const Module* module,
280  std::unordered_map<const DataContainer*, std::string>& aliases,
281  std::unordered_map<std::string, u32>& identifier_occurrences,
282  std::unordered_map<const Module*, std::string>& module_type_aliases) const
283  {
284  res_stream << " " << escape(module_type_aliases.at(module));
285  if (auto res = write_parameter_assignments(res_stream, module); res.is_error())
286  {
287  return ERR_APPEND(res.get_error(), "could not write sub-module '" + module->get_name() + "' with ID " + std::to_string(module->get_id()) + ": failed to write parameter assignments");
288  }
289  aliases[module] = escape(get_unique_alias(identifier_occurrences, module->get_name()));
290  res_stream << " " << aliases.at(module);
291 
292  // extract port assignments
293  std::vector<std::pair<std::string, std::vector<const Net*>>> port_assignments;
294 
295  for (const ModulePin* pin : module->get_pins())
296  {
297  port_assignments.push_back(std::make_pair(pin->get_name(), std::vector<const Net*>({pin->get_net()})));
298  }
299 
300  if (auto res = write_pin_assignments(res_stream, port_assignments, aliases); res.is_error())
301  {
302  return ERR_APPEND(res.get_error(), "could not write sub-module '" + module->get_name() + "' with ID " + std::to_string(module->get_id()) + ": failed to write pin assignments");
303  }
304 
305  res_stream << ";" << std::endl;
306 
307  return OK({});
308  }
309 
310  Result<std::monostate> VerilogWriter::write_parameter_assignments(std::stringstream& res_stream, const DataContainer* container) const
311  {
312  bool first_parameter = true;
313  const auto open_or_separator = [&]() {
314  if (first_parameter)
315  {
316  res_stream << " #(" << std::endl;
317  first_parameter = false;
318  }
319  else
320  {
321  res_stream << "," << std::endl;
322  }
323  };
324 
325  for (const auto& [name, decl_and_value] : container->get_parameters())
326  {
327  const auto& [decl, value] = decl_and_value;
328  open_or_separator();
329  res_stream << " ." << escape(name) << "(";
330  if (auto res = write_typed_parameter_value(res_stream, decl, value); res.is_error())
331  {
332  return ERR_APPEND(res.get_error(), "could not write parameter assignments for '" + name + "'");
333  }
334  res_stream << ")";
335  }
336 
337  if (!first_parameter)
338  {
339  res_stream << std::endl << " )";
340  }
341 
342  return OK({});
343  }
344 
345  Result<std::monostate> VerilogWriter::write_pin_assignments(std::stringstream& res_stream,
346  const std::vector<std::pair<std::string, std::vector<const Net*>>>& pin_assignments,
347  std::unordered_map<const DataContainer*, std::string>& aliases) const
348  {
349  static u32 unused_signal_counter = 0;
350 
351  res_stream << " (" << std::endl;
352  bool first_pin = true;
353  for (const auto& [pin, nets] : pin_assignments)
354  {
355  if (first_pin)
356  {
357  first_pin = false;
358  }
359  else
360  {
361  res_stream << "," << std::endl;
362  }
363 
364  res_stream << " ." << escape(pin) << "(";
365  if (nets.size() > 1)
366  {
367  res_stream << "{" << std::endl << " ";
368  }
369 
370  bool first_net = true;
371  for (auto it = nets.begin(); it != nets.end(); it++)
372  {
373  const Net* net = *it;
374 
375  if (!first_net)
376  {
377  res_stream << "," << std::endl << " ";
378  }
379  first_net = false;
380 
381  if (net != nullptr)
382  {
383  if (const auto alias_it = aliases.find(net); alias_it != aliases.end())
384  {
385  res_stream << alias_it->second;
386  }
387  else
388  {
389  return ERR("could not write pin assignments: no alias for net '" + net->get_name() + "' with ID " + std::to_string(net->get_id()) + " found");
390  }
391  }
392  else
393  {
394  // unconnected pin of a group with at least one connection
395  res_stream << "HAL_UNUSED_SIGNAL_" + std::to_string(unused_signal_counter++);
396  }
397  }
398 
399  if (nets.size() > 1)
400  {
401  res_stream << std::endl << " }";
402  }
403 
404  res_stream << ")";
405  }
406 
407  res_stream << std::endl << " )";
408 
409  return OK({});
410  }
411 
412  Result<std::monostate> VerilogWriter::write_typed_parameter_value(std::stringstream& res_stream, const Parameter& decl, const std::string& value) const
413  {
414  switch (decl.get_type())
415  {
419  res_stream << value;
420  break;
421 
424  res_stream << "\"" << value << "\"";
425  break;
426 
428  res_stream << (value == "true" ? "1'b1" : "1'b0");
429  break;
430 
433  // value is "0b...", "0o...", or "0x..." — strip prefix and map to Verilog base notation
434  if (value.size() < 3 || value[0] != '0')
435  {
436  return ERR("could not write parameter '" + decl.get_name() + "': unexpected value format '" + value + "'");
437  }
438  const char base = static_cast<char>(std::tolower(static_cast<unsigned char>(value[1])));
439  const auto digits = value.substr(2);
440  char verilog_base;
441  switch (base)
442  {
443  case 'b': verilog_base = 'b'; break;
444  case 'o': verilog_base = 'o'; break;
445  case 'x': verilog_base = 'h'; break;
446  default:
447  return ERR("could not write parameter '" + decl.get_name() + "': unknown base '" + std::string(1, base) + "' in value '" + value + "'");
448  }
449  res_stream << decl.get_size() << "'" << verilog_base << digits;
450  break;
451  }
452  }
453 
454  return OK({});
455  }
456 
457  std::string VerilogWriter::get_unique_alias(std::unordered_map<std::string, u32>& name_occurrences, const std::string& name) const
458  {
459  name_occurrences[name]++;
460 
461  // if the name only appears once, we don't have to suffix it
462  if (name_occurrences[name] < 2)
463  {
464  return name;
465  }
466 
467  // otherwise, add a unique string to the name
468  return name + "__[" + std::to_string(name_occurrences[name]) + "]__";
469  }
470 
471  std::string VerilogWriter::escape(const std::string& s) const
472  {
473  if (s.empty())
474  {
475  return "";
476  }
477 
478  const char first = s.at(0);
479  if (!(first >= 'a' && first <= 'z') && !(first >= 'A' && first <= 'Z') && first != '_')
480  {
481  return "\\" + s + " ";
482  }
483  else if (std::any_of(s.begin(), s.end(), [](const char c) { return (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9') && c != '_' && c != '$'); }))
484  {
485  return "\\" + s + " ";
486  }
487 
488  return s;
489  }
490 } // namespace hal
const std::unordered_map< std::string, std::pair< Parameter, std::string > > & get_parameters() const
const std::unordered_set< Net * > & get_nets() const
Definition: module.cpp:512
std::vector< ModulePin * > get_pins(const std::function< bool(ModulePin *)> &filter=nullptr) const
Definition: module.cpp:880
bool is_top_module() const
Definition: module.cpp:321
const std::vector< Gate * > & get_gates() const
Definition: module.cpp:400
std::string get_name() const
Definition: module.cpp:88
const std::unordered_set< Net * > & get_input_nets() const
Definition: module.cpp:549
Netlist * get_netlist() const
Definition: module.cpp:326
std::vector< Module * > get_submodules(const std::function< bool(Module *)> &filter=nullptr, bool recursive=false) const
Definition: module.cpp:268
std::string get_type() const
Definition: module.cpp:107
const std::unordered_set< Net * > & get_output_nets() const
Definition: module.cpp:554
u32 get_id() const
Definition: module.cpp:83
const std::vector< Module * > & get_modules() const
Definition: netlist.cpp:626
const std::string & get_design_name() const
Definition: netlist.cpp:106
Result< std::monostate > write(Netlist *netlist, const std::filesystem::path &file_path) override
uint32_t u32
Definition: defines.h:41
#define ERR(message)
Definition: result.h:60
#define OK(...)
Definition: result.h:56
#define ERR_APPEND(prev_error, message)
Definition: result.h:64
const Module * module(const Gate *g, const NodeBoxes &boxes)
Definition: defines.h:45
std::string enum_to_string(T e)
Definition: enums.h:53
Net * net
std::string name