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