6#include <nlohmann/json.hpp>
8#include <boost/algorithm/string.hpp>
12using json = nlohmann::json;
14namespace ChimeraTK::detail {
18 struct JsonAddressSpaceEntry;
20 struct JsonMapFileParser::Imp {
21 std::pair<NumericAddressedRegisterCatalogue, MetadataCatalogue> parse(std::ifstream& stream);
24 NumericAddressedRegisterCatalogue catalogue;
25 MetadataCatalogue metadata;
30 JsonMapFileParser::JsonMapFileParser(std::string fileName) : _theImp(std::make_unique<Imp>(std::move(fileName))) {}
32 JsonMapFileParser::~JsonMapFileParser() =
default;
36 std::pair<NumericAddressedRegisterCatalogue, MetadataCatalogue> JsonMapFileParser::parse(std::ifstream& stream) {
37 return _theImp->parse(stream);
50 NLOHMANN_JSON_SERIALIZE_ENUM(
51 Access, {{Access::READ_ONLY,
"RO"}, {Access::READ_WRITE,
"RW"}, {Access::WRITE_ONLY,
"WO"}})
56 enum RepresentationType {
63 NLOHMANN_JSON_SERIALIZE_ENUM(RepresentationType,
64 {{RepresentationType::FIXED_POINT,
"fixedPoint"}, {RepresentationType::IEEE754,
"IEEE754"},
65 {RepresentationType::VOID,
"void"}, {RepresentationType::ASCII,
"string"}})
70 enum AddressType { IO, DMA, addressTypeNotSet };
71 NLOHMANN_JSON_SERIALIZE_ENUM(AddressType, {{AddressType::IO,
"IO"}, {AddressType::DMA,
"DMA"}})
80 friend void from_json(
const json& j, HexValue& hv) {
82 auto sdata = std::string(j);
84 hv.v = std::stoll(sdata,
nullptr, 0);
86 catch(std::invalid_argument& e) {
87 throw json::type_error::create(0,
"Cannot parse string '" + sdata +
"' as number.", &j);
89 catch(std::out_of_range& e) {
90 throw json::type_error::create(0,
"Number '" + sdata +
"' out of range.", &j);
99 friend void to_json(
json& j,
const HexValue& hv) { j = hv.v; }
107 struct JsonAddressSpaceEntry {
109 std::string engineeringUnit;
110 std::string description;
111 Access access{Access::accessNotSet};
112 std::vector<size_t> triggeredByInterrupt;
113 size_t numberOfElements{1};
114 size_t bytesPerElement{0};
117 AddressType type{AddressType::IO};
119 HexValue offset{std::numeric_limits<size_t>::max()};
121 void fill(NumericAddressedRegisterInfo& info)
const {
122 assert(type != AddressType::addressTypeNotSet);
123 info.address = offset.v;
124 info.bar = channel + (type == AddressType::DMA ? 13 : 0);
127 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Address, type, channel, offset)
128 } address{AddressType::addressTypeNotSet};
130 struct Representation {
131 RepresentationType type{RepresentationType::FIXED_POINT};
132 uint32_t width{type != RepresentationType::representationNotSet ? 32U : 0U};
133 int32_t fractionalBits{0};
134 bool isSigned{
false};
136 void fill(NumericAddressedRegisterInfo& info,
size_t offset)
const {
137 if(type != RepresentationType::representationNotSet) {
139 type != RepresentationType::IEEE754 ? isSigned :
true);
142 Representation().fill(info, offset);
146 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Representation, type, width, fractionalBits, isSigned)
147 } representation{RepresentationType::representationNotSet};
150 size_t numberOfElements{0};
155 std::string engineeringUnit;
156 std::string description;
158 size_t bytesPerElement{4};
159 Representation representation{};
161 void fill(NumericAddressedRegisterInfo& info)
const { representation.fill(info, offset); }
163 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
164 Channel, name, engineeringUnit, description, offset, bytesPerElement, representation)
167 std::vector<Channel> channels;
169 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ChannelTab, numberOfElements, pitch, channels)
171 std::vector<ChannelTab> channelTabs;
173 void fill(NumericAddressedRegisterInfo& info,
const RegisterPath& parentName)
const {
174 info.pathName = parentName / name;
176 if(triggeredByInterrupt.empty()) {
181 if(access != Access::accessNotSet) {
183 "Register " + info.pathName +
": 'access' and 'triggeredByInterrupt' are mutually exclusive.");
185 info.interruptId = triggeredByInterrupt;
189 if(address.type != AddressType::addressTypeNotSet) {
190 if(representation.type == RepresentationType::VOID) {
194 if(channelTabs.empty()) {
195 info.elementPitchBits = bytesPerElement * 8;
196 info.nElements = numberOfElements;
197 representation.fill(info, 0);
200 if(channelTabs[0].channels.empty()) {
203 info.elementPitchBits = channelTabs[0].pitch * 8;
204 info.nElements = channelTabs[0].numberOfElements;
205 for(
const auto& channel : channelTabs[0].channels) {
211 if(representation.type != RepresentationType::VOID) {
212 throw ChimeraTK::logic_error(
"Address not set but representation given in register " + parentName / name);
214 if(triggeredByInterrupt.empty()) {
216 "Void-typed register " + parentName / name +
" needs 'triggeredByInterrupt' entry.");
220 info.interruptId = triggeredByInterrupt;
225 std::vector<JsonAddressSpaceEntry> children;
227 void addInfos(NumericAddressedRegisterCatalogue& catalogue,
const RegisterPath& parentName)
const {
231 if(address.type != AddressType::addressTypeNotSet ||
232 representation.type != RepresentationType::representationNotSet) {
233 NumericAddressedRegisterInfo my;
235 fill(my, parentName);
236 my.computeDataDescriptor();
237 catalogue.addRegister(my);
239 for(
const auto& child : children) {
240 child.addInfos(catalogue, parentName / name);
244 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(JsonAddressSpaceEntry, name, engineeringUnit, description, access,
245 triggeredByInterrupt, numberOfElements, bytesPerElement, address, representation, children, channelTabs)
250 struct InterruptHandlerEntry {
253 std::set<std::string> options;
255 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Controller, path, options, version)
258 std::map<std::string, InterruptHandlerEntry> subhandler;
260 void fill(
const std::vector<size_t>& intId, MetadataCatalogue& metadata)
const {
265 jsonController = INTC;
266 metadata.addMetadata(
"!" + jsonIntId.dump(), R
"({"INTC":)" + jsonController.dump() + "}");
269 for(
const auto& [subIntId, handler] : subhandler) {
270 std::vector<size_t> qualfiedSubIntId = intId;
271 qualfiedSubIntId.push_back(std::stoll(subIntId));
272 handler.fill(qualfiedSubIntId, metadata);
276 NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(InterruptHandlerEntry, INTC, subhandler)
282 std::pair<NumericAddressedRegisterCatalogue, MetadataCatalogue> JsonMapFileParser::Imp::parse(std::ifstream& stream) {
285 auto data = json::parse(stream);
287 std::vector<JsonAddressSpaceEntry> addressSpace = data.at(
"addressSpace");
288 for(
const auto& entry : addressSpace) {
289 entry.addInfos(catalogue,
"/");
292 for(
const auto& entry : data.at(
"metadata").items()) {
293 if(entry.key().empty()) {
295 "Error parsing JSON map file '" + fileName +
"': Metadata key must not be empty.");
297 if(entry.key()[0] ==
'_') {
300 metadata.addMetadata(entry.key(), entry.value());
304 InterruptHandlerEntry interruptHandler;
305 interruptHandler.subhandler = data.at(
"interruptHandler");
306 interruptHandler.fill({}, metadata);
308 return {std::move(catalogue), std::move(metadata)};
313 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.