REDAC HybridController
Firmware for LUCIDAC/REDAC Teensy
Loading...
Searching...
No Matches
mblock_mul.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 <algorithm>
6#include <bitset>
7
8#include "carrier/carrier.h"
9#include "carrier/cluster.h"
10
11#include "block/mblock.h"
12#include "teensy/mblock_mul.h"
13
14extern int abs_clamp(float in, int min, int max);
15
16blocks::MMulBlock *blocks::MMulBlock::from_entity_classifier(entities::EntityClassifier classifier,
17 const bus::addr_t block_address) {
18 if (!classifier or classifier.class_enum != CLASS_ or classifier.type != static_cast<uint8_t>(TYPE))
19 return nullptr;
20
21 // Currently, there are no different variants
22 if (classifier.variant != entities::EntityClassifier::DEFAULT_)
23 return nullptr;
24
25 SLOT slot = block_address % 8 == 4 ? SLOT::M0 : SLOT::M1;
26 // Return default implementation
27 if (classifier.version < entities::Version(1))
28 return nullptr;
29 if (classifier.version < entities::Version(1, 1)) {
30 auto *new_block = new MMulBlock(slot, new MMulBlockHAL_V_1_0_X(block_address));
31 new_block->classifier = classifier;
32 return new_block;
33 }
34 if (classifier.version <= entities::Version(1, 2)) {
35 auto *new_block = new MMulBlock_FullAutoCalibration(slot, new MMulBlockHAL_V_1_1_X(block_address));
36 new_block->classifier = classifier;
37 return new_block;
38 }
39 if (classifier.version == entities::Version(1, -1)) {
40 auto *new_block = new MMulBlock_FullAutoCalibration(slot, new MMulBlockHAL_V_1_M1_X(block_address));
41 new_block->classifier = classifier;
42 return new_block;
43 }
44 return nullptr;
45}
46
47UnitResult blocks::MMulBlock::calibrate(platform::Cluster *cluster, carrier::Carrier *carrier) {
48 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
49
50 // Our first simple calibration process has been developed empirically and goes
51 // 1. Set all inputs to zero
52 // - The input offsets are rather small, so inputs are 0+-epsilon
53 // - The output is then roughly 0 + epsilon^2 - offset_z ~= -offset_z
54 // 2. Measure output and use it as a first estimate of offset_z
55 // 3. Set inputs to 0 and 1 and change the first one's offset to get an output close to zero
56
57 std::function find_zeros = [&](size_t input) {
58 float lhs = input == 0 ? 1.0f : 0.0f;
59 float rhs = input == 1 ? 1.0f : 0.0f;
60 // Set some inputs to one
61
62 cluster->reset(entities::ResetAction::CIRCUIT_RESET);
63 for (auto idx = 0u; idx < NUM_MULTIPLIERS; ++idx) {
64 auto lhs_lane = idx * 2;
65 auto rhs_lane = idx * 2 + 1;
66 cluster->add_constant(UBlockHAL::Transmission_Mode::POS_REF, lhs_lane, lhs, slot_to_global_io_index(lhs_lane));
67 cluster->add_constant(UBlockHAL::Transmission_Mode::POS_REF, rhs_lane, rhs, slot_to_global_io_index(rhs_lane));
68 }
69
70 TRY(cluster->write_to_hardware());
71
72 for (size_t step = 1024; step > 0; step >>= 1) {
73 for (auto idx = 0u; idx < NUM_MULTIPLIERS; ++idx) {
74 auto &cal = calibration[idx];
75 hardware->write_calibration_input_offsets(idx, cal.offset_x, cal.offset_y);
76 }
77
78 delay(10);
79 auto read_outputs = daq::average(daq::sample, 4, 10);
80
81 for (auto idx = 0u; idx < NUM_MULTIPLIERS; ++idx) {
82 auto& cal = calibration[idx];
83
84 bool is_neg = read_outputs[idx] < 0.0f;
85 if (input == 0) {
86 cal.offset_x -= is_neg ? step : -step;
87 } else {
88 cal.offset_y -= is_neg ? step : -step;
89 }
90 }
91 }
92
93 return UnitResult::ok();
94 };
95
96 // TODO: keep old circuits alive
97 cluster->reset(entities::ResetAction::CIRCUIT_RESET);
98 reset(entities::ResetAction::CALIBRATION_RESET);
99
100 delay(10);
101
102 // Connect zeros to all our inputs
103 LOG(ANABRID_DEBUG_CALIBRATION, "Calibrating output offsets...");
104 for (auto idx : SLOT_INPUT_IDX_RANGE()) {
105 TRY(cluster->add_constant(
106 UBlock::Transmission_Mode::GROUND,
107 slot_to_global_io_index(idx),
108 0.0f,
109 slot_to_global_io_index(idx)
110 ));
111 }
112
113 TRY(cluster->write_to_hardware());
114
115 // When setting routes we need to calibrate them
116 TRY(carrier->calibrate_offsets());
117
118 // When setting routes we need to calibrate them
119 TRY(carrier->calibrate_routes());
120
121 // Measure offset_z and set it
122 auto read_outputs = daq::average(daq::sample, 4, 10);
123
124 for (auto idx = 0u; idx < NUM_MULTIPLIERS; idx++) {
125 calibration[idx].offset_z = MMulBlockHAL_V_1_0_X::float_to_raw_calibration(-read_outputs[idx]);
126 if (!hardware->write_calibration_output_offset(idx, calibration[idx].offset_z))
127 return UnitResult::err("Writing output offsets failed");
128 }
129
130 delay(10); // Small transient time
131
132 for (auto idx = 0u; idx < NUM_MULTIPLIERS; idx++) {
133 auto& cal = calibration[idx];
134 cal.offset_x = 2047;
135 cal.offset_y = 2047;
136 }
137
138 LOG(ANABRID_DEBUG_CALIBRATION, "Calibrating x input offsets...");
139 TRY(find_zeros(0));
140
141 LOG(ANABRID_DEBUG_CALIBRATION, "Calibrating y input offsets...");
142 TRY(find_zeros(1));
143
144 return UnitResult::ok();
145}
146
147// V1.-1.0
148
152
154 auto res = MMulBlock::write_calibration_to_hardware();
155 if (!res)
156 return res;
157
158 for (size_t i = 0; i < MMulBlock::NUM_MULTIPLIERS; i++) {
159 if (!hardware->write_calibration_gain(i, calibration[i].gain))
160 return UnitResult::err_fmt("MMulBlock::calibration from json for multiplier %d values gain not accepted", i);
161 }
162 return UnitResult::ok();
163}
164
165UnitResult blocks::MMulBlock_FullAutoCalibration::calibrate(platform::Cluster *cluster, carrier::Carrier *carrier) {
166 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
167
168 // First do regular calibration
169 TRY(MMulBlock::calibrate(cluster, carrier));
170
171 cluster->reset(entities::ResetAction::CIRCUIT_RESET);
172 for (auto idx = 0u; idx < NUM_MULTIPLIERS; ++idx) {
173 auto lhs_lane = idx * 2;
174 auto rhs_lane = idx * 2 + 1;
175 cluster->add_constant(UBlockHAL::Transmission_Mode::POS_REF, lhs_lane, 1.0, slot_to_global_io_index(lhs_lane));
176 cluster->add_constant(UBlockHAL::Transmission_Mode::POS_REF, rhs_lane, 1.0, slot_to_global_io_index(rhs_lane));
177 }
178
179 TRY(cluster->write_to_hardware());
180
181 TRY(cluster->calibrate_offsets());
182 delay(10);
183
184 for (auto idx = 0u; idx < NUM_MULTIPLIERS; idx++) {
185 calibration[idx].gain = 127;
186 }
187
188 for (size_t step = 64; step > 0; step >>= 1) {
189 for (auto idx = 0u; idx < NUM_MULTIPLIERS; ++idx) {
190 hardware->write_calibration_gain(idx, calibration[idx].gain);
191 }
192
193 delay(10);
194 auto read_outputs = daq::average(daq::sample, 4, 10);
195 for (auto idx = 0u; idx < NUM_MULTIPLIERS; ++idx) {
196 calibration[idx].gain += read_outputs[idx] < 1.0f ? step : -step;
197 }
198 }
199
200 return UnitResult::ok();
201}
202
203// Hardware abstraction layer
204
206 : MMulBlockHAL_Parent(block_address, 7), f_overload_flags_reset(bus::address_from_tuple(block_address, 3)),
207 f_overload_flags(bus::replace_function_idx(block_address, 2), 7),
208 f_calibration_dac_0(bus::address_from_tuple(block_address, 4), 7, 2.0f),
209 f_calibration_dac_1(bus::address_from_tuple(block_address, 5), 7, 2.0f) {}
210
211UnitResult blocks::MMulBlockHAL::init() { return MBlockHAL::init(); }
212
214 bool error = true;
215 error &= f_calibration_dac_0.init();
216 error &= f_calibration_dac_0.set_external_reference();
217 error &= f_calibration_dac_0.set_double_gain();
218 error &= f_calibration_dac_1.init();
219 error &= f_calibration_dac_1.set_external_reference();
220 error &= f_calibration_dac_1.set_double_gain();
221 error &= MMulBlockHAL::init();
222 if (!error)
223 return UnitResult::err("Failed mmul-blck writing hardware");
224 return UnitResult::ok();
225}
226
228
230 uint16_t offset_y) {
231 return f_calibration_dac_0.set_channel_raw(idx * 2 + 1, offset_x) and
232 f_calibration_dac_0.set_channel_raw(idx * 2, offset_y);
233}
234
236 return f_calibration_dac_1.set_channel_raw(idx, offset_z);
237}
238
240 return f_overload_flags.read8();
241}
242
244 : MMulBlockHAL_V_1_0_X(block_address), f_gain_ch0_1(bus::replace_function_idx(block_address, 6), 7),
245 f_gain_ch2_3(bus::replace_function_idx(block_address, 7), 7) {}
246
248 if (idx < 2)
249 return f_gain_ch0_1.write_channel_raw(idx, gain);
250 else if (idx < 4)
251 return f_gain_ch2_3.write_channel_raw(idx - 2, gain);
252 else
253 return false;
254}
255
257 : MMulBlockHAL_V_1_0_X(block_address), f_gain(bus::replace_function_idx(block_address, 6), 7) {}
258
260 if (idx == 0)
261 return f_gain.write_channel_raw(2, gain);
262 else if (idx == 1)
263 return f_gain.write_channel_raw(0, gain);
264 else if (idx == 2)
265 return f_gain.write_channel_raw(1, gain);
266 if (idx == 3)
267 return f_gain.write_channel_raw(3, gain);
268 return false;
269}
bool write_calibration_output_offset(uint8_t idx, uint16_t offset_z) override
const functions::TriggerFunction f_overload_flags_reset
Definition mblock_mul.h:26
std::bitset< 8 > read_overload_flags() override
UnitResult init() override
void reset_overload_flags() override
const functions::SR74HC16X f_overload_flags
Definition mblock_mul.h:27
MMulBlockHAL_V_1_0_X(bus::addr_t block_address)
const functions::DAC60508 f_calibration_dac_0
Definition mblock_mul.h:28
bool write_calibration_input_offsets(uint8_t idx, uint16_t offset_x, uint16_t offset_y) override
const functions::DAC60508 f_calibration_dac_1
Definition mblock_mul.h:29
MMulBlockHAL_V_1_1_X(bus::addr_t block_address)
bool write_calibration_gain(uint8_t idx, uint8_t gain) override
functions::AD8403 f_gain
Definition mblock_mul.h:89
functions::AD8402 f_gain_ch0_1
Definition mblock_mul.h:64
functions::AD8402 f_gain_ch2_3
Definition mblock_mul.h:65
MMulBlockHAL_V_1_M1_X(bus::addr_t block_address)
bool write_calibration_gain(uint8_t idx, uint8_t gain) override
MMulBlock_FullAutoCalibration(SLOT slot, blocks::MMulBlockHAL_FullAutoCalibration *hardware)
UnitResult write_calibration_to_hardware() override
MMulBlockHAL_FullAutoCalibration * hardware
Definition mblock_mul.h:120
UnitResult calibrate(platform::Cluster *cluster, carrier::Carrier *carrier) override
int abs_clamp(float in, int min, int max)
Definition mblock.cpp:20
entities::EntitySharedHardware< MMulBlockHAL > MMulBlockHAL_Parent
Definition mblock_mul.h:17
Definition bus.h:21
Definition daq.h:14