scc  2022.4.0
SystemC components library
configurer_nocci.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 <cstring>
22 #include <fmt/format.h>
23 #include <fstream>
24 #include <rapidjson/istreamwrapper.h>
25 #include <rapidjson/ostreamwrapper.h>
26 #include <rapidjson/prettywriter.h>
27 #include <unordered_map>
28 
29 namespace scc {
30 using namespace rapidjson;
31 using writer_type = PrettyWriter<OStreamWrapper>;
32 namespace {
33 inline auto get_sc_objects(sc_core::sc_object* obj = nullptr) -> const std::vector<sc_core::sc_object*>& {
34  if(obj)
35  return obj->get_child_objects();
36  else
37  return sc_core::sc_get_top_level_objects();
38 }
39 
40 #define FDECL(TYPE, FUNC) \
41  inline void writeValue(writer_type& writer, std::string const& key, TYPE value) { \
42  writer.Key(key.c_str()); \
43  writer.FUNC(value); \
44  }
45 FDECL(int, Int)
46 FDECL(unsigned int, Uint)
47 FDECL(long, Int64)
48 FDECL(unsigned long, Uint64)
49 FDECL(long long, Int64)
50 FDECL(unsigned long long, Uint64)
51 FDECL(bool, Bool)
52 FDECL(float, Double)
53 FDECL(double, Double)
54 FDECL(char const*, String)
55 inline void writeValue(writer_type& writer, std::string const& key, std::string const& value) {
56  writer.Key(key.c_str());
57  writer.String(value.c_str());
58 }
59 
60 template <typename T> auto check_n_assign(writer_type& writer, sc_core::sc_attr_base* attr_base) -> bool {
61  auto* a = dynamic_cast<sc_core::sc_attribute<T>*>(attr_base);
62  if(a != nullptr) {
63  writeValue(writer, a->name(), a->value);
64  return true;
65  }
66  return false;
67 }
68 
69 template <> auto check_n_assign<sc_core::sc_time>(writer_type& writer, sc_core::sc_attr_base* attr_base) -> bool {
70  auto* a = dynamic_cast<sc_core::sc_attribute<sc_core::sc_time>*>(attr_base);
71  if(a != nullptr) {
72  writeValue(writer, a->name(), a->value.to_double());
73  return true;
74  }
75  return false;
76 }
77 
78 inline bool start_object(writer_type& writer, char const* key, bool started) {
79  if(!started) {
80  writer.Key(key);
81  writer.StartObject();
82  }
83  return true;
84 }
85 
86 struct config_dumper {
87  config_dumper() {}
88 
89  void dump_config(sc_core::sc_object* obj, writer_type& writer) {
90  auto start = std::string(obj->basename()).substr(0, 3);
91  if(start == "$$$")
92  return;
93  auto obj_started = false;
94  for(sc_core::sc_attr_base* attr_base : obj->attr_cltn()) {
95  obj_started = start_object(writer, obj->basename(), obj_started);
96  check_n_assign<int>(writer, attr_base) || check_n_assign<unsigned>(writer, attr_base) ||
97  check_n_assign<long>(writer, attr_base) || check_n_assign<unsigned long>(writer, attr_base) ||
98  check_n_assign<long long>(writer, attr_base) || check_n_assign<unsigned long long>(writer, attr_base) ||
99  check_n_assign<bool>(writer, attr_base) || check_n_assign<float>(writer, attr_base) ||
100  check_n_assign<double>(writer, attr_base) || check_n_assign<std::string>(writer, attr_base) ||
101  check_n_assign<char*>(writer, attr_base) || check_n_assign<sc_core::sc_time>(writer, attr_base);
102  }
103  for(auto* o : get_sc_objects(obj)) {
104  obj_started = start_object(writer, obj->basename(), obj_started);
105  dump_config(o, writer);
106  }
107  if(obj_started)
108  writer.EndObject();
109  }
110 };
111 
112 #define CHECK_N_ASSIGN(TYPE, ATTR, VAL) \
113  { \
114  auto* a = dynamic_cast<sc_core::sc_attribute<TYPE>*>(ATTR); \
115  if(a != nullptr) { \
116  a->value = VAL; \
117  return; \
118  } \
119  }
120 
121 void try_set_value(sc_core::sc_attr_base* attr_base, Value const& hier_val) {
122  CHECK_N_ASSIGN(int, attr_base, hier_val.Get<int>());
123  CHECK_N_ASSIGN(unsigned, attr_base, hier_val.Get<unsigned>());
124  CHECK_N_ASSIGN(int64_t, attr_base, hier_val.Get<int64_t>());
125  CHECK_N_ASSIGN(uint64_t, attr_base, hier_val.Get<uint64_t>());
126  CHECK_N_ASSIGN(bool, attr_base, hier_val.Get<bool>());
127  CHECK_N_ASSIGN(float, attr_base, hier_val.Get<float>());
128  CHECK_N_ASSIGN(double, attr_base, hier_val.Get<double>());
129  CHECK_N_ASSIGN(std::string, attr_base, std::string(hier_val.GetString()));
130  CHECK_N_ASSIGN(char*, attr_base, strdup(hier_val.GetString()));
131  {
132  auto* a = dynamic_cast<sc_core::sc_attribute<sc_core::sc_time>*>(attr_base);
133  if(a != nullptr) {
134  a->value = sc_core::sc_time(hier_val.Get<double>(), sc_core::SC_SEC);
135  return;
136  }
137  }
138 }
139 
140 void configure_sc_attribute_hierarchical(Value const& node, std::string const& prefix) {
141  for(auto itr = node.MemberBegin(); itr != node.MemberEnd(); ++itr) {
142  if(!itr->name.IsString())
143  return;
144  auto key_name = itr->name.GetString();
145  if(strncmp(key_name, "log_level", 9) == 0)
146  continue; // virtual attribute
147  auto hier_name = prefix.size() ? prefix + "." + key_name : key_name;
148  Value const& val = itr->value;
149  if(val.IsNull() || val.IsArray())
150  continue;
151  else if(val.IsObject()) {
152  if(sc_core::sc_find_object(hier_name.c_str())) {
153  configure_sc_attribute_hierarchical(val, hier_name);
154  }
155  } else {
156  auto pos = hier_name.rfind('.');
157  if(pos != std::string::npos) {
158  auto objname = hier_name.substr(0, pos);
159  auto attrname = hier_name.substr(pos + 1);
160  if(auto* obj = sc_core::sc_find_object(objname.c_str()))
161  if(auto attr = obj->get_attribute(attrname.c_str()))
162  try_set_value(attr, val);
163  }
164  }
165  }
166 }
167 
168 void check_config_hierarchical(Value const& node, std::string const& prefix) {
169  for(auto itr = node.MemberBegin(); itr != node.MemberEnd(); ++itr) {
170  if(!itr->name.IsString())
171  return;
172  auto key_name = itr->name.GetString();
173  if(strncmp(key_name, SCC_LOG_LEVEL_PARAM_NAME, 9) == 0)
174  continue; // virtual attribute
175  auto hier_name = prefix.size() ? prefix + "." + key_name : key_name;
176  Value const& val = itr->value;
177  if(val.IsNull() || val.IsArray())
178  continue;
179  else if(val.IsObject()) {
180  if(!sc_core::sc_find_object(hier_name.c_str())) {
181  if(prefix.size())
182  throw std::domain_error(hier_name);
183  } else
184  check_config_hierarchical(val, hier_name);
185  } else {
186  auto pos = hier_name.rfind('.');
187  if(pos != std::string::npos) {
188  auto objname = hier_name.substr(0, pos);
189  auto attrname = hier_name.substr(pos + 1);
190  auto* obj = sc_core::sc_find_object(objname.c_str());
191  if(!obj || !obj->get_attribute(attrname.c_str())) {
192  throw std::invalid_argument(hier_name);
193  }
194  }
195  }
196  }
197 }
198 } // namespace
199 
200 struct configurer::ConfigHolder {
201  Document document;
202 };
203 
204 configurer::configurer(const std::string& filename, unsigned config_phases)
205 : configurer(filename, config_phases, "$$$configurer$$$") {}
206 configurer::configurer(const std::string& filename, unsigned config_phases, sc_core::sc_module_name nm)
207 : base_type(nm)
208 , config_phases(config_phases)
209 , cci_broker(nullptr)
210 , root(new ConfigHolder) {
211  if(filename.length() > 0)
212  read_input_file(filename);
213 }
214 
215 configurer::~configurer() {}
216 
217 void configurer::read_input_file(const std::string& filename) {
218  std::ifstream is(filename);
219  if(is.is_open()) {
220  try {
221  IStreamWrapper stream(is);
222  root->document.ParseStream(stream);
223  if(root->document.HasParseError()) {
224  SCCERR() << "Could not parse input file " << filename << ", location " << (unsigned)root->document.GetErrorOffset()
225  << ", reason: " << GetParseError_En(root->document.GetParseError());
226  } else {
227  config_valid = true;
228  }
229  } catch(std::runtime_error& e) {
230  SCCERR() << "Could not parse input file " << filename << ", reason: " << e.what();
231  }
232  } else {
233  SCCERR() << "Could not open input file " << filename;
234  }
235 }
236 
237 void configurer::dump_configuration(std::ostream& os, bool as_yaml, bool with_description, sc_core::sc_object* obj) {
238  OStreamWrapper stream(os);
239  writer_type writer(stream);
240  writer.StartObject();
241  config_dumper dumper;
242  for(auto* o : get_sc_objects(obj)) {
243  dumper.dump_config(o, writer);
244  }
245  writer.EndObject();
246 }
247 
248 void configurer::configure() {
249  if(config_valid && root) {
250  configure_sc_attribute_hierarchical(root->document, "");
251  }
252 }
253 
254 auto get_value_from_hierarchy(const std::string& hier_name, Value const& value) -> Value const& {
255  size_t npos = hier_name.find_first_of('.');
256  auto member = value.FindMember(hier_name.substr(0, npos).c_str());
257  auto& val = member->value;
258  if(val.IsNull() || npos == std::string::npos || !val.IsObject())
259  return val;
260  return get_value_from_hierarchy(hier_name.substr(npos + 1, hier_name.size()), val);
261 }
262 
263 void configurer::set_configuration_value(sc_core::sc_attr_base* attr_base, sc_core::sc_object* owner) {
264  if(root) {
265  std::string name(owner->name());
266  name += ".";
267  name += attr_base->name();
268  size_t npos = name.find_first_of('.');
269  auto member = root->document.FindMember(name.substr(0, npos).c_str());
270  auto& val = get_value_from_hierarchy(name, member->value);
271  if(!val.IsNull())
272  try_set_value(attr_base, val);
273  }
274 }
275 void configurer::config_check() {
276  try {
277  if(root) {
278  check_config_hierarchical(root->document, "");
279  }
280  } catch(std::domain_error& e) {
281  SCCFATAL("scc::configurer") << "Illegal hierarchy name: '" << e.what() << "'";
282  } catch(std::invalid_argument& e) {
283  SCCFATAL("scc::configurer") << "Illegal parameter name: '" << e.what() << "'";
284  }
285 }
286 
287 void configurer::start_of_simulation() {
288  if(config_phases & START_OF_SIMULATION)
289  configure();
290  config_check();
291  if(dump_file_name.size()) {
292  std::ofstream of{dump_file_name};
293  if(of.is_open())
294  dump_configuration(of, with_description);
295  }
296 }
297 
298 } // namespace scc
SCC SystemC utilities.