Skip to content

Commit 6bb44dd

Browse files
kilograhamlurch
andauthored
Make all float->int/frac conversions round to nearest by default (#2065)
* making pio_calculate_clkdiv8_from_float round to the neareset 1/256 (not lower 1/256) * chage rounding of all float clkdivs to round to neareset; default to nearest (which is a backwards incompatible change, but I think OK), and add ability to turn it off * fix copy/paste errors in PICO_CONFIG * Calculate size of FRAC field using its own MSB and LSB, rather than hoping that INT_LSB is in the right place * Add a new REG_FIELD_WIDTH macro, and make the calculation of the clock-dividers more consistent --------- Co-authored-by: Andrew Scheller <[email protected]>
1 parent 5a98889 commit 6bb44dd

File tree

9 files changed

+71
-20
lines changed

9 files changed

+71
-20
lines changed

src/rp2040/hardware_regs/include/hardware/platform_defs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,6 @@
116116
#define FIRST_USER_IRQ (NUM_IRQS - NUM_USER_IRQS)
117117
#define VTABLE_FIRST_IRQ 16
118118

119+
#define REG_FIELD_WIDTH(f) (f ## _MSB + 1 - f ## _LSB)
120+
119121
#endif

src/rp2040/pico_platform/include/pico/platform.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@
7070
#define PICO_RAM_VECTOR_TABLE_SIZE (VTABLE_FIRST_IRQ + NUM_IRQS)
7171
#endif
7272

73+
// PICO_CONFIG: PICO_CLKDIV_ROUND_NEAREST, True if floating point clock divisors should be rounded to the nearest possible clock divisor by default rather than rounding down, type=bool, default=1, group=pico_platform
74+
#ifndef PICO_CLKDIV_ROUND_NEAREST
75+
#define PICO_CLKDIV_ROUND_NEAREST 1
76+
#endif
77+
7378
#ifndef __ASSEMBLER__
7479

7580
/*! \brief No-op function for the body of tight loops

src/rp2350/hardware_regs/include/hardware/platform_defs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,6 @@
160160
#endif
161161
#define FIRST_USER_IRQ (NUM_IRQS - NUM_USER_IRQS)
162162

163+
#define REG_FIELD_WIDTH(f) (f ## _MSB + 1 - f ## _LSB)
164+
163165
#endif

src/rp2350/pico_platform/include/pico/platform.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@
6363
#define PICO_USE_STACK_GUARDS 0
6464
#endif
6565

66+
// PICO_CONFIG: PICO_CLKDIV_ROUND_NEAREST, True if floating point clock divisors should be rounded to the nearest possible clock divisor by default rather than rounding down, type=bool, default=1, group=pico_platform
67+
#ifndef PICO_CLKDIV_ROUND_NEAREST
68+
#define PICO_CLKDIV_ROUND_NEAREST 1
69+
#endif
70+
6671
#ifndef __ASSEMBLER__
6772

6873
/*! \brief No-op function for the body of tight loops

src/rp2_common/hardware_adc/include/hardware/adc.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@
7070
#define ADC_TEMPERATURE_CHANNEL_NUM (NUM_ADC_CHANNELS - 1)
7171
#endif
7272

73+
// PICO_CONFIG: PICO_ADC_CLKDIV_ROUND_NEAREST, True if floating point ADC clock divisors should be rounded to the nearest possible clock divisor rather than rounding down, type=bool, default=PICO_CLKDIV_ROUND_NEAREST, group=hardware_adc
74+
#ifndef PICO_ADC_CLKDIV_ROUND_NEAREST
75+
#define PICO_ADC_CLKDIV_ROUND_NEAREST PICO_CLKDIV_ROUND_NEAREST
76+
#endif
77+
7378
#ifdef __cplusplus
7479
extern "C" {
7580
#endif
@@ -197,8 +202,12 @@ static inline void adc_run(bool run) {
197202
* \param clkdiv If non-zero, conversion will be started at intervals rather than back to back.
198203
*/
199204
static inline void adc_set_clkdiv(float clkdiv) {
200-
invalid_params_if(HARDWARE_ADC, clkdiv >= 1 << (ADC_DIV_INT_MSB - ADC_DIV_INT_LSB + 1));
201-
adc_hw->div = (uint32_t)(clkdiv * (float) (1 << ADC_DIV_INT_LSB));
205+
invalid_params_if(HARDWARE_ADC, clkdiv >= 1 << REG_FIELD_WIDTH(ADC_DIV_INT));
206+
const int frac_bit_count = REG_FIELD_WIDTH(ADC_DIV_FRAC);
207+
#if PICO_ADC_CLKDIV_ROUND_NEAREST
208+
clkdiv += 0.5f / (1 << frac_bit_count); // round to the nearest fraction
209+
#endif
210+
adc_hw->div = (uint32_t)(clkdiv * (float) (1 << frac_bit_count));
202211
}
203212

204213
/*! \brief Setup the ADC FIFO

src/rp2_common/hardware_clocks/clocks.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,13 +245,13 @@ void clock_gpio_init_int_frac16(uint gpio, uint src, uint32_t div_int, uint16_t
245245
invalid_params_if(HARDWARE_CLOCKS, true);
246246
}
247247

248-
invalid_params_if(HARDWARE_CLOCKS, div_int >> (CLOCKS_CLK_GPOUT0_DIV_INT_MSB - CLOCKS_CLK_GPOUT0_DIV_INT_LSB + 1));
248+
invalid_params_if(HARDWARE_CLOCKS, div_int >> REG_FIELD_WIDTH(CLOCKS_CLK_GPOUT0_DIV_INT));
249249
// Set up the gpclk generator
250250
clocks_hw->clk[gpclk].ctrl = (src << CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_LSB) |
251251
CLOCKS_CLK_GPOUT0_CTRL_ENABLE_BITS;
252-
#if CLOCKS_CLK_GPOUT0_DIV_FRAC_MSB - CLOCKS_CLK_GPOUT0_DIV_FRAC_LSB == 15
252+
#if REG_FIELD_WIDTH(CLOCKS_CLK_GPOUT0_DIV_FRAC) == 16
253253
clocks_hw->clk[gpclk].div = (div_int << CLOCKS_CLK_GPOUT0_DIV_INT_LSB) | (div_frac16 << CLOCKS_CLK_GPOUT0_DIV_FRAC_LSB);
254-
#elif CLOCKS_CLK_GPOUT0_DIV_FRAC_MSB - CLOCKS_CLK_GPOUT0_DIV_FRAC_LSB == 7
254+
#elif REG_FIELD_WIDTH(CLOCKS_CLK_GPOUT0_DIV_FRAC) == 8
255255
clocks_hw->clk[gpclk].div = (div_int << CLOCKS_CLK_GPOUT0_DIV_INT_LSB) | ((div_frac16>>8u) << CLOCKS_CLK_GPOUT0_DIV_FRAC_LSB);
256256
#else
257257
#error unsupported number of fractional bits
@@ -439,4 +439,4 @@ bool check_sys_clock_khz(uint32_t freq_khz, uint *vco_out, uint *postdiv1_out, u
439439
}
440440
}
441441
return false;
442-
}
442+
}

src/rp2_common/hardware_clocks/include/hardware/clocks.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,11 @@ extern "C" {
253253
#else
254254
#define PARAM_ASSERTIONS_ENABLED_HARDWARE_CLOCKS 0
255255
#endif
256+
#endif
257+
258+
// PICO_CONFIG: PICO_CLOCK_GPIO_CLKDIV_ROUND_NEAREST, True if floating point GPIO clock divisors should be rounded to the nearest possible clock divisor rather than rounding down, type=bool, default=PICO_CLKDIV_ROUND_NEAREST, group=hardware_clocks
259+
#ifndef PICO_CLOCK_GPIO_CLKDIV_ROUND_NEAREST
260+
#define PICO_CLOCK_GPIO_CLKDIV_ROUND_NEAREST PICO_CLKDIV_ROUND_NEAREST
256261
#endif
257262

258263
typedef clock_num_t clock_handle_t;
@@ -387,11 +392,15 @@ static inline void clock_gpio_init_int_frac(uint gpio, uint src, uint32_t div_in
387392
static inline void clock_gpio_init(uint gpio, uint src, float div)
388393
{
389394
uint div_int = (uint)div;
390-
#if CLOCKS_CLK_GPOUT0_DIV_FRAC_MSB - CLOCKS_CLK_GPOUT0_DIV_FRAC_LSB == 15
391-
uint16_t frac = (uint16_t)((div - (float)div_int) * (1u << 16));
395+
const int frac_bit_count = REG_FIELD_WIDTH(CLOCKS_CLK_GPOUT0_DIV_FRAC);
396+
#if PICO_CLOCK_GPIO_CLKDIV_ROUND_NEAREST
397+
div += 0.5f / (1 << frac_bit_count); // round to the nearest fraction
398+
#endif
399+
#if REG_FIELD_WIDTH(CLOCKS_CLK_GPOUT0_DIV_FRAC) == 16
400+
uint16_t frac = (uint16_t)((div - (float)div_int) * (1u << frac_bit_count));
392401
clock_gpio_init_int_frac16(gpio, src, div_int, frac);
393-
#elif CLOCKS_CLK_GPOUT0_DIV_FRAC_MSB - CLOCKS_CLK_GPOUT0_DIV_FRAC_LSB == 7
394-
uint8_t frac = (uint8_t)((div - (float)div_int) * (1u << 8));
402+
#elif REG_FIELD_WIDTH(CLOCKS_CLK_GPOUT0_DIV_FRAC) == 8
403+
uint8_t frac = (uint8_t)((div - (float)div_int) * (1u << frac_bit_count));
395404
clock_gpio_init_int_frac8(gpio, src, div_int, frac);
396405
#else
397406
#error unsupported number of fractional bits

src/rp2_common/hardware_pio/include/hardware/pio.h

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@
3131
#define PICO_PIO_VERSION 0
3232
#endif
3333
#endif
34+
35+
// PICO_CONFIG: PICO_PIO_CLKDIV_ROUND_NEAREST, True if floating point PIO clock divisors should be rounded to the nearest possible clock divisor rather than rounding down, type=bool, default=PICO_CLKDIV_ROUND_NEAREST, group=hardware_pio
36+
#ifndef PICO_PIO_CLKDIV_ROUND_NEAREST
37+
#define PICO_PIO_CLKDIV_ROUND_NEAREST PICO_CLKDIV_ROUND_NEAREST
38+
#endif
39+
3440
/** \file hardware/pio.h
3541
* \defgroup hardware_pio hardware_pio
3642
*
@@ -472,10 +478,10 @@ static inline void sm_config_set_sideset(pio_sm_config *c, uint bit_count, bool
472478
* \sa sm_config_set_clkdiv()
473479
*/
474480
static inline void sm_config_set_clkdiv_int_frac8(pio_sm_config *c, uint32_t div_int, uint8_t div_frac8) {
475-
static_assert(PIO_SM0_CLKDIV_INT_MSB - PIO_SM0_CLKDIV_INT_LSB == 15, "");
481+
static_assert(REG_FIELD_WIDTH(PIO_SM0_CLKDIV_INT) == 16, "");
476482
invalid_params_if(HARDWARE_PIO, div_int >> 16);
477483
invalid_params_if(HARDWARE_PIO, div_int == 0 && div_frac8 != 0);
478-
static_assert(PIO_SM0_CLKDIV_FRAC_MSB - PIO_SM0_CLKDIV_FRAC_LSB == 7, "");
484+
static_assert(REG_FIELD_WIDTH(PIO_SM0_CLKDIV_FRAC) == 8, "");
479485
c->clkdiv =
480486
(((uint)div_frac8) << PIO_SM0_CLKDIV_FRAC_LSB) |
481487
(((uint)div_int) << PIO_SM0_CLKDIV_INT_LSB);
@@ -488,14 +494,18 @@ static inline void sm_config_set_clkdiv_int_frac(pio_sm_config *c, uint16_t div_
488494

489495
static inline void pio_calculate_clkdiv8_from_float(float div, uint32_t *div_int, uint8_t *div_frac8) {
490496
valid_params_if(HARDWARE_PIO, div >= 1 && div <= 65536);
497+
const int frac_bit_count = REG_FIELD_WIDTH(PIO_SM0_CLKDIV_FRAC);
498+
#if PICO_PIO_CLKDIV_ROUND_NEAREST
499+
div += 0.5f / (1 << frac_bit_count); // round to the nearest 1/256
500+
#endif
491501
*div_int = (uint16_t)div;
492502
// not a strictly necessary check, but if this changes, then this method should
493503
// probably no longer be used in favor of one with a larger fraction
494-
static_assert(PIO_SM0_CLKDIV_FRAC_MSB - PIO_SM0_CLKDIV_FRAC_LSB == 7, "");
504+
static_assert(REG_FIELD_WIDTH(PIO_SM0_CLKDIV_FRAC) == 8, "");
495505
if (*div_int == 0) {
496506
*div_frac8 = 0;
497507
} else {
498-
*div_frac8 = (uint8_t)((div - (float)*div_int) * (1u << 8u));
508+
*div_frac8 = (uint8_t)((div - (float)*div_int) * (1u << frac_bit_count));
499509
}
500510
}
501511

@@ -1675,10 +1685,10 @@ void pio_sm_drain_tx_fifo(PIO pio, uint sm);
16751685
static inline void pio_sm_set_clkdiv_int_frac8(PIO pio, uint sm, uint32_t div_int, uint8_t div_frac8) {
16761686
check_pio_param(pio);
16771687
check_sm_param(sm);
1678-
static_assert(PIO_SM0_CLKDIV_INT_MSB - PIO_SM0_CLKDIV_INT_LSB == 15, "");
1688+
static_assert(REG_FIELD_WIDTH(PIO_SM0_CLKDIV_INT) == 16, "");
16791689
invalid_params_if(HARDWARE_PIO, div_int >> 16);
16801690
invalid_params_if(HARDWARE_PIO, div_int == 0 && div_frac8 != 0);
1681-
static_assert(PIO_SM0_CLKDIV_FRAC_MSB - PIO_SM0_CLKDIV_FRAC_LSB == 7, "");
1691+
static_assert(REG_FIELD_WIDTH(PIO_SM0_CLKDIV_FRAC) == 8, "");
16821692
pio->sm[sm].clkdiv =
16831693
(((uint)div_frac8) << PIO_SM0_CLKDIV_FRAC_LSB) |
16841694
(((uint)div_int) << PIO_SM0_CLKDIV_INT_LSB);

src/rp2_common/hardware_pwm/include/hardware/pwm.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ static_assert(DREQ_PWM_WRAP7 == DREQ_PWM_WRAP0 + 7, "");
103103
})
104104
#endif
105105

106+
// PICO_CONFIG: PICO_PWM_CLKDIV_ROUND_NEAREST, True if floating point PWM clock divisors should be rounded to the nearest possible clock divisor rather than rounding down, type=bool, default=PICO_CLKDIV_ROUND_NEAREST, group=hardware_pwm
107+
#ifndef PICO_PWM_CLKDIV_ROUND_NEAREST
108+
#define PICO_PWM_CLKDIV_ROUND_NEAREST PICO_CLKDIV_ROUND_NEAREST
109+
#endif
110+
106111
static inline void check_slice_num_param(__unused uint slice_num) {
107112
valid_params_if(HARDWARE_PWM, slice_num < NUM_PWM_SLICES);
108113
}
@@ -155,7 +160,11 @@ static inline void pwm_config_set_phase_correct(pwm_config *c, bool phase_correc
155160
*/
156161
static inline void pwm_config_set_clkdiv(pwm_config *c, float div) {
157162
valid_params_if(HARDWARE_PWM, div >= 1.f && div < 256.f);
158-
c->div = (uint32_t)(div * (float)(1u << PWM_CH0_DIV_INT_LSB));
163+
const int frac_bit_count = REG_FIELD_WIDTH(PWM_CH0_DIV_FRAC);
164+
#if PICO_PWM_CLKDIV_ROUND_NEAREST
165+
div += 0.5f / (1 << frac_bit_count); // round to the nearest fraction
166+
#endif
167+
c->div = (uint32_t)(div * (float)(1u << frac_bit_count));
159168
}
160169

161170
/** \brief Set PWM clock divider in a PWM configuration using an 8:4 fractional value
@@ -170,9 +179,9 @@ static inline void pwm_config_set_clkdiv(pwm_config *c, float div) {
170179
* before passing them on to the PWM counter.
171180
*/
172181
static inline void pwm_config_set_clkdiv_int_frac4(pwm_config *c, uint32_t div_int, uint8_t div_frac4) {
173-
static_assert(PWM_CH0_DIV_INT_MSB - PWM_CH0_DIV_INT_LSB == 7, "");
182+
static_assert(REG_FIELD_WIDTH(PWM_CH0_DIV_INT) == 8, "");
174183
valid_params_if(HARDWARE_PWM, div_int >= 1 && div_int < 256);
175-
static_assert(PWM_CH0_DIV_FRAC_MSB - PWM_CH0_DIV_FRAC_LSB == 3, "");
184+
static_assert(REG_FIELD_WIDTH(PWM_CH0_DIV_FRAC) == 4, "");
176185
valid_params_if(HARDWARE_PWM, div_frac4 < 16);
177186
c->div = (((uint)div_int) << PWM_CH0_DIV_INT_LSB) | (((uint)div_frac4) << PWM_CH0_DIV_FRAC_LSB);
178187
}
@@ -439,7 +448,7 @@ static inline void pwm_retard_count(uint slice_num) {
439448
static inline void pwm_set_clkdiv_int_frac4(uint slice_num, uint8_t div_int, uint8_t div_frac4) {
440449
check_slice_num_param(slice_num);
441450
valid_params_if(HARDWARE_PWM, div_int >= 1);
442-
static_assert(PWM_CH0_DIV_FRAC_MSB - PWM_CH0_DIV_FRAC_LSB == 3, "");
451+
static_assert(REG_FIELD_WIDTH(PWM_CH0_DIV_FRAC) == 4, "");
443452
valid_params_if(HARDWARE_PWM, div_frac4 < 16);
444453
pwm_hw->slice[slice_num].div = (((uint)div_int) << PWM_CH0_DIV_INT_LSB) | (((uint)div_frac4) << PWM_CH0_DIV_FRAC_LSB);
445454
}

0 commit comments

Comments
 (0)