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