HAL  v4.5.0-83-g30c8f0afc
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
verilator.cpp
Go to the documentation of this file.
1 
2 #include "verilator/verilator.h"
4 
9 #include "hal_core/netlist/net.h"
14 #include "hal_core/utilities/log.h"
17 #include "verilator/templates.h"
18 
19 #include <algorithm>
20 #include <filesystem>
21 #include <fstream>
22 #include <sstream>
23 #include <string>
24 #include <vector>
25 #include <regex>
26 
27 namespace hal
28 {
29  namespace verilator
30  {
32  {
33  const auto* gl = nl->get_gate_library();
34 
35  std::vector<std::pair<std::string, std::vector<std::string>>> lut_init_data;
36  for (const auto& lut_type : gl->get_gate_types([](const GateType* gt) { return gt->has_property(GateTypeProperty::c_lut); }))
37  {
38  InitComponent* init_component = lut_type.second->get_component_as<InitComponent>([](const GateTypeComponent* c) { return InitComponent::is_class_of(c); });
39  if (init_component == nullptr)
40  {
41  continue;
42  }
43 
44  lut_init_data.push_back(std::make_pair(init_component->get_init_category(), init_component->get_init_identifiers()));
45  }
46 
47  for (const auto& gate : nl->get_gates())
48  {
49  const auto& data_category = gl->get_gate_location_data_category();
50  const auto& data_identifier = gl->get_gate_location_data_identifiers();
51 
52  gate->delete_data(data_category, data_identifier.first);
53  gate->delete_data(data_category, data_identifier.second);
54 
55  // TODO: remove this from netlist
56  gate->delete_data("generic", "READ_MODE");
57  gate->delete_data("generic", "WRITE_MODE");
58 
59  if (!gate->get_type()->has_property(hal::GateTypeProperty::c_lut) && gate->get_type()->has_property(hal::GateTypeProperty::combinational))
60  {
61  for (const auto& [init_category, init_identifiers] : lut_init_data)
62  {
63  for (const auto& init_identifier : init_identifiers)
64  {
65  gate->delete_data(init_category, init_identifier);
66  }
67  }
68  }
69  }
70  }
71 
72  std::string escape_net_name(std::string net_name)
73  {
74  std::string new_net_name;
75  new_net_name = utils::replace(net_name, std::string("__"), std::string("___05F"));
76  new_net_name = utils::replace(new_net_name, std::string("'"), std::string("__027"));
77  new_net_name = utils::replace(new_net_name, std::string("("), std::string("__028"));
78  new_net_name = utils::replace(new_net_name, std::string(")"), std::string("__029"));
79  new_net_name = utils::replace(new_net_name, std::string("["), std::string("__05b"));
80  new_net_name = utils::replace(new_net_name, std::string("]"), std::string("__05d"));
81  return new_net_name;
82  }
83 
84  void cleanup_gate_type_names(const Netlist* nl, const std::filesystem::path& netlist_verilog)
85  {
86  std::unordered_map<GateType*,std::string> gate_type_names = converter::get_gate_gate_types_from_netlist(nl);
87  std::vector<std::pair<std::string,std::string> > replace_names;
88  for (auto it : gate_type_names)
89  if (it.first->get_name() != it.second)
90  replace_names.push_back(std::make_pair(it.first->get_name(), it.second));
91  if (replace_names.empty()) return;
92  std::ifstream ifstr;
93  ifstr.open(netlist_verilog);
94  std::string buffer;
95  std::string line;
96  while(ifstr)
97  {
98  std::getline(ifstr, line);
99  for (auto jt : replace_names)
100  {
101  size_t pos = 0;
102  for (;;) // loop until string not found (break)
103  {
104  pos = line.find(jt.first, pos);
105  if (pos == std::string::npos) break;
106  line.replace(pos, jt.first.size(), jt.second);
107  pos += jt.second.size();
108  }
109  }
110  buffer += line + "\n";
111  }
112  ifstr.close();
113  std::ofstream ofstr(netlist_verilog, std::ofstream::trunc);
114  ofstr << buffer;
115  ofstr.close();
116  }
117 
118  const int VerilatorEngine::s_command_lines = 3;
119 
121  {
122  mSimulationInput = simInput;
123 
124  //server_execution = false;
125  const std::vector<const Gate*> simulation_gates(simInput->get_gates().begin(), simInput->get_gates().end());
126  m_partial_netlist = netlist_utils::get_partial_netlist(simulation_gates.at(0)->get_netlist(), simulation_gates);
127 
129 
130  //m_simulator_dir = "/mnt/scratch/nils.albartus/simulation_100s_spi/";
132  m_design_name = m_partial_netlist->get_design_name();
133  m_compiler = get_engine_property("compiler");
134 
135  if (m_design_name.empty())
136  {
137  m_design_name = "dummy";
138  log_warning("verilator", "no design name for partial metlist, set to '{}'.", m_design_name);
139  }
140 
141  std::filesystem::path netlist_verilog = m_simulator_dir / std::string(m_partial_netlist->get_design_name() + ".v");
142 
143  // check for provided models and set path
144  std::filesystem::path provided_models = get_engine_property("provided_models");
145  if (provided_models.empty())
146  {
147  log_info("verilator", "the property 'provided_models' has not been set (use set_engine_property method to assign).");
148  }
149 
150  // get engine properties
151  if (!get_engine_property("num_of_threads").empty())
152  {
153  m_num_of_threads = std::stoi(get_engine_property("num_of_threads"));
154  }
155 
156  // prepare folder
157  if (!write_testbench_files(simInput))
158  {
159  log_error("verilator", "error, testbench files for verilog could not be written");
160  }
161 
163  {
164  log_error("verilator", "could not create gate definitions in verilog");
165  return false;
166  };
167 
168  netlist_writer_manager::write(m_partial_netlist.get(), netlist_verilog);
169 
170  cleanup_gate_type_names(m_partial_netlist.get(), netlist_verilog);
171 
172  return true; // everything ok
173  }
174 
175  bool VerilatorEngine::write_testbench_files(SimulationInput* simInput)
176  {
177  // write necessary parser files
178  if (!install_saleae_parser(m_simulator_dir.string()))
179  {
180  log_error("verilator", "could not install saleae parser in directory '{}'.", m_simulator_dir.string());
181  return false;
182  }
183 
184  // write / copy saleae binaries
185  std::string testbench_cpp = get_testbench_cpp_template();
186  testbench_cpp = utils::replace(testbench_cpp, std::string("<design_name>"), m_partial_netlist->get_design_name());
187 
188  // set callbacks in parser
189  std::stringstream callbacks;
190  for (const auto& input_net : simInput->get_input_nets())
191  {
192  std::string net_name = escape_net_name(input_net->get_name());
193  std::stringstream callback;
194  callback << "\n"
195  << " auto " << net_name << "_net = netMap.find(std::string(\"" << input_net->get_name() << "\"));\n"
196  << " if (" << net_name << "_net == netMap.end()) {\n"
197  << " std::cerr << \"no SALEAE input data found for net " << net_name << "\" << std::endl;\n"
198  << " }\n"
199  << " else {\n"
200  << " if (!sp.register_callback(" << net_name << "_net->second, set_simulation_value, &dut->" << net_name << ")) {\n"
201  << " std::cerr << \"cannot initialize callback for net " << net_name << "\" << std::endl;\n"
202  << " }\n"
203  << " }\n"
204  << std::endl;
205 
206  //callback << "printf(\"dut->" << net_name << ": %x\\n\", &dut->" << net_name << ");" << std::endl;
207 
208  callbacks << callback.str() << std::endl;
209  }
210 
211  testbench_cpp = utils::replace(testbench_cpp, std::string("<set_callbacks>"), callbacks.str());
212 
213  std::ofstream testbench_cpp_file(m_simulator_dir / "testbench.cpp");
214  testbench_cpp_file << testbench_cpp;
215  testbench_cpp_file.close();
216 
217  return true;
218  }
219 
221  {
222  return s_command_lines;
223  }
224 
225  std::vector<std::string> VerilatorEngine::commandLine(int lineIndex) const
226  {
227  // returns commands to be executed
228  switch (lineIndex)
229  {
230  case 0: {
231  std::vector<std::string> retval = {"verilator", // 0
232  "-I.", // 1
233  "-Wall", // 2
234  "-Wno-fatal", // 3
235  "-CFLAGS", // 4
236  "-O3", // 5
237  "--MMD",
238  "-trace",
239  // "--trace-threads",
240  // "1",
241  // "--threads",
242  // "1",
243  "-y",
244  "gate_definitions/",
245  "--Mdir",
246  "obj_dir",
247  "-O3",
248  "--noassert",
249  "--exe",
250  "-cc",
251  "-DSIM_VERILATOR",
252  "--trace-depth",
253  "2",
254  "--trace-underscore",
255  "--coverage-underscore",
256  "testbench.cpp",
257  "saleae_directory.cpp",
258  "saleae_parser.cpp",
259  "saleae_file.cpp",
260  m_design_name + ".v"};
261 
262 #if defined(__APPLE__)
263  if (strlen(path_to_verilator_executable))
264  {
265  retval[0] = path_to_verilator_executable + std::string("verilator");
266  }
267 #endif
268 
269  if (strlen(path_to_rapidjson_includedir))
270  {
271  retval[5] = retval[5] + " -I" + path_to_rapidjson_includedir;
272  }
273 
274  if (!m_compiler.empty())
275  {
276  retval.push_back("--compiler");
277  retval.push_back(m_compiler);
278  }
279 
280  return retval;
281  break;
282  }
283  case 1: {
284  return {"make", "-j" + std::to_string(m_num_of_threads), "--no-print-directory", "-C", "obj_dir/", "-f", "V" + m_design_name + ".mk"};
285  break;
286  }
287  case 2: {
288  return {"obj_dir/V" + m_design_name};
289  break;
290  }
291  default:
292  break;
293  }
294  return {};
295  }
296 
298  {
299  mRequireClockEvents = true;
300  }
301 
303  {
304  mResultFilename = std::string(m_simulator_dir / "waveform.vcd");
305  mState = Done;
306  return true;
307  }
308 
310  {
311  return new VerilatorEngine(mName);
312  }
313 
314  } // namespace verilator
315 } // namespace hal
T * get_component_as(const std::function< bool(const GateTypeComponent *)> &filter=nullptr)
const std::string & get_init_category() const
static bool is_class_of(const GateTypeComponent *component)
const std::vector< std::string > & get_init_identifiers() const
const std::vector< Gate * > & get_gates() const
Definition: netlist.cpp:204
const GateLibrary * get_gate_library() const
Definition: netlist.cpp:132
std::string get_working_directory() const
virtual std::string get_engine_property(const std::string &key)
SimulationInput * mSimulationInput
bool install_saleae_parser(std::string dirname) const
const std::unordered_set< const Net * > & get_input_nets() const
const std::unordered_set< const Gate * > & get_gates() const
SimulationEngine * createEngine() const override
Definition: verilator.cpp:309
VerilatorEngine(const std::string &nam)
Definition: verilator.cpp:297
bool setSimulationInput(SimulationInput *simInput) override
Definition: verilator.cpp:120
std::unique_ptr< Netlist > m_partial_netlist
Definition: verilator.h:169
std::vector< std::string > commandLine(int lineIndex) const override
Definition: verilator.cpp:225
int numberCommandLines() const override
Definition: verilator.cpp:220
std::filesystem::path m_simulator_dir
Definition: verilator.h:170
#define log_error(channel,...)
Definition: log.h:78
#define log_info(channel,...)
Definition: log.h:70
#define log_warning(channel,...)
Definition: log.h:76
std::unique_ptr< Netlist > get_partial_netlist(const Netlist *nl, const std::vector< const Gate * > &subgraph_gates)
bool write(Netlist *netlist, const ProgramArguments &args)
T replace(const T &str, const T &search, const T &replace)
Definition: utils.h:384
bool convert_gate_library_to_verilog(const Netlist *nl, const std::filesystem::path verilator_sim_path, const std::filesystem::path model_path="")
Definition: converter.cpp:29
std::unordered_map< GateType *, std::string > get_gate_gate_types_from_netlist(const Netlist *nl)
Definition: converter.cpp:144
std::string escape_net_name(std::string net_name)
Definition: verilator.cpp:72
void remove_unwanted_parameters_from_netlist(Netlist *nl)
Definition: verilator.cpp:31
const std::string get_testbench_cpp_template()
Definition: templates.h:37
void cleanup_gate_type_names(const Netlist *nl, const std::filesystem::path &netlist_verilog)
Definition: verilator.cpp:84
Definition: defines.h:45
const char * path_to_rapidjson_includedir
const char * path_to_verilator_executable