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
47status blocks::MMulBlock::calibrate(platform::Cluster *cluster, carrier::Carrier *carrier) {
49
50 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
51
52 // Our first simple calibration process has been developed empirically and goes
53 // 1. Set all inputs to zero
54 // - The input offsets are rather small, so inputs are 0+-epsilon
55 // - The output is then roughly 0 + epsilon^2 - offset_z ~= -offset_z
56 // 2. Measure output and use it as a first estimate of offset_z
57 // 3. Set inputs to 0 and 1 and change the first one's offset to get an output close to zero
58
59 success.attach(MBlock::calibrate(cluster, carrier));
60
61 // TODO: keep old circuits alive
62 cluster->reset(entities::ResetAction::CIRCUIT_RESET);
63
64 // We can't iterativly calibrate, so we need to delete the old calibration values.
65 reset(entities::ResetAction::CALIBRATION_RESET);
66
67 // Connect zeros to all our inputs
68 LOG(ANABRID_DEBUG_CALIBRATION, "Calibrating output offsets...");
69 for (auto idx : SLOT_INPUT_IDX_RANGE())
70 success.attach(cluster->add_constant(UBlock::Transmission_Mode::GROUND, slot_to_global_io_index(idx), 0.0f,
71 slot_to_global_io_index(idx)));
72 success.attach(cluster->write_to_hardware());
73
74 // When setting routes we need to calibrate them
75 success.attach(carrier->calibrate_routes());
76
77 // Measure offset_z and set it
78 auto read_outputs = daq::average(daq::sample, 4, 10);
79
80 for (auto idx = 0u; idx < NUM_MULTIPLIERS; idx++) {
81 calibration[idx].offset_z = MMulBlockHAL_V_1_0_X::float_to_raw_calibration(-read_outputs[idx]);
82 if (!hardware->write_calibration_output_offset(idx, calibration[idx].offset_z))
83 success.attach("Writing output offsets failed");
84 }
85 delay(10); // Small transient time
86
87 // Set some inputs to one
88 LOG(ANABRID_DEBUG_CALIBRATION, "Calibrating x input offsets...");
89 cluster->ublock->change_b_side_transmission_mode(UBlock::Transmission_Mode::POS_REF); // Set constant source
90 for (auto idx = 0u; idx < NUM_MULTIPLIERS; idx++)
91 (void)cluster->cblock->set_factor(slot_to_global_io_index(idx * 2), 1.0f); // Setting 1.0 to Y inputs
92
93 if (!cluster->cblock->write_to_hardware())
94 success.attach("Writing to c-block failed");
95 // When changing a factor, we always have to calibrate offset
96 success.attach(cluster->calibrate_offsets());
97
98 delay(10);
99
100 const float target_precision = 0.0005f;
101 const int max_loops = 100;
102
103 std::bitset<NUM_MULTIPLIERS> done_channels;
104 int loop_count = 0;
105
106 // start varying the x input offsets
107 while (!done_channels.all()) {
108 read_outputs = daq::average(daq::sample, 4, 10);
109
110 for (auto idx = 0u; idx < NUM_MULTIPLIERS; idx++) {
111 if (fabs(read_outputs[idx]) < target_precision) {
112 done_channels[idx] = true; // Already precice
113 continue;
114 }
115 done_channels[idx] = false;
116
117 // Little bounded proportional controller, to decrease calibration time
118 calibration[idx].offset_x += abs_clamp(read_outputs[idx] * 6000.0f, 1, 50);
119
120 if (!hardware->write_calibration_input_offsets(idx, calibration[idx].offset_x,
121 calibration[idx].offset_y)) {
122 calibration[idx].offset_x = std::clamp(
123 calibration[idx].offset_x,
124 MMulBlockHAL_V_1_0_X::RAW_ZERO,
125 MMulBlockHAL_V_1_0_X::RAW_MAX
126 );
127 success.attach("Input X offsets out of range");
128 done_channels[idx] = true;
129 continue;
130 }
131 }
132 delay(10); // Small transient time
133
134 loop_count++;
135 if (loop_count > max_loops) {
136 success.attach("Calibration timed out!");
137 break;
138 }
139 }
140
141 LOG(ANABRID_DEBUG_CALIBRATION, "Calibrating y input offsets...");
142 for (auto idx = 0u; idx < NUM_MULTIPLIERS; idx++) {
143 // Setting 0.0 to Y inputs which previously were 1.0
144 // Setting 1.0 to X inputs
145 (void)cluster->cblock->set_factor(slot_to_global_io_index(idx * 2), 0.0f);
146 (void)cluster->cblock->set_factor(slot_to_global_io_index(idx * 2 + 1), 1.0f);
147 }
148 if (!cluster->cblock->write_to_hardware())
149 success.attach("Writing to c-block failed");
150 // When changing a factor, we always have to calibrate offset
151 success.attach(cluster->calibrate_offsets());
152
153 delay(10);
154
155 read_outputs = daq::average(daq::sample, 4, 10);
156
157 done_channels.reset();
158 loop_count = 0;
159
160 // start varying the y input offsets
161 while (!done_channels.all()) {
162 read_outputs = daq::average(daq::sample, 4, 10);
163
164 for (auto idx = 0u; idx < NUM_MULTIPLIERS; idx++) {
165 if (fabs(read_outputs[idx]) < target_precision) {
166 done_channels[idx] = true; // Already precice
167 continue;
168 }
169 done_channels[idx] = false;
170
171 // Little bounded proportional controller, to decrease calibration time
172 calibration[idx].offset_y += abs_clamp(read_outputs[idx] * 6000.0f, 1, 50);
173
174 if (!hardware->write_calibration_input_offsets(idx, calibration[idx].offset_x,
175 calibration[idx].offset_y)) {
176 calibration[idx].offset_y = std::clamp(calibration[idx].offset_y, MMulBlockHAL_V_1_0_X::RAW_ZERO,
177 MMulBlockHAL_V_1_0_X::RAW_MAX);
178 success.attach("Input Y offsets out of range");
179 done_channels[idx] = true;
180 continue;
181 }
182 }
183 delay(10); // Small transient time
184
185 loop_count++;
186 if (loop_count > max_loops) {
187 success.attach("Calibration timed out!");
188 break;
189 }
190 }
191
192 return success;
193}
194
195// V1.-1.0
196
200
202 auto res = MMulBlock::write_calibration_to_hardware();
203 if (!res)
204 return res;
205
206 for (size_t i = 0; i < MMulBlock::NUM_MULTIPLIERS; i++) {
207 if (!hardware->write_calibration_gain(i, calibration[i].gain))
208 return utils::status(777, "MMulBlock::calibration from json for multiplier %d values gain not accepted",
209 i);
210 }
211 return utils::status::success();
212}
213
215 carrier::Carrier *carrier) {
217 LOG(ANABRID_DEBUG_CALIBRATION, __PRETTY_FUNCTION__);
218
219 // First do regular calibration
220 success.attach(MMulBlock::calibrate(cluster, carrier));
221
222 // Automatic gain calibration can only be done by version 1.1
223 LOG(ANABRID_DEBUG_CALIBRATION, "Calibrating output gains...");
224
225 // Setting 1.0 to Y inputs which previously were 0.0
226 for (auto idx = 0u; idx < NUM_MULTIPLIERS; idx++)
227 (void)cluster->cblock->set_factor(slot_to_global_io_index(idx * 2), 1.0f);
228
229 if (!cluster->cblock->write_to_hardware())
230 success.attach("Writing to c-block failed");
231 // When changing a factor, we always have to calibrate offset
232 success.attach(cluster->calibrate_offsets());
233
234 delay(10);
235
236 const float target_precision = 0.0005f;
237 const int max_loops = 100;
238
239 std::bitset<NUM_MULTIPLIERS> done_channels;
240 int loop_count = 0;
241
242 // start varying the gain factors
243 while (!done_channels.all()) {
244 auto read_outputs = daq::average(daq::sample, 4, 10);
245
246 for (auto idx = 0u; idx < NUM_MULTIPLIERS; idx++) {
247 if (fabs(read_outputs[idx] - 1.0f) < target_precision) {
248 done_channels[idx] = true; // Already precice
249 continue;
250 }
251 done_channels[idx] = false;
252
253 // Little bounded proportional controller, to decrease calibration time
254 int step = abs_clamp((read_outputs[idx] - 1.0f) * -1000.0f, 1, 70);
255
256 if (calibration[idx].gain + step > 0xff || calibration[idx].gain + step < 0) {
257 success.attach("Output gain out of range");
258 done_channels[idx] = true;
259 continue;
260 }
261
262 calibration[idx].gain += step;
263
264 if (!hardware->write_calibration_gain(idx, calibration[idx].gain)) {
265 success.attach("Writing output gain failed");
266 done_channels[idx] = true;
267 continue;
268 }
269 }
270 delay(10); // Small transient time
271
272 loop_count++;
273 if (loop_count > max_loops) {
274 success.attach("Calibration timed out!");
275 break;
276 }
277 }
278
279 return success;
280}
281
282// Hardware abstraction layer
283
285 : MMulBlockHAL_Parent(block_address, 7), f_overload_flags_reset(bus::address_from_tuple(block_address, 3)),
286 f_overload_flags(bus::replace_function_idx(block_address, 2), 7),
287 f_calibration_dac_0(bus::address_from_tuple(block_address, 4), 7, 2.0f),
288 f_calibration_dac_1(bus::address_from_tuple(block_address, 5), 7, 2.0f) {}
289
290bool blocks::MMulBlockHAL::init() { return MBlockHAL::init(); }
291
293 bool error = true;
294 error &= f_calibration_dac_0.init();
295 error &= f_calibration_dac_0.set_external_reference();
296 error &= f_calibration_dac_0.set_double_gain();
297 error &= f_calibration_dac_1.init();
298 error &= f_calibration_dac_1.set_external_reference();
299 error &= f_calibration_dac_1.set_double_gain();
300 error &= MMulBlockHAL::init();
301 return error;
302}
303
305
307 uint16_t offset_y) {
308 return f_calibration_dac_0.set_channel_raw(idx * 2 + 1, offset_x) and
309 f_calibration_dac_0.set_channel_raw(idx * 2, offset_y);
310}
311
313 return f_calibration_dac_1.set_channel_raw(idx, offset_z);
314}
315
317 return f_overload_flags.read8();
318}
319
321 : MMulBlockHAL_V_1_0_X(block_address), f_gain_ch0_1(bus::replace_function_idx(block_address, 6), 7),
322 f_gain_ch2_3(bus::replace_function_idx(block_address, 7), 7) {}
323
325 if (idx < 2)
326 return f_gain_ch0_1.write_channel_raw(idx, gain);
327 else if (idx < 4)
328 return f_gain_ch2_3.write_channel_raw(idx - 2, gain);
329 else
330 return false;
331}
332
334 : MMulBlockHAL_V_1_0_X(block_address), f_gain(bus::replace_function_idx(block_address, 6), 7) {}
335
337 if (idx == 0)
338 return f_gain.write_channel_raw(2, gain);
339 else if (idx == 1)
340 return f_gain.write_channel_raw(0, gain);
341 else if (idx == 2)
342 return f_gain.write_channel_raw(1, gain);
343 if (idx == 3)
344 return f_gain.write_channel_raw(3, gain);
345 return false;
346}
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
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)
MMulBlockHAL_FullAutoCalibration * hardware
Definition mblock_mul.h:120
status calibrate(platform::Cluster *cluster, carrier::Carrier *carrier) override
status write_calibration_to_hardware() override
int abs_clamp(float in, int min, int max)
Definition mblock.cpp:20
utils::status status
Definition daq.h:21
int abs_clamp(float in, int min, int max)
Definition mblock.cpp:20
static constexpr int success
Definition flasher.cpp:275
entities::EntitySharedHardware< MMulBlockHAL > MMulBlockHAL_Parent
Definition mblock_mul.h:17
Definition bus.h:21
Definition daq.h:14