1 /*
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #ifndef _HARDWARE_INTERP_H
8 #define _HARDWARE_INTERP_H
9
10 #include "pico.h"
11 #include "hardware/structs/interp.h"
12 #include "hardware/regs/sio.h"
13
14 // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_HARDWARE_INTERP, Enable/disable assertions in the hardware_interp module, type=bool, default=0, group=hardware_interp
15 #ifndef PARAM_ASSERTIONS_ENABLED_HARDWARE_INTERP
16 #ifdef PARAM_ASSERTIONS_ENABLED_INTERP // backwards compatibility with SDK < 2.0.0
17 #define PARAM_ASSERTIONS_ENABLED_HARDWARE_INTERP PARAM_ASSERTIONS_ENABLED_INTERP
18 #else
19 #define PARAM_ASSERTIONS_ENABLED_HARDWARE_INTERP 0
20 #endif
21 #endif
22
23 #ifdef __cplusplus
24 extern "C" {
25 #endif
26
27 /** \file hardware/interp.h
28 * \defgroup hardware_interp hardware_interp
29 *
30 * \brief Hardware Interpolator API
31 *
32 * Each core is equipped with two interpolators (INTERP0 and INTERP1) which can be used to accelerate
33 * tasks by combining certain pre-configured simple operations into a single processor cycle. Intended
34 * for cases where the pre-configured operation is repeated a large number of times, this results in
35 * code which uses both fewer CPU cycles and fewer CPU registers in the time critical sections of the
36 * code.
37 *
38 * The interpolators are used heavily to accelerate audio operations within the SDK, but their
39 * flexible configuration make it possible to optimise many other tasks such as quantization and
40 * dithering, table lookup address generation, affine texture mapping, decompression and linear feedback.
41 *
42 * Please refer to the appropriate RP-series microcontroller datasheet for more information on the HW
43 * interpolators and how they work.
44 */
45
46 #define interp0 interp0_hw
47 #define interp1 interp1_hw
48
49 /** \brief Interpolator configuration
50 * \defgroup interp_config interp_config
51 * \ingroup hardware_interp
52 *
53 * Each interpolator needs to be configured, these functions provide handy helpers to set up configuration
54 * structures.
55 *
56 */
57
58 typedef struct {
59 uint32_t ctrl;
60 } interp_config;
61
interp_index(interp_hw_t * interp)62 static inline uint interp_index(interp_hw_t *interp) {
63 valid_params_if(HARDWARE_INTERP, interp == interp0 || interp == interp1);
64 return interp == interp1 ? 1 : 0;
65 }
66
67 /*! \brief Claim the interpolator lane specified
68 * \ingroup hardware_interp
69 *
70 * Use this function to claim exclusive access to the specified interpolator lane.
71 *
72 * This function will panic if the lane is already claimed.
73 *
74 * \param interp Interpolator on which to claim a lane. interp0 or interp1
75 * \param lane The lane number, 0 or 1.
76 */
77 void interp_claim_lane(interp_hw_t *interp, uint lane);
78 // The above really should be called this for consistency
79 #define interp_lane_claim interp_claim_lane
80
81 /*! \brief Claim the interpolator lanes specified in the mask
82 * \ingroup hardware_interp
83 *
84 * \param interp Interpolator on which to claim lanes. interp0 or interp1
85 * \param lane_mask Bit pattern of lanes to claim (only bits 0 and 1 are valid)
86 */
87 void interp_claim_lane_mask(interp_hw_t *interp, uint lane_mask);
88
89 /*! \brief Release a previously claimed interpolator lane
90 * \ingroup hardware_interp
91 *
92 * \param interp Interpolator on which to release a lane. interp0 or interp1
93 * \param lane The lane number, 0 or 1
94 */
95 void interp_unclaim_lane(interp_hw_t *interp, uint lane);
96 // The above really should be called this for consistency
97 #define interp_lane_unclaim interp_unclaim_lane
98
99 /*! \brief Determine if an interpolator lane is claimed
100 * \ingroup hardware_interp
101 *
102 * \param interp Interpolator whose lane to check
103 * \param lane The lane number, 0 or 1
104 * \return true if claimed, false otherwise
105 * \see interp_claim_lane
106 * \see interp_claim_lane_mask
107 */
108 bool interp_lane_is_claimed(interp_hw_t *interp, uint lane);
109
110 /*! \brief Release previously claimed interpolator lanes \see interp_claim_lane_mask
111 * \ingroup hardware_interp
112 *
113 * \param interp Interpolator on which to release lanes. interp0 or interp1
114 * \param lane_mask Bit pattern of lanes to unclaim (only bits 0 and 1 are valid)
115 */
116 void interp_unclaim_lane_mask(interp_hw_t *interp, uint lane_mask);
117
118 /*! \brief Set the interpolator shift value
119 * \ingroup interp_config
120 *
121 * Sets the number of bits the accumulator is shifted before masking, on each iteration.
122 *
123 * \param c Pointer to an interpolator config
124 * \param shift Number of bits
125 */
interp_config_set_shift(interp_config * c,uint shift)126 static inline void interp_config_set_shift(interp_config *c, uint shift) {
127 valid_params_if(HARDWARE_INTERP, shift < 32);
128 c->ctrl = (c->ctrl & ~SIO_INTERP0_CTRL_LANE0_SHIFT_BITS) |
129 ((shift << SIO_INTERP0_CTRL_LANE0_SHIFT_LSB) & SIO_INTERP0_CTRL_LANE0_SHIFT_BITS);
130 }
131
132 /*! \brief Set the interpolator mask range
133 * \ingroup interp_config
134 *
135 * Sets the range of bits (least to most) that are allowed to pass through the interpolator
136 *
137 * \param c Pointer to interpolation config
138 * \param mask_lsb The least significant bit allowed to pass
139 * \param mask_msb The most significant bit allowed to pass
140 */
interp_config_set_mask(interp_config * c,uint mask_lsb,uint mask_msb)141 static inline void interp_config_set_mask(interp_config *c, uint mask_lsb, uint mask_msb) {
142 valid_params_if(HARDWARE_INTERP, mask_msb < 32);
143 valid_params_if(HARDWARE_INTERP, mask_lsb <= mask_msb);
144 c->ctrl = (c->ctrl & ~(SIO_INTERP0_CTRL_LANE0_MASK_LSB_BITS | SIO_INTERP0_CTRL_LANE0_MASK_MSB_BITS)) |
145 ((mask_lsb << SIO_INTERP0_CTRL_LANE0_MASK_LSB_LSB) & SIO_INTERP0_CTRL_LANE0_MASK_LSB_BITS) |
146 ((mask_msb << SIO_INTERP0_CTRL_LANE0_MASK_MSB_LSB) & SIO_INTERP0_CTRL_LANE0_MASK_MSB_BITS);
147 }
148
149 /*! \brief Enable cross input
150 * \ingroup interp_config
151 *
152 * Allows feeding of the accumulator content from the other lane back in to this lanes shift+mask hardware.
153 * This will take effect even if the interp_config_set_add_raw option is set as the cross input mux is before the
154 * shift+mask bypass
155 *
156 * \param c Pointer to interpolation config
157 * \param cross_input If true, enable the cross input.
158 */
interp_config_set_cross_input(interp_config * c,bool cross_input)159 static inline void interp_config_set_cross_input(interp_config *c, bool cross_input) {
160 c->ctrl = (c->ctrl & ~SIO_INTERP0_CTRL_LANE0_CROSS_INPUT_BITS) |
161 (cross_input ? SIO_INTERP0_CTRL_LANE0_CROSS_INPUT_BITS : 0);
162 }
163
164 /*! \brief Enable cross results
165 * \ingroup interp_config
166 *
167 * Allows feeding of the other lane’s result into this lane’s accumulator on a POP operation.
168 *
169 * \param c Pointer to interpolation config
170 * \param cross_result If true, enables the cross result
171 */
interp_config_set_cross_result(interp_config * c,bool cross_result)172 static inline void interp_config_set_cross_result(interp_config *c, bool cross_result) {
173 c->ctrl = (c->ctrl & ~SIO_INTERP0_CTRL_LANE0_CROSS_RESULT_BITS) |
174 (cross_result ? SIO_INTERP0_CTRL_LANE0_CROSS_RESULT_BITS : 0);
175 }
176
177 /*! \brief Set sign extension
178 * \ingroup interp_config
179 *
180 * Enables signed mode, where the shifted and masked accumulator value is sign-extended to 32 bits
181 * before adding to BASE1, and LANE1 PEEK/POP results appear extended to 32 bits when read by processor.
182 *
183 * \param c Pointer to interpolation config
184 * \param _signed If true, enables sign extension
185 */
interp_config_set_signed(interp_config * c,bool _signed)186 static inline void interp_config_set_signed(interp_config *c, bool _signed) {
187 c->ctrl = (c->ctrl & ~SIO_INTERP0_CTRL_LANE0_SIGNED_BITS) |
188 (_signed ? SIO_INTERP0_CTRL_LANE0_SIGNED_BITS : 0);
189 }
190
191 /*! \brief Set raw add option
192 * \ingroup interp_config
193 *
194 * When enabled, mask + shift is bypassed for LANE0 result. This does not affect the FULL result.
195 *
196 * \param c Pointer to interpolation config
197 * \param add_raw If true, enable raw add option.
198 */
interp_config_set_add_raw(interp_config * c,bool add_raw)199 static inline void interp_config_set_add_raw(interp_config *c, bool add_raw) {
200 c->ctrl = (c->ctrl & ~SIO_INTERP0_CTRL_LANE0_ADD_RAW_BITS) |
201 (add_raw ? SIO_INTERP0_CTRL_LANE0_ADD_RAW_BITS : 0);
202 }
203
204 /*! \brief Set blend mode
205 * \ingroup interp_config
206 *
207 * If enabled, LANE1 result is a linear interpolation between BASE0 and BASE1, controlled
208 * by the 8 LSBs of lane 1 shift and mask value (a fractional number between 0 and 255/256ths)
209 *
210 * LANE0 result does not have BASE0 added (yields only the 8 LSBs of lane 1 shift+mask value)
211 *
212 * FULL result does not have lane 1 shift+mask value added (BASE2 + lane 0 shift+mask)
213 *
214 * LANE1 SIGNED flag controls whether the interpolation is signed or unsig
215 *
216 * \param c Pointer to interpolation config
217 * \param blend Set true to enable blend mode.
218 */
interp_config_set_blend(interp_config * c,bool blend)219 static inline void interp_config_set_blend(interp_config *c, bool blend) {
220 c->ctrl = (c->ctrl & ~SIO_INTERP0_CTRL_LANE0_BLEND_BITS) |
221 (blend ? SIO_INTERP0_CTRL_LANE0_BLEND_BITS : 0);
222 }
223
224 /*! \brief Set interpolator clamp mode (Interpolator 1 only)
225 * \ingroup interp_config
226 *
227 * Only present on INTERP1 on each core. If CLAMP mode is enabled:
228 * - LANE0 result is a shifted and masked ACCUM0, clamped by a lower bound of BASE0 and an upper bound of BASE1.
229 * - Signedness of these comparisons is determined by LANE0_CTRL_SIGNED
230 *
231 * \param c Pointer to interpolation config
232 * \param clamp Set true to enable clamp mode
233 */
interp_config_set_clamp(interp_config * c,bool clamp)234 static inline void interp_config_set_clamp(interp_config *c, bool clamp) {
235 c->ctrl = (c->ctrl & ~SIO_INTERP1_CTRL_LANE0_CLAMP_BITS) |
236 (clamp ? SIO_INTERP1_CTRL_LANE0_CLAMP_BITS : 0);
237 }
238
239 /*! \brief Set interpolator Force bits
240 * \ingroup interp_config
241 *
242 * ORed into bits 29:28 of the lane result presented to the processor on the bus.
243 *
244 * No effect on the internal 32-bit datapath. Handy for using a lane to generate sequence
245 * of pointers into flash or SRAM
246 *
247 * \param c Pointer to interpolation config
248 * \param bits Sets the force bits to that specified. Range 0-3 (two bits)
249 */
interp_config_set_force_bits(interp_config * c,uint bits)250 static inline void interp_config_set_force_bits(interp_config *c, uint bits) {
251 invalid_params_if(HARDWARE_INTERP, bits > 3);
252 // note cannot use hw_set_bits on SIO
253 c->ctrl = (c->ctrl & ~SIO_INTERP0_CTRL_LANE0_FORCE_MSB_BITS) |
254 (bits << SIO_INTERP0_CTRL_LANE0_FORCE_MSB_LSB);
255 }
256
257 /*! \brief Get a default configuration
258 * \ingroup interp_config
259 *
260 * \return A default interpolation configuration
261 */
interp_default_config(void)262 static inline interp_config interp_default_config(void) {
263 interp_config c = {0};
264 // Just pass through everything
265 interp_config_set_mask(&c, 0, 31);
266 return c;
267 }
268
269 /*! \brief Send configuration to a lane
270 * \ingroup interp_config
271 *
272 * If an invalid configuration is specified (ie a lane specific item is set on wrong lane),
273 * depending on setup this function can panic.
274 *
275 * \param interp Interpolator instance, interp0 or interp1.
276 * \param lane The lane to set
277 * \param config Pointer to interpolation config
278 */
279
interp_set_config(interp_hw_t * interp,uint lane,interp_config * config)280 static inline void interp_set_config(interp_hw_t *interp, uint lane, interp_config *config) {
281 invalid_params_if(HARDWARE_INTERP, lane > 1);
282 invalid_params_if(HARDWARE_INTERP, config->ctrl & SIO_INTERP1_CTRL_LANE0_CLAMP_BITS &&
283 (!interp_index(interp) || lane)); // only interp1 lane 0 has clamp bit
284 invalid_params_if(HARDWARE_INTERP, config->ctrl & SIO_INTERP0_CTRL_LANE0_BLEND_BITS &&
285 (interp_index(interp) || lane)); // only interp0 lane 0 has blend bit
286 interp->ctrl[lane] = config->ctrl;
287 }
288
289 /*! \brief Directly set the force bits on a specified lane
290 * \ingroup hardware_interp
291 *
292 * These bits are ORed into bits 29:28 of the lane result presented to the processor on the bus.
293 * There is no effect on the internal 32-bit datapath.
294 *
295 * Useful for using a lane to generate sequence of pointers into flash or SRAM, saving a subsequent
296 * OR or add operation.
297 *
298 * \param interp Interpolator instance, interp0 or interp1.
299 * \param lane The lane to set
300 * \param bits The bits to set (bits 0 and 1, value range 0-3)
301 */
interp_set_force_bits(interp_hw_t * interp,uint lane,uint bits)302 static inline void interp_set_force_bits(interp_hw_t *interp, uint lane, uint bits) {
303 // note cannot use hw_set_bits on SIO
304 interp->ctrl[lane] = interp->ctrl[lane] | (bits << SIO_INTERP0_CTRL_LANE0_FORCE_MSB_LSB);
305 }
306
307 typedef struct {
308 uint32_t accum[2];
309 uint32_t base[3];
310 uint32_t ctrl[2];
311 } interp_hw_save_t;
312
313 /*! \brief Save the specified interpolator state
314 * \ingroup hardware_interp
315 *
316 * Can be used to save state if you need an interpolator for another purpose, state
317 * can then be recovered afterwards and continue from that point
318 *
319 * \param interp Interpolator instance, interp0 or interp1.
320 * \param saver Pointer to the save structure to fill in
321 */
322 void interp_save(interp_hw_t *interp, interp_hw_save_t *saver);
323
324 /*! \brief Restore an interpolator state
325 * \ingroup hardware_interp
326 *
327 * \param interp Interpolator instance, interp0 or interp1.
328 * \param saver Pointer to save structure to reapply to the specified interpolator
329 */
330 void interp_restore(interp_hw_t *interp, interp_hw_save_t *saver);
331
332 /*! \brief Sets the interpolator base register by lane
333 * \ingroup hardware_interp
334 *
335 * \param interp Interpolator instance, interp0 or interp1.
336 * \param lane The lane number, 0 or 1 or 2
337 * \param val The value to apply to the register
338 */
interp_set_base(interp_hw_t * interp,uint lane,uint32_t val)339 static inline void interp_set_base(interp_hw_t *interp, uint lane, uint32_t val) {
340 interp->base[lane] = val;
341 }
342
343 /*! \brief Gets the content of interpolator base register by lane
344 * \ingroup hardware_interp
345 *
346 * \param interp Interpolator instance, interp0 or interp1.
347 * \param lane The lane number, 0 or 1 or 2
348 * \return The current content of the lane base register
349 */
interp_get_base(interp_hw_t * interp,uint lane)350 static inline uint32_t interp_get_base(interp_hw_t *interp, uint lane) {
351 return interp->base[lane];
352 }
353
354 /*! \brief Sets the interpolator base registers simultaneously
355 * \ingroup hardware_interp
356 *
357 * The lower 16 bits go to BASE0, upper bits to BASE1 simultaneously.
358 * Each half is sign-extended to 32 bits if that lane’s SIGNED flag is set.
359 *
360 * \param interp Interpolator instance, interp0 or interp1.
361 * \param val The value to apply to the register
362 */
interp_set_base_both(interp_hw_t * interp,uint32_t val)363 static inline void interp_set_base_both(interp_hw_t *interp, uint32_t val) {
364 interp->base01 = val;
365 }
366
367
368 /*! \brief Sets the interpolator accumulator register by lane
369 * \ingroup hardware_interp
370 *
371 * \param interp Interpolator instance, interp0 or interp1.
372 * \param lane The lane number, 0 or 1
373 * \param val The value to apply to the register
374 */
interp_set_accumulator(interp_hw_t * interp,uint lane,uint32_t val)375 static inline void interp_set_accumulator(interp_hw_t *interp, uint lane, uint32_t val) {
376 interp->accum[lane] = val;
377 }
378
379 /*! \brief Gets the content of the interpolator accumulator register by lane
380 * \ingroup hardware_interp
381 *
382 * \param interp Interpolator instance, interp0 or interp1.
383 * \param lane The lane number, 0 or 1
384 * \return The current content of the register
385 */
interp_get_accumulator(interp_hw_t * interp,uint lane)386 static inline uint32_t interp_get_accumulator(interp_hw_t *interp, uint lane) {
387 return interp->accum[lane];
388 }
389
390 /*! \brief Read lane result, and write lane results to both accumulators to update the interpolator
391 * \ingroup hardware_interp
392 *
393 * \param interp Interpolator instance, interp0 or interp1.
394 * \param lane The lane number, 0 or 1
395 * \return The content of the lane result register
396 */
interp_pop_lane_result(interp_hw_t * interp,uint lane)397 static inline uint32_t interp_pop_lane_result(interp_hw_t *interp, uint lane) {
398 return interp->pop[lane];
399 }
400
401 /*! \brief Read lane result
402 * \ingroup hardware_interp
403 *
404 * \param interp Interpolator instance, interp0 or interp1.
405 * \param lane The lane number, 0 or 1
406 * \return The content of the lane result register
407 */
interp_peek_lane_result(interp_hw_t * interp,uint lane)408 static inline uint32_t interp_peek_lane_result(interp_hw_t *interp, uint lane) {
409 return interp->peek[lane];
410 }
411
412 /*! \brief Read lane result, and write lane results to both accumulators to update the interpolator
413 * \ingroup hardware_interp
414 *
415 * \param interp Interpolator instance, interp0 or interp1.
416 * \return The content of the FULL register
417 */
interp_pop_full_result(interp_hw_t * interp)418 static inline uint32_t interp_pop_full_result(interp_hw_t *interp) {
419 return interp->pop[2];
420 }
421
422 /*! \brief Read lane result
423 * \ingroup hardware_interp
424 *
425 * \param interp Interpolator instance, interp0 or interp1.
426 * \return The content of the FULL register
427 */
interp_peek_full_result(interp_hw_t * interp)428 static inline uint32_t interp_peek_full_result(interp_hw_t *interp) {
429 return interp->peek[2];
430 }
431
432 /*! \brief Add to accumulator
433 * \ingroup hardware_interp
434 *
435 * Atomically add the specified value to the accumulator on the specified lane
436 *
437 * \param interp Interpolator instance, interp0 or interp1.
438 * \param lane The lane number, 0 or 1
439 * \param val Value to add
440 */
interp_add_accumulator(interp_hw_t * interp,uint lane,uint32_t val)441 static inline void interp_add_accumulator(interp_hw_t *interp, uint lane, uint32_t val) {
442 interp->add_raw[lane] = val;
443 }
444 // backwards incompatibility with old incorrect spelling
445 #define interp_add_accumulater(interp, lane, val) interp_add_accumulator(interp, lane, val)
446
447 /*! \brief Get raw lane value
448 * \ingroup hardware_interp
449 *
450 * Returns the raw shift and mask value from the specified lane, BASE0 is NOT added
451 *
452 * \param interp Interpolator instance, interp0 or interp1.
453 * \param lane The lane number, 0 or 1
454 * \return The raw shift/mask value
455 */
interp_get_raw(interp_hw_t * interp,uint lane)456 static inline uint32_t interp_get_raw(interp_hw_t *interp, uint lane) {
457 return interp->add_raw[lane];
458 }
459
460 #ifdef __cplusplus
461 }
462 #endif
463
464 #endif
465