1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <math.h>
10 #include "pico/stdlib.h"
11 #include "hardware/dma.h"
12 #include "hardware/irq.h"
13 #include "hardware/sync.h"
14 
15 volatile bool failed;
16 volatile uint32_t count[3];
17 volatile bool done;
18 
19 #define FAILED() ({ failed = true; })
20 //#define FAILED() ({ failed = true; __breakpoint(); })
21 
22 //#define DOUBLE_ONLY 1
23 
timer_callback(repeating_timer_t * t)24 bool timer_callback(repeating_timer_t *t) {
25     count[0]++;
26     static int z;
27 #if !DOUBLE_ONLY
28     for (int i=0; i<100;i++) {
29         z += 23;
30         int a = z / 7;
31         int b = z % 7;
32         if (z != a * 7 + b) {
33             FAILED();
34         }
35         a = z / -7;
36         b = z % -7;
37         if (z != a * -7 + b) {
38             FAILED();
39         }
40     }
41 #else
42     for(int i=0;i<10;i++) {
43 #endif
44     float fz = (float) z;
45     float fa = fz / 11.0f;
46 //    float fb = fmodf(fz, 11.0f);
47 //    if (fabs(fz - (fa * 11.0f + fb)) > 1e-9f) {
48 //        FAILED();
49 //    }
50     if (fabsf(fz - fa * 11.0f) > 1e-3f) {
51         FAILED();
52     }
53     double dz = z;
54     double da = dz / 11.0;
55 //    double db = fmod(dz, 11.0);
56 //    if (abs(dz - (da * 11.0 + db)) > 1e-9) {
57 //        FAILED();
58 //    }
59     if (fabs(dz - da * 11.0) > 1e-6f) {
60         FAILED();
61     }
62 #if DOUBLE_ONLY
63     }
64 #endif
65     return !done;
66 }
67 
do_dma_start(uint ch)68 void do_dma_start(uint ch) {
69     static uint32_t word[2];
70     assert(ch < 2);
71     dma_channel_config c = dma_channel_get_default_config(ch);
72     // todo remove this; landing in a separate PR
73 #ifndef DREQ_DMA_TIMER0
74 #define DREQ_DMA_TIMER0 0x3b
75 #endif
76     channel_config_set_dreq(&c, DREQ_DMA_TIMER0);
77     dma_channel_configure(ch, &c, &word[ch], &word[ch], 513 + ch * 23, true);
78 }
79 
80 double d0c, d0s, d0t, dz;
81 float f0c, f0s, f0t, fz;
82 
83 double flarn = 25.5;
84 double zzd = 13.3;
85 
test_irq_handler0()86 void test_irq_handler0() {
87     count[1]++;
88     dma_hw->ints0 |= 1u;
89     static uint z;
90     static uint dz;
91     for (int i=0; i<80;i++) {
92 #if !DOUBLE_ONLY
93         z += 31;
94         uint a = z / 11;
95         uint b = z % 11;
96         if (z != a * 11 + b) {
97             FAILED();
98         }
99 #else
100         zzd += flarn/(flarn + 1.35);
101         break;
102 #endif
103     }
104     if (done) dma_channel_abort(0);
105     else      do_dma_start(0);
106 }
107 
test_irq_handler1()108 void test_irq_handler1() {
109     static uint z;
110     dma_hw->ints1 |= 2u;
111     count[2]++;
112     for (int i=0; i<130;i++) {
113 #if !DOUBLE_ONLY
114         z += 47;
115         uint a = z / -13;
116         uint b = z % -13;
117         if (z != a * -13 + b) {
118             FAILED();
119         }
120         static uint64_t z64;
121         z64 -= 47;
122         uint64_t a64 = z64 / -13;
123         uint64_t b64 = z64 % -13;
124         if (z64 != a64 * -13 + b64) {
125             FAILED();
126         }
127 #else
128         zzd += flarn/123.3;
129         break;
130 #endif
131     }
132 
133     if (done) dma_channel_abort(1);
134     else      do_dma_start(1);
135 }
136 
test_nesting()137 void test_nesting() {
138     uint z = 0;
139 
140     // We have 3 different IRQ handlers, one for timer, two for DMA completion (on DMA_IRQ0/1)
141     // thus we expect re-entrancy even between IRQs
142     //
143     // They all busily make use of the dividers, to expose any issues with nested use
144 
145     repeating_timer_t timer;
146     add_repeating_timer_us(929, timer_callback, NULL, &timer);
147     irq_set_exclusive_handler(DMA_IRQ_0, test_irq_handler0);
148     irq_set_exclusive_handler(DMA_IRQ_1, test_irq_handler1);
149 
150     dma_set_irq0_channel_mask_enabled(1u, true);
151     dma_set_irq1_channel_mask_enabled(2u, true);
152     dma_hw->timer[0] = (1 << 16) | 32; // run at 1/32 system clock
153 
154     irq_set_enabled(DMA_IRQ_0, 1);
155     irq_set_enabled(DMA_IRQ_1, 1);
156     do_dma_start(0);
157     do_dma_start(1);
158     absolute_time_t end = delayed_by_ms(get_absolute_time(), 10000);
159     int count_local=0;
160     while (!time_reached(end)) {
161         for(uint i=0;i<100;i++) {
162             z += 31;
163             uint a = z / 11;
164             uint b = z % 11;
165             if (z != a * 11 + b) {
166                 FAILED();
167             }
168             int zz = (int)z;
169             int aa = zz / -11;
170             int bb = zz % -11;
171             if (zz != aa * -11 + bb) {
172                 FAILED();
173             }
174             aa = -zz / -11;
175             bb = -zz % -11;
176             if (-zz != aa * -11 + bb) {
177                 FAILED();
178             }
179             aa = -zz / 11;
180             bb = -zz % 11;
181             if (-zz != aa * 11 + bb) {
182                 FAILED();
183             }
184             a = 0xffffffffu / 11;
185             b = 0xffffffffu % 11;
186             if (0xffffffffu != a * 11 + b) {
187                 FAILED();
188             }
189             static uint64_t z64;
190             z64 -= 47;
191             uint64_t a64 = z64 / -13635;
192             uint64_t b64 = z64 % -13635;
193             if (z64 != a64 * -13635 + b64) {
194                 FAILED();
195             }
196             // specifically check 64/32 divide
197             static uint64_t c64 = 0x13ffffffffull;
198             static uint32_t cd = 1;
199             a64 = c64 / cd;
200             b64 = c64 % cd;
201             if (c64 != a64 * cd + b64) {
202                 FAILED();
203             }
204             cd++;
205             static float zf = 1.f;
206             float ff = zf / -13635.f;
207             if (fabsf(zf - ff * -13635.f) > 1e-2f) {
208                 FAILED();
209             }
210             zf += 0.0331f;
211             z += (int)ff;
212             static double zd = 1.0;
213             double dd = zd / -13635.0;
214             if (fabs(zd - dd * -13635.0) > 1e-6) {
215                 FAILED();
216             }
217             zd += 0.331;
218             z += (int)dd;
219 
220         }
221         // todo this still seems broken on RP2350
222 #if PICO_RP2040
223         // these use the divider
224         for(uint i=0;i<=100;i+=20) {
225             // both in and out bootrom range (we perform mod in wrapper code if necessarry)
226             f0t = tanf(i * 50);
227             f0c = cosf(i * 50);
228             f0s = sinf(i * 50);
229             d0t = tan(i * 1000);
230             d0c = cos(i * 1000);
231             d0s = sin(i * 1000);
232         }
233 #endif
234         count_local++;
235     }
236     done = true;
237     cancel_repeating_timer(&timer);
238     printf("%d: %d %d %d\n", count_local, (int)count[0], (int)count[1], (int)count[2]);
239     printf("%d\n", z);
240     // make sure all the IRQs ran
241     if (!(count_local && count[0] && count[1] && count[2])) {
242         printf("DID NOT RUN\n");
243         exit(1);
244     }
245     if (failed) {
246         printf("FAILED\n");
247         exit(1);
248     }
249 }
250 
main()251 int main() {
252 #ifndef uart_default
253 #warning test/pico_divider requires a default uart
254 #else
255     stdio_init_all();
256 #endif
257     test_nesting();
258     printf("PASSED\n");
259     return 0;
260 }
261 
262