8#include <nlohmann/json.hpp>
10#include <boost/algorithm/string.hpp>
16using json = nlohmann::json;
18namespace ChimeraTK::detail {
22 struct JsonAddressSpaceEntry;
24 struct JsonMapFileParser::Imp {
25 std::pair<NumericAddressedRegisterCatalogue, MetadataCatalogue> parse(std::ifstream& stream);
28 NumericAddressedRegisterCatalogue catalogue;
29 MetadataCatalogue metadata;
34 JsonMapFileParser::JsonMapFileParser(std::string fileName) : _theImp(std::make_unique<Imp>(std::move(fileName))) {}
36 JsonMapFileParser::~JsonMapFileParser() =
default;
40 std::pair<NumericAddressedRegisterCatalogue, MetadataCatalogue> JsonMapFileParser::parse(std::ifstream& stream) {
41 return _theImp->parse(stream);
54 NLOHMANN_JSON_SERIALIZE_ENUM(
55 Access, {{Access::READ_ONLY,
"RO"}, {Access::READ_WRITE,
"RW"}, {Access::WRITE_ONLY,
"WO"}})
60 enum RepresentationType {
67 NLOHMANN_JSON_SERIALIZE_ENUM(RepresentationType,
68 {{RepresentationType::FIXED_POINT,
"fixedPoint"}, {RepresentationType::IEEE754,
"IEEE754"},
69 {RepresentationType::VOID,
"void"}, {RepresentationType::ASCII,
"string"}})
74 enum AddressType { IO, DMA, addressTypeNotSet };
75 NLOHMANN_JSON_SERIALIZE_ENUM(AddressType, {{AddressType::IO,
"IO"}, {AddressType::DMA,
"DMA"}})
84 friend void from_json(
const json& j, HexValue& hv) {
86 auto sdata = std::string(j);
88 hv.v = std::stoll(sdata,
nullptr, 0);
90 catch(std::invalid_argument& e) {
91 throw json::type_error::create(0,
"Cannot parse string '" + sdata +
"' as number.", &j);
93 catch(std::out_of_range& e) {
94 throw json::type_error::create(0,
"Number '" + sdata +
"' out of range.", &j);
103 friend void to_json(
json& j,
const HexValue& hv) { j = hv.v; }
111 struct JsonAddressSpaceEntry {
112 std::string engineeringUnit;
113 std::string description;
114 Access access{Access::accessNotSet};
115 std::vector<size_t> triggeredByInterrupt;
116 size_t numberOfElements{1};
117 size_t bytesPerElement{0};
119 struct DoubleBufferingInfo {
120 struct SecondAddress {
121 AddressType type{AddressType::DMA};
125 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(SecondAddress, type, channel, offset)
128 SecondAddress secondaryBufferAddress;
129 std::string enableRegister;
130 std::string readBufferRegister;
133 void fill(NumericAddressedRegisterInfo& info)
const {
134 info.doubleBuffer->address = secondaryBufferAddress.offset.v;
135 info.doubleBuffer->enableRegisterPath = enableRegister;
136 info.doubleBuffer->inactiveBufferRegisterPath = readBufferRegister;
137 info.doubleBuffer->index = index;
140 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
141 DoubleBufferingInfo, secondaryBufferAddress, enableRegister, readBufferRegister, index)
143 std::optional<DoubleBufferingInfo> doubleBuffering;
146 AddressType type{AddressType::IO};
148 HexValue offset{std::numeric_limits<size_t>::max()};
150 void fill(NumericAddressedRegisterInfo& info)
const {
151 assert(type != AddressType::addressTypeNotSet);
152 info.address = offset.v;
153 info.bar = channel + (type == AddressType::DMA ? 13 : 0);
156 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Address, type, channel, offset)
157 } address{AddressType::addressTypeNotSet};
160 struct Representation {
161 RepresentationType type{RepresentationType::FIXED_POINT};
162 uint32_t width{type != RepresentationType::representationNotSet ? 32U : 0U};
163 int32_t fractionalBits{0};
164 bool isSigned{
false};
165 uint32_t bitShift{0};
167 void fill(NumericAddressedRegisterInfo& info,
size_t offset,
size_t bytesPerElem)
const {
168 if(type != RepresentationType::representationNotSet) {
170 type != RepresentationType::IEEE754 ? isSigned :
true,
172 info.channels.back().bitOffset += bitShift;
174 info.isBitRange =
true;
178 Representation().fill(info, offset, bytesPerElem);
182 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Representation, type, width, fractionalBits, isSigned, bitShift)
183 } representation{RepresentationType::representationNotSet};
186 size_t numberOfElements{0};
190 std::string engineeringUnit;
191 std::string description;
193 size_t bytesPerElement{4};
194 Representation representation{};
197 void fill(NumericAddressedRegisterInfo& info)
const { representation.fill(info, offset, bytesPerElement); }
199 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
200 Channel, engineeringUnit, description, offset, bytesPerElement, representation)
206 [[nodiscard]] std::vector<std::pair<std::string, const Channel*>> channelsInOffsetOrder()
const {
207 std::vector<std::pair<std::string, const Channel*>> result;
208 result.reserve(channels.size());
209 for(
const auto& [channelName, channel] : channels) {
210 result.emplace_back(channelName, &channel);
212 std::ranges::sort(result, [](
const auto& a,
const auto& b) {
return a.second->offset < b.second->offset; });
216 std::map<std::string, Channel> channels;
218 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ChannelTab, numberOfElements, pitch, channels)
220 std::vector<ChannelTab> channelTabs;
222 void fill(NumericAddressedRegisterInfo& info,
const std::string& name,
const RegisterPath& parentName,
223 bool addressSetByParent)
const {
224 info.pathName = parentName / name;
225 info.pathName.setAltSeparator(
".");
227 if(triggeredByInterrupt.empty()) {
228 if(access != Access::accessNotSet) {
231 else if(!addressSetByParent) {
236 if(access != Access::accessNotSet) {
238 "Register " + info.pathName +
": 'access' and 'triggeredByInterrupt' are mutually exclusive.");
240 info.interruptId = triggeredByInterrupt;
244 if(representation.type != RepresentationType::VOID) {
245 if(address.type != AddressType::addressTypeNotSet) {
247 if(channelTabs.empty()) {
248 auto bPerElem = (bytesPerElement != 0 ? bytesPerElement : 4);
249 info.elementPitchBits = bPerElem * 8;
250 info.nElements = numberOfElements;
251 representation.fill(info, 0, bPerElem);
254 if(channelTabs[0].channels.empty()) {
257 info.elementPitchBits = channelTabs[0].pitch * 8;
258 info.nElements = channelTabs[0].numberOfElements;
261 for(
const auto& [channelName, channel] : channelTabs[0].channelsInOffsetOrder()) {
267 else if(addressSetByParent) {
268 if(channelTabs.empty()) {
269 if(representation.type == RepresentationType::representationNotSet) {
271 " which inherited the address from parent!");
274 representation.fill(info, 0, (bytesPerElement != 0 ? bytesPerElement : info.elementPitchBits / 8));
277 throw ChimeraTK::logic_error(
"Address must be set for entries in channel tabs: register " + info.pathName);
281 throw ChimeraTK::logic_error(
"Address not set but representation given in register " + parentName / name);
286 if(address.type != AddressType::addressTypeNotSet) {
289 if(triggeredByInterrupt.empty()) {
291 "Void-typed register " + parentName / name +
" needs 'triggeredByInterrupt' entry.");
295 info.interruptId = triggeredByInterrupt;
297 info.channels.clear();
301 if(doubleBuffering) {
302 info.doubleBuffer.emplace();
303 doubleBuffering->fill(info);
306 info.doubleBuffer.reset();
309 info.description = description;
310 info.engineeringUnit = engineeringUnit;
313 std::map<std::string, JsonAddressSpaceEntry> children;
315 void addInfos(NumericAddressedRegisterCatalogue& catalogue,
const std::string& name,
const RegisterPath& parentName,
316 bool addressSetByParent)
const {
320 if(address.type != AddressType::addressTypeNotSet) {
322 NumericAddressedRegisterInfo my;
324 fill(my, name, parentName, addressSetByParent);
325 my.computeDataDescriptor();
326 catalogue.addRegister(my);
327 if(!channelTabs.empty()) {
332 for(
const auto& [channelName, channel] : channelTabs[0].channelsInOffsetOrder()) {
333 RegisterPath slicePath = my.pathName / channelName;
334 slicePath.setAltSeparator(
".");
337 if(catalogue.hasRegister(slicePath)) {
340 const auto& rep = channel->representation;
341 NumericAddressedRegisterInfo::ChannelInfo ci{rep.bitShift,
343 rep.type != RepresentationType::IEEE754 ? rep.isSigned :
true,
352 NumericAddressedRegisterInfo slice(slicePath, my.bar, my.address + channel->offset, my.nElements,
353 my.elementPitchBits, {ci}, sliceAccessType, my.interruptId, my.doubleBuffer);
354 slice.isBitRange = (rep.bitShift != 0);
355 slice.computeDataDescriptor();
356 slice.engineeringUnit = channel->engineeringUnit;
357 slice.description = channel->description;
358 catalogue.addRegister(slice);
361 if(doubleBuffering.has_value()) {
363 NumericAddressedRegisterInfo buf0Register = my;
364 buf0Register.pathName = my.pathName +
"/BUF0";
365 buf0Register.doubleBuffer.reset();
367 buf0Register.computeDataDescriptor();
368 catalogue.addRegister(buf0Register);
369 NumericAddressedRegisterInfo buf1Register = my;
370 buf1Register.pathName = my.pathName +
"/BUF1";
371 buf1Register.doubleBuffer.reset();
372 buf1Register.address = doubleBuffering->secondaryBufferAddress.offset.v;
376 buf1Register.computeDataDescriptor();
377 catalogue.addRegister(buf1Register);
380 else if(representation.type != RepresentationType::representationNotSet) {
382 auto my = catalogue.getBackendRegister(parentName);
384 fill(my, name, parentName, addressSetByParent);
385 my.computeDataDescriptor();
386 catalogue.addRegister(my);
389 for(
const auto& [childName, child] : children) {
390 child.addInfos(catalogue, childName, parentName / name,
391 addressSetByParent || (address.type != AddressType::addressTypeNotSet));
395 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(JsonAddressSpaceEntry, engineeringUnit, description, access,
396 triggeredByInterrupt, numberOfElements, bytesPerElement, address, representation, children, channelTabs,
402 struct InterruptHandlerEntry {
405 std::set<std::string> options;
407 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Controller, path, options, version)
410 std::map<std::string, InterruptHandlerEntry> subhandler;
412 void fill(
const std::vector<size_t>& intId, MetadataCatalogue& metadata)
const {
417 jsonController = INTC;
418 metadata.addMetadata(
"!" + jsonIntId.dump(), R
"({"INTC":)" + jsonController.dump() + "}");
421 for(
const auto& [subIntId, handler] : subhandler) {
422 std::vector<size_t> qualfiedSubIntId = intId;
423 qualfiedSubIntId.push_back(std::stoll(subIntId));
424 handler.fill(qualfiedSubIntId, metadata);
428 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(InterruptHandlerEntry, INTC, subhandler)
434 std::pair<NumericAddressedRegisterCatalogue, MetadataCatalogue> JsonMapFileParser::Imp::parse(std::ifstream& stream) {
437 auto data = json::parse(stream);
439 std::map<std::string, JsonAddressSpaceEntry> addressSpace = data.at(
"addressSpace");
440 for(
const auto& [addressSpaceName, entry] : addressSpace) {
441 entry.addInfos(catalogue, addressSpaceName,
"/",
false);
447 std::set<std::pair<uint64_t, uint64_t>> addressesWithBitRange;
448 for(
auto& reg : catalogue) {
450 addressesWithBitRange.insert({reg.bar, reg.address});
453 if(addressesWithBitRange.size()) {
454 for(
auto& reg : catalogue) {
455 if((reg.channels.size() == 1) && (reg.channels[0].bitOffset == 0) &&
456 (reg.channels[0].width < reg.elementPitchBits)) {
457 if(addressesWithBitRange.find({reg.bar, reg.address}) != addressesWithBitRange.end()) {
458 reg.isBitRange =
true;
464 for(
const auto& entry : data.at(
"metadata").items()) {
465 if(entry.key().empty()) {
467 "Error parsing JSON map file '" + fileName +
"': Metadata key must not be empty.");
469 if(entry.key()[0] ==
'_') {
472 metadata.addMetadata(entry.key(), entry.value());
476 InterruptHandlerEntry interruptHandler;
477 interruptHandler.subhandler = data.at(
"interruptHandler");
478 interruptHandler.fill({}, metadata);
480 return {std::move(catalogue), std::move(metadata)};
485 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)