annotate core/PRU.cpp @ 41:4255ecbb9bec ultra-staging

Timers to measure performances, ultra experimental
author Giulio Moro <giuliomoro@yahoo.it>
date Tue, 19 May 2015 16:41:07 +0100
parents a9af130097e8
children 3068421c0737
rev   line source
andrewm@0 1 /*
andrewm@0 2 * PRU.cpp
andrewm@0 3 *
andrewm@0 4 * Code for communicating with the Programmable Realtime Unit (PRU)
andrewm@0 5 * on the BeagleBone AM335x series processors. The PRU loads and runs
andrewm@0 6 * a separate code image compiled from an assembly file. Here it is
andrewm@0 7 * used to handle audio and SPI ADC/DAC data.
andrewm@0 8 *
andrewm@0 9 * This code is specific to the PRU code in the assembly file; for example,
andrewm@0 10 * it uses certain GPIO resources that correspond to that image.
andrewm@0 11 *
andrewm@0 12 * Created on: May 27, 2014
andrewm@0 13 * Author: andrewm
andrewm@0 14 */
andrewm@0 15
andrewm@0 16 #include "../include/PRU.h"
andrewm@0 17 #include "../include/prussdrv.h"
andrewm@0 18 #include "../include/pruss_intc_mapping.h"
giuliomoro@19 19 #include "../include/digital_gpio_mapping.h"
andrewm@0 20 #include "../include/GPIOcontrol.h"
andrewm@0 21 #include "../include/render.h"
andrewm@15 22 #include "../include/pru_rtaudio_bin.h"
giuliomoro@41 23 #include "../include/intervals.h"
andrewm@0 24
andrewm@0 25 #include <iostream>
andrewm@0 26 #include <stdlib.h>
andrewm@0 27 #include <cstdio>
andrewm@0 28 #include <cerrno>
andrewm@0 29 #include <fcntl.h>
andrewm@0 30 #include <sys/mman.h>
giuliomoro@16 31 #include <unistd.h>
andrewm@0 32
andrewm@0 33 // Xenomai-specific includes
andrewm@0 34 #include <sys/mman.h>
andrewm@0 35 #include <native/task.h>
andrewm@0 36 #include <native/timer.h>
andrewm@0 37 #include <rtdk.h>
andrewm@0 38
andrewm@0 39 using namespace std;
andrewm@0 40
andrewm@0 41 #define PRU_MEM_MCASP_OFFSET 0x2000 // Offset within PRU-SHARED RAM
andrewm@0 42 #define PRU_MEM_MCASP_LENGTH 0x2000 // Length of McASP memory, in bytes
andrewm@0 43 #define PRU_MEM_DAC_OFFSET 0x0 // Offset within PRU0 RAM
andrewm@0 44 #define PRU_MEM_DAC_LENGTH 0x2000 // Length of ADC+DAC memory, in bytes
andrewm@0 45 #define PRU_MEM_COMM_OFFSET 0x0 // Offset within PRU-SHARED RAM
giuliomoro@19 46 #define PRU_MEM_DIGITAL_OFFSET 0x1000 //Offset within PRU-SHARED RAM
giuliomoro@19 47 #define MEM_DIGITAL_BUFFER1_OFFSET 0x400 //Start pointer to DIGITAL_BUFFER1, which is 256 words.
giuliomoro@16 48 // 256 is the maximum number of frames allowed
andrewm@0 49 #define PRU_SHOULD_STOP 0
andrewm@0 50 #define PRU_CURRENT_BUFFER 1
andrewm@0 51 #define PRU_BUFFER_FRAMES 2
andrewm@0 52 #define PRU_SHOULD_SYNC 3
andrewm@0 53 #define PRU_SYNC_ADDRESS 4
andrewm@0 54 #define PRU_SYNC_PIN_MASK 5
andrewm@0 55 #define PRU_LED_ADDRESS 6
andrewm@0 56 #define PRU_LED_PIN_MASK 7
andrewm@0 57 #define PRU_FRAME_COUNT 8
andrewm@0 58 #define PRU_USE_SPI 9
andrewm@12 59 #define PRU_SPI_NUM_CHANNELS 10
giuliomoro@38 60 #define PRU_USE_DIGITAL 11
giuliomoro@16 61
giuliomoro@19 62 short int digitalPins[NUM_DIGITALS]={
giuliomoro@16 63 GPIO_NO_BIT_0,
giuliomoro@16 64 GPIO_NO_BIT_1,
giuliomoro@16 65 GPIO_NO_BIT_2,
giuliomoro@16 66 GPIO_NO_BIT_3,
giuliomoro@16 67 GPIO_NO_BIT_4,
giuliomoro@16 68 GPIO_NO_BIT_5,
giuliomoro@16 69 GPIO_NO_BIT_6,
giuliomoro@16 70 GPIO_NO_BIT_7,
giuliomoro@16 71 GPIO_NO_BIT_8,
giuliomoro@16 72 GPIO_NO_BIT_9,
giuliomoro@16 73 GPIO_NO_BIT_10,
giuliomoro@16 74 GPIO_NO_BIT_11,
giuliomoro@16 75 GPIO_NO_BIT_12,
giuliomoro@16 76 GPIO_NO_BIT_13,
giuliomoro@16 77 GPIO_NO_BIT_14,
giuliomoro@16 78 GPIO_NO_BIT_15,
giuliomoro@16 79 };
andrewm@0 80
andrewm@12 81 #define PRU_SAMPLE_INTERVAL_NS 11338 // 88200Hz per SPI sample = 11.338us
andrewm@0 82
andrewm@0 83 #define GPIO0_ADDRESS 0x44E07000
andrewm@0 84 #define GPIO1_ADDRESS 0x4804C000
andrewm@0 85 #define GPIO_SIZE 0x198
andrewm@0 86 #define GPIO_CLEARDATAOUT (0x190 / 4)
andrewm@0 87 #define GPIO_SETDATAOUT (0x194 / 4)
andrewm@0 88
andrewm@0 89 #define TEST_PIN_GPIO_BASE GPIO0_ADDRESS // Use GPIO0(31) for debugging
andrewm@0 90 #define TEST_PIN_MASK (1 << 31)
andrewm@0 91 #define TEST_PIN2_MASK (1 << 26)
andrewm@0 92
andrewm@0 93 #define USERLED3_GPIO_BASE GPIO1_ADDRESS // GPIO1(24) is user LED 3
andrewm@0 94 #define USERLED3_PIN_MASK (1 << 24)
andrewm@0 95
andrewm@0 96 const unsigned int PRU::kPruGPIODACSyncPin = 5; // GPIO0(5); P9-17
andrewm@0 97 const unsigned int PRU::kPruGPIOADCSyncPin = 48; // GPIO1(16); P9-15
andrewm@0 98
andrewm@0 99 const unsigned int PRU::kPruGPIOTestPin = 60; // GPIO1(28); P9-12
andrewm@0 100 const unsigned int PRU::kPruGPIOTestPin2 = 31; // GPIO0(31); P9-13
andrewm@0 101 const unsigned int PRU::kPruGPIOTestPin3 = 26; // GPIO0(26); P8-14
andrewm@0 102
giuliomoro@41 103 //extern int gShouldStop;
andrewm@0 104 extern int gRTAudioVerbose;
giuliomoro@41 105 extern PRU *gPRU;
andrewm@0 106
andrewm@0 107 // Constructor: specify a PRU number (0 or 1)
andrewm@0 108 PRU::PRU()
giuliomoro@41 109 : renderTimer(100,0,44100.0,"renderTimer"), sleepTimer(100,0,44100.0,"sleepTimer"),
giuliomoro@41 110 pru_number(0), running(false), spi_enabled(false), gpio_enabled(false), led_enabled(false),
andrewm@12 111 gpio_test_pin_enabled(false), spi_num_channels(0), xenomai_gpio_fd(-1), xenomai_gpio(0)
andrewm@0 112 {
andrewm@0 113
andrewm@0 114 }
andrewm@0 115
andrewm@0 116 // Destructor
andrewm@0 117 PRU::~PRU()
andrewm@0 118 {
andrewm@0 119 if(running)
andrewm@0 120 disable();
andrewm@0 121 if(gpio_enabled)
andrewm@0 122 cleanupGPIO();
andrewm@0 123 if(xenomai_gpio_fd >= 0)
andrewm@0 124 close(xenomai_gpio_fd);
andrewm@0 125 }
andrewm@0 126
andrewm@0 127 // Prepare the GPIO pins needed for the PRU
andrewm@0 128 // If include_test_pin is set, the GPIO output
andrewm@0 129 // is also prepared for an output which can be
andrewm@0 130 // viewed on a scope. If include_led is set,
andrewm@0 131 // user LED 3 on the BBB is taken over by the PRU
andrewm@0 132 // to indicate activity
giuliomoro@19 133 int PRU::prepareGPIO(int use_spi, int use_digital, int include_test_pin, int include_led)
andrewm@0 134 {
andrewm@0 135 if(use_spi) {
andrewm@0 136 // Prepare DAC CS/ pin: output, high to begin
andrewm@0 137 if(gpio_export(kPruGPIODACSyncPin)) {
andrewm@0 138 if(gRTAudioVerbose)
andrewm@0 139 cout << "Warning: couldn't export DAC sync pin\n";
andrewm@0 140 }
andrewm@0 141 if(gpio_set_dir(kPruGPIODACSyncPin, OUTPUT_PIN)) {
andrewm@0 142 if(gRTAudioVerbose)
andrewm@0 143 cout << "Couldn't set direction on DAC sync pin\n";
andrewm@0 144 return -1;
andrewm@0 145 }
andrewm@0 146 if(gpio_set_value(kPruGPIODACSyncPin, HIGH)) {
andrewm@0 147 if(gRTAudioVerbose)
andrewm@0 148 cout << "Couldn't set value on DAC sync pin\n";
andrewm@0 149 return -1;
andrewm@0 150 }
andrewm@0 151
andrewm@0 152 // Prepare ADC CS/ pin: output, high to begin
andrewm@0 153 if(gpio_export(kPruGPIOADCSyncPin)) {
andrewm@0 154 if(gRTAudioVerbose)
andrewm@0 155 cout << "Warning: couldn't export ADC sync pin\n";
andrewm@0 156 }
andrewm@0 157 if(gpio_set_dir(kPruGPIOADCSyncPin, OUTPUT_PIN)) {
andrewm@0 158 if(gRTAudioVerbose)
andrewm@0 159 cout << "Couldn't set direction on ADC sync pin\n";
andrewm@0 160 return -1;
andrewm@0 161 }
andrewm@0 162 if(gpio_set_value(kPruGPIOADCSyncPin, HIGH)) {
andrewm@0 163 if(gRTAudioVerbose)
andrewm@0 164 cout << "Couldn't set value on ADC sync pin\n";
andrewm@0 165 return -1;
andrewm@0 166 }
andrewm@0 167
andrewm@0 168 spi_enabled = true;
andrewm@0 169 }
andrewm@0 170
giuliomoro@19 171 if(use_digital){
giuliomoro@19 172 printf("gNumDigitalChannels: %d;\n",gNumDigitalChannels);
giuliomoro@19 173 for(int i=0; i<gNumDigitalChannels; i++){
giuliomoro@19 174 if(gpio_export(digitalPins[i])) {
giuliomoro@16 175 if(gRTAudioVerbose)
giuliomoro@38 176 cerr << "Warning: couldn't export digital GPIO pin " << digitalPins[i] << "\n"; // this is left as a warning because if the pin has been exported by somebody else, can still be used
giuliomoro@16 177 }
giuliomoro@38 178 if(gpio_set_dir(digitalPins[i], INPUT_PIN)) {
giuliomoro@16 179 if(gRTAudioVerbose)
giuliomoro@38 180 cerr << "Error: Couldn't set direction on digital GPIO pin " << digitalPins[i] << "\n";
giuliomoro@16 181 return -1;
giuliomoro@16 182 }
giuliomoro@16 183 }
giuliomoro@19 184 digital_enabled=true;
giuliomoro@16 185 }
giuliomoro@16 186
andrewm@0 187 if(include_test_pin) {
andrewm@0 188 // Prepare GPIO test output (for debugging), low to begin
andrewm@0 189 if(gpio_export(kPruGPIOTestPin)) {
andrewm@0 190 if(gRTAudioVerbose)
andrewm@0 191 cout << "Warning: couldn't export GPIO test pin\n";
andrewm@0 192 }
andrewm@0 193 if(gpio_set_dir(kPruGPIOTestPin, OUTPUT_PIN)) {
andrewm@0 194 if(gRTAudioVerbose)
andrewm@0 195 cout << "Couldn't set direction on GPIO test pin\n";
andrewm@0 196 return -1;
andrewm@0 197 }
andrewm@0 198 if(gpio_set_value(kPruGPIOTestPin, LOW)) {
andrewm@0 199 if(gRTAudioVerbose)
andrewm@0 200 cout << "Couldn't set value on GPIO test pin\n";
andrewm@0 201 return -1;
andrewm@0 202 }
andrewm@0 203
andrewm@0 204 if(gpio_export(kPruGPIOTestPin2)) {
andrewm@0 205 if(gRTAudioVerbose)
andrewm@0 206 cout << "Warning: couldn't export GPIO test pin 2\n";
andrewm@0 207 }
andrewm@0 208 if(gpio_set_dir(kPruGPIOTestPin2, OUTPUT_PIN)) {
andrewm@0 209 if(gRTAudioVerbose)
andrewm@0 210 cout << "Couldn't set direction on GPIO test pin 2\n";
andrewm@0 211 return -1;
andrewm@0 212 }
andrewm@0 213 if(gpio_set_value(kPruGPIOTestPin2, LOW)) {
andrewm@0 214 if(gRTAudioVerbose)
andrewm@0 215 cout << "Couldn't set value on GPIO test pin 2\n";
andrewm@0 216 return -1;
andrewm@0 217 }
andrewm@0 218
andrewm@0 219 if(gpio_export(kPruGPIOTestPin3)) {
andrewm@0 220 if(gRTAudioVerbose)
andrewm@0 221 cout << "Warning: couldn't export GPIO test pin 3\n";
andrewm@0 222 }
andrewm@0 223 if(gpio_set_dir(kPruGPIOTestPin3, OUTPUT_PIN)) {
andrewm@0 224 if(gRTAudioVerbose)
andrewm@0 225 cout << "Couldn't set direction on GPIO test pin 3\n";
andrewm@0 226 return -1;
andrewm@0 227 }
andrewm@0 228 if(gpio_set_value(kPruGPIOTestPin3, LOW)) {
andrewm@0 229 if(gRTAudioVerbose)
andrewm@0 230 cout << "Couldn't set value on GPIO test pin 3\n";
andrewm@0 231 return -1;
andrewm@0 232 }
andrewm@0 233 gpio_test_pin_enabled = true;
andrewm@0 234 }
andrewm@0 235
andrewm@0 236 if(include_led) {
andrewm@0 237 // Turn off system function for LED3 so it can be reused by PRU
andrewm@0 238 led_set_trigger(3, "none");
andrewm@0 239 led_enabled = true;
andrewm@0 240 }
andrewm@0 241
andrewm@0 242 gpio_enabled = true;
andrewm@0 243
andrewm@0 244 return 0;
andrewm@0 245 }
andrewm@0 246
andrewm@0 247 // Clean up the GPIO at the end
andrewm@0 248 void PRU::cleanupGPIO()
andrewm@0 249 {
andrewm@0 250 if(!gpio_enabled)
andrewm@0 251 return;
andrewm@0 252 if(spi_enabled) {
andrewm@0 253 gpio_unexport(kPruGPIODACSyncPin);
andrewm@0 254 gpio_unexport(kPruGPIOADCSyncPin);
andrewm@0 255 }
giuliomoro@19 256 if(digital_enabled){
giuliomoro@19 257 for(int i=0; i<gNumDigitalChannels; i++){
giuliomoro@19 258 gpio_unexport(digitalPins[i]);
giuliomoro@16 259 }
giuliomoro@16 260 }
andrewm@0 261 if(gpio_test_pin_enabled) {
andrewm@0 262 gpio_unexport(kPruGPIOTestPin);
andrewm@0 263 gpio_unexport(kPruGPIOTestPin2);
andrewm@0 264 gpio_unexport(kPruGPIOTestPin3);
andrewm@0 265 }
andrewm@0 266 if(led_enabled) {
andrewm@0 267 // Set LED back to default eMMC status
andrewm@0 268 // TODO: make it go back to its actual value before this program,
andrewm@0 269 // rather than the system default
andrewm@0 270 led_set_trigger(3, "mmc1");
andrewm@0 271 }
andrewm@0 272 gpio_enabled = gpio_test_pin_enabled = false;
andrewm@0 273 }
andrewm@0 274
andrewm@0 275 // Initialise and open the PRU
andrewm@12 276 int PRU::initialise(int pru_num, int frames_per_buffer, int spi_channels, bool xenomai_test_pin)
andrewm@0 277 {
andrewm@0 278 uint32_t *pruMem = 0;
andrewm@0 279
andrewm@0 280 if(!gpio_enabled) {
andrewm@0 281 rt_printf("initialise() called before GPIO enabled\n");
andrewm@0 282 return 1;
andrewm@0 283 }
andrewm@0 284
andrewm@0 285 pru_number = pru_num;
andrewm@0 286
andrewm@12 287 /* Set number of SPI ADC / DAC channels to use. This implicitly
andrewm@12 288 * also determines the sample rate relative to the audio clock
andrewm@12 289 * (half audio clock for 8 channels, full audio clock for 4,
andrewm@12 290 * double audio clock for 2)
andrewm@12 291 */
andrewm@12 292 spi_num_channels = spi_channels;
andrewm@12 293
andrewm@0 294 /* Initialize structure used by prussdrv_pruintc_intc */
andrewm@0 295 /* PRUSS_INTC_INITDATA is found in pruss_intc_mapping.h */
andrewm@0 296 tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
andrewm@0 297
andrewm@0 298 /* Allocate and initialize memory */
andrewm@0 299 prussdrv_init();
andrewm@15 300 if(prussdrv_open(pru_number == 0 ? PRU_EVTOUT_0 : PRU_EVTOUT_1)) {
andrewm@0 301 rt_printf("Failed to open PRU driver\n");
andrewm@0 302 return 1;
andrewm@0 303 }
andrewm@0 304
andrewm@0 305 /* Map PRU's INTC */
andrewm@0 306 prussdrv_pruintc_init(&pruss_intc_initdata);
andrewm@0 307
andrewm@0 308 spi_buffer_frames = frames_per_buffer;
andrewm@12 309 audio_buffer_frames = spi_buffer_frames * spi_num_channels / 4;
giuliomoro@19 310 digital_buffer_frames = audio_buffer_frames;
andrewm@0 311
andrewm@0 312 /* Map PRU memory to pointers */
andrewm@0 313 prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem);
andrewm@0 314 pru_buffer_comm = (uint32_t *)&pruMem[PRU_MEM_COMM_OFFSET/sizeof(uint32_t)];
andrewm@0 315 pru_buffer_audio_dac = (int16_t *)&pruMem[PRU_MEM_MCASP_OFFSET/sizeof(uint32_t)];
andrewm@0 316
andrewm@12 317 /* ADC memory starts 2(ch)*2(buffers)*bufsize samples later */
andrewm@12 318 pru_buffer_audio_adc = &pru_buffer_audio_dac[4 * audio_buffer_frames];
andrewm@0 319
andrewm@0 320 if(spi_enabled) {
andrewm@0 321 prussdrv_map_prumem (pru_number == 0 ? PRUSS0_PRU0_DATARAM : PRUSS0_PRU1_DATARAM, (void **)&pruMem);
andrewm@0 322 pru_buffer_spi_dac = (uint16_t *)&pruMem[PRU_MEM_DAC_OFFSET/sizeof(uint32_t)];
andrewm@0 323
andrewm@12 324 /* ADC memory starts after N(ch)*2(buffers)*bufsize samples */
andrewm@12 325 pru_buffer_spi_adc = &pru_buffer_spi_dac[2 * spi_num_channels * spi_buffer_frames];
andrewm@0 326 }
andrewm@0 327 else {
andrewm@0 328 pru_buffer_spi_dac = pru_buffer_spi_adc = 0;
andrewm@0 329 }
andrewm@0 330
giuliomoro@19 331 if(digital_enabled) {
giuliomoro@16 332 prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem);
giuliomoro@19 333 pru_buffer_digital = (uint32_t *)&pruMem[PRU_MEM_DIGITAL_OFFSET/sizeof(uint32_t)];
giuliomoro@16 334 }
giuliomoro@16 335 else {
giuliomoro@19 336 pru_buffer_digital = 0;
giuliomoro@16 337 }
andrewm@0 338 /* Set up flags */
andrewm@0 339 pru_buffer_comm[PRU_SHOULD_STOP] = 0;
andrewm@0 340 pru_buffer_comm[PRU_CURRENT_BUFFER] = 0;
andrewm@0 341 pru_buffer_comm[PRU_BUFFER_FRAMES] = spi_buffer_frames;
andrewm@0 342 pru_buffer_comm[PRU_SHOULD_SYNC] = 0;
andrewm@0 343 pru_buffer_comm[PRU_SYNC_ADDRESS] = 0;
andrewm@0 344 pru_buffer_comm[PRU_SYNC_PIN_MASK] = 0;
andrewm@0 345 if(led_enabled) {
andrewm@0 346 pru_buffer_comm[PRU_LED_ADDRESS] = USERLED3_GPIO_BASE;
andrewm@0 347 pru_buffer_comm[PRU_LED_PIN_MASK] = USERLED3_PIN_MASK;
andrewm@0 348 }
andrewm@0 349 else {
andrewm@0 350 pru_buffer_comm[PRU_LED_ADDRESS] = 0;
andrewm@0 351 pru_buffer_comm[PRU_LED_PIN_MASK] = 0;
andrewm@0 352 }
andrewm@0 353 if(spi_enabled) {
andrewm@0 354 pru_buffer_comm[PRU_USE_SPI] = 1;
andrewm@12 355 pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = spi_num_channels;
andrewm@0 356 }
andrewm@0 357 else {
andrewm@0 358 pru_buffer_comm[PRU_USE_SPI] = 0;
andrewm@12 359 pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = 0;
andrewm@0 360 }
giuliomoro@19 361 if(digital_enabled) {
giuliomoro@38 362 pru_buffer_comm[PRU_USE_DIGITAL] = 1;
giuliomoro@38 363 //TODO: add mask
giuliomoro@16 364 }
giuliomoro@16 365 else {
giuliomoro@38 366 pru_buffer_comm[PRU_USE_DIGITAL] = 0;
giuliomoro@38 367
giuliomoro@16 368 }
andrewm@0 369
giuliomoro@38 370 /* Clear ADC and DAC memory.*/
giuliomoro@38 371 //TODO: this initialisation should only address the memory effectively used by these buffers, i.e.:depend on the number of frames
giuliomoro@38 372 // (otherwise might cause issues if we move memory locations later on)
andrewm@0 373 if(spi_enabled) {
andrewm@0 374 for(int i = 0; i < PRU_MEM_DAC_LENGTH / 2; i++)
andrewm@0 375 pru_buffer_spi_dac[i] = 0;
giuliomoro@38 376 if(digital_enabled){
giuliomoro@38 377 for(int i = 0; i < PRU_MEM_DIGITAL_OFFSET*2; i++)
giuliomoro@38 378 pru_buffer_digital[i] = 0x0000ffff; // set to all inputs, to avoid unexpected spikes
giuliomoro@38 379 }
andrewm@0 380 }
andrewm@0 381 for(int i = 0; i < PRU_MEM_MCASP_LENGTH / 2; i++)
andrewm@0 382 pru_buffer_audio_dac[i] = 0;
giuliomoro@16 383 //TODO: maybe the lines below are to be deleted, as we removed the test code from pru_rtaudio.p ?
andrewm@0 384 /* If using GPIO test pin for Xenomai (for debugging), initialise the pointer now */
andrewm@0 385 if(xenomai_test_pin && xenomai_gpio_fd < 0) {
andrewm@0 386 xenomai_gpio_fd = open("/dev/mem", O_RDWR);
andrewm@0 387 if(xenomai_gpio_fd < 0)
andrewm@0 388 rt_printf("Unable to open /dev/mem for GPIO test pin\n");
andrewm@0 389 else {
andrewm@0 390 xenomai_gpio = (uint32_t *)mmap(0, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, xenomai_gpio_fd, TEST_PIN_GPIO_BASE);
andrewm@0 391 if(xenomai_gpio == MAP_FAILED) {
andrewm@0 392 rt_printf("Unable to map GPIO address for test pin\n");
andrewm@0 393 xenomai_gpio = 0;
andrewm@0 394 close(xenomai_gpio_fd);
andrewm@0 395 xenomai_gpio_fd = -1;
andrewm@0 396 }
andrewm@0 397 }
andrewm@0 398 }
andrewm@0 399
andrewm@0 400 return 0;
andrewm@0 401 }
andrewm@0 402
andrewm@0 403 // Run the code image in the specified file
giuliomoro@16 404 int PRU::start(char * const filename)
andrewm@0 405 {
andrewm@0 406 /* Clear any old interrupt */
andrewm@15 407 if(pru_number == 0)
andrewm@15 408 prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
andrewm@15 409 else
andrewm@15 410 prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_ARM_INTERRUPT);
giuliomoro@16 411 /* Load and execute binary on PRU */
giuliomoro@16 412 if(filename[0] == '\0') { //if the string is empty, load the embedded code
giuliomoro@16 413 if(gRTAudioVerbose)
giuliomoro@16 414 rt_printf("Using embedded PRU code\n");
giuliomoro@16 415 if(prussdrv_exec_code(pru_number, PRUcode, sizeof(PRUcode))) {
giuliomoro@16 416 rt_printf("Failed to execute PRU code\n");
giuliomoro@16 417 return 1;
giuliomoro@16 418 }
giuliomoro@16 419 } else {
giuliomoro@16 420 if(gRTAudioVerbose)
giuliomoro@16 421 rt_printf("Using PRU code from %s\n",filename);
giuliomoro@16 422 if(prussdrv_exec_program(pru_number, filename)) {
giuliomoro@16 423 rt_printf("Failed to execute PRU code from %s\n", filename);
giuliomoro@16 424 return 1;
giuliomoro@16 425 }
giuliomoro@16 426 }
andrewm@0 427
andrewm@0 428 running = true;
andrewm@0 429 return 0;
andrewm@0 430 }
giuliomoro@16 431 uint32_t empty[1024]={0x0};
andrewm@0 432
andrewm@0 433 // Main loop to read and write data from/to PRU
andrewm@0 434 void PRU::loop()
andrewm@0 435 {
andrewm@0 436 // Polling interval is 1/4 of the period
andrewm@12 437 RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * (spi_num_channels / 2) * spi_buffer_frames / 4;
andrewm@0 438 float *audioInBuffer, *audioOutBuffer;
giuliomoro@23 439 float *analogInBuffer, *analogOutBuffer, *lastAnalogOutFrame;
giuliomoro@23 440 uint32_t *digitalBuffer0, *digitalBuffer1, *lastDigitalBuffer;
andrewm@0 441 audioInBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float));
andrewm@0 442 audioOutBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float));
giuliomoro@19 443 analogInBuffer = (float *)malloc(spi_num_channels * spi_buffer_frames * sizeof(float));
giuliomoro@19 444 analogOutBuffer = (float *)malloc(spi_num_channels * spi_buffer_frames * sizeof(float));
giuliomoro@23 445 lastAnalogOutFrame = (float *)malloc(spi_num_channels * sizeof(float));
giuliomoro@19 446 digitalBuffer0 = pru_buffer_digital;
giuliomoro@19 447 digitalBuffer1 = pru_buffer_digital+MEM_DIGITAL_BUFFER1_OFFSET/sizeof(uint32_t);
giuliomoro@23 448 digital_buffer_frames = digital_enabled ? audio_buffer_frames : 0; //TODO: find a more elegant solution for when the digital is disabled e.g.:
giuliomoro@19 449 // - embed in the digitalWrite/Read macros a check whether digital is enabled
giuliomoro@16 450 // - allocate some memory in ARM just to allow render() to run regardless.
giuliomoro@19 451 // in this case it can be digitalBuffer0 == digitalBuffer1
giuliomoro@23 452 lastDigitalBuffer = (uint32_t *)malloc(digital_buffer_frames*sizeof(uint32_t)); //temp buffer to hold previous states
andrewm@0 453 if(audioInBuffer == 0 || audioOutBuffer == 0) {
giuliomoro@16 454 rt_printf("Error: couldn't allocate audio buffers\n");
andrewm@0 455 return;
andrewm@0 456 }
giuliomoro@23 457 if(analogInBuffer == 0 || analogOutBuffer == 0 || lastAnalogOutFrame == 0) {
giuliomoro@19 458 rt_printf("Error: couldn't allocate analog buffers\n");
giuliomoro@16 459 return;
giuliomoro@16 460 }
giuliomoro@23 461 if(lastDigitalBuffer == 0) {
giuliomoro@19 462 rt_printf("Error: couldn't allocate digital buffers\n");
giuliomoro@16 463 return;
giuliomoro@23 464 }
giuliomoro@38 465
giuliomoro@41 466 if(digital_enabled){
giuliomoro@41 467 for(unsigned int n=0; n<digital_buffer_frames; n++){ //initialize lastDigitalFrames to all inputs
giuliomoro@41 468 lastDigitalBuffer[n]= 0x0000ffff;
giuliomoro@41 469 }
giuliomoro@41 470 }
giuliomoro@41 471 int count=0;
giuliomoro@41 472 sleepTimer.setNumFrames(audio_buffer_frames);
giuliomoro@41 473 renderTimer.setNumFrames(audio_buffer_frames);
andrewm@0 474 while(!gShouldStop) {
andrewm@0 475 // Wait for PRU to move to buffer 1
giuliomoro@41 476 sleepTimer.start();
andrewm@0 477 while(pru_buffer_comm[PRU_CURRENT_BUFFER] == 0 && !gShouldStop) {
andrewm@0 478 rt_task_sleep(sleepTime);
andrewm@0 479 }
giuliomoro@41 480 sleepTimer.split();
giuliomoro@41 481
andrewm@0 482 if(gShouldStop)
andrewm@0 483 break;
giuliomoro@41 484 renderTimer.start();
andrewm@0 485 if(xenomai_gpio != 0) {
andrewm@0 486 // Set the test pin high
andrewm@0 487 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK;
andrewm@0 488 }
andrewm@0 489
andrewm@0 490 // Render from/to buffer 0
andrewm@0 491
andrewm@0 492 // Convert short (16-bit) samples to float
andrewm@0 493 for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++)
andrewm@0 494 audioInBuffer[n] = (float)pru_buffer_audio_adc[n] / 32768.0;
giuliomoro@16 495 if(spi_enabled) {
giuliomoro@16 496 for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++)
giuliomoro@19 497 analogInBuffer[n] = (float)pru_buffer_spi_adc[n] / 65536.0;
giuliomoro@23 498 //initialize the output buffer with the values that were in the last frame of the previous output
giuliomoro@23 499 for(int n = 0; n < spi_num_channels; n++){
giuliomoro@23 500 for(unsigned int j = 0; j < spi_buffer_frames; j++){
giuliomoro@41 501 analogOutBuffer[j*spi_num_channels + n] = lastAnalogOutFrame[n];
giuliomoro@23 502 }
giuliomoro@23 503 }
giuliomoro@41 504
giuliomoro@23 505 //use past digital values to initialize the array properly.
giuliomoro@23 506 //For each frame:
giuliomoro@23 507 //- pins previously set as outputs will keep the output value they had in the last frame of the previous buffer,
giuliomoro@16 508 //- pins previously set as inputs will carry the newly read input value
giuliomoro@19 509 if(digital_enabled){
giuliomoro@19 510 for(unsigned int n = 0; n < digital_buffer_frames; n++){
giuliomoro@23 511 uint16_t inputs=lastDigitalBuffer[n]&0xffff;//half-word, has 1 for inputs and 0 for outputs
giuliomoro@16 512 // printf("inputs: 0x%x\n",inputs);
giuliomoro@16 513 uint16_t outputs=~inputs; //half-word has 1 for outputs and 0 for inputs;
giuliomoro@23 514 digitalBuffer0[n]=(lastDigitalBuffer[digital_buffer_frames-1]&(outputs<<16))| //keep output values set in the last frame of the previous buffer
giuliomoro@19 515 (digitalBuffer0[n]&(inputs<<16)) | //inputs from current digitalBuffer0[n];
giuliomoro@23 516 (lastDigitalBuffer[n]&(inputs)); //keep pin configuration from previous digitalBuffer1[n]
giuliomoro@19 517 // digitalBuffer0[n]=digitalBufferTemp[n]; //ignores inputs
giuliomoro@16 518 }
giuliomoro@16 519 }
giuliomoro@19 520 render(spi_buffer_frames, digital_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer,
giuliomoro@19 521 analogInBuffer, analogOutBuffer, digitalBuffer0);
giuliomoro@23 522 //remember the content of the lastAnalogOutFrame
giuliomoro@23 523 for(int n = 0; n < spi_num_channels; n++){
giuliomoro@23 524 lastAnalogOutFrame[n] = analogOutBuffer[spi_buffer_frames*(spi_buffer_frames-1) + n];
giuliomoro@23 525 }
giuliomoro@16 526 for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++) {
giuliomoro@19 527 int out = analogOutBuffer[n] * 65536.0;
giuliomoro@16 528 if(out < 0) out = 0;
giuliomoro@16 529 else if(out > 65535) out = 65535;
giuliomoro@16 530 pru_buffer_spi_dac[n] = (uint16_t)out;
giuliomoro@16 531 }
giuliomoro@19 532 if(digital_enabled){ // keep track of past digital values
giuliomoro@19 533 for(unsigned int n = 0; n < digital_buffer_frames; n++){
giuliomoro@23 534 lastDigitalBuffer[n]=digitalBuffer0[n];
giuliomoro@16 535 }
giuliomoro@16 536 }
giuliomoro@16 537 }
andrewm@0 538 else
giuliomoro@19 539 render(0, 0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0, 0); // we still pass digitalBuffer, just it is unused
giuliomoro@16 540 // Convert float back to short
andrewm@0 541 for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) {
andrewm@0 542 int out = audioOutBuffer[n] * 32768.0;
andrewm@0 543 if(out < -32768) out = -32768;
andrewm@0 544 else if(out > 32767) out = 32767;
andrewm@0 545 pru_buffer_audio_dac[n] = (int16_t)out;
andrewm@0 546 }
andrewm@0 547
andrewm@0 548 if(xenomai_gpio != 0) {
andrewm@0 549 // Set the test pin high
andrewm@0 550 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK;
andrewm@0 551 }
giuliomoro@41 552 renderTimer.split();
andrewm@0 553 // Wait for PRU to move to buffer 0
giuliomoro@41 554 sleepTimer.start();
andrewm@0 555 while(pru_buffer_comm[PRU_CURRENT_BUFFER] != 0 && !gShouldStop) {
andrewm@0 556 rt_task_sleep(sleepTime);
andrewm@0 557 }
giuliomoro@41 558 sleepTimer.split();
andrewm@0 559 if(gShouldStop)
andrewm@0 560 break;
andrewm@0 561
giuliomoro@41 562 renderTimer.start();
andrewm@0 563 if(xenomai_gpio != 0) {
andrewm@0 564 // Set the test pin high
andrewm@0 565 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK;
andrewm@0 566 }
andrewm@0 567
andrewm@0 568 // Render from/to buffer 1
andrewm@0 569
andrewm@0 570 // Convert short (16-bit) samples to float
andrewm@0 571 for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++)
andrewm@0 572 audioInBuffer[n] = (float)pru_buffer_audio_adc[n + audio_buffer_frames * 2] / 32768.0;
andrewm@0 573
giuliomoro@16 574 if(spi_enabled) {
giuliomoro@23 575 //convert input values TODO: move to PRU
giuliomoro@23 576 for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++){
giuliomoro@19 577 analogInBuffer[n] = (float)pru_buffer_spi_adc[n + spi_buffer_frames * spi_num_channels] / 65536.0;
giuliomoro@23 578 }
giuliomoro@23 579 //initialize the output buffer with the values that were in the last frame of the previous output
giuliomoro@23 580 for(int n = 0; n < spi_num_channels; n++){
giuliomoro@23 581 for(unsigned int j = 0; j < spi_buffer_frames; j++){
giuliomoro@41 582 analogOutBuffer[j*spi_num_channels + n] = lastAnalogOutFrame[n];
giuliomoro@23 583 }
giuliomoro@23 584 }
giuliomoro@19 585 if(digital_enabled){
giuliomoro@23 586 for(unsigned int n = 0; n < digital_buffer_frames; n++){
giuliomoro@23 587 uint16_t inputs=lastDigitalBuffer[n]&0xffff;//half-word, has 1 for inputs and 0 for outputs
giuliomoro@16 588 uint16_t outputs=~inputs; //half-word has 1 for outputs and one for inputs;
giuliomoro@23 589 digitalBuffer1[n]=(lastDigitalBuffer[digital_buffer_frames-1]&(outputs<<16))| //keep output values set in the last frame of the previous buffer
giuliomoro@19 590 (digitalBuffer1[n]&(inputs<<16)) | //inputs from current digitalBuffer1[n];
giuliomoro@23 591 (lastDigitalBuffer[n]&(inputs)); //keep pin configuration from previous digitalBuffer1[n]
giuliomoro@19 592 // digitalBuffer1[n]=digitalBufferTemp[n]; //ignores inputs
giuliomoro@16 593 }
giuliomoro@16 594 }
giuliomoro@19 595 render(spi_buffer_frames, digital_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer,
giuliomoro@19 596 analogInBuffer, analogOutBuffer, digitalBuffer1);
giuliomoro@23 597 //remember the content of the lastAnalogOutFrame
giuliomoro@23 598 for(int n = 0; n < spi_num_channels; n++){
giuliomoro@23 599 lastAnalogOutFrame[n] = analogOutBuffer[spi_buffer_frames*(spi_buffer_frames-1) + n];
giuliomoro@23 600 }
giuliomoro@23 601
giuliomoro@16 602 for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++) {
giuliomoro@19 603 int out = analogOutBuffer[n] * 65536.0;
giuliomoro@16 604 if(out < 0) out = 0;
giuliomoro@16 605 else if(out > 65535) out = 65535;
giuliomoro@16 606 pru_buffer_spi_dac[n + spi_buffer_frames * spi_num_channels] = (uint16_t)out;
giuliomoro@16 607 }
giuliomoro@19 608 if(digital_enabled){ // keep track of past digital values
giuliomoro@19 609 for(unsigned int n = 0; n < digital_buffer_frames; n++){
giuliomoro@23 610 lastDigitalBuffer[n]=digitalBuffer1[n];
giuliomoro@16 611 }
giuliomoro@16 612 }
giuliomoro@16 613 }
andrewm@0 614 else
giuliomoro@19 615 render(0, 0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0, 0); // we still pass digitalBuffer, just it is unused
andrewm@0 616
andrewm@0 617 // Convert float back to short
andrewm@0 618 for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) {
andrewm@0 619 int out = audioOutBuffer[n] * 32768.0;
andrewm@0 620 if(out < -32768) out = -32768;
andrewm@0 621 else if(out > 32767) out = 32767;
andrewm@0 622 pru_buffer_audio_dac[n + audio_buffer_frames * 2] = (int16_t)out;
andrewm@0 623 }
andrewm@0 624
andrewm@0 625 if(xenomai_gpio != 0) {
andrewm@0 626 // Set the test pin high
andrewm@0 627 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK;
andrewm@0 628 }
giuliomoro@41 629 renderTimer.split();
giuliomoro@41 630 count+=audio_buffer_frames;
giuliomoro@41 631 if((count&32767)==0){
giuliomoro@41 632 scheduleAuxiliaryTask(gPRU->printIntervalsTask);
giuliomoro@41 633 }
andrewm@0 634 }
andrewm@0 635
andrewm@0 636 // Tell PRU to stop
andrewm@0 637 pru_buffer_comm[PRU_SHOULD_STOP] = 1;
giuliomoro@19 638 free(analogOutBuffer);
giuliomoro@41 639 free(analogInBuffer);
giuliomoro@41 640 free(audioOutBuffer);
andrewm@0 641 free(audioInBuffer);
giuliomoro@23 642 free(lastAnalogOutFrame);
giuliomoro@23 643 free(lastDigitalBuffer);
andrewm@0 644 }
andrewm@0 645
andrewm@0 646 // Wait for an interrupt from the PRU indicate it is finished
andrewm@0 647 void PRU::waitForFinish()
andrewm@0 648 {
andrewm@0 649 if(!running)
andrewm@0 650 return;
andrewm@15 651 prussdrv_pru_wait_event (pru_number == 0 ? PRU_EVTOUT_0 : PRU_EVTOUT_1);
andrewm@15 652 if(pru_number == 0)
andrewm@15 653 prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
andrewm@15 654 else
andrewm@15 655 prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_ARM_INTERRUPT);
andrewm@0 656 }
andrewm@0 657
andrewm@0 658 // Turn off the PRU when done
andrewm@0 659 void PRU::disable()
andrewm@0 660 {
andrewm@0 661 /* Disable PRU and close memory mapping*/
andrewm@0 662 prussdrv_pru_disable(pru_number);
andrewm@0 663 prussdrv_exit();
andrewm@0 664 running = false;
andrewm@0 665 }
andrewm@0 666
andrewm@0 667 // Debugging
andrewm@0 668 void PRU::setGPIOTestPin()
andrewm@0 669 {
andrewm@0 670 if(!xenomai_gpio)
andrewm@0 671 return;
andrewm@0 672 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN2_MASK;
andrewm@0 673 }
andrewm@0 674
andrewm@0 675 void PRU::clearGPIOTestPin()
andrewm@0 676 {
andrewm@0 677 if(!xenomai_gpio)
andrewm@0 678 return;
andrewm@0 679 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN2_MASK;
andrewm@0 680 }