| /* |
| * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #ifndef _HARDWARE_INTERP_H |
| #define _HARDWARE_INTERP_H |
| |
| #include "pico.h" |
| #include "hardware/structs/interp.h" |
| #include "hardware/regs/sio.h" |
| |
| // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_INTERP, Enable/disable assertions in the interpolation module, type=bool, default=0, group=hardware_interp |
| #ifndef PARAM_ASSERTIONS_ENABLED_INTERP |
| #define PARAM_ASSERTIONS_ENABLED_INTERP 0 |
| #endif |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| /** \file hardware/interp.h |
| * \defgroup hardware_interp hardware_interp |
| * |
| * Hardware Interpolator API |
| * |
| * Each core is equipped with two interpolators (INTERP0 and INTERP1) which can be used to accelerate |
| * tasks by combining certain pre-configured simple operations into a single processor cycle. Intended |
| * for cases where the pre-configured operation is repeated a large number of times, this results in |
| * code which uses both fewer CPU cycles and fewer CPU registers in the time critical sections of the |
| * code. |
| * |
| * The interpolators are used heavily to accelerate audio operations within the Pico SDK, but their |
| * flexible configuration make it possible to optimise many other tasks such as quantization and |
| * dithering, table lookup address generation, affine texture mapping, decompression and linear feedback. |
| * |
| * Please refer to the RP2040 datasheet for more information on the HW interpolators and how they work. |
| */ |
| |
| #define interp0 interp0_hw |
| #define interp1 interp1_hw |
| |
| /** \brief Interpolator configuration |
| * \defgroup interp_config interp_config |
| * \ingroup hardware_interp |
| * |
| * Each interpolator needs to be configured, these functions provide handy helpers to set up configuration |
| * structures. |
| * |
| */ |
| |
| typedef struct { |
| uint32_t ctrl; |
| } interp_config; |
| |
| static inline uint interp_index(interp_hw_t *interp) { |
| assert(interp == interp0 || interp == interp1); |
| return interp == interp1 ? 1 : 0; |
| } |
| |
| /*! \brief Claim the interpolator lane specified |
| * \ingroup hardware_interp |
| * |
| * Use this function to claim exclusive access to the specified interpolator lane. |
| * |
| * This function will panic if the lane is already claimed. |
| * |
| * \param interp Interpolator on which to claim a lane. interp0 or interp1 |
| * \param lane The lane number, 0 or 1. |
| */ |
| void interp_claim_lane(interp_hw_t *interp, uint lane); |
| |
| /*! \brief Claim the interpolator lanes specified in the mask |
| * \ingroup hardware_interp |
| * |
| * \param interp Interpolator on which to claim lanes. interp0 or interp1 |
| * \param lane_mask Bit pattern of lanes to claim (only bits 0 and 1 are valid) |
| */ |
| void interp_claim_lane_mask(interp_hw_t *interp, uint lane_mask); |
| |
| /*! \brief Release a previously claimed interpolator lane |
| * \ingroup hardware_interp |
| * |
| * \param interp Interpolator on which to release a lane. interp0 or interp1 |
| * \param lane The lane number, 0 or 1 |
| */ |
| void interp_unclaim_lane(interp_hw_t *interp, uint lane); |
| |
| /*! \brief Set the interpolator shift value |
| * \ingroup interp_config |
| * |
| * Sets the number of bits the accumulator is shifted before masking, on each iteration. |
| * |
| * \param c Pointer to an interpolator config |
| * \param shift Number of bits |
| */ |
| static inline void interp_config_set_shift(interp_config *c, uint shift) { |
| valid_params_if(INTERP, shift < 32); |
| c->ctrl = (c->ctrl & ~SIO_INTERP0_CTRL_LANE0_SHIFT_BITS) | |
| ((shift << SIO_INTERP0_CTRL_LANE0_SHIFT_LSB) & SIO_INTERP0_CTRL_LANE0_SHIFT_BITS); |
| } |
| |
| /*! \brief Set the interpolator mask range |
| * \ingroup interp_config |
| * |
| * Sets the range of bits (least to most) that are allowed to pass through the interpolator |
| * |
| * \param c Pointer to interpolation config |
| * \param mask_lsb The least significant bit allowed to pass |
| * \param mask_msb The most significant bit allowed to pass |
| */ |
| static inline void interp_config_set_mask(interp_config *c, uint mask_lsb, uint mask_msb) { |
| valid_params_if(INTERP, mask_msb < 32); |
| valid_params_if(INTERP, mask_lsb <= mask_msb); |
| c->ctrl = (c->ctrl & ~(SIO_INTERP0_CTRL_LANE0_MASK_LSB_BITS | SIO_INTERP0_CTRL_LANE0_MASK_MSB_BITS)) | |
| ((mask_lsb << SIO_INTERP0_CTRL_LANE0_MASK_LSB_LSB) & SIO_INTERP0_CTRL_LANE0_MASK_LSB_BITS) | |
| ((mask_msb << SIO_INTERP0_CTRL_LANE0_MASK_MSB_LSB) & SIO_INTERP0_CTRL_LANE0_MASK_MSB_BITS); |
| } |
| |
| /*! \brief Enable cross input |
| * \ingroup interp_config |
| * |
| * Allows feeding of the accumulator content from the other lane back in to this lanes shift+mask hardware. |
| * This will take effect even if the interp_config_set_add_raw option is set as the cross input mux is before the |
| * shift+mask bypass |
| * |
| * \param c Pointer to interpolation config |
| * \param cross_input If true, enable the cross input. |
| */ |
| static inline void interp_config_set_cross_input(interp_config *c, bool cross_input) { |
| c->ctrl = (c->ctrl & ~SIO_INTERP0_CTRL_LANE0_CROSS_INPUT_BITS) | |
| (cross_input ? SIO_INTERP0_CTRL_LANE0_CROSS_INPUT_BITS : 0); |
| } |
| |
| /*! \brief Enable cross results |
| * \ingroup interp_config |
| * |
| * Allows feeding of the other lane’s result into this lane’s accumulator on a POP operation. |
| * |
| * \param c Pointer to interpolation config |
| * \param cross_result If true, enables the cross result |
| */ |
| static inline void interp_config_set_cross_result(interp_config *c, bool cross_result) { |
| c->ctrl = (c->ctrl & ~SIO_INTERP0_CTRL_LANE0_CROSS_RESULT_BITS) | |
| (cross_result ? SIO_INTERP0_CTRL_LANE0_CROSS_RESULT_BITS : 0); |
| } |
| |
| /*! \brief Set sign extension |
| * \ingroup interp_config |
| * |
| * Enables signed mode, where the shifted and masked accumulator value is sign-extended to 32 bits |
| * before adding to BASE1, and LANE1 PEEK/POP results appear extended to 32 bits when read by processor. |
| * |
| * \param c Pointer to interpolation config |
| * \param _signed If true, enables sign extension |
| */ |
| static inline void interp_config_set_signed(interp_config *c, bool _signed) { |
| c->ctrl = (c->ctrl & ~SIO_INTERP0_CTRL_LANE0_SIGNED_BITS) | |
| (_signed ? SIO_INTERP0_CTRL_LANE0_SIGNED_BITS : 0); |
| } |
| |
| /*! \brief Set raw add option |
| * \ingroup interp_config |
| * |
| * When enabled, mask + shift is bypassed for LANE0 result. This does not affect the FULL result. |
| * |
| * \param c Pointer to interpolation config |
| * \param add_raw If true, enable raw add option. |
| */ |
| static inline void interp_config_set_add_raw(interp_config *c, bool add_raw) { |
| c->ctrl = (c->ctrl & ~SIO_INTERP0_CTRL_LANE0_ADD_RAW_BITS) | |
| (add_raw ? SIO_INTERP0_CTRL_LANE0_ADD_RAW_BITS : 0); |
| } |
| |
| /*! \brief Set blend mode |
| * \ingroup interp_config |
| * |
| * If enabled, LANE1 result is a linear interpolation between BASE0 and BASE1, controlled |
| * by the 8 LSBs of lane 1 shift and mask value (a fractional number between 0 and 255/256ths) |
| * |
| * LANE0 result does not have BASE0 added (yields only the 8 LSBs of lane 1 shift+mask value) |
| * |
| * FULL result does not have lane 1 shift+mask value added (BASE2 + lane 0 shift+mask) |
| * |
| * LANE1 SIGNED flag controls whether the interpolation is signed or unsig |
| * |
| * \param c Pointer to interpolation config |
| * \param blend Set true to enable blend mode. |
| */ |
| static inline void interp_config_set_blend(interp_config *c, bool blend) { |
| c->ctrl = (c->ctrl & ~SIO_INTERP0_CTRL_LANE0_BLEND_BITS) | |
| (blend ? SIO_INTERP0_CTRL_LANE0_BLEND_BITS : 0); |
| } |
| |
| /*! \brief Set interpolator clamp mode (Interpolator 1 only) |
| * \ingroup interp_config |
| * |
| * Only present on INTERP1 on each core. If CLAMP mode is enabled: |
| * - LANE0 result is a shifted and masked ACCUM0, clamped by a lower bound of BASE0 and an upper bound of BASE1. |
| * - Signedness of these comparisons is determined by LANE0_CTRL_SIGNED |
| * |
| * \param c Pointer to interpolation config |
| * \param clamp Set true to enable clamp mode |
| */ |
| static inline void interp_config_set_clamp(interp_config *c, bool clamp) { |
| c->ctrl = (c->ctrl & ~SIO_INTERP1_CTRL_LANE0_CLAMP_BITS) | |
| (clamp ? SIO_INTERP1_CTRL_LANE0_CLAMP_BITS : 0); |
| } |
| |
| /*! \brief Set interpolator Force bits |
| * \ingroup interp_config |
| * |
| * ORed into bits 29:28 of the lane result presented to the processor on the bus. |
| * |
| * No effect on the internal 32-bit datapath. Handy for using a lane to generate sequence |
| * of pointers into flash or SRAM |
| * |
| * \param c Pointer to interpolation config |
| * \param bits Sets the force bits to that specified. Range 0-3 (two bits) |
| */ |
| static inline void interp_config_set_force_bits(interp_config *c, uint bits) { |
| invalid_params_if(INTERP, bits > 3); |
| // note cannot use hw_set_bits on SIO |
| c->ctrl = (c->ctrl & ~SIO_INTERP0_CTRL_LANE0_FORCE_MSB_BITS) | |
| (bits << SIO_INTERP0_CTRL_LANE0_FORCE_MSB_LSB); |
| } |
| |
| /*! \brief Get a default configuration |
| * \ingroup interp_config |
| * |
| * \return A default interpolation configuration |
| */ |
| static inline interp_config interp_default_config() { |
| interp_config c = {0}; |
| // Just pass through everything |
| interp_config_set_mask(&c, 0, 31); |
| return c; |
| } |
| |
| /*! \brief Send configuration to a lane |
| * \ingroup interp_config |
| * |
| * If an invalid configuration is specified (ie a lane specific item is set on wrong lane), |
| * depending on setup this function can panic. |
| * |
| * \param interp Interpolator instance, interp0 or interp1. |
| * \param lane The lane to set |
| * \param config Pointer to interpolation config |
| */ |
| |
| static inline void interp_set_config(interp_hw_t *interp, uint lane, interp_config *config) { |
| invalid_params_if(INTERP, lane > 1); |
| invalid_params_if(INTERP, config->ctrl & SIO_INTERP1_CTRL_LANE0_CLAMP_BITS && |
| (!interp_index(interp) || lane)); // only interp1 lane 0 has clamp bit |
| invalid_params_if(INTERP, config->ctrl & SIO_INTERP0_CTRL_LANE0_BLEND_BITS && |
| (interp_index(interp) || lane)); // only interp0 lane 0 has blend bit |
| interp->ctrl[lane] = config->ctrl; |
| } |
| |
| /*! \brief Directly set the force bits on a specified lane |
| * \ingroup hardware_interp |
| * |
| * These bits are ORed into bits 29:28 of the lane result presented to the processor on the bus. |
| * There is no effect on the internal 32-bit datapath. |
| * |
| * Useful for using a lane to generate sequence of pointers into flash or SRAM, saving a subsequent |
| * OR or add operation. |
| * |
| * \param interp Interpolator instance, interp0 or interp1. |
| * \param lane The lane to set |
| * \param bits The bits to set (bits 0 and 1, value range 0-3) |
| */ |
| static inline void interp_set_force_bits(interp_hw_t *interp, uint lane, uint bits) { |
| // note cannot use hw_set_bits on SIO |
| interp->ctrl[lane] |= (bits << SIO_INTERP0_CTRL_LANE0_FORCE_MSB_LSB); |
| } |
| |
| typedef struct { |
| io_rw_32 accum[2]; |
| io_rw_32 base[3]; |
| io_rw_32 ctrl[2]; |
| } interp_hw_save_t; |
| |
| /*! \brief Save the specified interpolator state |
| * \ingroup hardware_interp |
| * |
| * Can be used to save state if you need an interpolator for another purpose, state |
| * can then be recovered afterwards and continue from that point |
| * |
| * \param interp Interpolator instance, interp0 or interp1. |
| * \param saver Pointer to the save structure to fill in |
| */ |
| void interp_save(interp_hw_t *interp, interp_hw_save_t *saver); |
| |
| /*! \brief Restore an interpolator state |
| * \ingroup hardware_interp |
| * |
| * \param interp Interpolator instance, interp0 or interp1. |
| * \param saver Pointer to save structure to reapply to the specified interpolator |
| */ |
| void interp_restore(interp_hw_t *interp, interp_hw_save_t *saver); |
| |
| /*! \brief Sets the interpolator base register by lane |
| * \ingroup hardware_interp |
| * |
| * \param interp Interpolator instance, interp0 or interp1. |
| * \param lane The lane number, 0 or 1 or 2 |
| * \param val The value to apply to the register |
| */ |
| static inline void interp_set_base(interp_hw_t *interp, uint lane, uint32_t val) { |
| interp->base[lane] = val; |
| } |
| |
| /*! \brief Gets the content of interpolator base register by lane |
| * \ingroup hardware_interp |
| * |
| * \param interp Interpolator instance, interp0 or interp1. |
| * \param lane The lane number, 0 or 1 or 2 |
| * \return The current content of the lane base register |
| */ |
| static inline uint32_t interp_get_base(interp_hw_t *interp, uint lane) { |
| return interp->base[lane]; |
| } |
| |
| /*! \brief Sets the interpolator base registers simultaneously |
| * \ingroup hardware_interp |
| * |
| * The lower 16 bits go to BASE0, upper bits to BASE1 simultaneously. |
| * Each half is sign-extended to 32 bits if that lane’s SIGNED flag is set. |
| * |
| * \param interp Interpolator instance, interp0 or interp1. |
| * \param val The value to apply to the register |
| */ |
| static inline void interp_set_base_both(interp_hw_t *interp, uint32_t val) { |
| interp->base01 = val; |
| } |
| |
| |
| /*! \brief Sets the interpolator accumulator register by lane |
| * \ingroup hardware_interp |
| * |
| * \param interp Interpolator instance, interp0 or interp1. |
| * \param lane The lane number, 0 or 1 |
| * \param val The value to apply to the register |
| */ |
| static inline void interp_set_accumulator(interp_hw_t *interp, uint lane, uint32_t val) { |
| interp->accum[lane] = val; |
| } |
| |
| /*! \brief Gets the content of the interpolator accumulator register by lane |
| * \ingroup hardware_interp |
| * |
| * \param interp Interpolator instance, interp0 or interp1. |
| * \param lane The lane number, 0 or 1 |
| * \return The current content of the register |
| */ |
| static inline uint32_t interp_get_accumulator(interp_hw_t *interp, uint lane) { |
| return interp->accum[lane]; |
| } |
| |
| /*! \brief Read lane result, and write lane results to both accumulators to update the interpolator |
| * \ingroup hardware_interp |
| * |
| * \param interp Interpolator instance, interp0 or interp1. |
| * \param lane The lane number, 0 or 1 |
| * \return The content of the lane result register |
| */ |
| static inline uint32_t interp_pop_lane_result(interp_hw_t *interp, uint lane) { |
| return interp->pop[lane]; |
| } |
| |
| /*! \brief Read lane result |
| * \ingroup hardware_interp |
| * |
| * \param interp Interpolator instance, interp0 or interp1. |
| * \param lane The lane number, 0 or 1 |
| * \return The content of the lane result register |
| */ |
| static inline uint32_t interp_peek_lane_result(interp_hw_t *interp, uint lane) { |
| return interp->peek[lane]; |
| } |
| |
| /*! \brief Read lane result, and write lane results to both accumulators to update the interpolator |
| * \ingroup hardware_interp |
| * |
| * \param interp Interpolator instance, interp0 or interp1. |
| * \return The content of the FULL register |
| */ |
| static inline uint32_t interp_pop_full_result(interp_hw_t *interp) { |
| return interp->pop[2]; |
| } |
| |
| /*! \brief Read lane result |
| * \ingroup hardware_interp |
| * |
| * \param interp Interpolator instance, interp0 or interp1. |
| * \return The content of the FULL register |
| */ |
| static inline uint32_t interp_peek_full_result(interp_hw_t *interp) { |
| return interp->peek[2]; |
| } |
| |
| /*! \brief Add to accumulator |
| * \ingroup hardware_interp |
| * |
| * Atomically add the specified value to the accumulator on the specified lane |
| * |
| * \param interp Interpolator instance, interp0 or interp1. |
| * \param lane The lane number, 0 or 1 |
| * \param val Value to add |
| * \return The content of the FULL register |
| */ |
| static inline void interp_add_accumulater(interp_hw_t *interp, uint lane, uint32_t val) { |
| interp->add_raw[lane] = val; |
| } |
| |
| /*! \brief Get raw lane value |
| * \ingroup hardware_interp |
| * |
| * Returns the raw shift and mask value from the specified lane, BASE0 is NOT added |
| * |
| * \param interp Interpolator instance, interp0 or interp1. |
| * \param lane The lane number, 0 or 1 |
| * \return The raw shift/mask value |
| */ |
| static inline uint32_t interp_get_raw(interp_hw_t *interp, uint lane) { |
| return interp->add_raw[lane]; |
| } |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif |