REDAC HybridController
Firmware for LUCIDAC/REDAC Teensy
Loading...
Searching...
No Matches
carrier.cpp
Go to the documentation of this file.
1// Copyright (c) 2024 anabrid GmbH
2// Contact: https://www.anabrid.com/licensing/
3// SPDX-License-Identifier: MIT OR GPL-2.0-or-later
4
5#include "pb_decode.h"
6
7#include <entity/visitor.h>
8#include <lucidac/lucidac.h>
9#include <mode/mode.h>
10#include <net/settings.h>
11#include <redac/redac.h>
12#include <utils/is_number.h>
13
14carrier::Carrier::Carrier(std::vector<platform::Cluster> clusters, carrier::Carrier_HAL *hardware)
15 : Entity("", hardware), hardware(hardware), clusters(std::move(clusters)) {
16 classifier.class_enum = CLASS_;
17}
18
19carrier::Carrier *carrier::Carrier::from_entity_classifier(entities::EntityClassifier classifier,
20 __attribute__((__unused__))
21 const bus::addr_t block_address) {
22 if (!classifier or classifier.class_enum != entities::EntityClass::CARRIER)
23 return nullptr;
24
25 auto type = classifier.type_as<TYPES>();
26 switch (type) {
27 case TYPES::UNKNOWN:
28 // This is already checked by !classifier above
29 return nullptr;
30 case TYPES::LUCIDAC:
31 return platform::LUCIDAC::from_entity_classifier(classifier, block_address);
32 case TYPES::mREDAC:
33 return platform::REDAC::from_entity_classifier(classifier, block_address);
34 }
35 // Any unknown value results in a nullptr here.
36 // Adding default case to switch suppresses warnings about missing cases.
37 return nullptr;
38}
39
40carrier::Carrier *carrier::Carrier::detect() {
41 return entities::detect<carrier::Carrier>(bus::address_from_tuple(bus::CARRIER_BADDR, 0));
42}
43
44status carrier::Carrier::better_init() {
45 LOG(ANABRID_DEBUG_INIT, __PRETTY_FUNCTION__);
46
47 entity_id = net::StartupConfig::get().mac;
48
49 if (entity_id.empty())
50 return status("Cannot determine Carrier MAC");
51
52 // Detect CTRL-block
53 ctrl_block = entities::detect<blocks::CTRLBlock>(bus::address_from_tuple(bus::CTRL_BLOCK_BADDR, 0));
54 if (!ctrl_block) {
55 return status("Missing CTRL Block");
56 }
57
58 for (auto &cluster : clusters) {
59 if (!cluster.init())
60 return status("Cannot initialize cluster");
61 }
62
63 reset(entities::ResetAction::EVERYTHING);
64
65 return status::success();
66}
67
68uint8_t carrier::Carrier::get_active_adc_channel_range() const{
69 uint8_t num_adc_channels = 0;
70 for(size_t idx = 0; idx < adc_channels.size(); idx++){
71 if(adc_channels[idx] != ADC_CHANNEL_DISABLED)
72 num_adc_channels = idx + 1;
73 }
74 return num_adc_channels;
75}
76
77std::vector<entities::Entity *> carrier::Carrier::get_child_entities() {
78 std::vector<entities::Entity *> children;
79 for (auto &cluster : clusters) {
80 children.push_back(&cluster);
81 }
82 if (ctrl_block)
83 children.push_back(ctrl_block);
84 return children;
85}
86
87entities::Entity *carrier::Carrier::get_child_entity(std::string_view child_id) {
88 auto opt_int = utils::view_to_number(child_id);
89 if (opt_int.has_value()) {
90 auto cluster_idx = opt_int.value();
91 if (cluster_idx < 0 or clusters.size() < cluster_idx)
92 return nullptr;
93 return &clusters[cluster_idx];
94 }
95 if (child_id == "CTRL")
96 return ctrl_block;
97 if (child_id.size() == 17)
98 return this;
99 return nullptr;
100}
101
102ConfigResult carrier::Carrier::config(const pb_Config &cfg) {
103 if (!hardware)
104 return ConfigResult::err("Carrier cannot be configured without hardware");
105
106 if (cfg.which_kind == pb_Config_adc_config_tag) {
107 auto& carrier_config = cfg.kind.adc_config;
108
109 adc_channels.fill(ADC_CHANNEL_DISABLED);
110 size_t min_size = std::min(
111 static_cast<size_t>(carrier_config.channels_count),
112 adc_channels.size()
113 );
114
115 for (size_t idx = 0; idx < min_size; ++idx) {
116 auto& config_channel = carrier_config.channels[idx];
117 adc_channels[idx] = config_channel.idx;
118 adc_gains[idx] = config_channel.gain;
119 adc_offsets[idx] = config_channel.offset;
120 }
121
122 auto res = hardware->write_adc_bus_mux(adc_channels);
123 if (!res)
124 return ConfigResult::err("Could not configure ACLs from configuration");
125
126 return ConfigResult::ok(true);
127 }
128
129 return ConfigResult::ok(false);
130}
131
132void carrier::Carrier::extract(entities::ExtractVisitor &collector) {
133 Entity::extract(collector);
134
135 auto& cfg = collector.create(pb_Config_adc_config_tag);
136 auto& carrier_config = cfg.kind.adc_config;
137 for (size_t idx = 0; idx < adc_channels.size(); ++idx) {
138 auto channel = adc_channels[idx];
139 if (channel == ADC_CHANNEL_DISABLED) continue;
140 carrier_config.channels[carrier_config.channels_count++] = {
141 .idx = static_cast<uint32_t>(channel),
142 .gain = adc_gains[idx],
143 .offset = adc_offsets[idx],
144 };
145 }
146}
147
148status carrier::Carrier::write_to_hardware() {
149 status error;
150 int cluster_index = 1;
151 for (auto &cluster : clusters) {
152 if (!cluster.write_to_hardware()) {
153 error.code += cluster_index; // maxes to 6 = 0b110
154 error.msg += "Cluster write failed.";
155 }
156 cluster_index++;
157 }
158
159 error.attach(write_adcs_to_hardware());
160
161 return error;
162}
163
164status carrier::Carrier::calibrate_offsets() {
165 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
166
168 for (auto &cluster : clusters)
169 success.attach(cluster.calibrate_offsets());
170 return success;
171
172 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
173}
174
175status carrier::Carrier::calibrate_routes() {
176 entities::Setup setup;
177 setup.extract(this);
178 status result = calibrate_routes_raw();
179 setup.apply(this, true);
180 return result;
181}
182
183// This calibration function is working for a generic array of clusters that don't have any shared connections.
184// LUCIDAC relies on this function, an mREDAC overwrites this function, as it can have cross cluster
185// connections.
186status carrier::Carrier::calibrate_routes_raw() {
187 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
188
190
191 bool gain_correction_exceeding = false;
192 for (auto &cluster : clusters) {
193 LOG_ANABRID_DEBUG_CALIBRATION(
194 ("Calibrating routes in cluster " + std::to_string(cluster.get_cluster_idx())).c_str());
195 // Save and change ADC bus selection
196 auto old_adcbus = ctrl_block->get_adc_bus();
197 auto old_adc_channels = get_adc_channels();
198
199 reset_adc_channels();
200
201 ctrl_block->set_adc_bus(blocks::CTRLBlock::ADCBus::ADC);
202 if (!ctrl_block->write_to_hardware())
203 success.attach("Writing to ctrl-block failed");
204
205 // Save current U-block transmission modes and set them to zero
206 LOG_ANABRID_DEBUG_CALIBRATION("Starting calibration");
207 auto old_transmission_modes = cluster.ublock->get_all_transmission_modes();
208 auto old_reference_magnitude = cluster.ublock->get_reference_magnitude();
209
210 // Enable reference signal for calibration
211 LOG_ANABRID_DEBUG_CALIBRATION("Enable u-block reference");
212 cluster.ublock->change_all_transmission_modes(blocks::UBlock::Transmission_Mode::POS_REF);
213 if (!cluster.ublock->write_to_hardware())
214 success.attach("Resetting u-block failed");
215
216 // Save C-Block factors
217 LOG_ANABRID_DEBUG_CALIBRATION("Reset c-block");
218 auto old_c_block_factors = cluster.cblock->get_factors();
219 cluster.cblock->set_factors({});
220 if (!cluster.cblock->write_to_hardware())
221 success.attach("Resetting c-block failed");
222
223 // Find an M block usable for its identity lanes
224 blocks::MBlock *id_block = nullptr;
225 if (cluster.m0block && cluster.m0block->has_id_lanes())
226 id_block = cluster.m0block;
227
228 if (!id_block && cluster.m1block && cluster.m1block->has_id_lanes())
229 id_block = cluster.m1block;
230
231 if (!id_block) {
232 success.attach("No M Block with ID Lanes found, calibration impossible!");
233 continue;
234 }
235
236 uint8_t id_in_lane = 0, id_out_lane = 4;
237 for (auto i = 0; i < id_block->ID_OUTPUT_CONNECTIONS().size(); i++) {
238 if (id_block->ID_OUTPUT_CONNECTIONS()[i] != -1) {
239 id_out_lane = id_block->slot_to_global_io_index(i);
240 id_in_lane = id_block->slot_to_global_io_index(id_block->ID_OUTPUT_CONNECTIONS()[i]);
241 break;
242 }
243 }
244
245 std::array<std::vector<uint8_t>, blocks::IBlock::NUM_OUTPUTS> old_i_block_connections;
246 for (auto i_out_idx : blocks::IBlock::OUTPUT_IDX_RANGE()) {
247 for (auto i_in_idx : blocks::IBlock::INPUT_IDX_RANGE()) {
248
249 // Only do something is this lane is used in the original I-block configuration
250 if (!cluster.iblock->is_connected(i_in_idx, i_out_idx))
251 continue;
252
253 // If we don't have a complete connection through the u block, we don't need / we can't calibrate this
254 // route from here
255 if (!cluster.ublock->is_output_connected(i_in_idx))
256 continue;
257
258 // Store this i block input to output connection
259 old_i_block_connections[i_out_idx].emplace_back(i_in_idx);
260 }
261 }
262
263 LOG_ANABRID_DEBUG_CALIBRATION("Reset i-block");
264 cluster.iblock->reset_outputs();
265 if (!cluster.iblock->write_to_hardware())
266 success.attach("Resetting i-block failed");
267
268 // Now we restore each summation isolated from the rest and route each summation output / i block output to
269 // an M-Mul block
270 for (auto i_out_idx : blocks::IBlock::OUTPUT_IDX_RANGE()) {
271 for (auto i_in_idx : old_i_block_connections[i_out_idx])
272 if (!cluster.iblock->connect(i_in_idx, id_in_lane))
273 LOG_ANABRID_DEBUG_CALIBRATION("Connecting i-block failed");
274
275 if (!cluster.iblock->write_to_hardware())
276 success.attach("Writing to i-block failed");
277
278 // Now switch through all relevant input signals and put one of them to 1.0
279 for (auto i_in_idx : old_i_block_connections[i_out_idx]) {
280 // Setup measure path
281 if (!set_adc_channel(0, id_out_lane))
282 success.attach("id_out_lane invalid");
283 if (!hardware->write_adc_bus_mux(adc_channels))
284 success.attach("Writing adcs failed!");
285
286 // Depending on whether upscaling is enabled for this lane, we apply +1 or +0.1 reference
287 // This is done on all lanes (but no other I-block connection exists, so no other current flows)
288 bool upscaled_channel = cluster.iblock->get_upscaling(i_in_idx);
289 cluster.ublock->change_reference_magnitude(upscaled_channel
290 ? blocks::UBlock::Reference_Magnitude::ONE_TENTH
291 : blocks::UBlock::Reference_Magnitude::ONE);
292 if (!cluster.ublock->write_to_hardware())
293 success.attach("Writing to u-block failed");
294
295 // First measure the offset of each measuring lane. The SH Block can not calibrate offsets that occur
296 // after himself so we need to take care of that.
297 (void)cluster.cblock->set_factor(i_in_idx, 0.0f);
298 (void)cluster.cblock->set_gain_correction(i_in_idx, 1.0f);
299 if (!cluster.cblock->write_to_hardware())
300 success.attach("Writing to c-block failed");
301
302 // Calibrate offsets for this specific route
303 success.attach(calibrate_offsets());
304
305 auto measured_offset = -daq::average(daq::sample, 4, 10)[0];
306
307 // Allow this connection to go up to full scale. Those values are allways legal so we can ignore the
308 // return value
309 (void)cluster.cblock->set_factor(i_in_idx, 1.0f);
310 if (!cluster.cblock->write_to_hardware())
311 success.attach("Writing to c-block failed");
312
313 delay(10);
314
315 // Measure gain output
316 auto measured_gain = -daq::average(daq::sample, 4, 10)[0];
317 LOG_ANABRID_DEBUG_CALIBRATION(measured_gain);
318 // Calculate necessary gain correction
319 auto gain_correction = (upscaled_channel ? 0.8f : 1.0f) / (measured_gain - measured_offset);
320 LOG_ANABRID_DEBUG_CALIBRATION(gain_correction);
321 if (gain_correction > 1.1f) {
322 gain_correction_exceeding = true;
323 gain_correction = 1.1f;
324 }
325 // Set gain correction on C-block, which will automatically get applied when writing to hardware
326 if (!cluster.cblock->set_gain_correction(i_in_idx, gain_correction)) {
327 (void)cluster.cblock->set_gain_correction(i_in_idx, 1.0f);
328 success.attach("Gain correction could not be set as it is out of range. Resetting this "
329 "channel to default correction");
330 }
331 // Deactivate this lane again
332 (void)cluster.cblock->set_factor(i_in_idx, 0.0f);
333
334 LOG_ANABRID_DEBUG_CALIBRATION(" ");
335 }
336
337 cluster.iblock->reset_outputs();
338 reset_adc_channels();
339 }
340
341 // Restore original U-block transmission modes and reference
342 cluster.ublock->change_all_transmission_modes(old_transmission_modes);
343 cluster.ublock->change_reference_magnitude(old_reference_magnitude);
344 // Write them to hardware
345 LOG_ANABRID_DEBUG_CALIBRATION("Restoring u-block");
346 if (!cluster.ublock->write_to_hardware())
347 success.attach("Restoring u-block failed");
348
349 // Restore C-block factors
350 cluster.cblock->set_factors(old_c_block_factors);
351 LOG_ANABRID_DEBUG_CALIBRATION("Restoring c-block");
352 if (!cluster.cblock->write_to_hardware())
353 success.attach("Restoring c-block failed");
354
355 // Restore I-block connections
356 LOG_ANABRID_DEBUG_CALIBRATION("Restoring i-block");
357 for (auto i_out_idx : blocks::IBlock::OUTPUT_IDX_RANGE())
358 for (auto i_in_idx : old_i_block_connections[i_out_idx])
359 if (!cluster.iblock->connect(i_in_idx, i_out_idx))
360 LOG_ANABRID_DEBUG_CALIBRATION("Connecting i-block failed");
361
362 if (!cluster.iblock->write_to_hardware())
363 success.attach("Restoring i-block failed");
364
365 // Restore ADC bus selection
366 ctrl_block->set_adc_bus(old_adcbus);
367 if (!ctrl_block->write_to_hardware())
368 success.attach("Writing to ctrl-block failed");
369
370 (void)set_adc_channels(old_adc_channels);
371 if (!hardware->write_adc_bus_mux(adc_channels))
372 success.attach("Writing adcs failed");
373 }
374
375 if (gain_correction_exceeding)
376 success.attach("Gain correction is too high, this is probably a problem with the hardware");
377
378
379 // Calibrate offsets for complete system
380 success.attach(Carrier::calibrate_offsets());
381 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
382 return success;
383}
384
385status carrier::Carrier::calibrate_mblock(platform::Cluster &cluster, blocks::MBlock &mblock) {
386 // CARE: This function does not preserve the currently configured routes
387 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
388
390
391 // The calibration for each M-Block is prepared by connecting all outputs
392 // to the ADCs. This limits the calibration to one M-Block (and one cluster) at a time,
393 // which is not a problem though.
394 // Each M-Block must connect required reference signals by itself.
395
396 LOG(ANABRID_DEBUG_CALIBRATION, "Connecting outputs to ADC...");
397 for (auto output_idx : blocks::MBlock::SLOT_OUTPUT_IDX_RANGE()) {
398 auto lane_idx = mblock.slot_to_global_io_index(output_idx);
399 if (!set_adc_channel(output_idx, lane_idx, cluster.get_cluster_idx()))
400 success.attach("Writing adc select failed");
401 }
402 // Write to hardware
403 success.attach(write_to_hardware());
404
405 // Pass to calibration function
406 LOG(ANABRID_DEBUG_CALIBRATION, "Passing control to M-block...");
407 success.attach(mblock.calibrate(&cluster, this));
408
409 LOG(ANABRID_DEBUG_CALIBRATION, "Cleanup ADC connections...");
410 reset_adc_channels();
411
412 LOG(ANABRID_DEBUG_CALIBRATION, "Cleaning up calibration signals...");
413 reset(entities::ResetAction::CIRCUIT_RESET);
414
415 // Write final clean-up to hardware
416 success.attach(write_to_hardware());
417
418 LOG(ANABRID_DEBUG_CALIBRATION, "Calibration done.");
419 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
420
421 return success;
422}
423
424status carrier::Carrier::calibrate_m_blocks() {
426 for (auto &cluster : clusters)
427 for (auto mblock : {cluster.m0block, cluster.m1block})
428 if (mblock)
429 success.attach(calibrate_mblock(cluster, *mblock));
430
431 return success;
432}
433
434void carrier::Carrier::reset(entities::ResetAction action) {
435 for (auto &cluster : clusters) {
436 cluster.reset(action);
437 }
438 if (ctrl_block)
439 ctrl_block->reset(action);
440 reset_adc_channels();
441}
442
443const std::array<int8_t, 8> &carrier::Carrier::get_adc_channels() const { return adc_channels; }
444const std::array<double, 8> &carrier::Carrier::get_adc_gains() const { return adc_gains; }
445const std::array<double, 8> &carrier::Carrier::get_adc_offsets() const { return adc_offsets; }
446
447bool carrier::Carrier::set_adc_channels(const std::array<int8_t, 8> &channels) {
448 // Check that all inputs are in a valid range
449 for (auto channel : channels) {
450 if (channel < 0)
451 continue;
452 if (channel >= clusters.size() * 16)
453 return false;
454 }
455 // Check that all inputs are different
456 // This is actually not necessary, in principle one could split one signal to multiple ADC.
457 // But for now we prohibit this, as it's more likely an error than an actual use-case.
458 for (auto &channel : channels) {
459 if (channel < 0)
460 continue;
461 for (auto &other_channel : channels) {
462 if (std::addressof(channel) == std::addressof(other_channel))
463 continue;
464 if (channel == other_channel)
465 return false;
466 }
467 }
468 adc_channels = channels;
469 return true;
470}
471
472bool carrier::Carrier::set_adc_channel(uint8_t adc_channel, int8_t src_channel) {
473 if (adc_channel >= adc_channels.size())
474 return false;
475 if (src_channel < 0)
476 src_channel = ADC_CHANNEL_DISABLED;
477
478 // Check if channel is already routed to an ADC
479 // This is not really necessary, but probably a good idea (see set_adc_channels)
480 if (src_channel != ADC_CHANNEL_DISABLED)
481 for (auto other_idx = 0u; other_idx < adc_channels.size(); other_idx++)
482 if (adc_channel != other_idx and adc_channels[other_idx] == src_channel)
483 return false;
484
485 adc_channels[adc_channel] = src_channel;
486 return true;
487}
488
489bool carrier::Carrier::set_adc_channel(uint8_t adc_channel, int8_t src_idx, uint8_t cluster_idx) {
490 return Carrier::set_adc_channel(adc_channel, src_idx + cluster_idx * 16);
491}
492
493void carrier::Carrier::reset_adc_channels() {
494 adc_channels.fill(ADC_CHANNEL_DISABLED);
495}
496
497status carrier::Carrier::measure_all_signals(std::array<std::array<float, 8>, 6> &data) {
498 auto old_adc_channels = get_adc_channels();
499 reset_adc_channels();
500 size_t idx = static_cast<size_t>(-1);
501 for (const auto &cluster : clusters) {
502 for (const auto mblock : {cluster.m0block, cluster.m1block}) {
503 ++idx;
504 if (!mblock) continue;
505
506 for (auto slot : blocks::MBlock::SLOT_OUTPUT_IDX_RANGE())
507 if (!set_adc_channel(slot, mblock->slot_to_global_io_index(slot), cluster.get_cluster_idx()))
508 return status("Error while setting ADC channels.");
509 if (!write_to_hardware())
510 return status("Error while writing ADC channels.");
511 data[idx] = daq::average();
512 }
513 }
514 if (!set_adc_channels(old_adc_channels) or !write_to_hardware())
515 LOG_ERROR("Error while restoring ADC channels.");
516 return status::success();
517}
518
519status carrier::Carrier::write_adcs_to_hardware() {
520 status error;
521
522 if (ctrl_block && !ctrl_block->write_to_hardware()) {
523 error.code |= 0x8;
524 error.msg += "CTRL Block write failed.";
525 }
526 if (!hardware->write_adc_bus_mux(adc_channels)) {
527 error.code |= 0x10;
528 error.msg += "ADC Bus write failed.";
529 }
530
531 return error;
532}
533
534status carrier::Carrier::user_set_extended_config(Carrier* carrier, const pb_ConfigCommand& msg_in, pb_ConfigResponse &msg_out) {
535#ifdef ANABRID_DEBUG_COMMS
536 Serial.println(__PRETTY_FUNCTION__);
537#endif
538
539 bool default_reset_before = true, default_calibrate_mblock = false, default_calibrate_offset = false, default_calibrate_routes = true;
540
541 if (msg_in.reset_before | default_reset_before) {
542 carrier->reset(entities::ResetAction::CIRCUIT_RESET | entities::ResetAction::OVERLOAD_RESET);
543 (void)carrier->write_to_hardware();
544 }
545
546 if(msg_in.calibrate_mblock || default_calibrate_mblock)
547 RETURN_IF_FAILED(carrier->calibrate_m_blocks());
548
549 auto res = setup(carrier, msg_in, msg_out);
550
551 if (!res)
552 return res;
553
554 if (msg_in.calibrate_offset || default_calibrate_offset)
555 RETURN_IF_FAILED(carrier->calibrate_offsets());
556
557 if (msg_in.calibrate_routes || default_calibrate_routes)
558 RETURN_IF_FAILED(carrier->calibrate_routes());
559
560 return status::success();
561}
562
563status carrier::Carrier::user_get_overload_status(pb_OverloadStatus &overload_status) {
564 entities::OverloadVisitor visitor;
565 visitor.visit(this, true);
566 visitor.to(overload_status);
567 return status::success();
568}
utils::status status
Definition daq.h:21
uint32_t
Definition flasher.cpp:195
uint32_t uint32_t size
Definition flasher.cpp:63
static constexpr int success
Definition flasher.cpp:275
__attribute__((section(".fastrun"), noinline, noclone, optimize("Os"))) int flash_sector_not_erased(uint32_t address)
Definition flasher.cpp:114
void setup()
Definition daq.h:14