|
15 | 15 | #include "gpio_extra.h"
|
16 | 16 | #include <stdarg.h>
|
17 | 17 |
|
| 18 | +/* |
| 19 | + * IMPORTANT: bitfields & hardware registers |
| 20 | + * ----------------------------------------- |
| 21 | + * TL;DR Be sure to compile with gcc flag -fstrict-volatile-bitfields |
| 22 | + * |
| 23 | + * This flag tells gcc to generate 32-bit load/store instructions (i.e. lw/sw) |
| 24 | + * to access volatile bitfields. Without flag, gcc can generate 8 or 16-bit |
| 25 | + * instructions (i.e. sh or lb) that access subword. Subword access appears to |
| 26 | + * interact badly with pwm hardware registers. This did not appear to be documented |
| 27 | + * anywhere; I only found out the hard way when observing garbled bits and lost |
| 28 | + * updates. |
| 29 | + * |
| 30 | + * I don't know if this behavior is specific to pwm or affects all peripheral |
| 31 | + * registers. I think it best to assume it is needed for all volatile bitfields |
| 32 | + * (i.e. any bitfield within peripheral registers) |
| 33 | +*/ |
| 34 | + |
18 | 35 | typedef union {
|
19 | 36 | struct {
|
20 | 37 | uint32_t pier;
|
@@ -152,22 +169,21 @@ static bool set_pin_fn_to_pwm(pwm_channel_id_t ch, gpio_id_t pin) {
|
152 | 169 | }
|
153 | 170 |
|
154 | 171 | /*
|
155 |
| - * IMPORTANT CAUTION: update of PPR (period) register |
156 |
| - * -------------------------------------------------- |
157 |
| - * TLDR: must use single 32-bit write to ppr that sets both active/entire in one go |
| 172 | + * IMPORTANT: update of PPR (period) register |
| 173 | + * ------------------------------------------ |
| 174 | + * TL;DR: must use single 32-bit write to ppr that sets both active/entire in one go |
158 | 175 | *
|
159 | 176 | * I originally defined ppr as bitfield with upper/lower 16-bit.
|
160 |
| - * However, writes to the fields did not seem to do correct thing. |
161 |
| - * The gcc-generated code was using lh/sh instructions and those were |
162 |
| - * did not place nice with hardware register. |
163 |
| - * Applying flag fstrict-volatile-bitfields forces gcc to generate |
164 |
| - * 32-bit lw/sw, which corrected some of the problems, but not all. |
165 |
| - * Write of active and entire field in two separate actions ( |
| 177 | + * The gcc-generated code was using lh/sh instructions which |
| 178 | + * did not place nice with hardware register. (see note above) |
| 179 | + * Apply flag fstrict-volatile-bitfields forces gcc to generate |
| 180 | + * 32-bit lw/sw. This corrected most of the problems, but not all. |
| 181 | + * Write of active and entire field in two separate actions |
166 | 182 | * (i.e. two read-modify-write) was still problematic.
|
167 | 183 | * The observed behavior was erratic (timing-sensitive?), sometimes second
|
168 | 184 | * update just silently dropped. (seemed most likely to happen on the
|
169 | 185 | * very first set of ppr after init module)
|
170 |
| - * Instead write to ppr in single operation that sets |
| 186 | + * Change code to write to ppr in single operation that sets |
171 | 187 | * both fields in one go seems to behave perfectly, no glitch
|
172 | 188 | */
|
173 | 189 | static void set_period(pwm_channel_id_t ch, int n_active, int n_entire) {
|
|
0 commit comments