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