scc 2025.09
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
29namespace scc {
30using namespace rapidjson;
31using writer_type = PrettyWriter<OStreamWrapper>;
32namespace {
33inline 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 }
45FDECL(int, Int)
46FDECL(unsigned int, Uint)
47FDECL(long, Int64)
48FDECL(unsigned long, Uint64)
49FDECL(long long, Int64)
50FDECL(unsigned long long, Uint64)
51FDECL(bool, Bool)
52FDECL(float, Double)
53FDECL(double, Double)
54FDECL(char const*, String)
55inline 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
60template <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
69template <> 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
78inline 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
86struct 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
121void 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
140void 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
168void 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
201 Document document;
202};
203
204configurer::configurer(const std::string& filename, unsigned config_phases)
205: configurer(filename, config_phases, "$$$configurer$$$") {}
206configurer::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
215configurer::~configurer() {}
216
217void 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
237void 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
248void configurer::configure() {
249 if(config_valid && root) {
250 configure_sc_attribute_hierarchical(root->document, "");
251 }
252}
253
254auto 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
263void 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}
275void 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
287void 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
design configuration reader
Definition configurer.h:41
configurer(std::string const &filename, unsigned sc_attr_config_phases=BEFORE_END_OF_ELABORATION)
SCC TLM utilities.