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 0 : m_hsi_device = std::make_unique<uhal::HwInterface>(m_connection_manager->getDevice(m_timing_device));
78 0 : } catch (const uhal::exception::ConnectionUIDDoesNotExist& exception) {
79 0 : std::stringstream message;
80 0 : message << "UHAL device name not " << m_timing_device << " in connections file";
81 0 : throw timinglibs::UHALDeviceNameIssue(ERS_HERE, message.str(), exception);
82 0 : }
83 :
84 0 : m_thread.start_working_thread("gather-hsi-info");
85 :
86 0 : configure_hardware_or_recover_state<timinglibs::TimingEndpointNotReady>(data, "HSI endpoint", m_endpoint_state);
87 :
88 0 : TLOG() << get_name() << " conf done for hsi endpoint, device: " << m_timing_device;
89 0 : }
90 :
91 : void
92 0 : HSIController::do_start(const CommandData_t& data)
93 : {
94 0 : TimingController::do_start(data); // set sent cmd counters to 0
95 0 : do_hsi_start(data);
96 0 : }
97 :
98 : void
99 0 : HSIController::do_stop(const CommandData_t& data)
100 : {
101 0 : do_hsi_stop(data);
102 0 : }
103 :
104 : void
105 0 : HSIController::do_scrap(const CommandData_t& data)
106 : {
107 0 : m_thread.stop_working_thread();
108 0 : scrap_uhal();
109 :
110 0 : m_timing_device = "";
111 0 : m_control_hardware_io = false;
112 0 : m_endpoint_state = 0x0;
113 :
114 0 : TimingController::do_scrap(data);
115 0 : }
116 :
117 : void
118 0 : HSIController::send_configure_hardware_commands(const CommandData_t& data)
119 : {
120 0 : if (m_control_hardware_io) {
121 0 : m_thread.stop_working_thread();
122 0 : do_io_reset(data);
123 0 : m_thread.start_working_thread("gather-hsi-info");
124 : }
125 0 : do_hsi_reset(data);
126 0 : do_endpoint_reset(data);
127 0 : do_hsi_configure();
128 0 : }
129 :
130 : void
131 0 : HSIController::do_io_reset(const CommandData_t&)
132 : {
133 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
134 :
135 0 : auto clock_config = m_hsi_configuration->get_clock_config();
136 0 : if (m_hsi_configuration->get_soft()) {
137 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device << " soft io reset";
138 0 : design->soft_reset_io();
139 0 : } else if (!clock_config.empty()) {
140 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device << " io reset, with supplied clk file: " << clock_config;
141 0 : design->reset_io(clock_config);
142 : } else {
143 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device
144 0 : << " io reset, with supplied clk source: " << m_hsi_configuration->get_clock_source();
145 0 : design->reset_io(static_cast<timing::ClockSource>(m_hsi_configuration->get_clock_source()));
146 : }
147 :
148 0 : ++(m_sent_hw_command_counters.at(0).atomic);
149 0 : }
150 :
151 : void
152 0 : HSIController::do_endpoint_enable(const CommandData_t& /*data*/)
153 : {
154 0 : auto ept_address = m_hsi_configuration->get_address();
155 0 : TLOG_DEBUG(0) << "ept enable hw cmd; a: " << ept_address;
156 :
157 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
158 0 : design->get_endpoint_node_plain(m_managed_endpoint_id)->enable(ept_address, 0);
159 :
160 0 : ++(m_sent_hw_command_counters.at(1).atomic);
161 0 : }
162 :
163 : void
164 0 : HSIController::do_endpoint_disable(const CommandData_t& /*data*/)
165 : {
166 0 : TLOG_DEBUG(0) << "ept disable hw cmd";
167 :
168 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
169 0 : design->get_endpoint_node_plain(m_managed_endpoint_id)->disable();
170 0 : ++(m_sent_hw_command_counters.at(2).atomic);
171 0 : }
172 :
173 : void
174 0 : HSIController::do_endpoint_reset(const CommandData_t& /*data*/)
175 : {
176 0 : auto ept_address = m_hsi_configuration->get_address();
177 0 : TLOG_DEBUG(0) << "ept reset hw cmd; a: " << ept_address;
178 :
179 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
180 0 : design->get_endpoint_node_plain(m_managed_endpoint_id)->reset(ept_address, 0);
181 0 : ++(m_sent_hw_command_counters.at(3).atomic);
182 0 : }
183 :
184 : void
185 0 : HSIController::do_hsi_reset(const nlohmann::json&)
186 : {
187 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device << " hsi reset";
188 :
189 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
190 0 : design->get_hsi_node().reset_hsi();
191 0 : ++(m_sent_hw_command_counters.at(4).atomic);
192 0 : }
193 :
194 : void
195 0 : HSIController::do_hsi_configure()
196 : {
197 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device << ", hsi configure";
198 0 : auto random_rate = m_hsi_configuration->get_trigger_rate();
199 0 : do_hsi_configure(random_rate);
200 0 : }
201 :
202 : void
203 0 : HSIController::do_hsi_configure(double random_rate)
204 : {
205 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device << " hsi configure";
206 :
207 0 : auto data_source = m_hsi_configuration->get_data_source();
208 0 : auto rising_edge_mask = m_hsi_configuration->get_rising_edge_mask();
209 0 : auto falling_edge_mask = m_hsi_configuration->get_falling_edge_mask();
210 0 : auto invert_edge_mask = m_hsi_configuration->get_invert_edge_mask();
211 :
212 0 : if (random_rate <= 0) {
213 0 : throw timinglibs::InvalidTriggerRateValue(ERS_HERE, random_rate);
214 : }
215 :
216 0 : TLOG_DEBUG(0) << get_name() << " Setting emulated event rate [Hz] to: " << random_rate;
217 :
218 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
219 0 : design->configure_hsi(data_source, rising_edge_mask, falling_edge_mask, invert_edge_mask, random_rate);
220 0 : ++(m_sent_hw_command_counters.at(5).atomic);
221 0 : }
222 :
223 : void
224 0 : HSIController::do_hsi_start(const nlohmann::json&)
225 : {
226 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device << " hsi start";
227 :
228 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
229 0 : design->get_hsi_node().start_hsi();
230 0 : ++(m_sent_hw_command_counters.at(6).atomic);
231 0 : }
232 :
233 : void
234 0 : HSIController::do_hsi_stop(const nlohmann::json&)
235 : {
236 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device << " hsi stop";
237 :
238 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
239 0 : design->get_hsi_node().stop_hsi();
240 0 : ++(m_sent_hw_command_counters.at(7).atomic);
241 0 : }
242 :
243 : void
244 0 : HSIController::do_hsi_print_status(const nlohmann::json&)
245 : {
246 0 : TLOG_DEBUG(0) << get_name() << ": " << m_timing_device << " hsi print status";
247 :
248 0 : auto design = dynamic_cast<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""));
249 0 : TLOG_DEBUG(0) << std::endl << design->get_hsi_node().get_status();
250 0 : ++(m_sent_hw_command_counters.at(8).atomic);
251 0 : }
252 :
253 : void
254 0 : HSIController::generate_opmon_data()
255 : {
256 : // send counters internal to the module
257 0 : opmon::HSIControllerInfo info;
258 0 : info.set_sent_hsi_io_reset_cmds(m_sent_hw_command_counters.at(0).atomic);
259 0 : info.set_sent_hsi_endpoint_enable_cmds(m_sent_hw_command_counters.at(1).atomic);
260 0 : info.set_sent_hsi_endpoint_disable_cmds(m_sent_hw_command_counters.at(2).atomic);
261 0 : info.set_sent_hsi_endpoint_reset_cmds(m_sent_hw_command_counters.at(3).atomic);
262 0 : info.set_sent_hsi_reset_cmds(m_sent_hw_command_counters.at(4).atomic);
263 0 : info.set_sent_hsi_configure_cmds(m_sent_hw_command_counters.at(5).atomic);
264 0 : info.set_sent_hsi_start_cmds(m_sent_hw_command_counters.at(6).atomic);
265 0 : info.set_sent_hsi_stop_cmds(m_sent_hw_command_counters.at(7).atomic);
266 0 : info.set_sent_hsi_print_status_cmds(m_sent_hw_command_counters.at(8).atomic);
267 0 : info.set_device_infos_received_count(m_device_infos_received_count);
268 :
269 0 : publish(std::move(info));
270 0 : }
271 :
272 : void
273 0 : HSIController::process_device_info(nlohmann::json info)
274 : {
275 0 : ++m_device_infos_received_count;
276 :
277 0 : timing::timingfirmwareinfo::TimingDeviceInfo device_info;
278 0 : from_json(info, device_info);
279 :
280 0 : auto ept_info = device_info.endpoint_info;
281 0 : m_endpoint_state = device_info.endpoint_info.state;
282 0 : bool ready = ept_info.ready;
283 :
284 0 : bool ept_good = (m_endpoint_state == 0x8) && ready;
285 :
286 0 : auto hsi_info = device_info.hsi_info;
287 0 : auto buffer_enabled = hsi_info.buffer_enabled;
288 0 : auto buffer_error = hsi_info.buffer_error;
289 0 : auto buffer_warning = hsi_info.buffer_warning;
290 0 : auto re_mask = hsi_info.re_mask;
291 0 : auto fe_mask = hsi_info.fe_mask;
292 0 : auto inv_mask = hsi_info.inv_mask;
293 0 : auto data_source = hsi_info.source;
294 :
295 0 : bool hsi_good =
296 0 : buffer_enabled && !buffer_error && !buffer_warning && re_mask == m_hsi_configuration->get_rising_edge_mask() &&
297 0 : fe_mask == m_hsi_configuration->get_falling_edge_mask() &&
298 0 : inv_mask == m_hsi_configuration->get_invert_edge_mask() && data_source == m_hsi_configuration->get_data_source();
299 :
300 0 : TLOG_DEBUG(0) << "EPT good: " << ept_good << ", HSI good: " << hsi_good
301 0 : << ", infos received: " << m_device_infos_received_count;
302 :
303 0 : TLOG_DEBUG(0) << "device data: " << info.dump();
304 :
305 0 : if (ept_good && hsi_good) {
306 0 : if (!m_device_ready) {
307 0 : m_device_ready = true;
308 0 : TLOG_DEBUG(2) << "HSI device became ready";
309 : }
310 : } else {
311 0 : if (m_device_ready) {
312 0 : m_device_ready = false;
313 0 : TLOG_DEBUG(2) << "HSI device no longer ready";
314 : }
315 : }
316 0 : }
317 :
318 : void
319 0 : HSIController::gather_monitor_data(std::atomic<bool>& running_flag)
320 : {
321 0 : while (running_flag.load()) {
322 :
323 0 : timing::timingfirmwareinfo::TimingDeviceInfo device_info;
324 : // collect the data from the hardware
325 0 : try {
326 0 : auto design = cast_timing_device<const timing::HSIDesignInterface*>(&m_hsi_device->getNode(""), m_timing_device);
327 0 : design->get_info(device_info);
328 0 : } catch (const std::exception& excpt) {
329 0 : ers::warning(timinglibs::FailedToCollectOpMonInfo(ERS_HERE, m_timing_device, excpt));
330 0 : }
331 :
332 0 : nlohmann::json info;
333 0 : to_json(info, device_info);
334 0 : process_device_info(info);
335 :
336 0 : auto prev_gather_time = std::chrono::steady_clock::now();
337 0 : auto next_gather_time = prev_gather_time + std::chrono::milliseconds(500);
338 :
339 : // check running_flag periodically
340 0 : auto slice_period = std::chrono::microseconds(10000);
341 0 : auto next_slice_gather_time = prev_gather_time + slice_period;
342 :
343 0 : bool break_flag = false;
344 0 : while (next_gather_time > next_slice_gather_time + slice_period) {
345 0 : if (!running_flag.load()) {
346 0 : TLOG_DEBUG(0) << "while waiting to gather data, negative run flag detected.";
347 0 : break_flag = true;
348 0 : break;
349 : }
350 0 : std::this_thread::sleep_until(next_slice_gather_time);
351 0 : next_slice_gather_time = next_slice_gather_time + slice_period;
352 : }
353 0 : if (break_flag == false) {
354 0 : std::this_thread::sleep_until(next_gather_time);
355 : }
356 0 : }
357 0 : }
358 : } // namespace hsilibs
359 : } // namespace dunedaq
360 :
361 0 : DEFINE_DUNE_DAQ_MODULE(dunedaq::hsilibs::HSIController)
362 :
363 : // Local Variables:
364 : // c-basic-offset: 2
365 : // End:
|