Line data Source code
1 : /**
2 : * @file SI534xNode.cpp
3 : *
4 : * This is part of the DUNE DAQ Software Suite, copyright 2020.
5 : * Licensing/copyright details are in the COPYING file that you should have
6 : * received with this code.
7 : */
8 :
9 : #include "timing/SI534xNode.hpp"
10 :
11 : // PDT headers
12 : #include "ers/ers.hpp"
13 : #include "logging/Logging.hpp"
14 :
15 : #include "timing/toolbox.hpp"
16 :
17 : #include <boost/algorithm/string/predicate.hpp>
18 : #include <boost/tuple/tuple.hpp>
19 :
20 : #include <chrono>
21 : #include <fstream>
22 : #include <map>
23 : #include <sstream>
24 : #include <string>
25 : #include <thread>
26 : #include <vector>
27 :
28 : namespace dunedaq {
29 : namespace timing {
30 :
31 : // uHAL Node registation
32 0 : UHAL_REGISTER_DERIVED_NODE(SI534xNode)
33 :
34 : //-----------------------------------------------------------------------------
35 0 : SI534xSlave::SI534xSlave(const I2CMasterNode* i2c_master, uint8_t address) // NOLINT(build/unsigned)
36 0 : : SIChipSlave(i2c_master, address)
37 0 : {}
38 : //-----------------------------------------------------------------------------
39 :
40 : //-----------------------------------------------------------------------------
41 0 : SI534xSlave::~SI534xSlave() {}
42 : //-----------------------------------------------------------------------------
43 :
44 : //-----------------------------------------------------------------------------
45 : std::string
46 0 : SI534xSlave::read_config_id() const
47 : {
48 0 : std::string id;
49 :
50 0 : for (size_t i(0); i < 8; ++i) {
51 0 : char id_char = static_cast<char>(read_clock_register(0x26b + i));
52 : // in case null paddding was used for spaces
53 0 : if (id_char == '\0')
54 : {
55 0 : id += ' ';
56 : }
57 : else
58 : {
59 0 : id += id_char;
60 : }
61 : }
62 0 : return id;
63 0 : }
64 : //-----------------------------------------------------------------------------
65 :
66 : //-----------------------------------------------------------------------------
67 : std::string
68 0 : SI534xSlave::seek_header(std::ifstream& file) const
69 : {
70 :
71 : // std::string config_line;
72 0 : std::string design_id;
73 :
74 0 : std::string config_line;
75 0 : uint32_t line_number; // NOLINT(build/unsigned)
76 0 : for (line_number = 1; std::getline(file, config_line); ++line_number) {
77 :
78 : // Gracefully deal with those damn dos-encoded files
79 0 : if (config_line.back() == '\r')
80 0 : config_line.pop_back();
81 :
82 : // Section end found. Break here
83 0 : if (boost::starts_with(config_line, "# Design ID:")) {
84 0 : design_id = config_line.substr(13);
85 : }
86 :
87 : // Skip comments
88 0 : if (config_line[0] == '#')
89 0 : continue;
90 :
91 : // Stop if the line is empty
92 0 : if (config_line.length() == 0)
93 0 : continue;
94 :
95 : // OK, header found, stop here
96 0 : if (config_line == "Address,Data")
97 : break;
98 :
99 0 : if (file.eof()) {
100 0 : throw SI534xConfigError(ERS_HERE, "Incomplete file: End of file detected while seeking the header.");
101 : }
102 : }
103 :
104 0 : TLOG_DEBUG(8) << "Found desing ID " << design_id;
105 :
106 0 : return design_id;
107 0 : }
108 :
109 : //-----------------------------------------------------------------------------
110 : // Seek Header
111 : // Seek conf start
112 : // read data
113 : // Stop on conf end
114 :
115 : std::vector<SI534xSlave::RegisterSetting_t>
116 0 : SI534xSlave::read_config_section(std::ifstream& file, std::string tag) const
117 : {
118 :
119 : // Line buffer
120 : // std::string config_line;
121 :
122 0 : bool section_found(false);
123 :
124 0 : std::vector<RegisterSetting_t> config;
125 0 : std::string config_line;
126 0 : uint32_t line_number; // NOLINT(build/unsigned)
127 0 : for (line_number = 1; std::getline(file, config_line); ++line_number) {
128 :
129 : // Gracefully deal with those damn dos-encoded files
130 0 : if (config_line.back() == '\r')
131 0 : config_line.pop_back();
132 :
133 : // Is it a comment
134 0 : if (config_line[0] == '#') {
135 :
136 0 : if (tag.empty())
137 0 : continue;
138 :
139 0 : if (boost::starts_with(config_line, "# Start configuration " + tag)) {
140 0 : section_found = true;
141 : }
142 :
143 : // Section end found. Break here
144 0 : if (boost::starts_with(config_line, "# End configuration " + tag)) {
145 : break;
146 : }
147 :
148 0 : continue;
149 : }
150 :
151 : // Oops
152 0 : if (file.eof()) {
153 0 : if (tag.empty())
154 0 : return config;
155 : else
156 0 : throw SI534xConfigError(ERS_HERE,
157 0 : "Incomplete file: End of file detected before the end of " + tag + " section.");
158 : }
159 :
160 : // Stop if the line is empty
161 0 : if (config_line.length() == 0)
162 0 : continue;
163 :
164 : // If no sec
165 0 : if (!section_found && !tag.empty()) {
166 0 : throw SI534xMissingConfigSectionError(ERS_HERE, tag);
167 : }
168 :
169 0 : uint32_t address, data; // NOLINT(build/unsigned)
170 0 : char dummy;
171 :
172 0 : std::istringstream line_stream(config_line);
173 0 : line_stream >> std::hex >> address >> dummy >> std::hex >> data;
174 :
175 0 : std::stringstream debug_stream;
176 0 : debug_stream << std::showbase << std::hex << "Address: " << address << dummy << " Data: " << data;
177 0 : TLOG_DEBUG(8) << debug_stream.str();
178 :
179 0 : config.push_back(RegisterSetting_t(address, data));
180 0 : }
181 :
182 : return config;
183 0 : }
184 : //-----------------------------------------------------------------------------
185 :
186 : //-----------------------------------------------------------------------------
187 : void
188 0 : SI534xSlave::configure(const std::string& filename) const
189 : {
190 :
191 0 : throw_if_not_file(filename);
192 :
193 0 : std::ifstream config_file(filename);
194 0 : std::string config_line;
195 0 : std::string conf_design_id;
196 :
197 : // Seek the header line first
198 0 : conf_design_id = seek_header(config_file);
199 0 : std::ifstream::pos_type header_end = config_file.tellg();
200 :
201 : // auto preamble = this->read_config_section(config_file, "preamble");
202 : // TIMING_LOG(kDebug) << "Preamble size = " << preamble.size();
203 : // auto registers = this->read_config_section(config_file, "registers");
204 : // TIMING_LOG(kDebug) << "Registers size = " << registers.size();
205 : // auto postamble = this->read_config_section(config_file, "postamble");
206 : // TIMING_LOG(kDebug) << "PostAmble size = " << postamble.size();
207 :
208 0 : std::vector<SI534xSlave::RegisterSetting_t> preamble, registers, postamble;
209 :
210 0 : try {
211 0 : preamble = this->read_config_section(config_file, "preamble");
212 0 : registers = this->read_config_section(config_file, "registers");
213 0 : postamble = this->read_config_section(config_file, "postamble");
214 0 : } catch (SI534xMissingConfigSectionError&) {
215 0 : config_file.seekg(header_end);
216 0 : preamble.clear();
217 0 : registers = this->read_config_section(config_file, "");
218 0 : postamble.clear();
219 0 : }
220 :
221 0 : TLOG_DEBUG(8) << "Preamble size = " << preamble.size();
222 0 : TLOG_DEBUG(8) << "Registers size = " << registers.size();
223 0 : TLOG_DEBUG(8) << "PostAmble size = " << postamble.size();
224 :
225 0 : config_file.close();
226 :
227 0 : try {
228 0 : this->write_clock_register(0x1E, 0x2);
229 0 : } catch (timing::I2CException& excp) {
230 : // Do nothing.
231 0 : }
232 :
233 0 : std::this_thread::sleep_for(std::chrono::milliseconds(1000));
234 :
235 0 : this->upload_config(preamble);
236 0 : std::this_thread::sleep_for(std::chrono::milliseconds(300));
237 0 : this->upload_config(registers);
238 0 : this->upload_config(postamble);
239 :
240 0 : std::string chip_design_id = this->read_config_id();
241 :
242 0 : if (conf_design_id != chip_design_id) {
243 0 : std::ostringstream message;
244 0 : message << "Post-configuration check failed: Loaded design ID " << chip_design_id
245 0 : << " does not match the configurationd design id " << conf_design_id << std::endl;
246 0 : ers::error(SI534xConfigError(ERS_HERE, message.str()));
247 0 : }
248 0 : }
249 : //-----------------------------------------------------------------------------
250 :
251 : //-----------------------------------------------------------------------------
252 : void
253 0 : SI534xSlave::upload_config(const std::vector<SI534xSlave::RegisterSetting_t>& config) const
254 : {
255 :
256 0 : size_t k(0), notify_percent(10);
257 0 : size_t notify_every = (notify_percent < config.size() ? config.size() / notify_percent : 1);
258 :
259 0 : for (const auto& setting : config) {
260 0 : std::stringstream debug_stream;
261 0 : debug_stream << std::showbase << std::hex << "Writing to " << (uint32_t)setting.get<0>() // NOLINT(build/unsigned)
262 0 : << " data " << (uint32_t)setting.get<1>(); // NOLINT(build/unsigned)
263 0 : TLOG_DEBUG(9) << debug_stream.str();
264 :
265 0 : uint32_t max_attempts(2), attempt(0); // NOLINT(build/unsigned)
266 0 : while (attempt < max_attempts) {
267 0 : TLOG_DEBUG(9) << "Attempt " << attempt;
268 0 : if (attempt > 0) {
269 0 : ers::warning(SI534xRegWriteRetry(ERS_HERE,
270 0 : format_reg_value(attempt, 10),
271 0 : format_reg_value((uint32_t)setting.get<0>()))); // NOLINT(build/unsigned)
272 : }
273 0 : try {
274 0 : this->write_clock_register(setting.get<0>(), setting.get<1>());
275 0 : } catch (const std::exception& e) {
276 0 : ers::error(SI534xRegWriteFailed(ERS_HERE,
277 0 : format_reg_value((uint32_t)setting.get<0>()), // NOLINT(build/unsigned)
278 0 : format_reg_value((uint32_t)setting.get<1>()), // NOLINT(build/unsigned)
279 : e));
280 0 : ++attempt;
281 0 : continue;
282 0 : }
283 : break;
284 : }
285 :
286 0 : ++k;
287 0 : if ((k % notify_every) == 0) {
288 0 : TLOG_DEBUG(9) << (k / notify_every) * notify_percent << "%";
289 : }
290 0 : }
291 0 : }
292 : //-----------------------------------------------------------------------------
293 :
294 : //-----------------------------------------------------------------------------
295 : std::map<uint16_t, uint8_t> // NOLINT(build/unsigned)
296 0 : SI534xSlave::registers() const
297 : {
298 : // boost::format fmthex("%d");
299 0 : std::map<uint16_t, uint8_t> values; // NOLINT(build/unsigned)
300 :
301 0 : for (uint8_t reg_addr = 0xc; reg_addr <= 0x12; reg_addr++) { // NOLINT(build/unsigned)
302 0 : if (reg_addr > 0xf && reg_addr < 0x11) {
303 0 : continue;
304 : }
305 :
306 : // if( reg_addr > 25 && reg_addr < 31 ) {
307 : // continue;
308 : // }
309 :
310 : // if( reg_addr > 36 && reg_addr < 40 ) {
311 : // continue;
312 : // }
313 :
314 : // if( reg_addr > 48 && reg_addr < 55 ) {
315 : // continue;
316 : // }
317 :
318 : // if( reg_addr > 55 && reg_addr < 128 ) {
319 : // continue;
320 : // }
321 :
322 : // if( reg_addr == 133 ) {
323 : // continue;
324 : // }
325 :
326 : // if( reg_addr == 137 ) {
327 : // continue;
328 : // }
329 :
330 : // if( reg_addr > 139 && reg_addr < 142 ) {
331 : // continue;
332 : // }
333 :
334 0 : values[reg_addr] = read_clock_register(reg_addr);
335 : }
336 :
337 0 : return values;
338 0 : }
339 : //-----------------------------------------------------------------------------
340 :
341 : //-----------------------------------------------------------------------------
342 : void
343 0 : SI534xSlave::get_info(timinghardwareinfo::TimingPLLMonitorData& mon_data) const
344 : {
345 0 : mon_data.config_id = this->read_config_id();
346 :
347 : //lPLLVersion["Part number"] = pll->read_device_version();
348 : //lPLLVersion["Device grade"] = pll->read_clock_register(0x4);
349 : //lPLLVersion["Device revision"] = pll->read_clock_register(0x5);
350 :
351 0 : uint8_t pll_reg_c = this->read_clock_register(0xc); // NOLINT(build/unsigned)
352 0 : uint8_t pll_reg_d = this->read_clock_register(0xd); // NOLINT(build/unsigned)
353 0 : uint8_t pll_reg_e = this->read_clock_register(0xe); // NOLINT(build/unsigned)
354 0 : uint8_t pll_reg_f = this->read_clock_register(0xf); // NOLINT(build/unsigned)
355 0 : uint8_t pll_reg_11 = this->read_clock_register(0x11); // NOLINT(build/unsigned)
356 0 : uint8_t pll_reg_12 = this->read_clock_register(0x12); // NOLINT(build/unsigned)
357 :
358 0 : mon_data.cal_pll = dec_rng(pll_reg_f, 5);
359 0 : mon_data.hold = dec_rng(pll_reg_e, 5);
360 0 : mon_data.lol = dec_rng(pll_reg_e, 1);
361 0 : mon_data.los = dec_rng(pll_reg_d, 0, 4);
362 0 : mon_data.los_xaxb = dec_rng(pll_reg_c, 1);
363 0 : mon_data.los_xaxb_flg = dec_rng(pll_reg_11, 1);
364 :
365 0 : mon_data.oof = dec_rng(pll_reg_d, 4, 4);
366 0 : mon_data.oof_sticky = dec_rng(pll_reg_12, 4, 4);
367 :
368 0 : mon_data.smbus_timeout = dec_rng(pll_reg_c, 5);
369 0 : mon_data.smbus_timeout_flg = dec_rng(pll_reg_11, 5);
370 :
371 0 : mon_data.sys_in_cal = dec_rng(pll_reg_c, 0);
372 0 : mon_data.sys_in_cal_flg = dec_rng(pll_reg_11, 0);
373 :
374 0 : mon_data.xaxb_err = dec_rng(pll_reg_c, 3);
375 0 : mon_data.xaxb_err_flg = dec_rng(pll_reg_11, 3);
376 0 : }
377 : //-----------------------------------------------------------------------------
378 :
379 : //-----------------------------------------------------------------------------
380 : std::string
381 0 : SI534xSlave::get_status(bool print_out) const
382 : {
383 0 : std::stringstream status;
384 0 : status << "PLL configuration id : " << this->read_config_id() << std::endl;
385 :
386 0 : std::map<std::string, uint32_t> pll_version; // NOLINT(build/unsigned)
387 0 : pll_version["Part number"] = this->read_device_version();
388 0 : pll_version["Device grade"] = this->read_clock_register(0x4);
389 0 : pll_version["Device revision"] = this->read_clock_register(0x5);
390 :
391 0 : status << format_reg_table(pll_version, "PLL information") << std::endl;
392 :
393 0 : std::map<std::string, uint32_t> pll_registers; // NOLINT(build/unsigned)
394 :
395 0 : uint8_t pll_reg_c = this->read_clock_register(0xc); // NOLINT(build/unsigned)
396 0 : uint8_t pll_reg_d = this->read_clock_register(0xd); // NOLINT(build/unsigned)
397 0 : uint8_t pll_reg_e = this->read_clock_register(0xe); // NOLINT(build/unsigned)
398 0 : uint8_t pll_reg_f = this->read_clock_register(0xf); // NOLINT(build/unsigned)
399 0 : uint8_t pll_reg_11 = this->read_clock_register(0x11); // NOLINT(build/unsigned)
400 0 : uint8_t pll_reg_12 = this->read_clock_register(0x12); // NOLINT(build/unsigned)
401 :
402 0 : pll_registers["CAL_PLL"] = dec_rng(pll_reg_f, 5);
403 0 : pll_registers["HOLD"] = dec_rng(pll_reg_e, 5);
404 0 : pll_registers["LOL"] = dec_rng(pll_reg_e, 1);
405 0 : pll_registers["LOS"] = dec_rng(pll_reg_d, 0, 4);
406 0 : pll_registers["LOSXAXB"] = dec_rng(pll_reg_c, 1);
407 0 : pll_registers["LOSXAXB_FLG"] = dec_rng(pll_reg_11, 1);
408 :
409 0 : pll_registers["OOF"] = dec_rng(pll_reg_d, 4, 4);
410 0 : pll_registers["OOF (sticky)"] = dec_rng(pll_reg_12, 4, 4);
411 :
412 0 : pll_registers["SMBUS_TIMEOUT"] = dec_rng(pll_reg_c, 5);
413 0 : pll_registers["SMBUS_TIMEOUT_FLG"] = dec_rng(pll_reg_11, 5);
414 :
415 0 : pll_registers["SYSINCAL"] = dec_rng(pll_reg_c, 0);
416 0 : pll_registers["SYSINCAL_FLG"] = dec_rng(pll_reg_11, 0);
417 :
418 0 : pll_registers["XAXB_ERR"] = dec_rng(pll_reg_c, 3);
419 0 : pll_registers["XAXB_ERR_FLG"] = dec_rng(pll_reg_11, 3);
420 :
421 0 : status << format_reg_table(pll_registers, "PLL state");
422 :
423 0 : if (print_out)
424 0 : TLOG() << status.str();
425 0 : return status.str();
426 0 : }
427 : //-----------------------------------------------------------------------------
428 :
429 : //-----------------------------------------------------------------------------
430 0 : SI534xNode::SI534xNode(const uhal::Node& node)
431 : : I2CMasterNode(node)
432 0 : , SI534xSlave(this, this->get_slave_address("i2caddr"))
433 0 : {}
434 : //-----------------------------------------------------------------------------
435 :
436 : //-----------------------------------------------------------------------------
437 0 : SI534xNode::SI534xNode(const SI534xNode& node)
438 : : I2CMasterNode(node)
439 0 : , SI534xSlave(this, this->get_slave_address("i2caddr"))
440 0 : {}
441 : //-----------------------------------------------------------------------------
442 :
443 : //-----------------------------------------------------------------------------
444 0 : SI534xNode::~SI534xNode() {}
445 : //-----------------------------------------------------------------------------
446 :
447 : } // namespace timing
448 : } // namespace dunedaq
|