17 #include "configurer.h"
18 #include "rapidjson/document.h"
19 #include "rapidjson/error/en.h"
21 #ifdef FMT_SPDLOG_INTERNAL
24 #include <fmt/format.h>
26 #include <cci_configuration>
27 #include <cci_utils/broker.h>
30 #include <rapidjson/istreamwrapper.h>
31 #include <rapidjson/ostreamwrapper.h>
32 #include <rapidjson/prettywriter.h>
33 #include <unordered_map>
35 #include <yaml-cpp/exceptions.h>
36 #include <yaml-cpp/node/parse.h>
37 #include <yaml-cpp/yaml.h>
40 template <
typename T>
struct optional {
42 bool initialized{
false};
43 optional& operator=(T&& val) {
44 this->val = std::move(val);
48 operator bool()
const {
return initialized; }
49 T value() {
return val; }
54 template <
typename T>
struct as_if<T, optional<T>> {
55 explicit as_if(
const YAML::Node& node_)
57 const YAML::Node& node;
58 const optional<T> operator()()
const {
61 if(node.m_pNode && YAML::convert<T>::decode(node, t))
68 template <>
struct as_if<std::string, optional<std::string>> {
69 explicit as_if(
const YAML::Node& node_)
71 const YAML::Node& node;
72 const optional<std::string> operator()()
const {
73 optional<std::string> val;
75 if(node.m_pNode && YAML::convert<std::string>::decode(node, t))
84 inline auto get_sc_objects(sc_core::sc_object* obj =
nullptr) ->
const std::vector<sc_core::sc_object*>& {
86 return obj->get_child_objects();
88 return sc_core::sc_get_top_level_objects();
92 #define DIR_SEPARATOR '\\'
94 #define DIR_SEPARATOR '/'
97 struct config_reader {
98 std::vector<std::string> includes{
"."};
99 void add_to_includes(std::string
const& path) {
100 for(
auto& e : includes) {
104 includes.push_back(path);
107 std::string find_in_include_path(std::string
const& file_name) {
108 if(file_name[0] ==
'/' || file_name[0] ==
'\\')
111 for(
auto& incl : includes) {
112 auto full_name = incl + DIR_SEPARATOR + file_name;
113 std::ifstream ifs(full_name);
123 using namespace rapidjson;
124 using writer_type = PrettyWriter<OStreamWrapper>;
126 #define FDECL(TYPE, FUNC) \
127 inline void writeValue(writer_type& writer, std::string const& key, TYPE value) { \
128 writer.Key(key.c_str()); \
129 writer.FUNC(value); \
132 FDECL(
unsigned int, Uint)
134 FDECL(
unsigned long, Uint64)
135 FDECL(
long long, Int64)
136 FDECL(
unsigned long long, Uint64)
139 FDECL(
double, Double)
140 FDECL(
char const*, String)
141 inline void writeValue(writer_type& writer, std::string
const& key, std::string
const& value) {
142 writer.Key(key.c_str());
143 writer.String(value.c_str());
146 inline bool start_object(writer_type& writer,
char const* key,
bool started) {
149 writer.StartObject();
154 struct json_config_dumper {
155 configurer::broker_t
const& broker;
156 std::unordered_map<std::string, std::vector<cci::cci_param_untyped_handle>> lut;
157 json_config_dumper(configurer::broker_t
const& broker)
159 for(
auto& h : broker.get_param_handles()) {
160 auto value = h.get_cci_value();
161 std::string paramname{h.name()};
162 auto sep = paramname.rfind(
'.');
163 auto basename = paramname.substr(0, sep);
164 lut[basename].push_back(h);
168 void dump_config(sc_core::sc_object* obj, writer_type& writer) {
169 auto basename = std::string(obj->basename());
170 if(basename.substr(0, 3) ==
"$$$")
172 auto obj_started =
false;
173 auto log_lvl_set =
false;
174 auto it = lut.find(obj->name());
176 for(
auto& h : it->second) {
177 obj_started = start_object(writer, obj->basename(), obj_started);
178 auto value = h.get_cci_value();
179 std::string paramname{h.name()};
180 auto basename = paramname.substr(paramname.rfind(
'.') + 1);
181 if(basename == SCC_LOG_LEVEL_PARAM_NAME)
184 writeValue(writer, basename, (
bool)value.get_bool());
185 else if(value.is_int())
186 writeValue(writer, basename, (
int)value.get_int());
187 else if(value.is_int64())
188 writeValue(writer, basename,
static_cast<int64_t
>(value.get_int64()));
189 else if(value.is_uint())
190 writeValue(writer, basename, value.get_uint());
191 else if(value.is_uint64())
192 writeValue(writer, basename,
static_cast<uint64_t
>(value.get_uint64()));
193 else if(value.is_double())
194 writeValue(writer, basename, value.get_double());
195 else if(value.is_string())
196 writeValue(writer, basename, value.get_string().c_str());
198 auto mod =
dynamic_cast<sc_core::sc_module*
>(obj);
200 obj_started = start_object(writer, obj->basename(), obj_started);
201 auto val = broker.get_preset_cci_value(fmt::format(
"{}.{}", obj->name(), SCC_LOG_LEVEL_PARAM_NAME));
202 if(basename.substr(0, 3) !=
"$$$")
203 writeValue(writer, SCC_LOG_LEVEL_PARAM_NAME, val.is_int() ? val.get_int() :
static_cast<int>(
get_logging_level()));
205 for(
auto* o : get_sc_objects(obj)) {
206 obj_started = start_object(writer, obj->basename(), obj_started);
207 dump_config(o, writer);
214 struct json_config_reader :
public config_reader {
215 configurer::broker_t& broker;
219 json_config_reader(configurer::broker_t& broker)
222 void parse(std::istream& is) {
223 IStreamWrapper stream(is);
224 document.ParseStream(stream);
225 valid = !document.HasParseError();
227 std::string get_error_msg() {
228 std::ostringstream os;
229 os <<
" location " << (unsigned)document.GetErrorOffset() <<
", reason: " << GetParseError_En(document.GetParseError());
233 inline void configure_cci() { configure_cci_hierarchical(document,
""); }
235 void configure_cci_hierarchical(Value
const& value, std::string prefix) {
236 if(value.IsObject()) {
237 auto o = value.GetObject();
238 for(
auto itr = o.MemberBegin(); itr != o.MemberEnd(); ++itr) {
239 if(!itr->name.IsString())
241 std::string key_name = itr->name.GetString();
242 Value
const& val = itr->value;
243 auto hier_name = prefix.size() ? prefix +
"." + key_name : key_name;
244 if(val.IsNull() || val.IsArray())
246 else if(val.IsObject())
247 configure_cci_hierarchical(val, hier_name);
249 if(key_name ==
"!include") {
250 json_config_reader sub_reader(broker);
251 std::ifstream ifs(find_in_include_path(val.GetString()));
253 sub_reader.parse(ifs);
254 if(sub_reader.valid) {
255 sub_reader.configure_cci_hierarchical(sub_reader.document, prefix);
257 std::ostringstream os;
258 os <<
"Could not parse include file " << val.GetString();
259 throw std::runtime_error(os.str());
262 std::ostringstream os;
263 os <<
"Could not open include file " << val.GetString();
264 throw std::runtime_error(os.str());
267 auto param_handle = broker.get_param_handle(hier_name);
268 if(param_handle.is_valid()) {
270 param_handle.set_cci_value(cci::cci_value(std::string(val.GetString())));
271 }
else if(val.IsBool()) {
272 param_handle.set_cci_value(cci::cci_value(val.Get<
bool>()));
273 }
else if(val.IsInt()) {
274 param_handle.set_cci_value(cci::cci_value(val.Get<
int>()));
275 }
else if(val.IsInt64()) {
276 param_handle.set_cci_value(cci::cci_value(val.Get<int64_t>()));
277 }
else if(val.IsUint()) {
278 param_handle.set_cci_value(cci::cci_value(val.Get<
unsigned>()));
279 }
else if(val.IsUint64()) {
280 param_handle.set_cci_value(cci::cci_value(val.Get<uint64_t>()));
281 }
else if(val.IsDouble()) {
282 param_handle.set_cci_value(cci::cci_value(val.Get<
double>()));
286 broker.set_preset_cci_value(hier_name, cci::cci_value(std::string(val.GetString())));
287 }
else if(val.IsBool()) {
288 broker.set_preset_cci_value(hier_name, cci::cci_value(val.Get<
bool>()));
289 }
else if(val.IsInt()) {
290 broker.set_preset_cci_value(hier_name, cci::cci_value(val.Get<
int>()));
291 }
else if(val.IsInt64()) {
292 broker.set_preset_cci_value(hier_name, cci::cci_value(val.Get<int64_t>()));
293 }
else if(val.IsUint()) {
294 broker.set_preset_cci_value(hier_name, cci::cci_value(val.Get<
unsigned>()));
295 }
else if(val.IsUint64()) {
296 broker.set_preset_cci_value(hier_name, cci::cci_value(val.Get<uint64_t>()));
297 }
else if(val.IsDouble()) {
298 broker.set_preset_cci_value(hier_name, cci::cci_value(val.Get<
double>()));
314 struct yaml_config_dumper {
315 configurer::broker_t
const& broker;
316 bool with_description{
false};
317 std::unordered_map<std::string, std::vector<cci::cci_param_untyped_handle>> lut;
318 yaml_config_dumper(configurer::broker_t
const& broker,
bool with_description)
320 , with_description(with_description) {
321 for(
auto& h : broker.get_param_handles()) {
322 auto value = h.get_cci_value();
323 std::string paramname{h.name()};
324 auto sep = paramname.rfind(
'.');
325 auto basename = paramname.substr(0, sep);
326 lut[basename].push_back(h);
330 void dump_config(sc_core::sc_object* obj, YAML::Node& base_node) {
331 auto basename = std::string(obj->basename());
332 if(basename.substr(0, 3) ==
"$$$")
334 auto obj_started =
false;
335 auto log_lvl_set =
false;
336 auto it = lut.find(obj->name());
337 YAML::Node this_node;
339 for(
auto& h : it->second) {
340 auto value = h.get_cci_value();
341 std::string paramname{h.name()};
342 auto basename = paramname.substr(paramname.rfind(
'.') + 1);
343 if(basename == SCC_LOG_LEVEL_PARAM_NAME)
345 auto descr = h.get_description();
346 if(with_description && descr.size()) {
347 auto descr_name = fmt::format(
"{}::descr", basename);
348 this_node[descr_name] = descr;
349 this_node[descr_name].SetTag(
"desc");
353 this_node[basename] = (bool)value.get_bool();
354 else if(value.is_int())
355 this_node[basename] = (
int)value.get_int();
356 else if(value.is_int64())
357 this_node[basename] =
static_cast<int64_t
>(value.get_int64());
358 else if(value.is_uint())
359 this_node[basename] = value.get_uint();
360 else if(value.is_uint64())
361 this_node[basename] =
static_cast<uint64_t
>(value.get_uint64());
362 else if(value.is_double())
363 this_node[basename] = value.get_double();
364 else if(value.is_string())
365 this_node[basename] = value.get_string().c_str();
366 else if(value.try_get(t))
367 this_node[basename] = t.to_string();
369 auto mod =
dynamic_cast<sc_core::sc_module*
>(obj);
370 if(!log_lvl_set && mod) {
371 auto val = broker.get_preset_cci_value(fmt::format(
"{}.{}", obj->name(), SCC_LOG_LEVEL_PARAM_NAME));
373 if(basename.substr(0, 11) !=
"scc_tracer")
374 this_node[
"log_level"] = val.is_int() ? val.get_int() : global_verb;
376 for(
auto* o : get_sc_objects(obj)) {
377 dump_config(o, this_node);
380 base_node[obj->basename()] = this_node;
384 struct yaml_config_reader :
public config_reader {
385 configurer::broker_t& broker;
389 yaml_config_reader(configurer::broker_t& broker)
392 void parse(std::istream& is) {
393 std::string buf((std::istreambuf_iterator<char>(is)), std::istreambuf_iterator<char>());
394 document = YAML::Load(buf);
395 valid = document.IsDefined() && document.IsMap();
398 std::string get_error_msg() {
return "YAML file does not start with a map"; }
400 inline void configure_cci() {
402 configure_cci_hierarchical(document,
"");
403 }
catch(YAML::ParserException& e) {
404 throw std::runtime_error(e.what());
405 }
catch(YAML::BadFile& e) {
406 throw std::runtime_error(e.what());
407 }
catch(YAML::Exception& e) {
408 throw std::runtime_error(e.what());
412 void configure_cci_hierarchical(YAML::Node
const& value, std::string
const& prefix) {
414 for(
auto it = value.begin(); it != value.end(); ++it) {
415 auto key_name = it->first.as<std::string>();
416 YAML::Node
const& val = it->second;
417 auto hier_name = prefix.size() ? prefix +
"." + key_name : key_name;
418 if(!val.IsDefined() || val.IsSequence())
421 configure_cci_hierarchical(val, hier_name);
422 else if(val.IsScalar()) {
423 auto& tag = val.Tag();
424 if(tag ==
"!include") {
425 yaml_config_reader sub_reader(broker);
426 std::ifstream ifs(find_in_include_path(val.as<std::string>()));
428 sub_reader.parse(ifs);
429 if(sub_reader.valid) {
430 sub_reader.configure_cci_hierarchical(sub_reader.document, hier_name);
432 std::ostringstream os;
433 os <<
"Could not parse include file " << val.as<std::string>();
434 throw std::runtime_error(os.str());
437 std::ostringstream os;
438 os <<
"Could not open include file " << val.as<std::string>();
439 throw std::runtime_error(os.str());
441 }
else if(tag.size() && tag[0] ==
'?') {
442 auto param_handle = broker.get_param_handle(hier_name);
443 if(param_handle.is_valid()) {
444 auto param = param_handle.get_cci_value();
445 if(param.is_bool()) {
446 param.set_bool(val.as<
bool>());
447 }
else if(param.is_int()) {
448 param.set_int(val.as<
int>());
449 }
else if(param.is_uint()) {
450 param.set_uint(val.as<
unsigned>());
451 }
else if(param.is_int64()) {
452 param.set_int64(val.as<int64_t>());
453 }
else if(param.is_uint64()) {
454 param.set_uint64(val.as<uint64_t>());
455 }
else if(param.is_double()) {
456 param.set_double(val.as<
double>());
457 }
else if(param.is_string()) {
458 param.set_string(val.as<std::string>());
461 if(
auto res = YAML::as_if<
bool, optional<bool>>(val)()) {
462 broker.set_preset_cci_value(hier_name, cci::cci_value(res.value()));
463 }
else if(
auto res = YAML::as_if<
unsigned, optional<unsigned>>(val)()) {
464 broker.set_preset_cci_value(hier_name, cci::cci_value(res.value()));
465 }
else if(
auto res = YAML::as_if<uint64_t, optional<uint64_t>>(val)()) {
466 broker.set_preset_cci_value(hier_name, cci::cci_value(res.value()));
467 }
else if(
auto res = YAML::as_if<
int, optional<int>>(val)()) {
468 broker.set_preset_cci_value(hier_name, cci::cci_value(res.value()));
469 }
else if(
auto res = YAML::as_if<int64_t, optional<int64_t>>(val)()) {
470 broker.set_preset_cci_value(hier_name, cci::cci_value(res.value()));
471 }
else if(
auto res = YAML::as_if<
double, optional<double>>(val)()) {
472 broker.set_preset_cci_value(hier_name, cci::cci_value(res.value()));
473 }
else if(
auto res = YAML::as_if<std::string, optional<std::string>>(val)()) {
474 broker.set_preset_cci_value(hier_name, cci::cci_value(res.value()));
487 template <
typename T>
488 inline bool create_cci_param(sc_core::sc_attr_base* base_attr,
const std::string& hier_name, configurer::cci_param_cln& params,
489 configurer::broker_t& broker, cci::cci_originator& cci_originator) {
490 if(
auto attr =
dynamic_cast<sc_core::sc_attribute<T>*
>(base_attr)) {
491 auto par =
new cci::cci_param_typed<T>(hier_name, attr->value, broker,
"", cci::CCI_ABSOLUTE_NAME, cci_originator);
492 params.emplace_back(cci::cci_param_post_write_callback_untyped([attr](
const cci::cci_param_write_event<>& ev) {
494 if(ev.new_value.try_get(result))
495 attr->value = result;
498 par->register_post_write_callback(params.back().first);
499 attr->value = par->get_value();
506 inline bool create_cci_param<char*>(sc_core::sc_attr_base* base_attr,
const std::string& hier_name, configurer::cci_param_cln& params,
507 configurer::broker_t& broker, cci::cci_originator& cci_originator) {
508 if(
auto attr =
dynamic_cast<sc_core::sc_attribute<char*>*
>(base_attr)) {
509 auto par =
new cci::cci_param_typed<std::string>(hier_name, attr->value, broker,
"", cci::CCI_ABSOLUTE_NAME);
510 params.emplace_back(cci::cci_param_post_write_callback_untyped([attr](
const cci::cci_param_write_event<>& ev) {
513 attr->value = strdup(ev.new_value.get<std::string>().c_str());
516 par->register_post_write_callback(params.back().first);
517 attr->value = strdup(par->get_value().c_str());
523 template <
typename T>
inline bool update_cci_param(cci::cci_param_untyped_handle& param_handle, sc_core::sc_attr_base* base_attr) {
524 if(
auto attr =
dynamic_cast<sc_core::sc_attribute<T>*
>(base_attr)) {
525 param_handle.set_cci_value(cci::cci_value(attr->value));
531 template <>
inline bool update_cci_param<char*>(cci::cci_param_untyped_handle& param_handle, sc_core::sc_attr_base* base_attr) {
532 if(
auto attr =
dynamic_cast<sc_core::sc_attribute<char*>*
>(base_attr)) {
533 param_handle.set_cci_value(cci::cci_value(std::string(attr->value)));
539 inline bool mirror_sc_attribute(configurer::broker_t& broker, configurer::cci_param_cln& params, cci::cci_originator& cci_originator,
540 std::string hier_name, sc_core::sc_attr_base* base_attr,
bool update =
false) {
541 auto param_handle = broker.get_param_handle(hier_name);
542 if(!param_handle.is_valid()) {
543 if(create_cci_param<int>(base_attr, hier_name, params, broker, cci_originator))
545 if(create_cci_param<unsigned>(base_attr, hier_name, params, broker, cci_originator))
547 if(create_cci_param<long>(base_attr, hier_name, params, broker, cci_originator))
549 if(create_cci_param<unsigned long>(base_attr, hier_name, params, broker, cci_originator))
551 if(create_cci_param<long long>(base_attr, hier_name, params, broker, cci_originator))
553 if(create_cci_param<unsigned long long>(base_attr, hier_name, params, broker, cci_originator))
555 if(create_cci_param<bool>(base_attr, hier_name, params, broker, cci_originator))
557 if(create_cci_param<float>(base_attr, hier_name, params, broker, cci_originator))
559 if(create_cci_param<double>(base_attr, hier_name, params, broker, cci_originator))
561 if(create_cci_param<std::string>(base_attr, hier_name, params, broker, cci_originator))
563 if(create_cci_param<char*>(base_attr, hier_name, params, broker, cci_originator))
566 if(update_cci_param<int>(param_handle, base_attr))
568 if(update_cci_param<unsigned>(param_handle, base_attr))
570 if(update_cci_param<long>(param_handle, base_attr))
572 if(update_cci_param<unsigned long>(param_handle, base_attr))
574 if(update_cci_param<long long>(param_handle, base_attr))
576 if(update_cci_param<unsigned long long>(param_handle, base_attr))
578 if(update_cci_param<bool>(param_handle, base_attr))
580 if(update_cci_param<float>(param_handle, base_attr))
582 if(update_cci_param<double>(param_handle, base_attr))
584 if(update_cci_param<std::string>(param_handle, base_attr))
586 if(update_cci_param<char*>(param_handle, base_attr))
592 void mirror_sc_attributes(configurer::broker_t& broker, configurer::cci_param_cln& params, cci::cci_originator& cci_originator,
593 sc_core::sc_object* topobj =
nullptr,
bool update =
false) {
594 for(
auto obj : get_sc_objects(topobj)) {
595 if(
auto mod =
dynamic_cast<sc_core::sc_module*
>(obj)) {
596 for(
auto base_attr : mod->attr_cltn()) {
597 std::string hier_name = fmt::format(
"{}.{}", mod->name(), base_attr->name());
598 mirror_sc_attribute(broker, params, cci_originator, hier_name, base_attr, update);
600 mirror_sc_attributes(broker, params, cci_originator, mod, update);
605 bool cci_name_ignore(std::pair<std::string, cci::cci_value>
const& preset_value) {
606 std::string ending(SCC_LOG_LEVEL_PARAM_NAME);
607 auto& name = preset_value.first;
608 if(name.length() >= ending.length()) {
609 return (0 == name.compare(name.length() - ending.length(), ending.length(), ending));
616 struct configurer::ConfigHolder :
public yaml_config_reader {
617 ConfigHolder(configurer::broker_t& broker)
618 : yaml_config_reader(broker) {}
623 : json_config_reader(broker) {}
627 configurer::configurer(
const std::string& filename,
unsigned config_phases)
628 :
configurer(filename, config_phases,
"$$$configurer$$$") {}
630 configurer::configurer(
const std::string& filename,
unsigned config_phases, sc_core::sc_module_name nm)
632 , config_phases(config_phases)
636 if(filename.length() > 0)
637 read_input_file(filename);
640 configurer::~configurer() {}
642 void configurer::read_input_file(
const std::string& filename) {
644 std::ifstream is(filename);
649 SCCERR() <<
"Could not parse input file " << filename <<
", " << root->get_error_msg();
651 root->configure_cci();
653 }
catch(std::runtime_error& e) {
654 SCCERR() <<
"Could not parse input file " << filename <<
", reason: " << e.what();
657 SCCWARN() <<
"Could not open input file " << filename;
665 yaml_config_dumper dumper(
cci_broker, with_description);
666 for(
auto* o : get_sc_objects(obj)) {
667 dumper.dump_config(o, root);
673 OStreamWrapper stream(os);
674 writer_type writer(stream);
675 writer.StartObject();
677 for(
auto* o : get_sc_objects(obj)) {
678 dumper.dump_config(o, writer);
686 std::string hier_name = fmt::format(
"{}.{}", owner->name(), attr_base->name());
687 mirror_sc_attribute(
cci_broker, cci2sc_attr, cci_originator, hier_name, attr_base);
690 inline std::string hier_name_as_regex(std::string
const& parname) {
691 if(parname.find_first_of(
"*?[") != std::string::npos) {
693 }
else if(parname[0] ==
'^') {
700 auto regex_str = hier_name_as_regex(hier_name);
701 if(regex_str.length()) {
702 auto rr = std::regex(regex_str);
703 cci::cci_param_predicate pred = [rr](cci::cci_param_untyped_handle
const& hndl) {
return regex_match(hndl.name(), rr); };
704 for(
auto& hndl : cci_broker.get_param_handles(pred)) {
705 hndl.set_cci_value(value);
708 cci::cci_param_handle param_handle = cci_broker.get_param_handle(hier_name);
709 if(param_handle.is_valid()) {
710 param_handle.set_cci_value(value);
712 cci_broker.set_preset_cci_value(hier_name, value);
717 void configurer::config_check() {
719 cci_broker.ignore_unconsumed_preset_values(&cci_name_ignore);
720 auto res = cci_broker.get_unconsumed_preset_values();
722 std::ostringstream oss;
724 oss <<
"\t - " << val.first <<
"\n";
725 if(res.size() == 1) {
726 SCCWARN(
"scc::configurer") <<
"There is " << res.size() <<
" unused CCI preset value:\n"
727 << oss.str() <<
"Please check your setup!";
729 SCCWARN(
"scc::configurer") <<
"There are " << res.size() <<
" unused CCI preset values:\n"
730 << oss.str() <<
"Please check your setup!";
733 }
catch(std::domain_error& e) {
734 SCCFATAL(
"scc::configurer") <<
"Illegal hierarchy name: '" << e.what() <<
"'";
735 }
catch(std::invalid_argument& e) {
736 SCCFATAL(
"scc::configurer") <<
"Illegal parameter name: '" << e.what() <<
"'";
740 void configurer::start_of_simulation() {
741 if(config_phases & START_OF_SIMULATION)
744 if(dump_file_name.size()) {
745 auto as_json = util::ends_with(dump_file_name,
"json");
746 std::ofstream of{dump_file_name};
748 mirror_sc_attributes(cci_broker, cci2sc_attr, cci_originator,
nullptr,
true);
bool is_logging_initialized()
get the state of the SCC logging system
log get_logging_level()
get the SystemC logging level
std::string glob_to_regex(std::string val)
T dir_name(T const &path, T const &delims="/\\")