PandA-2024.02
SimulationTool.cpp
Go to the documentation of this file.
1 /*
2  *
3  * _/_/_/ _/_/ _/ _/ _/_/_/ _/_/
4  * _/ _/ _/ _/ _/_/ _/ _/ _/ _/ _/
5  * _/_/_/ _/_/_/_/ _/ _/_/ _/ _/ _/_/_/_/
6  * _/ _/ _/ _/ _/ _/ _/ _/ _/
7  * _/ _/ _/ _/ _/ _/_/_/ _/ _/
8  *
9  * ***********************************************
10  * PandA Project
11  * URL: http://panda.dei.polimi.it
12  * Politecnico di Milano - DEIB
13  * System Architectures Group
14  * ***********************************************
15  * Copyright (C) 2004-2024 Politecnico di Milano
16  *
17  * This file is part of the PandA framework.
18  *
19  * The PandA framework is free software; you can redistribute it and/or modify
20  * it under the terms of the GNU General Public License as published by
21  * the Free Software Foundation; either version 3 of the License, or
22  * (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License
30  * along with this program. If not, see <http://www.gnu.org/licenses/>.
31  *
32  */
41 #include "SimulationTool.hpp"
42 
43 #include "config_HAVE_ASSERTS.hpp"
44 #include "config_PANDA_DATA_INSTALLDIR.hpp"
45 
46 #include "Parameter.hpp"
47 #include "ToolManager.hpp"
48 #include "VIVADO_xsim_wrapper.hpp"
49 #include "VerilatorWrapper.hpp"
50 #include "compiler_wrapper.hpp"
51 #include "custom_set.hpp"
52 #include "fileIO.hpp"
53 #include "modelsimWrapper.hpp"
54 #include "string_manipulation.hpp"
56 #include "utility.hpp"
57 
58 #include <cmath>
59 #include <regex>
60 #include <string>
61 #include <thread>
62 #include <vector>
63 
65 {
66  if(str == "MODELSIM")
67  {
68  return SimulationTool::MODELSIM;
69  }
70  else if(str == "XSIM")
71  {
72  return SimulationTool::XSIM;
73  }
74  else if(str == "VERILATOR")
75  {
76  return SimulationTool::VERILATOR;
77  }
78  else
79  {
80  THROW_ERROR("Unknown simulator: " + str);
81  }
82  return SimulationTool::UNKNOWN;
83 }
84 
85 SimulationTool::SimulationTool(const ParameterConstRef& _Param, const std::string& _top_fname,
86  const std::string& _inc_dirs)
87  : Param(_Param),
88  debug_level(Param->getOption<int>(OPT_debug_level)),
89  output_level(Param->getOption<unsigned int>(OPT_output_level)),
90  top_fname(_top_fname),
91  inc_dirs(_inc_dirs)
92 {
93 }
94 
96 
98  const std::string& suffix, const std::string& top_fname,
99  const std::string& inc_dirs)
100 {
101  switch(type)
102  {
103  case UNKNOWN:
104  THROW_ERROR("Simulation tool not specified");
105  break;
106  case MODELSIM:
107  return SimulationToolRef(new modelsimWrapper(_Param, suffix, top_fname, inc_dirs));
108  break;
109  case XSIM:
110  return SimulationToolRef(new VIVADO_xsim_wrapper(_Param, suffix, top_fname, inc_dirs));
111  break;
112  case VERILATOR:
113  return SimulationToolRef(new VerilatorWrapper(_Param, suffix, top_fname, inc_dirs));
114  break;
115  default:
116  THROW_ERROR("Simulation tool currently not supported");
117  }
119  return SimulationToolRef();
120 }
121 
123 {
125 }
126 
127 void SimulationTool::Simulate(unsigned long long int& accum_cycles, unsigned long long& n_testcases)
128 {
129  if(generated_script.empty())
130  {
131  THROW_ERROR("Simulation script not yet generated");
132  }
133 
135  auto result_file = Param->getOption<std::string>(OPT_simulation_output);
136  if(std::filesystem::exists(result_file))
137  {
138  std::filesystem::remove_all(result_file);
139  }
140  auto profiling_result_file = Param->getOption<std::string>(OPT_profiling_output);
141  if(std::filesystem::exists(profiling_result_file))
142  {
143  std::filesystem::remove_all(profiling_result_file);
144  }
145  ToolManagerRef tool(new ToolManager(Param));
146  tool->configure(generated_script, "");
147  std::vector<std::string> parameters, input_files, output_files;
148  if(Param->isOption(OPT_testbench_argv))
149  {
150  const auto tb_argv = Param->getOption<std::string>(OPT_testbench_argv);
151  parameters.push_back(tb_argv);
152  }
153  tool->execute(parameters, input_files, output_files,
154  Param->getOption<std::string>(OPT_output_temporary_directory) + "/simulation_output", true);
155 
156  DetermineCycles(accum_cycles, n_testcases);
157 }
158 
159 void SimulationTool::DetermineCycles(unsigned long long int& accum_cycles, unsigned long long& n_testcases)
160 {
161  unsigned long long int num_cycles = 0;
162  unsigned long long i = 0;
163  const auto sim_period = 2.0l;
164  const auto result_file = Param->getOption<std::string>(OPT_simulation_output);
165  const auto profiling_result_file = Param->getOption<std::string>(OPT_profiling_output);
166  const auto discrepancy_enabled = Param->isOption(OPT_discrepancy) && Param->getOption<bool>(OPT_discrepancy);
167  const auto profiling_enabled = std::filesystem::exists(profiling_result_file);
168  if(!std::filesystem::exists(result_file))
169  {
170  THROW_ERROR("The simulation does not end correctly");
171  }
172 
173  std::ifstream res_file(result_file);
174  if(!res_file.is_open())
175  {
176  THROW_ERROR("Unable to open results file");
177  }
178  PRINT_OUT_MEX(OUTPUT_LEVEL_PEDANTIC, output_level, "File \"" + result_file + "\" opened");
179  std::string values;
180  std::getline(res_file, values);
181  if(values.empty())
182  {
183  THROW_ERROR("Result file was empty");
184  }
185  const auto sim_times = string_to_container<std::vector<std::string>>(values, ",");
186  for(const auto& start_end : sim_times)
187  {
188  if(start_end.back() == 'X')
189  {
190  if(discrepancy_enabled)
191  {
192  num_cycles = i = 1;
193  break;
194  }
195  THROW_ERROR("Simulation not terminated!");
196  }
197  else if(start_end.back() == 'A')
198  {
199  THROW_ERROR("Simulation terminated with abort call!");
200  }
201  if(!profiling_enabled)
202  {
203  const auto times = SplitString(start_end, "|");
204  THROW_ASSERT(times.size() == 2, "Unexpected simulation time format");
205  unsigned long long start_time = 0, end_time = 0;
206  if(!boost::conversion::try_lexical_convert<unsigned long long>(times.at(0), start_time) ||
207  !boost::conversion::try_lexical_convert<unsigned long long>(times.at(1), end_time))
208  {
209  THROW_ERROR("Unable to parse simulation time report: check simulator output for errors.");
210  }
211  THROW_ASSERT(end_time >= start_time, "Simulation went back in time");
212  const auto sim_time = static_cast<long double>(end_time - start_time);
213  const auto sim_cycles = static_cast<unsigned long long int>(std::ceil(sim_time / sim_period));
214  num_cycles += sim_cycles;
215  ++i;
217  "Run " << i << " execution time " << sim_cycles << " cycles;");
218  }
219  }
220 
221  if(profiling_enabled)
222  {
223  std::ifstream profiling_res_file(profiling_result_file.c_str());
224  if(!profiling_res_file.is_open())
225  {
226  THROW_ERROR("Profiling result file not correctly created");
227  }
228 
229  PRINT_OUT_MEX(OUTPUT_LEVEL_PEDANTIC, output_level, "File \"" + profiling_result_file + "\" opened");
230  std::getline(profiling_res_file, values);
231  const auto profile_times = string_to_container<std::vector<std::string>>(values, ",");
232  for(const auto& start_end : profile_times)
233  {
234  const auto times = SplitString(start_end, "|");
235  THROW_ASSERT(times.size() == 2, "Unexpected simulation time format");
236  unsigned long long start_time = 0, end_time = 0;
237  if(!boost::conversion::try_lexical_convert<unsigned long long>(times.at(0), start_time) ||
238  !boost::conversion::try_lexical_convert<unsigned long long>(times.at(1), end_time))
239  {
240  THROW_ERROR("Unable to parse simulation time report: check simulator output for errors.");
241  }
242  THROW_ASSERT(end_time >= start_time, "Profiling went back in time");
243  const auto sim_time = static_cast<long double>(end_time - start_time);
244  const auto sim_cycles = static_cast<unsigned long long int>(std::ceil(sim_time / sim_period));
245  num_cycles += sim_cycles;
246  ++i;
248  "Run " << i << " profiled execution time " << sim_cycles << " cycles;");
249  }
250  }
251 
252  std::getline(res_file, values);
253  if(values.back() == 'A')
254  {
255  THROW_ERROR("Co-simulation main aborted");
256  }
257  int cosim_retval;
258  try
259  {
260  cosim_retval = std::stoi(values);
261  }
262  catch(...)
263  {
264  THROW_ERROR("Co-simulation completed unexpectedly.");
265  }
266 
267  if(cosim_retval)
268  {
269  THROW_ERROR("Co-simulation main returned non-zero value: " + values);
270  }
271 
272  if(i == 0)
273  {
274  THROW_ERROR("Expected a number of cycles different from zero. Something wrong happened during the simulation!");
275  }
276  accum_cycles = num_cycles;
277  n_testcases = i;
278 }
279 
280 std::string SimulationTool::GenerateSimulationScript(const std::string& top_filename, std::list<std::string> file_list)
281 {
282  // Create the simulation script
283  generated_script = GetPath("./" + std::string("simulate_") + top_filename + std::string(".sh"));
284  std::ofstream script(generated_script);
285  std::filesystem::permissions(generated_script,
286  std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec |
287  std::filesystem::perms::others_exec,
289  script << "#!/bin/bash\n"
290  << "##########################################################\n"
291  << "# Automatically generated by the PandA framework #\n"
292  << "##########################################################\n"
293  << "# Simulation script for COMPONENT: " << top_filename << "\n"
294  << "set -e\n"
295  << "cd " << GetCurrentPath() << "\n"
296  << "if [ ! -z \"$APPDIR\" ]; then LD_LIBRARY_PATH=\"\"; fi\n";
297 
298  file_list.push_back(relocate_compiler_path(PANDA_DATA_INSTALLDIR) + "/panda/libmdpi/mdpi.c");
299 
300  const auto has_user_elf = Param->isOption(OPT_testbench_input_file) &&
301  starts_with(Param->getOption<std::string>(OPT_testbench_input_file), "elf:");
302  const auto default_compiler = Param->getOption<CompilerWrapper_CompilerTarget>(OPT_default_compiler);
303  const auto opt_set = Param->getOption<CompilerWrapper_OptimizationSet>(OPT_gcc_optimization_set);
304  const CompilerWrapperConstRef compiler_wrapper(new CompilerWrapper(Param, default_compiler, opt_set));
305  const auto sim_dir = Param->getOption<std::string>(OPT_output_directory) + "/simulation/";
306  auto compiler_env = std::regex_replace("\n" + compiler_wrapper->GetCompiler().gcc,
307  std::regex("([\\w\\d]+=(\".*\"|[^\\s]+))\\s*"), "export $1\n");
308  boost::replace_last(compiler_env, "\n", "\nCC=\"");
309  compiler_env += "\"";
310 
311  script << compiler_env << "\n";
312 
313  if(has_user_elf)
314  {
315  script << "SYS_ELF=\"" << Param->getOption<std::string>(OPT_testbench_input_file).substr(4) << "\"\n";
316  }
317  else
318  {
319  script << "SYS_ELF=\"" << sim_dir << "/testbench\"\n";
320  }
321 
322  script << "SIM_DIR=\"" << sim_dir << "\"\n"
323  << "OUT_LVL=\"" << Param->getOption<int>(OPT_output_level) << "\"\n\n"
324  << "### Do not edit below\n\n"
325  << "M_IPC_FILENAME=\"${SIM_DIR}/panda_sock\"\n";
326 
327  auto sim_cmd = GenerateScript(script, top_filename, file_list);
328  boost::replace_all(sim_cmd, "\"", "\\\"");
329  script
330  << "export M_IPC_SIM_CMD=\"" << sim_cmd << "; exit \\${PIPESTATUS[0]};\"\n\n"
331  << "if [ -f ${SYS_ELF} ]; then\n"
332  << " function get_class { readelf -h $1 | grep Class: | sed -E 's/.*Class:\\s*(\\w+)/\\1/'; }\n"
333  << " sys_elf_class=\"$(get_class ${SYS_ELF})\"\n"
334  << " driver_elf_class=\"$(get_class ${SIM_DIR}/libmdpi_driver.so)\"\n"
335  << " if [ \"${sys_elf_class}\" != \"${driver_elf_class}\" ]; then\n"
336  << " echo \"ERROR: Wrong system application ELF class: ${sys_elf_class} != ${driver_elf_class}\"; exit 1;\n"
337  << " fi\n"
338  << " echo \"Launch user testbench with args: $@\"\n ";
339  if(has_user_elf)
340  {
341  script << "LD_PRELOAD=${SIM_DIR}/libmdpi_driver.so ";
342  }
343  script << "${SYS_ELF} \"$@\" 2>&1 | tee ${SIM_DIR}/$(basename ${SYS_ELF}).log\n"
344  << " exit ${PIPESTATUS[0]}\n"
345  << "fi\n"
346  << "exit -1\n\n";
347 
348  return generated_script;
349 }
350 
351 std::string SimulationTool::GenerateLibraryBuildScript(std::ostream& script, const std::string& beh_dir,
352  std::string& beh_cflags) const
353 {
354  const auto default_compiler = Param->getOption<CompilerWrapper_CompilerTarget>(OPT_default_compiler);
355  const auto opt_set = Param->getOption<CompilerWrapper_OptimizationSet>(OPT_gcc_optimization_set);
356  const CompilerWrapperConstRef compiler_wrapper(new CompilerWrapper(Param, default_compiler, opt_set));
357 
358  const auto extra_compiler_flags = [&]() {
359  std::string flags = " -fwrapv -ffloat-store -flax-vector-conversions -msse2 -mfpmath=sse -fno-strict-aliasing "
360  "-D__builtin_bambu_time_start\\(\\)= -D__builtin_bambu_time_stop\\(\\)= -D__BAMBU_SIM__";
361  flags += " -isystem " + relocate_compiler_path(PANDA_DATA_INSTALLDIR) + "/panda/libmdpi/include";
362  if(!Param->isOption(OPT_input_format) ||
363  Param->getOption<Parameters_FileFormat>(OPT_input_format) == Parameters_FileFormat::FF_C)
364  {
365  flags += " -fexcess-precision=standard";
366  }
367  if(Param->isOption(OPT_gcc_optimizations))
368  {
369  const auto gcc_parameters = Param->getOption<CustomSet<std::string>>(OPT_gcc_optimizations);
370  if(gcc_parameters.find("tree-vectorize") != gcc_parameters.end())
371  {
372  boost::replace_all(flags, "-msse2", "");
373  flags += " -m32";
374  }
375  }
376  return flags;
377  }();
378  auto cflags = compiler_wrapper->GetCompilerParameters(extra_compiler_flags);
379  std::cmatch what;
380  std::string kill_printf;
381  if(std::regex_search(cflags.c_str(), what, std::regex("\\s*(\\-D'?printf[^=]*='?)'*")))
382  {
383  kill_printf.append(what[1].first, what[1].second);
384  cflags.erase(static_cast<size_t>(what[0].first - cflags.c_str()),
385  static_cast<size_t>(what[0].second - what[0].first));
386  }
387  beh_cflags += " -isystem " + relocate_compiler_path(PANDA_DATA_INSTALLDIR) + "/panda/libmdpi/include";
388  beh_cflags += " -D__M_IPC_FILENAME=\\\\\\\"${M_IPC_FILENAME}\\\\\\\"";
389  beh_cflags += " -D__M_OUT_LVL=${OUT_LVL}";
390  if(cflags.find("-m32") != std::string::npos)
391  {
392  beh_cflags += " -D__M32";
393  }
394  else if(cflags.find("-mx32") != std::string::npos)
395  {
396  beh_cflags += " -D__MX32";
397  }
398  else if(cflags.find("-m64") != std::string::npos)
399  {
400  beh_cflags += " -D__M64";
401  }
402  beh_cflags += " -O2";
403 
404  const auto m_top_fname = cxa_prefix_mangled(top_fname, "__m_");
405  const auto m_pp_top_fname = cxa_prefix_mangled(top_fname, "__m_pp_");
406  const auto srcs =
407  Param->getOption<Parameters_FileFormat>(OPT_input_format) != Parameters_FileFormat::FF_RAW ?
408  boost::replace_all_copy(Param->getOption<std::string>(OPT_input_file), STR_CST_string_separator, " ") :
409  "";
410  const std::string cosim_src = "${SIM_DIR}/" STR_CST_testbench_generation_basename ".c";
411  const auto pp_srcs = Param->isOption(OPT_pretty_print) ? Param->getOption<std::string>(OPT_pretty_print) : "";
412  const auto tb_srcs = [&]() {
413  std::string files;
414  if(Param->isOption(OPT_testbench_input_file))
415  {
416  const auto tb_files = Param->getOption<CustomSet<std::string>>(OPT_testbench_input_file);
417  if(starts_with(*tb_files.begin(), "elf:"))
418  {
419  return files;
420  }
421  for(const auto& filename : tb_files)
422  {
423  if(ends_with(filename, ".xml"))
424  {
425  files += cosim_src + " ";
426  }
427  else
428  {
429  files += filename + " ";
430  }
431  }
432  }
433  else if(Param->isOption(OPT_testbench_input_string))
434  {
435  files += cosim_src + " ";
436  }
437  if(Param->isOption(OPT_no_parse_files))
438  {
439  files +=
440  boost::replace_all_copy(Param->getOption<std::string>(OPT_no_parse_files), STR_CST_string_separator, " ") +
441  " ";
442  }
443  boost::trim(files);
444  return files;
445  }();
446  const auto tb_extra_cflags =
447  Param->isOption(OPT_tb_extra_gcc_options) ? Param->getOption<std::string>(OPT_tb_extra_gcc_options) : "";
448 
449  script << "make -C " << relocate_compiler_path(PANDA_DATA_INSTALLDIR) << "/panda/libmdpi \\\n"
450  << " SIM_DIR=\"${SIM_DIR}\" BEH_DIR=\"" << beh_dir << "\" \\\n"
451  << " TOP_FNAME=\"" << top_fname << "\" \\\n"
452  << " MTOP_FNAME=\"" << m_top_fname << "\" \\\n"
453  << " MPPTOP_FNAME=\"" << m_pp_top_fname << "\" \\\n"
454  << " CC=\"${CC}\" \\\n"
455  << " BEH_CC=\"${BEH_CC}\" \\\n"
456  << " CFLAGS=\"" << cflags << "\" \\\n"
457  << " BEH_CFLAGS=\"" << beh_cflags << "\" \\\n"
458  << " TB_CFLAGS=\"" << tb_extra_cflags << "\" \\\n"
459  << " SRCS=\"" << srcs << "\" \\\n"
460  << " COSIM_SRC=\"" << cosim_src << "\" \\\n"
461  << " PP_SRC=\"" << pp_srcs << "\" \\\n"
462  << " TB_SRCS=\"" << tb_srcs << "\" \\\n"
463  << " -j " << std::thread::hardware_concurrency() << " -f Makefile.mk\n\n";
464 
465  return cflags;
466 }
467 
469 {
470 }
int times(int x, int y)
Definition: operations.c:3
Main class for wrapping Verilator.
#define STR_CST_string_separator
The character used to separate concatenated string.
Definition: utility.hpp:61
std::string filename
const std::vector< std::string > SplitString(const std::string &input, const std::string &separators)
Function which splits a string into tokens.
Main class for wrapping VIVADO XSIM Xilinx.
const ParameterConstRef Param
class containing all the parameters
STD include.
virtual std::string GenerateSimulationScript(const std::string &top_filename, std::list< std::string > file_list)
Generates the proper simulation script.
CompilerWrapper_OptimizationSet
Possible optimization sets.
Auxiliary methods for manipulating string.
virtual std::string GenerateScript(std::ostream &script, const std::string &top_filename, const std::list< std::string > &file_list)=0
Performs the actual writing.
void configure(const std::string &_tool_, const std::string &setupscr, const std::string &_host_="", const std::string &_remote_path_="", bool force_remote=false)
Configuration of the tool.
static SimulationToolRef CreateSimulationTool(type_t type, const ParameterConstRef &Param, const std::string &suffix, const std::string &top_fname, const std::string &inc_dirs)
Factory method.
bool starts_with(const std::string &str, const std::string &pattern)
virtual ~SimulationTool()
Destructor.
void DetermineCycles(unsigned long long int &accum_cycles, unsigned long long &n_testcases)
Determines the average number of cycles for the simulation(s)
Wrapper to Verilator simulator.
std::string GenerateLibraryBuildScript(std::ostream &script, const std::string &libtb_filename, std::string &beh_cflags) const
refcount< SimulationTool > SimulationToolRef
refcount definition of the class
redefinition of set to manage ordered/unordered structures
utility function used to read files.
CompilerWrapper_CompilerTarget
target of the compiler
Wrapper to modelsim.
static type_t to_sim_type(const std::string &str)
bool ends_with(const std::string &str, const std::string &pattern)
This file collects some utility functions and macros.
#define THROW_ERROR(str_expr)
helper function used to throw an error in a standard way
Definition: exceptions.hpp:263
std::string generated_script
generated script
constants used in testbench generation
SimulationTool(const ParameterConstRef &Param, const std::string &top_fname, const std::string &inc_dirs)
Constructor.
void add(int accelnum, int startidx, int endidx)
Definition: add.c:11
Class to manage a wrapped tool.
#define OUTPUT_LEVEL_PEDANTIC
verbose debugging print is performed.
std::string GetPath(std::filesystem::path path)
Definition: fileIO.hpp:140
int debug_level
debug level of the class
unsigned int output_level
verbosity level of the class
Abstract class for a generic simulation tool.
virtual void CheckExecution()
Checks if the current specification can be executed or not.
Template borrowed from the ANTLR library by Terence Parr (http://www.jGuru.com - Software rights: htt...
Definition: refcount.hpp:94
virtual void Simulate(unsigned long long &accum_cycles, unsigned long long &n_testcases)
Performs the simulation and returns the number of cycles.
char str[25]
Definition: fixedptc.c:8
Parameters_FileFormat
File formats.
Definition: Parameter.hpp:261
Wrapper to XSIM by XILINX VIVADO.
this class is used to manage the command-line or XML options.
std::string GetCurrentPath()
Definition: fileIO.hpp:123
Main class for wrapping the frontend compiler.
enum { UNKNOWN=0, MODELSIM, XSIM, VERILATOR } type_t
supported synthesis tools
#define PRINT_OUT_MEX(profLevel, curprofLevel, mex)
#define OUTPUT_LEVEL_VERBOSE
verbose debugging print is performed.
std::string relocate_compiler_path(const std::string &path, bool resolve_path=false)
Definition: fileIO.hpp:149
const std::string top_fname
std::string cxa_prefix_mangled(const std::string &signature, const std::string &prefix)
int execute(const std::vector< std::string > &parameters, const std::vector< std::string > &input_files, const std::vector< std::string > &output_files=std::vector< std::string >(), const std::string &log_file=std::string(), bool permissive=false)
Execute the tool.
const std::string inc_dirs
comma separated list of include dirs
virtual void Clean() const
Remove files created during simulation FIXME: this should become pure virtual.
#define STR_CST_testbench_generation_basename
The basename of the testbench files.
#define THROW_ASSERT(cond, str_expr)
helper function used to check an assert and if needed to throw an error in a standard way ...
Definition: exceptions.hpp:289
Implementation of the wrapper to Gcc for C sources.

Generated on Mon Feb 12 2024 13:02:56 for PandA-2024.02 by doxygen 1.8.13