comparison core/PRU.cpp @ 0:8a575ba3ab52

Initial commit.
author andrewm
date Fri, 31 Oct 2014 19:10:17 +0100
parents
children a6beeba3a648
comparison
equal deleted inserted replaced
-1:000000000000 0:8a575ba3ab52
1 /*
2 * PRU.cpp
3 *
4 * Code for communicating with the Programmable Realtime Unit (PRU)
5 * on the BeagleBone AM335x series processors. The PRU loads and runs
6 * a separate code image compiled from an assembly file. Here it is
7 * used to handle audio and SPI ADC/DAC data.
8 *
9 * This code is specific to the PRU code in the assembly file; for example,
10 * it uses certain GPIO resources that correspond to that image.
11 *
12 * Created on: May 27, 2014
13 * Author: andrewm
14 */
15
16 #include "../include/PRU.h"
17 #include "../include/prussdrv.h"
18 #include "../include/pruss_intc_mapping.h"
19 #include "../include/GPIOcontrol.h"
20 #include "../include/render.h"
21
22 #include <iostream>
23 #include <stdlib.h>
24 #include <cstdio>
25 #include <cerrno>
26 #include <fcntl.h>
27 #include <sys/mman.h>
28
29 // Xenomai-specific includes
30 #include <sys/mman.h>
31 #include <native/task.h>
32 #include <native/timer.h>
33 #include <rtdk.h>
34
35 using namespace std;
36
37 #define PRU_MEM_MCASP_OFFSET 0x2000 // Offset within PRU-SHARED RAM
38 #define PRU_MEM_MCASP_LENGTH 0x2000 // Length of McASP memory, in bytes
39 #define PRU_MEM_DAC_OFFSET 0x0 // Offset within PRU0 RAM
40 #define PRU_MEM_DAC_LENGTH 0x2000 // Length of ADC+DAC memory, in bytes
41 #define PRU_MEM_COMM_OFFSET 0x0 // Offset within PRU-SHARED RAM
42
43 #define PRU_SHOULD_STOP 0
44 #define PRU_CURRENT_BUFFER 1
45 #define PRU_BUFFER_FRAMES 2
46 #define PRU_SHOULD_SYNC 3
47 #define PRU_SYNC_ADDRESS 4
48 #define PRU_SYNC_PIN_MASK 5
49 #define PRU_LED_ADDRESS 6
50 #define PRU_LED_PIN_MASK 7
51 #define PRU_FRAME_COUNT 8
52 #define PRU_USE_SPI 9
53
54 #define PRU_SAMPLE_INTERVAL_NS 45351 // 22050Hz per SPI sample = 45.351us
55
56 #define GPIO0_ADDRESS 0x44E07000
57 #define GPIO1_ADDRESS 0x4804C000
58 #define GPIO_SIZE 0x198
59 #define GPIO_CLEARDATAOUT (0x190 / 4)
60 #define GPIO_SETDATAOUT (0x194 / 4)
61
62 #define TEST_PIN_GPIO_BASE GPIO0_ADDRESS // Use GPIO0(31) for debugging
63 #define TEST_PIN_MASK (1 << 31)
64 #define TEST_PIN2_MASK (1 << 26)
65
66 #define USERLED3_GPIO_BASE GPIO1_ADDRESS // GPIO1(24) is user LED 3
67 #define USERLED3_PIN_MASK (1 << 24)
68
69 const unsigned int PRU::kPruGPIODACSyncPin = 5; // GPIO0(5); P9-17
70 const unsigned int PRU::kPruGPIOADCSyncPin = 48; // GPIO1(16); P9-15
71
72 const unsigned int PRU::kPruGPIOTestPin = 60; // GPIO1(28); P9-12
73 const unsigned int PRU::kPruGPIOTestPin2 = 31; // GPIO0(31); P9-13
74 const unsigned int PRU::kPruGPIOTestPin3 = 26; // GPIO0(26); P8-14
75
76 extern int gShouldStop;
77 extern int gRTAudioVerbose;
78
79 // Constructor: specify a PRU number (0 or 1)
80 PRU::PRU()
81 : pru_number(0), running(false), spi_enabled(false), gpio_enabled(false), led_enabled(false),
82 gpio_test_pin_enabled(false), xenomai_gpio_fd(-1), xenomai_gpio(0)
83 {
84
85 }
86
87 // Destructor
88 PRU::~PRU()
89 {
90 if(running)
91 disable();
92 if(gpio_enabled)
93 cleanupGPIO();
94 if(xenomai_gpio_fd >= 0)
95 close(xenomai_gpio_fd);
96 }
97
98 // Prepare the GPIO pins needed for the PRU
99 // If include_test_pin is set, the GPIO output
100 // is also prepared for an output which can be
101 // viewed on a scope. If include_led is set,
102 // user LED 3 on the BBB is taken over by the PRU
103 // to indicate activity
104 int PRU::prepareGPIO(int use_spi, int include_test_pin, int include_led)
105 {
106 if(use_spi) {
107 // Prepare DAC CS/ pin: output, high to begin
108 if(gpio_export(kPruGPIODACSyncPin)) {
109 if(gRTAudioVerbose)
110 cout << "Warning: couldn't export DAC sync pin\n";
111 }
112 if(gpio_set_dir(kPruGPIODACSyncPin, OUTPUT_PIN)) {
113 if(gRTAudioVerbose)
114 cout << "Couldn't set direction on DAC sync pin\n";
115 return -1;
116 }
117 if(gpio_set_value(kPruGPIODACSyncPin, HIGH)) {
118 if(gRTAudioVerbose)
119 cout << "Couldn't set value on DAC sync pin\n";
120 return -1;
121 }
122
123 // Prepare ADC CS/ pin: output, high to begin
124 if(gpio_export(kPruGPIOADCSyncPin)) {
125 if(gRTAudioVerbose)
126 cout << "Warning: couldn't export ADC sync pin\n";
127 }
128 if(gpio_set_dir(kPruGPIOADCSyncPin, OUTPUT_PIN)) {
129 if(gRTAudioVerbose)
130 cout << "Couldn't set direction on ADC sync pin\n";
131 return -1;
132 }
133 if(gpio_set_value(kPruGPIOADCSyncPin, HIGH)) {
134 if(gRTAudioVerbose)
135 cout << "Couldn't set value on ADC sync pin\n";
136 return -1;
137 }
138
139 spi_enabled = true;
140 }
141
142 if(include_test_pin) {
143 // Prepare GPIO test output (for debugging), low to begin
144 if(gpio_export(kPruGPIOTestPin)) {
145 if(gRTAudioVerbose)
146 cout << "Warning: couldn't export GPIO test pin\n";
147 }
148 if(gpio_set_dir(kPruGPIOTestPin, OUTPUT_PIN)) {
149 if(gRTAudioVerbose)
150 cout << "Couldn't set direction on GPIO test pin\n";
151 return -1;
152 }
153 if(gpio_set_value(kPruGPIOTestPin, LOW)) {
154 if(gRTAudioVerbose)
155 cout << "Couldn't set value on GPIO test pin\n";
156 return -1;
157 }
158
159 if(gpio_export(kPruGPIOTestPin2)) {
160 if(gRTAudioVerbose)
161 cout << "Warning: couldn't export GPIO test pin 2\n";
162 }
163 if(gpio_set_dir(kPruGPIOTestPin2, OUTPUT_PIN)) {
164 if(gRTAudioVerbose)
165 cout << "Couldn't set direction on GPIO test pin 2\n";
166 return -1;
167 }
168 if(gpio_set_value(kPruGPIOTestPin2, LOW)) {
169 if(gRTAudioVerbose)
170 cout << "Couldn't set value on GPIO test pin 2\n";
171 return -1;
172 }
173
174 if(gpio_export(kPruGPIOTestPin3)) {
175 if(gRTAudioVerbose)
176 cout << "Warning: couldn't export GPIO test pin 3\n";
177 }
178 if(gpio_set_dir(kPruGPIOTestPin3, OUTPUT_PIN)) {
179 if(gRTAudioVerbose)
180 cout << "Couldn't set direction on GPIO test pin 3\n";
181 return -1;
182 }
183 if(gpio_set_value(kPruGPIOTestPin3, LOW)) {
184 if(gRTAudioVerbose)
185 cout << "Couldn't set value on GPIO test pin 3\n";
186 return -1;
187 }
188 gpio_test_pin_enabled = true;
189 }
190
191 if(include_led) {
192 // Turn off system function for LED3 so it can be reused by PRU
193 led_set_trigger(3, "none");
194 led_enabled = true;
195 }
196
197 gpio_enabled = true;
198
199 return 0;
200 }
201
202 // Clean up the GPIO at the end
203 void PRU::cleanupGPIO()
204 {
205 if(!gpio_enabled)
206 return;
207 if(spi_enabled) {
208 gpio_unexport(kPruGPIODACSyncPin);
209 gpio_unexport(kPruGPIOADCSyncPin);
210 }
211 if(gpio_test_pin_enabled) {
212 gpio_unexport(kPruGPIOTestPin);
213 gpio_unexport(kPruGPIOTestPin2);
214 gpio_unexport(kPruGPIOTestPin3);
215 }
216 if(led_enabled) {
217 // Set LED back to default eMMC status
218 // TODO: make it go back to its actual value before this program,
219 // rather than the system default
220 led_set_trigger(3, "mmc1");
221 }
222
223 gpio_enabled = gpio_test_pin_enabled = false;
224 }
225
226 // Initialise and open the PRU
227 int PRU::initialise(int pru_num, int frames_per_buffer, bool xenomai_test_pin)
228 {
229 uint32_t *pruMem = 0;
230
231 if(!gpio_enabled) {
232 rt_printf("initialise() called before GPIO enabled\n");
233 return 1;
234 }
235
236 pru_number = pru_num;
237
238 /* Initialize structure used by prussdrv_pruintc_intc */
239 /* PRUSS_INTC_INITDATA is found in pruss_intc_mapping.h */
240 tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
241
242 /* Allocate and initialize memory */
243 prussdrv_init();
244 if(prussdrv_open(PRU_EVTOUT_0)) {
245 rt_printf("Failed to open PRU driver\n");
246 return 1;
247 }
248
249 /* Map PRU's INTC */
250 prussdrv_pruintc_init(&pruss_intc_initdata);
251
252 spi_buffer_frames = frames_per_buffer;
253 audio_buffer_frames = spi_buffer_frames * 2;
254
255 /* Map PRU memory to pointers */
256 prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem);
257 pru_buffer_comm = (uint32_t *)&pruMem[PRU_MEM_COMM_OFFSET/sizeof(uint32_t)];
258 pru_buffer_audio_dac = (int16_t *)&pruMem[PRU_MEM_MCASP_OFFSET/sizeof(uint32_t)];
259
260 /* ADC memory starts 2(ch)*2(buffers)*2(samples/spi)*bufsize samples later */
261 pru_buffer_audio_adc = &pru_buffer_audio_dac[8 * spi_buffer_frames];
262
263 if(spi_enabled) {
264 prussdrv_map_prumem (pru_number == 0 ? PRUSS0_PRU0_DATARAM : PRUSS0_PRU1_DATARAM, (void **)&pruMem);
265 pru_buffer_spi_dac = (uint16_t *)&pruMem[PRU_MEM_DAC_OFFSET/sizeof(uint32_t)];
266
267 /* ADC memory starts after 8(ch)*2(buffers)*bufsize samples */
268 pru_buffer_spi_adc = &pru_buffer_spi_dac[16 * spi_buffer_frames];
269 }
270 else {
271 pru_buffer_spi_dac = pru_buffer_spi_adc = 0;
272 }
273
274 /* Set up flags */
275 pru_buffer_comm[PRU_SHOULD_STOP] = 0;
276 pru_buffer_comm[PRU_CURRENT_BUFFER] = 0;
277 pru_buffer_comm[PRU_BUFFER_FRAMES] = spi_buffer_frames;
278 pru_buffer_comm[PRU_SHOULD_SYNC] = 0;
279 pru_buffer_comm[PRU_SYNC_ADDRESS] = 0;
280 pru_buffer_comm[PRU_SYNC_PIN_MASK] = 0;
281 if(led_enabled) {
282 pru_buffer_comm[PRU_LED_ADDRESS] = USERLED3_GPIO_BASE;
283 pru_buffer_comm[PRU_LED_PIN_MASK] = USERLED3_PIN_MASK;
284 }
285 else {
286 pru_buffer_comm[PRU_LED_ADDRESS] = 0;
287 pru_buffer_comm[PRU_LED_PIN_MASK] = 0;
288 }
289 if(spi_enabled) {
290 pru_buffer_comm[PRU_USE_SPI] = 1;
291 }
292 else {
293 pru_buffer_comm[PRU_USE_SPI] = 0;
294 }
295
296 /* Clear ADC and DAC memory */
297 if(spi_enabled) {
298 for(int i = 0; i < PRU_MEM_DAC_LENGTH / 2; i++)
299 pru_buffer_spi_dac[i] = 0;
300 }
301 for(int i = 0; i < PRU_MEM_MCASP_LENGTH / 2; i++)
302 pru_buffer_audio_dac[i] = 0;
303
304 /* If using GPIO test pin for Xenomai (for debugging), initialise the pointer now */
305 if(xenomai_test_pin && xenomai_gpio_fd < 0) {
306 xenomai_gpio_fd = open("/dev/mem", O_RDWR);
307 if(xenomai_gpio_fd < 0)
308 rt_printf("Unable to open /dev/mem for GPIO test pin\n");
309 else {
310 xenomai_gpio = (uint32_t *)mmap(0, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, xenomai_gpio_fd, TEST_PIN_GPIO_BASE);
311 if(xenomai_gpio == MAP_FAILED) {
312 rt_printf("Unable to map GPIO address for test pin\n");
313 xenomai_gpio = 0;
314 close(xenomai_gpio_fd);
315 xenomai_gpio_fd = -1;
316 }
317 }
318 }
319
320 return 0;
321 }
322
323 // Run the code image in the specified file
324 int PRU::start(char * const filename)
325 {
326 /* Clear any old interrupt */
327 prussdrv_pru_clear_event(pru_number == 0 ? PRU0_ARM_INTERRUPT : PRU1_ARM_INTERRUPT);
328
329 /* Load and execute binary on PRU */
330 if(prussdrv_exec_program(pru_number, filename)) {
331 rt_printf("Failed to execute PRU code from %s\n", filename);
332 return 1;
333 }
334
335 running = true;
336 return 0;
337 }
338
339 // Main loop to read and write data from/to PRU
340 void PRU::loop()
341 {
342 // Polling interval is 1/4 of the period
343 RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * spi_buffer_frames / 4;
344 float *audioInBuffer, *audioOutBuffer;
345
346 audioInBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float));
347 audioOutBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float));
348
349 if(audioInBuffer == 0 || audioOutBuffer == 0) {
350 rt_printf("Error: couldn't allocated audio buffers\n");
351 return;
352 }
353
354 while(!gShouldStop) {
355 // Wait for PRU to move to buffer 1
356 while(pru_buffer_comm[PRU_CURRENT_BUFFER] == 0 && !gShouldStop) {
357 rt_task_sleep(sleepTime);
358 }
359 if(gShouldStop)
360 break;
361
362 if(xenomai_gpio != 0) {
363 // Set the test pin high
364 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK;
365 }
366
367 // Render from/to buffer 0
368
369 // Convert short (16-bit) samples to float
370 for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++)
371 audioInBuffer[n] = (float)pru_buffer_audio_adc[n] / 32768.0;
372
373 if(spi_enabled)
374 render(spi_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer,
375 pru_buffer_spi_adc, pru_buffer_spi_dac);
376 else
377 render(0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0);
378
379 // Convert float back to short
380 for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) {
381 int out = audioOutBuffer[n] * 32768.0;
382 if(out < -32768) out = -32768;
383 else if(out > 32767) out = 32767;
384 pru_buffer_audio_dac[n] = (int16_t)out;
385 }
386
387 if(xenomai_gpio != 0) {
388 // Set the test pin high
389 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK;
390 }
391
392 // Wait for PRU to move to buffer 0
393 while(pru_buffer_comm[PRU_CURRENT_BUFFER] != 0 && !gShouldStop) {
394 rt_task_sleep(sleepTime);
395 }
396
397 if(gShouldStop)
398 break;
399
400 if(xenomai_gpio != 0) {
401 // Set the test pin high
402 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK;
403 }
404
405 // Render from/to buffer 1
406
407 // Convert short (16-bit) samples to float
408 for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++)
409 audioInBuffer[n] = (float)pru_buffer_audio_adc[n + audio_buffer_frames * 2] / 32768.0;
410
411 if(spi_enabled)
412 render(spi_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer,
413 &pru_buffer_spi_adc[spi_buffer_frames * 8], &pru_buffer_spi_dac[spi_buffer_frames * 8]);
414 else
415 render(0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0);
416
417 // Convert float back to short
418 for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) {
419 int out = audioOutBuffer[n] * 32768.0;
420 if(out < -32768) out = -32768;
421 else if(out > 32767) out = 32767;
422 pru_buffer_audio_dac[n + audio_buffer_frames * 2] = (int16_t)out;
423 }
424
425 if(xenomai_gpio != 0) {
426 // Set the test pin high
427 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK;
428 }
429 }
430
431 // Tell PRU to stop
432 pru_buffer_comm[PRU_SHOULD_STOP] = 1;
433
434 free(audioInBuffer);
435 free(audioOutBuffer);
436 }
437
438 // Wait for an interrupt from the PRU indicate it is finished
439 void PRU::waitForFinish()
440 {
441 if(!running)
442 return;
443 prussdrv_pru_wait_event (PRU_EVTOUT_0);
444 prussdrv_pru_clear_event(pru_number == 0 ? PRU0_ARM_INTERRUPT : PRU1_ARM_INTERRUPT);
445 }
446
447 // Turn off the PRU when done
448 void PRU::disable()
449 {
450 /* Disable PRU and close memory mapping*/
451 prussdrv_pru_disable(pru_number);
452 prussdrv_exit();
453 running = false;
454 }
455
456 // Debugging
457 void PRU::setGPIOTestPin()
458 {
459 if(!xenomai_gpio)
460 return;
461 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN2_MASK;
462 }
463
464 void PRU::clearGPIOTestPin()
465 {
466 if(!xenomai_gpio)
467 return;
468 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN2_MASK;
469 }