REDAC HybridController
Firmware for LUCIDAC/REDAC Teensy
Loading...
Searching...
No Matches
tblock.cpp
Go to the documentation of this file.
1#include "teensy/tblock.h"
2
3using namespace blocks;
4
6 4'000'000, LSBFIRST,
7 SPI_MODE2 /* Chip expects MODE0, CLK is inverted on the way, but MOSI is not, thus CLK must be shifted */};
8
9FLASHMEM TBlockHAL_V_1_0_X::TBlockHAL_V_1_0_X(bus::addr_t block_address)
10 : TBlockHAL_Parent(block_address),
11 f_set_mux_disable{bus::replace_function_idx(block_address, 5)},
12 f_reset_mux_disable{bus::replace_function_idx(block_address, 6)},
13 f_reset_connections{bus::replace_function_idx(block_address, 4)},
14 f_connections{bus::replace_function_idx(block_address, 2), F_CONNECTION_SPI_SETTINGS},
15 f_connections_sync{bus::replace_function_idx(block_address, 3)} {}
16
18
20
21FLASHMEM uint8_t four_muxes_to_bit_sequence(Mux mux_0, Mux mux_1, Mux mux_2, Mux mux_3) {
22 return mux_0.encode() | (mux_1.encode() << 2) | (mux_2.encode() << 4) | (mux_3.encode() << 6);
23}
24
25FLASHMEM bool TBlockHAL_V_1_0_X::write_muxes(std::array<Mux, 96> muxes) {
26 // Order of muxes in function argument is [channel 8, 9, ..., 31]
27 std::array<uint8_t, 24> data{};
28 for (auto idx = 0u; idx < data.size(); idx++) {
29 // Fill up MOSI buffer in reverse to ensure data arrives in correct order
30 data[data.size() - 1 - idx] = four_muxes_to_bit_sequence(muxes[idx * 4 + 3], muxes[idx * 4 + 2],
31 muxes[idx * 4 + 1], muxes[idx * 4 + 0]);
32 }
33 auto success = f_connections.transfer(data.data(), nullptr, data.size());
35 return success;
36}
37
40 return true;
41#ifdef ANABRID_PEDANTIC
42 std::array<uint8_t, 24> data{0};
43 f_connections.transfer(data.data(), data.data(), data.size());
44 return std::all_of(data.begin(), data.end(), [](auto value) { return value == 0; });
45#else
46 return true;
47#endif
48}
49
50FLASHMEM TBlock::TBlock(TBlockHAL *hardware) : FunctionBlock("T", hardware), hardware(hardware) {
51 classifier.class_enum = CLASS_;
52}
53
54FLASHMEM TBlock::TBlock() : TBlock(new DummyTBlockHAL()) {}
55
56FLASHMEM
57TBlock *TBlock::from_entity_classifier(entities::EntityClassifier classifier, bus::addr_t block_address) {
58 if (!classifier or classifier.class_enum != CLASS_ or classifier.type != TYPE)
59 return nullptr;
60
61 if (classifier.version < entities::Version(1, 0, 2))
62 // Version 1.0.1 was manufactured without any muxers on them :)
63 return nullptr;
64 if (classifier.version < entities::Version(1, 1))
65 return new TBlock(new TBlockHAL_V_1_0_X(block_address));
66 return nullptr;
67}
68
69FLASHMEM utils::status TBlock::config_self_from_json(JsonObjectConst cfg) {
70 for (auto cfgItr = cfg.begin(); cfgItr != cfg.end(); ++cfgItr) {
71 if (cfgItr->key() == "muxes") {
72 auto res = config_self_from_muxes(cfgItr->value());
73 if (!res)
74 return res;
75 } else {
76 return utils::status("CBlock: Unknown configuration key");
77 }
78 }
79 return utils::status::success();
80}
81
82FLASHMEM utils::status TBlock::config_self_from_muxes(const JsonVariantConst &muxes) {
83 if (!muxes.is<JsonArrayConst>())
84 return utils::status("TBlock: Muxes must be array");
85 size_t idx = 0;
86 for (const JsonVariantConst &mux : muxes.as<JsonArrayConst>()) {
87 if (!mux.is<int>())
88 return utils::status("TBlock: Mux must be int");
89 int mux_value = mux.as<int>();
90 if (mux_value < 0 || 4 <= mux_value)
91 return utils::status("TBlock: Mux must be interval [0, 3]");
92 f_data[idx] = Mux::from(mux_value);
93 idx++;
94 }
95
96 return status::success();
97}
98
99FLASHMEM void TBlock::config_self_to_json(JsonObject &cfg) {
100 Entity::config_self_to_json(cfg);
101 auto muxes_cfg = cfg.createNestedArray("muxes");
102 for (auto mux : f_data) {
103 muxes_cfg.add(mux.index());
104 }
105}
106
107FLASHMEM status TBlock::connect(uint8_t src_sector, uint8_t dst_sector, uint8_t sector_lane) {
108 assert(src_sector < 4 && dst_sector < 4);
109 auto dst_lane = dst_sector * 24 + sector_lane;
110 if (f_used.test(dst_lane)) {
111 // already used
112 return status("TBlock output lane already used");
113 }
114
115 f_used.set(dst_lane);
116 f_data[dst_lane] = Mux::from(src_sector);
117 return status::success();
118}
119
120FLASHMEM void TBlock::reset(entities::ResetAction action) {
121 Entity::reset(action);
122 if (action.has(entities::ResetAction::CIRCUIT_RESET))
123 reset_connections();
124 if (action.has(entities::ResetAction::EVERYTHING))
125 enabled = true;
126}
127
128FLASHMEM void TBlock::reset_connections() {
129 // Note that the T-block always has connections, unless it is completely disabled (powered-off).
130 // The least surprising default connection is to have signals stay in their originating cluster.
131 f_used.reset();
132 f_data = {Mux::A, Mux::B, Mux::C, Mux::D, Mux::A, Mux::B, Mux::C, Mux::D, Mux::A, Mux::B, Mux::C, Mux::D,
133 Mux::A, Mux::B, Mux::C, Mux::D, Mux::A, Mux::B, Mux::C, Mux::D, Mux::A, Mux::B, Mux::C, Mux::D,
134 Mux::A, Mux::B, Mux::C, Mux::D, Mux::A, Mux::B, Mux::C, Mux::D, Mux::A, Mux::B, Mux::C, Mux::D,
135 Mux::A, Mux::B, Mux::C, Mux::D, Mux::A, Mux::B, Mux::C, Mux::D, Mux::A, Mux::B, Mux::C, Mux::D,
136 Mux::A, Mux::B, Mux::C, Mux::D, Mux::A, Mux::B, Mux::C, Mux::D, Mux::A, Mux::B, Mux::C, Mux::D,
137 Mux::A, Mux::B, Mux::C, Mux::D, Mux::A, Mux::B, Mux::C, Mux::D, Mux::A, Mux::B, Mux::C, Mux::D,
138 Mux::A, Mux::B, Mux::C, Mux::D, Mux::A, Mux::B, Mux::C, Mux::D, Mux::A, Mux::B, Mux::C, Mux::D,
139 Mux::A, Mux::B, Mux::C, Mux::D, Mux::A, Mux::B, Mux::C, Mux::D, Mux::A, Mux::B, Mux::C, Mux::D};
140}
141
142FLASHMEM status TBlock::write_to_hardware() {
143 RETURN_IF_FAILED(Entity::write_to_hardware());
144 if (!hardware->write_muxes(f_data))
145 return status("Error writing mux settings to hardware.");
146 if (enabled)
147 hardware->reset_mux_disable();
148 else
149 hardware->set_mux_disable();
150 return status::success();
151}
152
153FLASHMEM bool TBlock::is_enabled() const { return enabled; }
154
155FLASHMEM void TBlock::set_enabled(bool enabled_) { enabled = enabled_; }
156
157FLASHMEM status Router::route_cluster(SignalId output, SignalId input) {
158 auto output_carrier = output.cluster.carrier;
159 auto input_carrier = input.cluster.carrier;
160 assert(output_carrier == input_carrier);
161
162 if (output.id < 8)
163 return status(output == input);
164
165 // use T-Block inside carrier board
166 auto output_sector_lane = output.id % 24;
167 auto input_sector_lane = input.id % 24;
168
169 if (output_sector_lane != input_sector_lane)
170 return status("Connections inside carrier only allowed between same lane indices!");
171
172 auto tblock = find_carrier_t_block(input_carrier);
173 return tblock->connect(output.cluster.id, input.cluster.id, input_sector_lane);
174}
175
176FLASHMEM status Router::route_carrier(SignalId output, SignalId input) {
177 auto output_carrier = output.cluster.carrier;
178 auto input_carrier = input.cluster.carrier;
179 if (output_carrier == input_carrier) {
180 return route_cluster(output, input);
181 }
182
183 auto output_stack = output_carrier.stack;
184 auto input_stack = input_carrier.stack;
185
186 auto output_carrier_block = find_carrier_t_block(output_carrier);
187 auto input_carrier_block = find_carrier_t_block(input_carrier);
188
189 // Can only use lanes 8 .. 16 in cyclic connection inside one cluster
190 if (8 <= output.id && output.id < 16) {
191 bool short_loop = stack_is_long_loop.find(output_stack) == stack_is_long_loop.end();
192 size_t loop_size = short_loop ? 3 : 6;
193 size_t carrier_offset = output.id < 12 ? 1 : loop_size - 1;
194 size_t next_carrier = (output_carrier.id + carrier_offset) % loop_size;
195
196 // right short loop correction
197 if (output_carrier.id >= 3)
198 next_carrier += 3;
199
200 CarrierId wired_carrier = CarrierId::from(input_stack.id, static_cast<uint8_t>(next_carrier));
201
202 if (wired_carrier != input_carrier)
203 return status("Connections between lanes [8, 15] are hard wired to different carrier");
204
205 int lane_offset = output.id < 12 ? 4 : -4;
206 auto wired_lane = output.id + lane_offset;
207 if (wired_lane != input.id)
208 return status("Output connections between lanes [8, 15] are hard wired to different input lanes");
209
210 auto output_sector_lane = output.id % 24;
211 auto input_sector_lane = input.id % 24;
212
213 status result = status::success();
214 result.attach(output_carrier_block->connect(output.cluster.id, 3, output_sector_lane));
215 result.attach(input_carrier_block->connect(3, input.cluster.id, input_sector_lane));
216 return result;
217 }
218
219 if (16 <= output.id && output.id < 32) {
220 if (output.id != input.id)
221 return status("Carrier connections only allowed between common lanes");
222
223 status result = status::success();
224 auto sector_lane = output.id;
225 result.attach(output_carrier_block->connect(output.cluster.id, 3, sector_lane));
226 result.attach(input_carrier_block->connect(3, input.cluster.id, sector_lane));
227 auto stack_block = find_stack_t_block(output_carrier);
228 return stack_block->connect(output.cluster.id % 3, input.cluster.id % 3, sector_lane);
229 }
230
231 return status::failure();
232}
233
234FLASHMEM int clock_idx(CarrierId carrier) {
235 auto cluster_id = carrier.id / 3;
236 auto stack_id = carrier.stack.id;
237 return 2 * stack_id + (stack_id ^ cluster_id);
238}
239
240FLASHMEM status Router::route_stack(SignalId output, SignalId input) {
241 if (output.id < 16 || 32 <= output.id)
242 return status("Stack connection lanes out of range");
243
244 if (output.id != input.id)
245 return status("Can only connect mRedac's common lanes");
246
247 auto output_carrier = output.cluster.carrier;
248 auto input_carrier = input.cluster.carrier;
249
250 auto carrier_sector_lane = output.id - 8;
251 auto output_carrier_block = find_carrier_t_block(output_carrier);
252
253 // Same stack connections
254 if (output_carrier.is_lhs() == input_carrier.is_lhs())
255 return output_carrier_block->connect(output.cluster.id, input.cluster.id, carrier_sector_lane);
256
257 auto output_clock_idx = clock_idx(output_carrier);
258 auto input_clock_idx = clock_idx(input_carrier);
259
260 if (16 <= output.id && output.id < 24) {
261 bool cw = (output_clock_idx + 1) % 4 == input_clock_idx;
262 if (!cw)
263 return status("Routing clockwise loop only on output lanes [16, 23]");
264 } else if (24 <= output.id && output.id < 32) {
265 bool ccw = output_clock_idx == (input_clock_idx + 1) % 4;
266 if (!ccw)
267 return status("Routing counter clockwise loop only on output lanes [24, 31]");
268 }
269
270 auto input_carrier_block = find_carrier_t_block(input_carrier);
271
272 status result = status::success();
273 result.attach(output_carrier_block->connect(output.cluster.id, 3, carrier_sector_lane));
274 result.attach(input_carrier_block->connect(3, input.cluster.id, carrier_sector_lane));
275
276 auto output_stack_block = find_stack_t_block(output_carrier);
277 auto input_stack_block = find_stack_t_block(input_carrier);
278 result.attach(output_stack_block->connect(output.cluster.id, 3, carrier_sector_lane));
279 result.attach(input_stack_block->connect(3, input.cluster.id, carrier_sector_lane));
280 return result;
281}
282
283FLASHMEM status Router::route(SignalId output, SignalId input) {
284 assert(input.id < 32 && output.id < 32);
285
286 auto output_node = output.cluster;
287 auto input_node = input.cluster;
288 assert(output_node.id < 4 && input_node.id < 4);
289
290 auto output_carrier = output_node.carrier;
291 auto input_carrier = input_node.carrier;
292 assert(output_carrier.id < 6 && input_carrier.id < 6);
293
294 auto output_stack = output_carrier.stack;
295 auto input_stack = input_carrier.stack;
296 assert(output_stack.id < 2 && input_stack.id < 2);
297
298 if (output_stack != input_stack || output_carrier.is_lhs() != input_carrier.is_lhs())
299 return route_stack(output, input);
300
301 return route_carrier(output, input);
302}
HAL class for TBlock version 1.0.1.
Definition tblock.h:20
const functions::TriggerFunction f_reset_mux_disable
Definition tblock.h:23
TBlockHAL_V_1_0_X(bus::addr_t block_address)
Definition tblock.cpp:9
static const SPISettings F_CONNECTION_SPI_SETTINGS
Definition tblock.h:28
bool reset_muxes() override
Definition tblock.cpp:38
void reset_mux_disable() override
Definition tblock.cpp:19
const functions::TriggerFunction f_set_mux_disable
Definition tblock.h:22
const functions::TriggerFunction f_connections_sync
Definition tblock.h:26
const functions::TriggerFunction f_reset_connections
Definition tblock.h:24
const functions::SR74HCT595 f_connections
Definition tblock.h:25
void set_mux_disable() override
Definition tblock.cpp:17
bool write_muxes(std::array< Mux, 96 > muxes)
Definition tblock.cpp:25
bool transfer(const void *mosi_buf, void *miso_buf, size_t count) const
utils::status status
Definition daq.h:21
static constexpr int success
Definition flasher.cpp:275
Definition bus.h:21
Definition daq.h:14
FLASHMEM int clock_idx(CarrierId carrier)
Definition tblock.cpp:234
FLASHMEM uint8_t four_muxes_to_bit_sequence(Mux mux_0, Mux mux_1, Mux mux_2, Mux mux_3)
Definition tblock.cpp:21