8#include <nlohmann/json.hpp>
10#include <boost/algorithm/string.hpp>
14using json = nlohmann::json;
16namespace ChimeraTK::detail {
20 struct JsonAddressSpaceEntry;
22 struct JsonMapFileParser::Imp {
23 std::pair<NumericAddressedRegisterCatalogue, MetadataCatalogue> parse(std::ifstream& stream);
26 NumericAddressedRegisterCatalogue catalogue;
27 MetadataCatalogue metadata;
32 JsonMapFileParser::JsonMapFileParser(std::string fileName) : _theImp(std::make_unique<Imp>(std::move(fileName))) {}
34 JsonMapFileParser::~JsonMapFileParser() =
default;
38 std::pair<NumericAddressedRegisterCatalogue, MetadataCatalogue> JsonMapFileParser::parse(std::ifstream& stream) {
39 return _theImp->parse(stream);
52 NLOHMANN_JSON_SERIALIZE_ENUM(
53 Access, {{Access::READ_ONLY,
"RO"}, {Access::READ_WRITE,
"RW"}, {Access::WRITE_ONLY,
"WO"}})
58 enum RepresentationType {
65 NLOHMANN_JSON_SERIALIZE_ENUM(RepresentationType,
66 {{RepresentationType::FIXED_POINT,
"fixedPoint"}, {RepresentationType::IEEE754,
"IEEE754"},
67 {RepresentationType::VOID,
"void"}, {RepresentationType::ASCII,
"string"}})
72 enum AddressType { IO, DMA, addressTypeNotSet };
73 NLOHMANN_JSON_SERIALIZE_ENUM(AddressType, {{AddressType::IO,
"IO"}, {AddressType::DMA,
"DMA"}})
82 friend void from_json(
const json& j, HexValue& hv) {
84 auto sdata = std::string(j);
86 hv.v = std::stoll(sdata,
nullptr, 0);
88 catch(std::invalid_argument& e) {
89 throw json::type_error::create(0,
"Cannot parse string '" + sdata +
"' as number.", &j);
91 catch(std::out_of_range& e) {
92 throw json::type_error::create(0,
"Number '" + sdata +
"' out of range.", &j);
101 friend void to_json(
json& j,
const HexValue& hv) { j = hv.v; }
109 struct JsonAddressSpaceEntry {
111 std::string engineeringUnit;
112 std::string description;
113 Access access{Access::accessNotSet};
114 std::vector<size_t> triggeredByInterrupt;
115 size_t numberOfElements{1};
116 size_t bytesPerElement{0};
118 struct DoubleBufferingInfo {
119 struct SecondAddress {
120 AddressType type{AddressType::DMA};
124 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(SecondAddress, type, channel, offset)
127 SecondAddress secondaryBufferAddress;
128 std::string enableRegister;
129 std::string readBufferRegister;
132 void fill(NumericAddressedRegisterInfo& info)
const {
133 info.doubleBuffer->address = secondaryBufferAddress.offset.v;
134 info.doubleBuffer->enableRegisterPath = enableRegister;
135 info.doubleBuffer->inactiveBufferRegisterPath = readBufferRegister;
138 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
139 DoubleBufferingInfo, secondaryBufferAddress, enableRegister, readBufferRegister, index)
141 std::optional<DoubleBufferingInfo> doubleBuffering;
144 AddressType type{AddressType::IO};
146 HexValue offset{std::numeric_limits<size_t>::max()};
148 void fill(NumericAddressedRegisterInfo& info)
const {
149 assert(type != AddressType::addressTypeNotSet);
150 info.address = offset.v;
151 info.bar = channel + (type == AddressType::DMA ? 13 : 0);
154 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Address, type, channel, offset)
155 } address{AddressType::addressTypeNotSet};
157 struct Representation {
158 RepresentationType type{RepresentationType::FIXED_POINT};
159 uint32_t width{type != RepresentationType::representationNotSet ? 32U : 0U};
160 int32_t fractionalBits{0};
161 bool isSigned{
false};
163 void fill(NumericAddressedRegisterInfo& info,
size_t offset,
size_t bytesPerElement)
const {
164 if(type != RepresentationType::representationNotSet) {
166 type != RepresentationType::IEEE754 ? isSigned :
true,
170 Representation().fill(info, offset, bytesPerElement);
174 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Representation, type, width, fractionalBits, isSigned)
175 } representation{RepresentationType::representationNotSet};
178 size_t numberOfElements{0};
183 std::string engineeringUnit;
184 std::string description;
186 size_t bytesPerElement{4};
187 Representation representation{};
189 void fill(NumericAddressedRegisterInfo& info)
const { representation.fill(info, offset, bytesPerElement); }
191 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
192 Channel, name, engineeringUnit, description, offset, bytesPerElement, representation)
195 std::vector<Channel> channels;
197 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ChannelTab, numberOfElements, pitch, channels)
199 std::vector<ChannelTab> channelTabs;
201 void fill(NumericAddressedRegisterInfo& info,
const RegisterPath& parentName)
const {
202 info.pathName = parentName / name;
204 if(triggeredByInterrupt.empty()) {
209 if(access != Access::accessNotSet) {
211 "Register " + info.pathName +
": 'access' and 'triggeredByInterrupt' are mutually exclusive.");
213 info.interruptId = triggeredByInterrupt;
217 if(address.type != AddressType::addressTypeNotSet) {
218 if(representation.type == RepresentationType::VOID) {
222 if(channelTabs.empty()) {
223 info.elementPitchBits = bytesPerElement * 8;
224 info.nElements = numberOfElements;
225 representation.fill(info, 0, bytesPerElement);
228 if(channelTabs[0].channels.empty()) {
231 info.elementPitchBits = channelTabs[0].pitch * 8;
232 info.nElements = channelTabs[0].numberOfElements;
233 for(
const auto& channel : channelTabs[0].channels) {
239 if(representation.type != RepresentationType::VOID) {
240 throw ChimeraTK::logic_error(
"Address not set but representation given in register " + parentName / name);
242 if(triggeredByInterrupt.empty()) {
244 "Void-typed register " + parentName / name +
" needs 'triggeredByInterrupt' entry.");
248 info.interruptId = triggeredByInterrupt;
251 if(doubleBuffering) {
252 info.doubleBuffer.emplace();
253 doubleBuffering->fill(info);
256 info.doubleBuffer.reset();
260 std::vector<JsonAddressSpaceEntry> children;
262 void addInfos(NumericAddressedRegisterCatalogue& catalogue,
const RegisterPath& parentName)
const {
266 if(address.type != AddressType::addressTypeNotSet ||
267 representation.type != RepresentationType::representationNotSet) {
268 NumericAddressedRegisterInfo my;
270 fill(my, parentName);
271 my.computeDataDescriptor();
272 catalogue.addRegister(my);
273 if(doubleBuffering.has_value()) {
275 NumericAddressedRegisterInfo buf0Register = my;
276 buf0Register.pathName = my.pathName +
"/BUF0";
277 buf0Register.doubleBuffer.reset();
279 buf0Register.computeDataDescriptor();
280 catalogue.addRegister(buf0Register);
281 NumericAddressedRegisterInfo buf1Register = my;
282 buf1Register.pathName = my.pathName +
"/BUF1";
283 buf1Register.doubleBuffer.reset();
284 buf1Register.address = doubleBuffering->secondaryBufferAddress.offset.v;
288 buf1Register.computeDataDescriptor();
289 catalogue.addRegister(buf1Register);
292 for(
const auto& child : children) {
293 child.addInfos(catalogue, parentName / name);
297 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(JsonAddressSpaceEntry, name, engineeringUnit, description, access,
298 triggeredByInterrupt, numberOfElements, bytesPerElement, address, representation, children, channelTabs,
304 struct InterruptHandlerEntry {
307 std::set<std::string> options;
309 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Controller, path, options, version)
312 std::map<std::string, InterruptHandlerEntry> subhandler;
314 void fill(
const std::vector<size_t>& intId, MetadataCatalogue& metadata)
const {
319 jsonController = INTC;
320 metadata.addMetadata(
"!" + jsonIntId.dump(), R
"({"INTC":)" + jsonController.dump() + "}");
323 for(
const auto& [subIntId, handler] : subhandler) {
324 std::vector<size_t> qualfiedSubIntId = intId;
325 qualfiedSubIntId.push_back(std::stoll(subIntId));
326 handler.fill(qualfiedSubIntId, metadata);
330 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(InterruptHandlerEntry, INTC, subhandler)
336 std::pair<NumericAddressedRegisterCatalogue, MetadataCatalogue> JsonMapFileParser::Imp::parse(std::ifstream& stream) {
339 auto data = json::parse(stream);
341 std::vector<JsonAddressSpaceEntry> addressSpace = data.at(
"addressSpace");
342 for(
const auto& entry : addressSpace) {
343 entry.addInfos(catalogue,
"/");
346 for(
const auto& entry : data.at(
"metadata").items()) {
347 if(entry.key().empty()) {
349 "Error parsing JSON map file '" + fileName +
"': Metadata key must not be empty.");
351 if(entry.key()[0] ==
'_') {
354 metadata.addMetadata(entry.key(), entry.value());
358 InterruptHandlerEntry interruptHandler;
359 interruptHandler.subhandler = data.at(
"interruptHandler");
360 interruptHandler.fill({}, metadata);
362 return {std::move(catalogue), std::move(metadata)};
367 catch(
const json::exception& e) {
Access
Enum describing the access mode of the register:
Type
Enum descibing the data interpretation:
Exception thrown when a logic error has occured.
const char * what() const noexcept override
Return the message describing what exactly went wrong.
std::string to_string(const std::string &v)