scc  2024.06
SystemC components library
configurer.cpp
1 /*******************************************************************************
2  * Copyright 2017-2022 MINRES Technologies GmbH
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *******************************************************************************/
16 
17 #include "configurer.h"
18 #include "rapidjson/document.h"
19 #include "rapidjson/error/en.h"
20 #include "report.h"
21 #include <cci_configuration>
22 #include <cstring>
23 #include <fmt/format.h>
24 #include <fstream>
25 #include <rapidjson/istreamwrapper.h>
26 #include <rapidjson/ostreamwrapper.h>
27 #include <rapidjson/prettywriter.h>
28 #include <unordered_map>
29 #ifdef HAS_YAMPCPP
30 #include <yaml-cpp/exceptions.h>
31 #include <yaml-cpp/node/parse.h>
32 #include <yaml-cpp/yaml.h>
33 
34 namespace {
35 template <typename T> struct optional {
36  T val{};
37  bool initialized{false};
38  optional& operator=(T&& val) {
39  this->val = std::move(val);
40  initialized = true;
41  return *this;
42  }
43  operator bool() const { return initialized; }
44  T value() { return val; }
45 };
46 } // namespace
47 
48 namespace YAML {
49 template <typename T> struct as_if<T, optional<T>> {
50  explicit as_if(const YAML::Node& node_)
51  : node(node_) {}
52  const YAML::Node& node;
53  const optional<T> operator()() const {
54  optional<T> val;
55  T t;
56  if(node.m_pNode && YAML::convert<T>::decode(node, t))
57  val = std::move(t);
58  return val;
59  }
60 };
61 
62 // There is already a std::string partial specialisation, so we need a full specialisation here
63 template <> struct as_if<std::string, optional<std::string>> {
64  explicit as_if(const YAML::Node& node_)
65  : node(node_) {}
66  const YAML::Node& node;
67  const optional<std::string> operator()() const {
68  optional<std::string> val;
69  std::string t;
70  if(node.m_pNode && YAML::convert<std::string>::decode(node, t))
71  val = std::move(t);
72  return val;
73  }
74 };
75 } // namespace YAML
76 #endif
77 namespace scc {
78 namespace {
79 inline auto get_sc_objects(sc_core::sc_object* obj = nullptr) -> const std::vector<sc_core::sc_object*>& {
80  if(obj)
81  return obj->get_child_objects();
82  else
83  return sc_core::sc_get_top_level_objects();
84 }
85 
86 #ifdef WIN32
87 #define DIR_SEPARATOR '\\'
88 #else
89 #define DIR_SEPARATOR '/'
90 #endif
91 
92 struct config_reader {
93  std::vector<std::string> includes{"."};
94  void add_to_includes(std::string const& path) {
95  for(auto& e : includes) {
96  if(e == path)
97  return;
98  }
99  includes.push_back(path);
100  }
101 
102  std::string find_in_include_path(std::string const& file_name) {
103  if(file_name[0] == '/' || file_name[0] == '\\')
104  return file_name;
105  else
106  for(auto& incl : includes) {
107  auto full_name = incl + DIR_SEPARATOR + file_name;
108  std::ifstream ifs(full_name);
109  if(ifs.is_open())
110  return full_name;
111  }
112  return file_name;
113  }
114 };
115 /*************************************************************************************************
116  * JSON config start
117  ************************************************************************************************/
118 using namespace rapidjson;
119 using writer_type = PrettyWriter<OStreamWrapper>;
120 
121 #define FDECL(TYPE, FUNC) \
122  inline void writeValue(writer_type& writer, std::string const& key, TYPE value) { \
123  writer.Key(key.c_str()); \
124  writer.FUNC(value); \
125  }
126 FDECL(int, Int)
127 FDECL(unsigned int, Uint)
128 FDECL(long, Int64)
129 FDECL(unsigned long, Uint64)
130 FDECL(long long, Int64)
131 FDECL(unsigned long long, Uint64)
132 FDECL(bool, Bool)
133 FDECL(float, Double)
134 FDECL(double, Double)
135 FDECL(char const*, String)
136 inline void writeValue(writer_type& writer, std::string const& key, std::string const& value) {
137  writer.Key(key.c_str());
138  writer.String(value.c_str());
139 }
140 
141 inline bool start_object(writer_type& writer, char const* key, bool started) {
142  if(!started) {
143  writer.Key(key);
144  writer.StartObject();
145  }
146  return true;
147 }
148 
149 struct json_config_dumper {
150  configurer::broker_t const& broker;
151  std::vector<std::string> const& stop_list;
152  std::unordered_map<std::string, std::vector<cci::cci_param_untyped_handle>> lut;
153  json_config_dumper(configurer::broker_t const& broker, std::vector<std::string> const& stop_list)
154  : broker(broker)
155  , stop_list(stop_list) {
156  for(auto& h : broker.get_param_handles()) {
157  auto value = h.get_cci_value();
158  std::string paramname{h.name()};
159  auto sep = paramname.rfind('.');
160  auto basename = paramname.substr(0, sep);
161  lut[basename].push_back(h);
162  }
163  }
164 
165  void dump_config(sc_core::sc_object* obj, writer_type& writer) {
166  auto basename = std::string(obj->basename());
167  if(basename.substr(0, 3) == "$$$" || std::find(std::begin(stop_list), std::end(stop_list), obj->name()) != std::end(stop_list))
168  return;
169  auto obj_started = false;
170  auto log_lvl_set = false;
171  auto it = lut.find(obj->name());
172  if(it != lut.end())
173  for(auto& h : it->second) {
174  obj_started = start_object(writer, obj->basename(), obj_started);
175  auto value = h.get_cci_value();
176  std::string paramname{h.name()};
177  auto basename = paramname.substr(paramname.rfind('.') + 1);
178  if(basename == SCC_LOG_LEVEL_PARAM_NAME)
179  log_lvl_set = true;
180  if(value.is_bool())
181  writeValue(writer, basename, (bool)value.get_bool());
182  else if(value.is_int())
183  writeValue(writer, basename, (int)value.get_int());
184  else if(value.is_int64())
185  writeValue(writer, basename, static_cast<int64_t>(value.get_int64()));
186  else if(value.is_uint())
187  writeValue(writer, basename, value.get_uint());
188  else if(value.is_uint64())
189  writeValue(writer, basename, static_cast<uint64_t>(value.get_uint64()));
190  else if(value.is_double())
191  writeValue(writer, basename, value.get_double());
192  else if(value.is_string())
193  writeValue(writer, basename, value.get_string().c_str());
194  }
195  auto mod = dynamic_cast<sc_core::sc_module*>(obj);
196  if(scc::is_logging_initialized() && !log_lvl_set && mod) {
197  obj_started = start_object(writer, obj->basename(), obj_started);
198  auto val = broker.get_preset_cci_value(fmt::format("{}.{}", obj->name(), SCC_LOG_LEVEL_PARAM_NAME));
199  if(basename.substr(0, 3) != "$$$")
200  writeValue(writer, SCC_LOG_LEVEL_PARAM_NAME, val.is_int() ? val.get_int() : static_cast<int>(get_logging_level()));
201  }
202  for(auto* o : get_sc_objects(obj)) {
203  obj_started = start_object(writer, obj->basename(), obj_started);
204  dump_config(o, writer);
205  }
206  if(obj_started)
207  writer.EndObject();
208  }
209 };
210 
211 struct json_config_reader : public config_reader {
212  configurer::broker_t& broker;
213  Document document;
214  bool valid{false};
215  bool empty{false};
216 
217  json_config_reader(configurer::broker_t& broker)
218  : broker(broker) {}
219 
220  void parse(std::istream& is) {
221  IStreamWrapper stream(is);
222  document.ParseStream(stream);
223  valid = !document.HasParseError();
224  empty = document.IsNull();
225  }
226  std::string get_error_msg() {
227  std::ostringstream os;
228  os << " location " << (unsigned)document.GetErrorOffset() << ", reason: " << GetParseError_En(document.GetParseError());
229  return os.str();
230  }
231 
232  inline void configure_cci() { configure_cci_hierarchical(document, ""); }
233 
234  void configure_cci_hierarchical(Value const& value, std::string prefix) {
235  if(value.IsObject()) {
236  auto o = value.GetObject();
237  for(auto itr = o.MemberBegin(); itr != o.MemberEnd(); ++itr) {
238  if(!itr->name.IsString())
239  return;
240  std::string key_name = itr->name.GetString();
241  Value const& val = itr->value;
242  auto hier_name = prefix.size() ? prefix + "." + key_name : key_name;
243  if(val.IsNull() || val.IsArray())
244  return;
245  else if(val.IsObject())
246  configure_cci_hierarchical(val, hier_name);
247  else {
248  if(key_name == "!include") {
249  json_config_reader sub_reader(broker);
250  std::ifstream ifs(find_in_include_path(val.GetString()));
251  if(ifs.is_open()) {
252  sub_reader.parse(ifs);
253  if(sub_reader.valid) {
254  sub_reader.configure_cci_hierarchical(sub_reader.document, prefix);
255  } else {
256  std::ostringstream os;
257  os << "Could not parse include file " << val.GetString();
258  throw std::runtime_error(os.str());
259  }
260  } else {
261  std::ostringstream os;
262  os << "Could not open include file " << val.GetString();
263  throw std::runtime_error(os.str());
264  }
265  } else {
266  auto param_handle = broker.get_param_handle(hier_name);
267  if(param_handle.is_valid()) {
268  if(val.IsString()) {
269  param_handle.set_cci_value(cci::cci_value(std::string(val.GetString())));
270  } else if(val.IsBool()) {
271  param_handle.set_cci_value(cci::cci_value(val.Get<bool>()));
272  } else if(val.IsInt()) {
273  param_handle.set_cci_value(cci::cci_value(val.Get<int>()));
274  } else if(val.IsInt64()) {
275  param_handle.set_cci_value(cci::cci_value(val.Get<int64_t>()));
276  } else if(val.IsUint()) {
277  param_handle.set_cci_value(cci::cci_value(val.Get<unsigned>()));
278  } else if(val.IsUint64()) {
279  param_handle.set_cci_value(cci::cci_value(val.Get<uint64_t>()));
280  } else if(val.IsDouble()) {
281  param_handle.set_cci_value(cci::cci_value(val.Get<double>()));
282  }
283  } else {
284  if(val.IsString()) {
285  broker.set_preset_cci_value(hier_name, cci::cci_value(std::string(val.GetString())));
286  } else if(val.IsBool()) {
287  broker.set_preset_cci_value(hier_name, cci::cci_value(val.Get<bool>()));
288  } else if(val.IsInt()) {
289  broker.set_preset_cci_value(hier_name, cci::cci_value(val.Get<int>()));
290  } else if(val.IsInt64()) {
291  broker.set_preset_cci_value(hier_name, cci::cci_value(val.Get<int64_t>()));
292  } else if(val.IsUint()) {
293  broker.set_preset_cci_value(hier_name, cci::cci_value(val.Get<unsigned>()));
294  } else if(val.IsUint64()) {
295  broker.set_preset_cci_value(hier_name, cci::cci_value(val.Get<uint64_t>()));
296  } else if(val.IsDouble()) {
297  broker.set_preset_cci_value(hier_name, cci::cci_value(val.Get<double>()));
298  }
299  }
300  }
301  }
302  }
303  }
304  }
305 };
306 /*************************************************************************************************
307  * JSON config end
308  ************************************************************************************************/
309 #ifdef HAS_YAMPCPP
310 /*************************************************************************************************
311  * YAML config start
312  ************************************************************************************************/
313 struct yaml_config_dumper {
314  configurer::broker_t const& broker;
315  bool with_description{false};
316  bool complete{true};
317  std::vector<std::string> const& stop_list;
318  std::unordered_map<std::string, std::vector<cci::cci_param_untyped_handle>> lut;
319  std::vector<cci::cci_param_untyped_handle> tl_lut;
320  yaml_config_dumper(configurer::broker_t const& broker, bool with_description, bool complete, std::vector<std::string> const& stop_list)
321  : broker(broker)
322  , with_description(with_description)
323  , complete(complete)
324  , stop_list(stop_list) {
325  for(auto& h : broker.get_param_handles()) {
326  auto value = h.get_cci_value();
327  std::string paramname{h.name()};
328  auto sep = paramname.rfind('.');
329  if(sep == std::string::npos) {
330  tl_lut.push_back(h);
331  } else {
332  auto basename = paramname.substr(0, sep);
333  lut[basename].push_back(h);
334  }
335  }
336  }
337 
338  void dump_config(YAML::Node& base_node) {
339  copy2yaml(tl_lut, base_node);
340  for(auto* o : sc_core::sc_get_top_level_objects()) {
341  dump_config(o, base_node);
342  }
343  }
344 
345  void dump_config(sc_core::sc_object* obj, YAML::Node& base_node) {
346  auto basename = std::string(obj->basename());
347  if(basename.substr(0, 3) == "$$$" || std::find(std::begin(stop_list), std::end(stop_list), obj->name()) != std::end(stop_list))
348  return;
349  auto obj_started = false;
350  auto log_lvl_set = false;
351  auto it = lut.find(obj->name());
352  YAML::Node this_node;
353  if(it != lut.end())
354  log_lvl_set |= copy2yaml(it->second, this_node);
355  auto mod = dynamic_cast<sc_core::sc_module*>(obj);
356  if(!log_lvl_set && mod && complete) {
357  auto val = broker.get_preset_cci_value(fmt::format("{}.{}", obj->name(), SCC_LOG_LEVEL_PARAM_NAME));
358  auto global_verb = static_cast<int>(get_logging_level());
359  if(basename.substr(0, 11) != "scc_tracer")
360  this_node["log_level"] = val.is_int() ? val.get_int() : global_verb;
361  }
362  for(auto* o : get_sc_objects(obj)) {
363  dump_config(o, this_node);
364  }
365  if(this_node.size())
366  base_node[obj->basename()] = this_node;
367  }
368 
369 private:
370  bool copy2yaml(const std::vector<cci::cci_param_untyped_handle>& params, YAML::Node& this_node) {
371  bool log_lvl_set = false;
372  for(auto& h : params) {
373  auto value = h.get_cci_value();
374  std::string paramname{h.name()};
375  auto basename = paramname.substr(paramname.rfind('.') + 1);
376  if(basename == SCC_LOG_LEVEL_PARAM_NAME)
377  log_lvl_set = true;
378 
379  auto descr = h.get_description();
380  if(with_description && descr.size()) {
381  auto descr_name = fmt::format("{}::descr", basename);
382  this_node[descr_name] = descr;
383  this_node[descr_name].SetTag("desc");
384  }
385  sc_core::sc_time t;
386  if(value.is_bool())
387  this_node[basename] = (bool)(value.get_bool());
388  else if(value.is_int())
389  this_node[basename] = (int)(value.get_int());
390  else if(value.is_int64())
391  this_node[basename] = static_cast<int64_t>(value.get_int64());
392  else if(value.is_uint())
393  this_node[basename] = value.get_uint();
394  else if(value.is_uint64())
395  this_node[basename] = static_cast<uint64_t>(value.get_uint64());
396  else if(value.is_double())
397  this_node[basename] = value.get_double();
398  else if(value.is_string())
399  this_node[basename] = value.get_string().c_str();
400  else if(value.try_get(t))
401  this_node[basename] = t.to_string();
402  }
403  return log_lvl_set;
404  }
405 };
406 
407 struct yaml_config_reader : public config_reader {
408  configurer::broker_t& broker;
409  YAML::Node document;
410  bool valid{false};
411  bool empty{true};
412 
413  yaml_config_reader(configurer::broker_t& broker)
414  : broker(broker) {}
415 
416  void parse(std::istream& is) {
417  std::string buf((std::istreambuf_iterator<char>(is)), std::istreambuf_iterator<char>());
418  document = YAML::Load(buf);
419  valid = document.IsDefined() && (document.IsMap() || document.IsNull());
420  empty = document.IsNull();
421  }
422 
423  std::string get_error_msg() { return "YAML file does not start with a map"; }
424 
425  inline void configure_cci() {
426  try {
427  configure_cci_hierarchical(document, "");
428  } catch(YAML::ParserException& e) {
429  throw std::runtime_error(e.what());
430  } catch(YAML::BadFile& e) {
431  throw std::runtime_error(e.what());
432  } catch(YAML::Exception& e) {
433  throw std::runtime_error(e.what());
434  }
435  }
436 
437  void configure_cci_hierarchical(YAML::Node const& value, std::string const& prefix) {
438  if(value.IsMap()) {
439  for(auto it = value.begin(); it != value.end(); ++it) {
440  auto key_name = it->first.as<std::string>();
441  YAML::Node const& val = it->second;
442  auto hier_name = prefix.size() ? prefix + "." + key_name : key_name;
443  if(!val.IsDefined() || val.IsSequence())
444  return;
445  else if(val.IsMap())
446  configure_cci_hierarchical(val, hier_name);
447  else if(val.IsScalar()) {
448  auto& tag = val.Tag();
449  if(tag == "!include") {
450  yaml_config_reader sub_reader(broker);
451  std::ifstream ifs(find_in_include_path(val.as<std::string>()));
452  if(ifs.is_open()) {
453  sub_reader.parse(ifs);
454  if(sub_reader.valid) {
455  sub_reader.configure_cci_hierarchical(sub_reader.document, hier_name);
456  } else {
457  std::ostringstream os;
458  os << "Could not parse include file " << val.as<std::string>();
459  throw std::runtime_error(os.str());
460  }
461  } else {
462  std::ostringstream os;
463  os << "Could not open include file " << val.as<std::string>();
464  throw std::runtime_error(os.str());
465  }
466  } else if(tag.size() && tag[0] == '?') {
467  auto param_handle = broker.get_param_handle(hier_name);
468  if(param_handle.is_valid()) {
469  auto param = param_handle.get_cci_value();
470  if(param.is_bool()) {
471  param.set_bool(val.as<bool>());
472  } else if(param.is_int()) {
473  param.set_int(val.as<int>());
474  } else if(param.is_uint()) {
475  param.set_uint(val.as<unsigned>());
476  } else if(param.is_int64()) {
477  param.set_int64(val.as<int64_t>());
478  } else if(param.is_uint64()) {
479  param.set_uint64(val.as<uint64_t>());
480  } else if(param.is_double()) {
481  param.set_double(val.as<double>());
482  } else if(param.is_string()) {
483  param.set_string(val.as<std::string>());
484  }
485  } else {
486  if(auto res = YAML::as_if<bool, optional<bool>>(val)()) {
487  broker.set_preset_cci_value(hier_name, cci::cci_value(res.value()));
488  } else if(auto res = YAML::as_if<int, optional<int>>(val)()) {
489  broker.set_preset_cci_value(hier_name, cci::cci_value(res.value()));
490  } else if(auto res = YAML::as_if<int64_t, optional<int64_t>>(val)()) {
491  broker.set_preset_cci_value(hier_name, cci::cci_value(res.value()));
492  } else if(auto res = YAML::as_if<unsigned, optional<unsigned>>(val)()) {
493  broker.set_preset_cci_value(hier_name, cci::cci_value(res.value()));
494  } else if(auto res = YAML::as_if<uint64_t, optional<uint64_t>>(val)()) {
495  broker.set_preset_cci_value(hier_name, cci::cci_value(res.value()));
496  } else if(auto res = YAML::as_if<double, optional<double>>(val)()) {
497  broker.set_preset_cci_value(hier_name, cci::cci_value(res.value()));
498  } else if(auto res = YAML::as_if<std::string, optional<std::string>>(val)()) {
499  broker.set_preset_cci_value(hier_name, cci::cci_value(res.value()));
500  }
501  }
502  }
503  }
504  }
505  }
506  }
507 };
508 /*************************************************************************************************
509  * YAML config end
510  ************************************************************************************************/
511 #endif
512 template <typename T>
513 inline bool create_cci_param(sc_core::sc_attr_base* base_attr, const std::string& hier_name, configurer::cci_param_cln& params,
514  configurer::broker_t& broker, cci::cci_originator& cci_originator) {
515  if(auto attr = dynamic_cast<sc_core::sc_attribute<T>*>(base_attr)) {
516  auto par = new cci::cci_param_typed<T>(hier_name, attr->value, broker, "", cci::CCI_ABSOLUTE_NAME, cci_originator);
517  params.emplace_back(cci::cci_param_post_write_callback_untyped([attr](const cci::cci_param_write_event<>& ev) {
518  T result;
519  if(ev.new_value.try_get(result))
520  attr->value = result;
521  }),
522  par);
523  par->register_post_write_callback(params.back().first);
524  attr->value = par->get_value(); // if we have a preset
525  return true;
526  }
527  return false;
528 }
529 
530 template <>
531 inline bool create_cci_param<char*>(sc_core::sc_attr_base* base_attr, const std::string& hier_name, configurer::cci_param_cln& params,
532  configurer::broker_t& broker, cci::cci_originator& cci_originator) {
533  if(auto attr = dynamic_cast<sc_core::sc_attribute<char*>*>(base_attr)) {
534  auto par = new cci::cci_param_typed<std::string>(hier_name, attr->value, broker, "", cci::CCI_ABSOLUTE_NAME);
535  params.emplace_back(cci::cci_param_post_write_callback_untyped([attr](const cci::cci_param_write_event<>& ev) {
536  if(attr->value)
537  free(attr->value);
538  attr->value = strdup(ev.new_value.get<std::string>().c_str());
539  }),
540  par);
541  par->register_post_write_callback(params.back().first);
542  attr->value = strdup(par->get_value().c_str()); // if we have a preset
543  return true;
544  }
545  return false;
546 }
547 
548 template <typename T> inline bool update_cci_param(cci::cci_param_untyped_handle& param_handle, sc_core::sc_attr_base* base_attr) {
549  if(auto attr = dynamic_cast<sc_core::sc_attribute<T>*>(base_attr)) {
550  param_handle.set_cci_value(cci::cci_value(attr->value));
551  return true;
552  }
553  return false;
554 }
555 
556 template <> inline bool update_cci_param<char*>(cci::cci_param_untyped_handle& param_handle, sc_core::sc_attr_base* base_attr) {
557  if(auto attr = dynamic_cast<sc_core::sc_attribute<char*>*>(base_attr)) {
558  param_handle.set_cci_value(cci::cci_value(std::string(attr->value)));
559  return true;
560  }
561  return false;
562 }
563 
564 inline bool mirror_sc_attribute(configurer::broker_t& broker, configurer::cci_param_cln& params, cci::cci_originator& cci_originator,
565  std::string hier_name, sc_core::sc_attr_base* base_attr, bool update = false) {
566  auto param_handle = broker.get_param_handle(hier_name);
567  if(!param_handle.is_valid()) {
568  if(create_cci_param<int>(base_attr, hier_name, params, broker, cci_originator))
569  return true;
570  if(create_cci_param<unsigned>(base_attr, hier_name, params, broker, cci_originator))
571  return true;
572  if(create_cci_param<long>(base_attr, hier_name, params, broker, cci_originator))
573  return true;
574  if(create_cci_param<unsigned long>(base_attr, hier_name, params, broker, cci_originator))
575  return true;
576  if(create_cci_param<long long>(base_attr, hier_name, params, broker, cci_originator))
577  return true;
578  if(create_cci_param<unsigned long long>(base_attr, hier_name, params, broker, cci_originator))
579  return true;
580  if(create_cci_param<bool>(base_attr, hier_name, params, broker, cci_originator))
581  return true;
582  if(create_cci_param<float>(base_attr, hier_name, params, broker, cci_originator))
583  return true;
584  if(create_cci_param<double>(base_attr, hier_name, params, broker, cci_originator))
585  return true;
586  if(create_cci_param<std::string>(base_attr, hier_name, params, broker, cci_originator))
587  return true;
588  if(create_cci_param<char*>(base_attr, hier_name, params, broker, cci_originator))
589  return true;
590  } else if(update) {
591  if(update_cci_param<int>(param_handle, base_attr))
592  return true;
593  if(update_cci_param<unsigned>(param_handle, base_attr))
594  return true;
595  if(update_cci_param<long>(param_handle, base_attr))
596  return true;
597  if(update_cci_param<unsigned long>(param_handle, base_attr))
598  return true;
599  if(update_cci_param<long long>(param_handle, base_attr))
600  return true;
601  if(update_cci_param<unsigned long long>(param_handle, base_attr))
602  return true;
603  if(update_cci_param<bool>(param_handle, base_attr))
604  return true;
605  if(update_cci_param<float>(param_handle, base_attr))
606  return true;
607  if(update_cci_param<double>(param_handle, base_attr))
608  return true;
609  if(update_cci_param<std::string>(param_handle, base_attr))
610  return true;
611  if(update_cci_param<char*>(param_handle, base_attr))
612  return true;
613  }
614  return false;
615 }
616 
617 void mirror_sc_attributes(configurer::broker_t& broker, configurer::cci_param_cln& params, cci::cci_originator& cci_originator,
618  sc_core::sc_object* topobj = nullptr, bool update = false) {
619  for(auto obj : get_sc_objects(topobj)) {
620  if(auto mod = dynamic_cast<sc_core::sc_module*>(obj)) {
621  for(auto base_attr : mod->attr_cltn()) {
622  std::string hier_name = fmt::format("{}.{}", mod->name(), base_attr->name());
623  mirror_sc_attribute(broker, params, cci_originator, hier_name, base_attr, update);
624  }
625  mirror_sc_attributes(broker, params, cci_originator, mod, update);
626  }
627  }
628 }
629 
630 bool cci_name_ignore(std::pair<std::string, cci::cci_value> const& preset_value) {
631  std::string ending(SCC_LOG_LEVEL_PARAM_NAME);
632  auto& name = preset_value.first;
633  if(name.length() >= ending.length()) {
634  return (0 == name.compare(name.length() - ending.length(), ending.length(), ending));
635  } else {
636  return false;
637  }
638 }
639 } // namespace
640 #ifdef HAS_YAMPCPP
641 struct configurer::ConfigHolder : public yaml_config_reader {
642  ConfigHolder(configurer::broker_t& broker)
643  : yaml_config_reader(broker) {}
644 };
645 #else
646 struct configurer::ConfigHolder : public json_config_reader {
647  ConfigHolder(configurer::broker_t& broker)
648  : json_config_reader(broker) {}
649 };
650 #endif
651 
652 configurer::configurer(const std::string& filename, unsigned config_phases)
653 : configurer(filename, config_phases, "$$$configurer$$$") {}
654 
655 configurer::configurer(const std::string& filename, unsigned config_phases, sc_core::sc_module_name nm)
656 : base_type(nm)
657 , config_phases(config_phases)
658 , cci_broker(cci::cci_get_broker())
659 , cci_originator(cci_broker.get_originator())
660 , root(new ConfigHolder(cci_broker)) {
661  if(filename.length() > 0)
662  read_input_file(filename);
663 }
664 
665 configurer::~configurer() {}
666 
667 void configurer::read_input_file(const std::string& filename) {
668  root->add_to_includes(util::dir_name(filename));
669  std::ifstream is(filename);
670  if(is.is_open()) {
671  try {
672  root->parse(is);
673  if(!root->valid) {
674  SCCERR() << "Could not parse input file " << filename << ", " << root->get_error_msg();
675  } else if(!root->empty) {
676  root->configure_cci();
677  }
678  } catch(std::runtime_error& e) {
679  SCCERR() << "Could not parse input file " << filename << ", reason: " << e.what();
680  }
681  } else {
682  SCCWARN() << "Could not open input file " << filename;
683  }
684 }
685 
686 void configurer::dump_configuration(std::ostream& os, bool as_yaml, bool with_description, bool complete, sc_core::sc_object* obj) {
687 #ifdef HAS_YAMPCPP
688  if(as_yaml) {
689  YAML::Node root; // starts out as null
690  yaml_config_dumper dumper(cci_broker, with_description, complete, stop_list);
691  if(obj)
692  for(auto* o : obj->get_child_objects()) {
693  dumper.dump_config(o, root);
694  }
695  else
696  dumper.dump_config(root);
697  os << root;
698  return;
699  }
700 #endif
701  OStreamWrapper stream(os);
702  writer_type writer(stream);
703  writer.StartObject();
704  json_config_dumper dumper(cci_broker, stop_list);
705  for(auto* o : get_sc_objects(obj)) {
706  dumper.dump_config(o, writer);
707  }
708  writer.EndObject();
709 }
710 
711 void configurer::configure() { mirror_sc_attributes(cci_broker, cci2sc_attr, cci_originator); }
712 
713 void configurer::set_configuration_value(sc_core::sc_attr_base* attr_base, sc_core::sc_object* owner) {
714  std::string hier_name = fmt::format("{}.{}", owner->name(), attr_base->name());
715  mirror_sc_attribute(cci_broker, cci2sc_attr, cci_originator, hier_name, attr_base);
716 }
717 
718 inline std::string hier_name_as_regex(std::string const& parname) {
719  if(parname.find_first_of("*?[") != std::string::npos) {
720  return util::glob_to_regex(parname);
721  } else if(parname[0] == '^') {
722  return parname;
723  } else
724  return "";
725 }
726 
727 void configurer::set_value(const std::string& hier_name, cci::cci_value value) {
728  auto regex_str = hier_name_as_regex(hier_name);
729  if(regex_str.length()) {
730  auto rr = std::regex(regex_str);
731  cci::cci_param_predicate pred = [rr](cci::cci_param_untyped_handle const& hndl) { return regex_match(hndl.name(), rr); };
732  for(auto& hndl : cci_broker.get_param_handles(pred)) {
733  hndl.set_cci_value(value);
734  }
735  } else {
736  cci::cci_param_handle param_handle = cci_broker.get_param_handle(hier_name);
737  if(param_handle.is_valid()) {
738  param_handle.set_cci_value(value);
739  return;
740  }
741  }
742  cci_broker.set_preset_cci_value(hier_name, value);
743 }
744 
745 void configurer::set_value_from_str(const std::string& hier_name, const std::string& value) {
746  try {
747  auto i = std::stoi(value);
748  set_value(hier_name, i);
749  return;
750  } catch(...) {
751  }
752  try {
753  auto l = std::stol(value);
754  set_value(hier_name, l);
755  return;
756  } catch(...) {
757  }
758  try {
759  auto ll = std::stoll(value);
760  set_value(hier_name, ll);
761  return;
762  } catch(...) {
763  }
764  try {
765  auto f = std::stof(value);
766  set_value(hier_name, f);
767  return;
768  } catch(...) {
769  }
770  try {
771  auto d = std::stod(value);
772  set_value(hier_name, d);
773  return;
774  } catch(...) {
775  }
776  auto lower_value = util::str_tolower(value);
777  if(lower_value == "true") {
778  set_value(hier_name, true);
779  return;
780  }
781  if(lower_value == "false") {
782  set_value(hier_name, false);
783  return;
784  }
785  set_value(hier_name, value);
786 }
787 
788 void configurer::config_check() {
789  try {
790  cci_broker.ignore_unconsumed_preset_values(&cci_name_ignore);
791  auto res = cci_broker.get_unconsumed_preset_values();
792  if(res.size()) {
793  std::ostringstream oss;
794  for(auto& val : res)
795  oss << "\t - " << val.first << "\n";
796  if(res.size() == 1) {
797  SCCWARN("scc::configurer") << "There is " << res.size() << " unused CCI preset value:\n"
798  << oss.str() << "Please check your setup!";
799  } else {
800  SCCWARN("scc::configurer") << "There are " << res.size() << " unused CCI preset values:\n"
801  << oss.str() << "Please check your setup!";
802  }
803  }
804  } catch(std::domain_error& e) {
805  SCCFATAL("scc::configurer") << "Illegal hierarchy name: '" << e.what() << "'";
806  } catch(std::invalid_argument& e) {
807  SCCFATAL("scc::configurer") << "Illegal parameter name: '" << e.what() << "'";
808  }
809 }
810 
811 void configurer::start_of_simulation() {
812  if(config_phases & START_OF_SIMULATION)
813  configure();
814  config_check();
815  if(dump_file_name.size()) {
816  auto as_json = util::ends_with(dump_file_name, "json");
817  std::ofstream of{dump_file_name};
818  if(of.is_open()) {
819  mirror_sc_attributes(cci_broker, cci2sc_attr, cci_originator, nullptr, true);
820  dump_configuration(of, !as_json, with_description, complete);
821  }
822  }
823 }
824 
825 } // namespace scc
design configuration reader
Definition: configurer.h:41
void set_configuration_value(sc_core::sc_attr_base *attr_base, sc_core::sc_object *owner)
Definition: configurer.cpp:713
void set_value_from_str(const std::string &hier_name, const std::string &value)
Definition: configurer.cpp:745
void set_value(std::string const &hier_name, T value)
Definition: configurer.h:104
void dump_configuration(std::ostream &os=std::cout, bool as_yaml=true, bool with_description=false, bool complete=true, sc_core::sc_object *obj=nullptr)
Definition: configurer.cpp:686
SCC TLM utilities.
bool is_logging_initialized()
get the state of the SCC logging system
Definition: report.cpp:437
log get_logging_level()
get the SystemC logging level
Definition: report.cpp:461
std::string glob_to_regex(std::string val)
Definition: ities.h:416
std::string str_tolower(std::string str)
Definition: ities.h:306
T dir_name(T const &path, T const &delims="/\\")
Definition: ities.h:383