Line data Source code
1 : /**
2 : * @file HSIController.cpp HSIController class
3 : * implementation
4 : *
5 : * This is part of the DUNE DAQ Software Suite, copyright 2020.
6 : * Licensing/copyright details are in the COPYING file that you should have
7 : * received with this code.
8 : */
9 :
10 : #include "HSIController.hpp"
11 : #include "hsilibs/dal/HSIController.hpp"
12 :
13 : #include "timinglibs/TimingIssues.hpp"
14 : #include "timinglibs/timingcmd/Nljs.hpp"
15 : #include "timinglibs/timingcmd/Structs.hpp"
16 :
17 : #include "timing/HSIDesignInterface.hpp"
18 : #include "timinglibs/dal/TimingController.hpp"
19 :
20 : #include "timing/timingfirmwareinfo/Nljs.hpp"
21 : #include "timing/timingfirmwareinfo/Structs.hpp"
22 :
23 : #include "hsilibs/opmon/hsi_controller_info.pb.cc"
24 :
25 : #include "ers/Issue.hpp"
26 : #include "logging/Logging.hpp"
27 :
28 : #include <chrono>
29 : #include <cstdlib>
30 : #include <string>
31 : #include <thread>
32 : #include <vector>
33 :
34 : namespace dunedaq {
35 : namespace hsilibs {
36 :
37 0 : HSIController::HSIController(const std::string& name)
38 : : dunedaq::timinglibs::TimingEndpointControllerBase(name, 9) // 2nd arg: how many hw commands can this module send?
39 0 : , m_control_hardware_io(false)
40 0 : , m_clock_frequency(62.5e6)
41 0 : , m_thread(std::bind(&HSIController::gather_monitor_data, this, std::placeholders::_1))
42 : {
43 : // this controller talks to the hw directly
44 0 : m_hw_command_out_connection = "";
45 :
46 0 : register_command("conf", &HSIController::do_configure);
47 0 : register_command("start", &HSIController::do_start);
48 0 : register_command("stop_trigger_sources", &HSIController::do_stop);
49 0 : register_command("scrap", &HSIController::do_scrap);
50 :
51 : // hsi hardware commands
52 : //register_command("hsi_reset", &HSIController::do_hsi_reset);
53 : //register_command("hsi_configure", &HSIController::do_hsi_configure);
54 : //register_command("hsi_start", &HSIController::do_hsi_start);
55 : //register_command("hsi_stop", &HSIController::do_hsi_stop);
56 : //register_command("hsi_print_status", &HSIController::do_hsi_print_status);
57 0 : }
58 :
59 : void
60 0 : HSIController::init(std::shared_ptr<appfwk::ConfigurationManager> mcfg)
61 : {
62 0 : TimingController::init(mcfg);
63 0 : auto mod_config = mcfg->get_dal<hsilibs::dal::HSIController>(get_name());
64 0 : m_hsi_configuration = mod_config->get_configuration()->cast<hsilibs::dal::HSIControllerConf>();
65 0 : }
66 :
67 : void
68 0 : HSIController::do_configure(const CommandData_t& data)
69 : {
70 0 : TimingController::do_configure(data);
71 :
72 0 : m_control_hardware_io = m_hsi_configuration->get_control_hardware_io();
73 :
74 0 : configure_uhal(m_hsi_configuration); // configure hw ipbus connection
75 :
76 0 : try
77 : {
78 0 : m_hsi_device = std::make_unique<uhal::HwInterface>(m_connection_manager->getDevice(m_timing_device));
79 0 : } catch (const uhal::exception::ConnectionUIDDoesNotExist& exception) {
80 0 : std::stringstream message;
81 0 : message << "UHAL device name not " << m_timing_device << " in connections file";
82 0 : throw timinglibs::UHALDeviceNameIssue(ERS_HERE, message.str(), exception);
83 0 : }
84 :
85 0 : m_thread.start_working_thread("gather-hsi-info");
86 :
87 0 : configure_hardware_or_recover_state<timinglibs::TimingEndpointNotReady>(data, "HSI endpoint", m_endpoint_state);
88 :
89 0 : TLOG() << get_name() << " conf done for hsi endpoint, device: " << m_timing_device;
90 0 : }
91 :
92 : void
93 0 : HSIController::do_start(const CommandData_t& data)
94 : {
95 0 : TimingController::do_start(data); // set sent cmd counters to 0
96 0 : do_hsi_start(data);
97 0 : }
98 :
99 : void
100 0 : HSIController::do_stop(const CommandData_t& data)
101 : {
102 0 : do_hsi_stop(data);
103 0 : }
104 :
105 : void
106 0 : HSIController::do_scrap(const CommandData_t& data)
107 : {
108 0 : m_thread.stop_working_thread();
109 0 : scrap_uhal();
110 :
111 0 : m_timing_device="";
112 0 : m_control_hardware_io=false;
113 0 : m_endpoint_state = 0x0;
114 :
115 0 : TimingController::do_scrap(data);
116 0 : }
117 :
118 : void
119 0 : HSIController::send_configure_hardware_commands(const CommandData_t& data)
120 : {
121 0 : if (m_control_hardware_io)
122 : {
123 0 : m_thread.stop_working_thread();
124 0 : do_io_reset(data);
125 0 : m_thread.start_working_thread("gather-hsi-info");
126 : }
127 0 : do_hsi_reset(data);
128 0 : do_endpoint_reset(data);
129 0 : do_hsi_configure();
130 0 : }
131 :
132 : void
133 0 : HSIController::do_io_reset(const CommandData_t&)
134 : {
135 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
136 :
137 0 : auto clock_config = m_hsi_configuration->get_clock_config();
138 0 : if (m_hsi_configuration->get_soft()) {
139 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device << " soft io reset";
140 0 : design->soft_reset_io();
141 0 : } else if (!clock_config.empty()) {
142 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device
143 0 : << " io reset, with supplied clk file: " << clock_config;
144 0 : design->reset_io(clock_config);
145 : }
146 : else
147 : {
148 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device
149 0 : << " io reset, with supplied clk source: " << m_hsi_configuration->get_clock_source();
150 0 : design->reset_io(static_cast<timing::ClockSource>(m_hsi_configuration->get_clock_source()));
151 : }
152 :
153 0 : ++(m_sent_hw_command_counters.at(0).atomic);
154 0 : }
155 :
156 : void
157 0 : HSIController::do_endpoint_enable(const CommandData_t& /*data*/)
158 : {
159 0 : auto ept_address = m_hsi_configuration->get_address();
160 0 : TLOG_DEBUG(0) << "ept enable hw cmd; a: " << ept_address;
161 :
162 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
163 0 : design->get_endpoint_node_plain(m_managed_endpoint_id)->enable(ept_address, 0);
164 :
165 0 : ++(m_sent_hw_command_counters.at(1).atomic);
166 0 : }
167 :
168 : void
169 0 : HSIController::do_endpoint_disable(const CommandData_t& /*data*/)
170 : {
171 0 : TLOG_DEBUG(0) << "ept disable hw cmd";
172 :
173 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
174 0 : design->get_endpoint_node_plain(m_managed_endpoint_id)->disable();
175 0 : ++(m_sent_hw_command_counters.at(2).atomic);
176 0 : }
177 :
178 : void
179 0 : HSIController::do_endpoint_reset(const CommandData_t& /*data*/)
180 : {
181 0 : auto ept_address = m_hsi_configuration->get_address();
182 0 : TLOG_DEBUG(0) << "ept reset hw cmd; a: " << ept_address;
183 :
184 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
185 0 : design->get_endpoint_node_plain(m_managed_endpoint_id)->reset(ept_address, 0);
186 0 : ++(m_sent_hw_command_counters.at(3).atomic);
187 0 : }
188 :
189 : void
190 0 : HSIController::do_hsi_reset(const nlohmann::json&)
191 : {
192 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device << " hsi reset";
193 :
194 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
195 0 : design->get_hsi_node().reset_hsi();
196 0 : ++(m_sent_hw_command_counters.at(4).atomic);
197 0 : }
198 :
199 : void
200 0 : HSIController::do_hsi_configure()
201 : {
202 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device << ", hsi configure";
203 0 : auto random_rate = m_hsi_configuration->get_trigger_rate();
204 0 : do_hsi_configure(random_rate);
205 0 : }
206 :
207 : void
208 0 : HSIController::do_hsi_configure(double random_rate)
209 : {
210 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device << " hsi configure";
211 :
212 0 : auto data_source = m_hsi_configuration->get_data_source();
213 0 : auto rising_edge_mask = m_hsi_configuration->get_rising_edge_mask();
214 0 : auto falling_edge_mask = m_hsi_configuration->get_falling_edge_mask();
215 0 : auto invert_edge_mask = m_hsi_configuration->get_invert_edge_mask();
216 :
217 0 : if (random_rate <= 0) {
218 0 : throw timinglibs::InvalidTriggerRateValue(ERS_HERE, random_rate);
219 : }
220 :
221 0 : TLOG_DEBUG(0) << get_name() << " Setting emulated event rate [Hz] to: "
222 0 : << random_rate;
223 :
224 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
225 0 : design->configure_hsi(
226 : data_source, rising_edge_mask, falling_edge_mask, invert_edge_mask, random_rate);
227 0 : ++(m_sent_hw_command_counters.at(5).atomic);
228 0 : }
229 :
230 : void
231 0 : HSIController::do_hsi_start(const nlohmann::json&)
232 : {
233 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device << " hsi start";
234 :
235 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
236 0 : design->get_hsi_node().start_hsi();
237 0 : ++(m_sent_hw_command_counters.at(6).atomic);
238 0 : }
239 :
240 : void
241 0 : HSIController::do_hsi_stop(const nlohmann::json&)
242 : {
243 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device << " hsi stop";
244 :
245 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
246 0 : design->get_hsi_node().stop_hsi();
247 0 : ++(m_sent_hw_command_counters.at(7).atomic);
248 0 : }
249 :
250 : void
251 0 : HSIController::do_hsi_print_status(const nlohmann::json&)
252 : {
253 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device << " hsi print status";
254 :
255 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
256 0 : TLOG_DEBUG(0) << std::endl << design->get_hsi_node().get_status();
257 0 : ++(m_sent_hw_command_counters.at(8).atomic);
258 0 : }
259 :
260 : void
261 0 : HSIController::generate_opmon_data()
262 : {
263 : // send counters internal to the module
264 0 : opmon::HSIControllerInfo info;
265 0 : info.set_sent_hsi_io_reset_cmds(m_sent_hw_command_counters.at(0).atomic);
266 0 : info.set_sent_hsi_endpoint_enable_cmds(m_sent_hw_command_counters.at(1).atomic);
267 0 : info.set_sent_hsi_endpoint_disable_cmds(m_sent_hw_command_counters.at(2).atomic);
268 0 : info.set_sent_hsi_endpoint_reset_cmds(m_sent_hw_command_counters.at(3).atomic);
269 0 : info.set_sent_hsi_reset_cmds(m_sent_hw_command_counters.at(4).atomic);
270 0 : info.set_sent_hsi_configure_cmds(m_sent_hw_command_counters.at(5).atomic);
271 0 : info.set_sent_hsi_start_cmds(m_sent_hw_command_counters.at(6).atomic);
272 0 : info.set_sent_hsi_stop_cmds(m_sent_hw_command_counters.at(7).atomic);
273 0 : info.set_sent_hsi_print_status_cmds(m_sent_hw_command_counters.at(8).atomic);
274 0 : info.set_device_infos_received_count(m_device_infos_received_count);
275 :
276 0 : publish(std::move(info));
277 0 : }
278 :
279 : void
280 0 : HSIController::process_device_info(nlohmann::json info)
281 : {
282 0 : ++m_device_infos_received_count;
283 :
284 0 : timing::timingfirmwareinfo::TimingDeviceInfo device_info;
285 0 : from_json(info, device_info);
286 :
287 0 : auto ept_info = device_info.endpoint_info;
288 0 : m_endpoint_state = device_info.endpoint_info.state;
289 0 : bool ready = ept_info.ready;
290 :
291 0 : bool ept_good = (m_endpoint_state == 0x8) && ready;
292 :
293 0 : auto hsi_info = device_info.hsi_info;
294 0 : auto buffer_enabled = hsi_info.buffer_enabled;
295 0 : auto buffer_error = hsi_info.buffer_error;
296 0 : auto buffer_warning = hsi_info.buffer_warning;
297 0 : auto re_mask = hsi_info.re_mask;
298 0 : auto fe_mask = hsi_info.fe_mask;
299 0 : auto inv_mask = hsi_info.inv_mask;
300 0 : auto data_source = hsi_info.source;
301 :
302 0 : bool hsi_good = buffer_enabled && !buffer_error && !buffer_warning
303 0 : && re_mask == m_hsi_configuration->get_rising_edge_mask()
304 0 : && fe_mask == m_hsi_configuration->get_falling_edge_mask()
305 0 : && inv_mask == m_hsi_configuration->get_invert_edge_mask()
306 0 : && data_source == m_hsi_configuration->get_data_source();
307 :
308 0 : TLOG_DEBUG(0) << "EPT good: " << ept_good << ", HSI good: " << hsi_good << ", infos received: " << m_device_infos_received_count;
309 :
310 0 : TLOG_DEBUG(0) << "device data: " << info.dump();
311 :
312 0 : if (ept_good && hsi_good)
313 : {
314 0 : if (!m_device_ready)
315 : {
316 0 : m_device_ready = true;
317 0 : TLOG_DEBUG(2) << "HSI device became ready";
318 : }
319 : }
320 : else
321 : {
322 0 : if (m_device_ready)
323 : {
324 0 : m_device_ready = false;
325 0 : TLOG_DEBUG(2) << "HSI device no longer ready";
326 : }
327 : }
328 0 : }
329 :
330 : void
331 0 : HSIController::gather_monitor_data(std::atomic<bool>& running_flag)
332 : {
333 0 : while (running_flag.load()) {
334 :
335 0 : timing::timingfirmwareinfo::TimingDeviceInfo device_info;
336 : // collect the data from the hardware
337 0 : try
338 : {
339 0 : auto design = cast_timing_device<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""), m_timing_device);
340 0 : design->get_info(device_info);
341 0 : } catch (const std::exception& excpt) {
342 0 : ers::warning(timinglibs::FailedToCollectOpMonInfo(ERS_HERE, m_timing_device, excpt));
343 0 : }
344 :
345 0 : nlohmann::json info;
346 0 : to_json(info, device_info);
347 0 : process_device_info(info);
348 :
349 0 : auto prev_gather_time = std::chrono::steady_clock::now();
350 0 : auto next_gather_time = prev_gather_time + std::chrono::milliseconds(500);
351 :
352 : // check running_flag periodically
353 0 : auto slice_period = std::chrono::microseconds(10000);
354 0 : auto next_slice_gather_time = prev_gather_time + slice_period;
355 :
356 0 : bool break_flag = false;
357 0 : while (next_gather_time > next_slice_gather_time + slice_period) {
358 0 : if (!running_flag.load()) {
359 0 : TLOG_DEBUG(0) << "while waiting to gather data, negative run flag detected.";
360 0 : break_flag = true;
361 0 : break;
362 : }
363 0 : std::this_thread::sleep_until(next_slice_gather_time);
364 0 : next_slice_gather_time = next_slice_gather_time + slice_period;
365 : }
366 0 : if (break_flag == false) {
367 0 : std::this_thread::sleep_until(next_gather_time);
368 : }
369 0 : }
370 0 : }
371 : } // namespace hsilibs
372 : } // namespace dunedaq
373 :
374 0 : DEFINE_DUNE_DAQ_MODULE(dunedaq::hsilibs::HSIController)
375 :
376 : // Local Variables:
377 : // c-basic-offset: 2
378 : // End:
|