diff core/PRU.cpp @ 108:3068421c0737 ultra-staging

Merged default into ultra-staging
author Giulio Moro <giuliomoro@yahoo.it>
date Tue, 18 Aug 2015 00:35:15 +0100
parents 4255ecbb9bec 92145ba7aabf
children
line wrap: on
line diff
--- a/core/PRU.cpp	Mon Jun 08 01:07:48 2015 +0100
+++ b/core/PRU.cpp	Tue Aug 18 00:35:15 2015 +0100
@@ -18,9 +18,8 @@
 #include "../include/pruss_intc_mapping.h"
 #include "../include/digital_gpio_mapping.h"
 #include "../include/GPIOcontrol.h"
-#include "../include/render.h"
+#include "../include/BeagleRT.h"
 #include "../include/pru_rtaudio_bin.h"
-#include "../include/intervals.h"
 
 #include <iostream>
 #include <stdlib.h>
@@ -100,15 +99,17 @@
 const unsigned int PRU::kPruGPIOTestPin2 = 31;	// GPIO0(31); P9-13
 const unsigned int PRU::kPruGPIOTestPin3 = 26;	// GPIO0(26); P8-14
 
-//extern int gShouldStop;
+extern bool gShouldStop;
 extern int gRTAudioVerbose;
-extern PRU *gPRU;
 
 // Constructor: specify a PRU number (0 or 1)
-PRU::PRU()
-:  renderTimer(100,0,44100.0,"renderTimer"), sleepTimer(100,0,44100.0,"sleepTimer"),
-  pru_number(0), running(false), spi_enabled(false), gpio_enabled(false), led_enabled(false),
-  gpio_test_pin_enabled(false), spi_num_channels(0), xenomai_gpio_fd(-1), xenomai_gpio(0)
+PRU::PRU(BeagleRTContext *input_context)
+: context(input_context), pru_number(0), running(false), analog_enabled(false),
+  digital_enabled(false), gpio_enabled(false), led_enabled(false),
+  gpio_test_pin_enabled(false),
+  pru_buffer_comm(0), pru_buffer_spi_dac(0), pru_buffer_spi_adc(0),
+  pru_buffer_digital(0), pru_buffer_audio_dac(0), pru_buffer_audio_adc(0),
+  xenomai_gpio_fd(-1), xenomai_gpio(0)
 {
 
 }
@@ -130,9 +131,9 @@
 // viewed on a scope. If include_led is set,
 // user LED 3 on the BBB is taken over by the PRU
 // to indicate activity
-int PRU::prepareGPIO(int use_spi, int use_digital, int include_test_pin, int include_led)
+int PRU::prepareGPIO(int include_test_pin, int include_led)
 {
-	if(use_spi) {
+	if(context->analogFrames != 0) {
 		// Prepare DAC CS/ pin: output, high to begin
 		if(gpio_export(kPruGPIODACSyncPin)) {
 			if(gRTAudioVerbose)
@@ -165,12 +166,11 @@
 			return -1;
 		}
 
-		spi_enabled = true;
+		analog_enabled = true;
 	}
 
-	if(use_digital){
-		printf("gNumDigitalChannels: %d;\n",gNumDigitalChannels);
-		for(int i=0; i<gNumDigitalChannels; i++){
+	if(context->digitalFrames != 0){
+		for(unsigned int i = 0; i < context->digitalChannels; i++){
 			if(gpio_export(digitalPins[i])) {
 				if(gRTAudioVerbose)
 					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
@@ -181,7 +181,7 @@
 				return -1;
 			}
 		}
-		digital_enabled=true;
+		digital_enabled = true;
 	}
 
 	if(include_test_pin) {
@@ -249,12 +249,12 @@
 {
 	if(!gpio_enabled)
 		return;
-	if(spi_enabled) {
+	if(analog_enabled) {
 		gpio_unexport(kPruGPIODACSyncPin);
 		gpio_unexport(kPruGPIOADCSyncPin);
 	}
 	if(digital_enabled){
-		for(int i=0; i<gNumDigitalChannels; i++){
+		for(unsigned int i = 0; i < context->digitalChannels; i++){
 			gpio_unexport(digitalPins[i]);
 		}
 	}
@@ -284,20 +284,13 @@
 
 	pru_number = pru_num;
 
-	/* Set number of SPI ADC / DAC channels to use. This implicitly
-	 * also determines the sample rate relative to the audio clock
-	 * (half audio clock for 8 channels, full audio clock for 4,
-	 * double audio clock for 2)
-	 */
-	spi_num_channels = spi_channels;
-
     /* Initialize structure used by prussdrv_pruintc_intc   */
     /* PRUSS_INTC_INITDATA is found in pruss_intc_mapping.h */
     tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
 
     /* Allocate and initialize memory */
     prussdrv_init();
-    if(prussdrv_open(pru_number == 0 ? PRU_EVTOUT_0 : PRU_EVTOUT_1)) {
+    if(prussdrv_open(PRU_EVTOUT_0)) {
     	rt_printf("Failed to open PRU driver\n");
     	return 1;
     }
@@ -305,24 +298,20 @@
     /* Map PRU's INTC */
     prussdrv_pruintc_init(&pruss_intc_initdata);
 
-    spi_buffer_frames = frames_per_buffer;
-    audio_buffer_frames = spi_buffer_frames * spi_num_channels / 4;
-    digital_buffer_frames = audio_buffer_frames;
-
     /* Map PRU memory to pointers */
 	prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem);
     pru_buffer_comm = (uint32_t *)&pruMem[PRU_MEM_COMM_OFFSET/sizeof(uint32_t)];
 	pru_buffer_audio_dac = (int16_t *)&pruMem[PRU_MEM_MCASP_OFFSET/sizeof(uint32_t)];
 
 	/* ADC memory starts 2(ch)*2(buffers)*bufsize samples later */
-	pru_buffer_audio_adc = &pru_buffer_audio_dac[4 * audio_buffer_frames];
+	pru_buffer_audio_adc = &pru_buffer_audio_dac[4 * context->audioFrames];
 
-	if(spi_enabled) {
+	if(analog_enabled) {
 		prussdrv_map_prumem (pru_number == 0 ? PRUSS0_PRU0_DATARAM : PRUSS0_PRU1_DATARAM, (void **)&pruMem);
 		pru_buffer_spi_dac = (uint16_t *)&pruMem[PRU_MEM_DAC_OFFSET/sizeof(uint32_t)];
 
 		/* ADC memory starts after N(ch)*2(buffers)*bufsize samples */
-		pru_buffer_spi_adc = &pru_buffer_spi_dac[2 * spi_num_channels * spi_buffer_frames];
+		pru_buffer_spi_adc = &pru_buffer_spi_dac[2 * context->analogChannels * context->analogFrames];
 	}
 	else {
 		pru_buffer_spi_dac = pru_buffer_spi_adc = 0;
@@ -335,10 +324,11 @@
 	else {
 		pru_buffer_digital = 0;
 	}
+
     /* Set up flags */
     pru_buffer_comm[PRU_SHOULD_STOP] = 0;
     pru_buffer_comm[PRU_CURRENT_BUFFER] = 0;
-    pru_buffer_comm[PRU_BUFFER_FRAMES] = spi_buffer_frames;
+    pru_buffer_comm[PRU_BUFFER_FRAMES] = context->analogFrames;
     pru_buffer_comm[PRU_SHOULD_SYNC] = 0;
     pru_buffer_comm[PRU_SYNC_ADDRESS] = 0;
     pru_buffer_comm[PRU_SYNC_PIN_MASK] = 0;
@@ -350,9 +340,9 @@
     	pru_buffer_comm[PRU_LED_ADDRESS] = 0;
     	pru_buffer_comm[PRU_LED_PIN_MASK] = 0;
     }
-    if(spi_enabled) {
+    if(analog_enabled) {
     	pru_buffer_comm[PRU_USE_SPI] = 1;
-    	pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = spi_num_channels;
+    	pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = context->analogChannels;
     }
     else {
     	pru_buffer_comm[PRU_USE_SPI] = 0;
@@ -370,7 +360,7 @@
     /* Clear ADC and DAC memory.*/
     //TODO: this initialisation should only address the memory effectively used by these buffers, i.e.:depend on the number of frames
     //  (otherwise might cause issues if we move memory locations later on)
-    if(spi_enabled) {
+    if(analog_enabled) {
 		for(int i = 0; i < PRU_MEM_DAC_LENGTH / 2; i++)
 			pru_buffer_spi_dac[i] = 0;
 		if(digital_enabled){
@@ -380,7 +370,7 @@
     }
 	for(int i = 0; i < PRU_MEM_MCASP_LENGTH / 2; i++)
 		pru_buffer_audio_dac[i] = 0;
-//TODO: maybe the lines below are to be deleted, as we removed the test code from pru_rtaudio.p ?
+
 	/* If using GPIO test pin for Xenomai (for debugging), initialise the pointer now */
 	if(xenomai_test_pin && xenomai_gpio_fd < 0) {
 		xenomai_gpio_fd = open("/dev/mem", O_RDWR);
@@ -397,6 +387,46 @@
 		}
 	}
 
+	// Allocate audio buffers
+	context->audioIn = (float *)malloc(2 * context->audioFrames * sizeof(float));
+	context->audioOut = (float *)malloc(2 * context->audioFrames * sizeof(float));
+	if(context->audioIn == 0 || context->audioOut == 0) {
+		rt_printf("Error: couldn't allocate audio buffers\n");
+		return 1;
+	}
+
+	// Allocate analog buffers
+	if(analog_enabled) {
+		context->analogIn = (float *)malloc(context->analogChannels * context->analogFrames * sizeof(float));
+		context->analogOut = (float *)malloc(context->analogChannels * context->analogFrames * sizeof(float));
+		last_analog_out_frame = (float *)malloc(context->analogChannels * sizeof(float));
+
+		if(context->analogIn == 0 || context->analogOut == 0 || last_analog_out_frame == 0) {
+			rt_printf("Error: couldn't allocate analog buffers\n");
+			return 1;
+		}
+
+		memset(last_analog_out_frame, 0, context->analogChannels * sizeof(float));
+	}
+
+	// Allocate digital buffers
+	digital_buffer0 = pru_buffer_digital;
+	digital_buffer1 = pru_buffer_digital + MEM_DIGITAL_BUFFER1_OFFSET / sizeof(uint32_t);
+	if(digital_enabled) {
+		last_digital_buffer = (uint32_t *)malloc(context->digitalFrames * sizeof(uint32_t)); //temp buffer to hold previous states
+		if(last_digital_buffer == 0) {
+			rt_printf("Error: couldn't allocate digital buffers\n");
+			return 1;
+		}
+
+		for(unsigned int n = 0; n < context->digitalFrames; n++){
+			// Initialize lastDigitalFrames to all inputs
+			last_digital_buffer[n] = 0x0000ffff;
+		}
+	}
+
+	context->digital = digital_buffer0;
+
 	return 0;
 }
 
@@ -404,10 +434,8 @@
 int PRU::start(char * const filename)
 {
 	/* Clear any old interrupt */
-	if(pru_number == 0)
-		prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
-	else
-		prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_ARM_INTERRUPT);
+	prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
+
 	/* Load and execute binary on PRU */
 	if(filename[0] == '\0') { //if the string is empty, load the embedded code
 		if(gRTAudioVerbose)
@@ -428,219 +456,232 @@
     running = true;
     return 0;
 }
-uint32_t empty[1024]={0x0};
 
 // Main loop to read and write data from/to PRU
-void PRU::loop()
+void PRU::loop(RT_INTR *pru_interrupt, void *userData)
 {
+#ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS
+	RTIME irqTimeout = PRU_SAMPLE_INTERVAL_NS * 1024;	// Timeout for PRU interrupt: about 10ms, much longer than any expected period
+#else
 	// Polling interval is 1/4 of the period
-	RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * (spi_num_channels / 2) * spi_buffer_frames / 4;
-	float *audioInBuffer, *audioOutBuffer;
-	float *analogInBuffer, *analogOutBuffer, *lastAnalogOutFrame;
-	uint32_t *digitalBuffer0, *digitalBuffer1, *lastDigitalBuffer;
-	audioInBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float));
-	audioOutBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float));
-	analogInBuffer = (float *)malloc(spi_num_channels * spi_buffer_frames * sizeof(float));
-	analogOutBuffer = (float *)malloc(spi_num_channels * spi_buffer_frames * sizeof(float));
-	lastAnalogOutFrame = (float *)malloc(spi_num_channels * sizeof(float));
-	digitalBuffer0 = pru_buffer_digital; 
-	digitalBuffer1 = pru_buffer_digital+MEM_DIGITAL_BUFFER1_OFFSET/sizeof(uint32_t);
-    digital_buffer_frames = digital_enabled ? audio_buffer_frames : 0; //TODO: find a more elegant solution for when the digital is disabled e.g.:
-    																	// - embed in the digitalWrite/Read macros a check whether digital is enabled
-    																	// - allocate some memory in ARM just to allow render() to run regardless.
-    																	//     in this case it can be digitalBuffer0 == digitalBuffer1
-    lastDigitalBuffer = (uint32_t *)malloc(digital_buffer_frames*sizeof(uint32_t)); //temp buffer to hold previous states
-	if(audioInBuffer == 0 || audioOutBuffer == 0) {
-		rt_printf("Error: couldn't allocate audio buffers\n");
-		return;
-	}
-	if(analogInBuffer == 0 || analogOutBuffer == 0 || lastAnalogOutFrame == 0) {
-		rt_printf("Error: couldn't allocate analog buffers\n");
-		return;
-	}
-	if(lastDigitalBuffer == 0) {
-		rt_printf("Error: couldn't allocate digital buffers\n");
-		return;
+	RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * (context->analogChannels / 2) * context->analogFrames / 4;
+#endif
+
+	uint32_t pru_audio_offset, pru_spi_offset;
+
+	// Before starting, look at the last state of the analog and digital outputs which might
+	// have been changed by the user during the setup() function. This lets us start with pin
+	// directions and output values at something other than defaults.
+
+	if(analog_enabled) {
+		if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) {
+			// Remember the content of the last_analog_out_frame
+			for(unsigned int ch = 0; ch < context->analogChannels; ch++){
+				last_analog_out_frame[ch] = context->analogOut[context->analogChannels * (context->analogFrames - 1) + ch];
+			}
+		}
 	}
 
-    if(digital_enabled){
-		for(unsigned int n=0; n<digital_buffer_frames; n++){ //initialize lastDigitalFrames to all inputs
-			lastDigitalBuffer[n]= 0x0000ffff;
+	if(digital_enabled) {
+		for(unsigned int n = 0; n < context->digitalFrames; n++){
+			last_digital_buffer[n] = context->digital[n];
 		}
-    }
-	int count=0;
-	sleepTimer.setNumFrames(audio_buffer_frames);
-	renderTimer.setNumFrames(audio_buffer_frames);
+	}
+
+	// TESTING
+	// uint32_t testCount = 0;
+	// RTIME startTime = rt_timer_read();
+
+#ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS
+	int result;
+#else
+	// Which buffer the PRU was last processing
+	uint32_t lastPRUBuffer = 0;
+#endif
+
 	while(!gShouldStop) {
-		// Wait for PRU to move to buffer 1
-		sleepTimer.start();
-		while(pru_buffer_comm[PRU_CURRENT_BUFFER] == 0 && !gShouldStop) {
+#ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS
+		// Wait for PRU to move to change buffers;
+		// PRU will send an interrupts which we wait for
+		rt_intr_enable(pru_interrupt);
+		while(!gShouldStop) {
+			result = rt_intr_wait(pru_interrupt, irqTimeout);
+			if(result >= 0)
+				break;
+			else if(result == -ETIMEDOUT)
+				rt_printf("Warning: PRU timeout!\n");
+			else {
+				rt_printf("Error: wait for interrupt failed (%d)\n", result);
+				gShouldStop = 1;
+			}
+		}
+
+		// Clear pending PRU interrupt
+		prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_ARM_INTERRUPT);
+#else
+		// Poll
+		while(pru_buffer_comm[PRU_CURRENT_BUFFER] == lastPRUBuffer && !gShouldStop) {
 			rt_task_sleep(sleepTime);
 		}
-		sleepTimer.split();
+
+		lastPRUBuffer = pru_buffer_comm[PRU_CURRENT_BUFFER];
+#endif
 
 		if(gShouldStop)
 			break;
-		renderTimer.start();
+
+		// Check which buffer we're on-- will have been set right
+		// before the interrupt was asserted
+		if(pru_buffer_comm[PRU_CURRENT_BUFFER] == 1) {
+			// PRU is on buffer 1. We read and write to buffer 0
+			pru_audio_offset = 0;
+			pru_spi_offset = 0;
+			if(digital_enabled)
+				context->digital = digital_buffer0;
+		}
+		else {
+			// PRU is on buffer 0. We read and write to buffer 1
+			pru_audio_offset = context->audioFrames * 2;
+			pru_spi_offset = context->analogFrames * context->analogChannels;
+			if(digital_enabled)
+				context->digital = digital_buffer1;
+		}
+
+		// FIXME: some sort of margin is needed here to prevent the audio
+		// code from completely eating the Linux system
+		// testCount++;
+		//rt_task_sleep(sleepTime*4);
+		//rt_task_sleep(sleepTime/4);
+
 		if(xenomai_gpio != 0) {
 			// Set the test pin high
 			xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK;
 		}
 
-		// Render from/to buffer 0
+		// Convert short (16-bit) samples to float
+		// TODO: NEON
+		for(unsigned int n = 0; n < 2 * context->audioFrames; n++)
+			context->audioIn[n] = (float)pru_buffer_audio_adc[n + pru_audio_offset] / 32768.0;
 
-		// Convert short (16-bit) samples to float
-		for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++)
-			audioInBuffer[n] = (float)pru_buffer_audio_adc[n] / 32768.0;
-		if(spi_enabled) {
-			for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++)
-				analogInBuffer[n] = (float)pru_buffer_spi_adc[n] / 65536.0;
-			//initialize the output buffer with the values that were in the last frame of the previous output
-			for(int n = 0; n < spi_num_channels; n++){
-				for(unsigned int j = 0; j < spi_buffer_frames; j++){
-					analogOutBuffer[j*spi_num_channels + n] = lastAnalogOutFrame[n];
+		if(analog_enabled) {
+			// TODO: NEON
+			for(unsigned int n = 0; n < context->analogChannels * context->analogFrames; n++)
+				context->analogIn[n] = (float)pru_buffer_spi_adc[n + pru_spi_offset] / 65536.0;
+
+			if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) {
+				// Initialize the output buffer with the values that were in the last frame of the previous output
+				for(unsigned int ch = 0; ch < context->analogChannels; ch++){
+					for(unsigned int n = 0; n < context->analogFrames; n++){
+						context->analogOut[n * context->analogChannels + ch] = last_analog_out_frame[ch];
+					}
+				}
+			}
+			else {
+				// Outputs are 0 unless set otherwise
+				memset(context->analogOut, 0, context->analogChannels * context->analogFrames * sizeof(float));
+			}
+		}
+
+        if(digital_enabled){
+			// Use past digital values to initialize the array properly.
+			// For each frame:
+			// - pins previously set as outputs will keep the output value they had in the last frame of the previous buffer,
+			// - pins previously set as inputs will carry the newly read input value
+
+			for(unsigned int n = 0; n < context->digitalFrames; n++){
+				uint16_t inputs = last_digital_buffer[n] & 0xffff; // half-word, has 1 for inputs and 0 for outputs
+
+				uint16_t outputs = ~inputs; // half-word has 1 for outputs and 0 for inputs;
+				context->digital[n] = (last_digital_buffer[context->digitalFrames - 1] & (outputs << 16)) | // keep output values set in the last frame of the previous buffer
+									   (context->digital[n] & (inputs << 16))   | // inputs from current context->digital[n];
+									   (last_digital_buffer[n] & (inputs));     // keep pin configuration from previous context->digital[n]
+//                    context->digital[n]=digitalBufferTemp[n]; //ignores inputs
+			}
+		}
+
+		// Call user render function
+        // ***********************
+		render(context, userData);
+		// ***********************
+
+		if(analog_enabled) {
+			if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) {
+				// Remember the content of the last_analog_out_frame
+				for(unsigned int ch = 0; ch < context->analogChannels; ch++){
+					last_analog_out_frame[ch] = context->analogOut[context->analogChannels * (context->analogFrames - 1) + ch];
 				}
 			}
 
-        //use past digital values to initialize the array properly.
-		//For each frame:
-        //- pins previously set as outputs will keep the output value they had in the last frame of the previous buffer,
-        //- pins previously set as inputs will carry the newly read input value
-            if(digital_enabled){
-                for(unsigned int n = 0; n < digital_buffer_frames; n++){ 
-                    uint16_t inputs=lastDigitalBuffer[n]&0xffff;//half-word, has 1 for inputs and 0 for outputs
-//                    printf("inputs: 0x%x\n",inputs);
-                    uint16_t outputs=~inputs; //half-word has 1 for outputs and 0 for inputs;
-                    digitalBuffer0[n]=(lastDigitalBuffer[digital_buffer_frames-1]&(outputs<<16))| //keep output values set in the last frame of the previous buffer
-                                         (digitalBuffer0[n]&(inputs<<16))   | //inputs from current digitalBuffer0[n];
-                                         (lastDigitalBuffer[n]&(inputs));     //keep pin configuration from previous digitalBuffer1[n]
-//                    digitalBuffer0[n]=digitalBufferTemp[n]; //ignores inputs
-                }
-            }
-			render(spi_buffer_frames, digital_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer,
-					analogInBuffer, analogOutBuffer, digitalBuffer0);
-			//remember the content of the lastAnalogOutFrame
-			for(int n = 0; n < spi_num_channels; n++){
-				lastAnalogOutFrame[n] = analogOutBuffer[spi_buffer_frames*(spi_buffer_frames-1) + n];
-			}
-			for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++) {
-				int out = analogOutBuffer[n] * 65536.0;
+			// Convert float back to short for SPI output
+			for(unsigned int n = 0; n < context->analogChannels * context->analogFrames; n++) {
+				int out = context->analogOut[n] * 65536.0;
 				if(out < 0) out = 0;
 				else if(out > 65535) out = 65535;
-				pru_buffer_spi_dac[n] = (uint16_t)out;
+				pru_buffer_spi_dac[n + pru_spi_offset] = (uint16_t)out;
 			}
-            if(digital_enabled){ // keep track of past digital values
-                for(unsigned int n = 0; n < digital_buffer_frames; n++){ 
-                    lastDigitalBuffer[n]=digitalBuffer0[n];
-                }
-            }
 		}
-		else
-			render(0, 0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0, 0); // we still pass digitalBuffer, just it is unused
-        // Convert float back to short
-		for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) {
-			int out = audioOutBuffer[n] * 32768.0;
+
+		if(digital_enabled) { // keep track of past digital values
+			for(unsigned int n = 0; n < context->digitalFrames; n++){
+				last_digital_buffer[n] = context->digital[n];
+			}
+		}
+
+        // Convert float back to short for audio
+		// TODO: NEON
+		for(unsigned int n = 0; n < 2 * context->audioFrames; n++) {
+			int out = context->audioOut[n] * 32768.0;
 			if(out < -32768) out = -32768;
 			else if(out > 32767) out = 32767;
-			pru_buffer_audio_dac[n] = (int16_t)out;
+			pru_buffer_audio_dac[n + pru_audio_offset] = (int16_t)out;
 		}
 
+		// Increment total number of samples that have elapsed
+		context->audioSampleCount += context->audioFrames;
+
 		if(xenomai_gpio != 0) {
 			// Set the test pin high
 			xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK;
 		}
-		renderTimer.split();
-		// Wait for PRU to move to buffer 0
-		sleepTimer.start();
-		while(pru_buffer_comm[PRU_CURRENT_BUFFER] != 0 && !gShouldStop) {
-			rt_task_sleep(sleepTime);
-		}
-		sleepTimer.split();
-		if(gShouldStop)
-			break;
 
-		renderTimer.start();
-		if(xenomai_gpio != 0) {
-			// Set the test pin high
-			xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK;
-		}
+		// FIXME: TESTING!!
+		// if(testCount > 100000)
+		//	break;
+	}
 
-		// Render from/to buffer 1
+#ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS
+	// Turn off the interrupt for the PRU if it isn't already off
+	rt_intr_disable(pru_interrupt);
+#endif
 
-		// Convert short (16-bit) samples to float
-		for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++)
-			audioInBuffer[n] = (float)pru_buffer_audio_adc[n + audio_buffer_frames * 2] / 32768.0;
-
-		if(spi_enabled) {
-			 //convert input values TODO: move to PRU
-			for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++){
-				analogInBuffer[n] = (float)pru_buffer_spi_adc[n + spi_buffer_frames * spi_num_channels] / 65536.0;
-			}
-			//initialize the output buffer with the values that were in the last frame of the previous output
-			for(int n = 0; n < spi_num_channels; n++){
-				for(unsigned int j = 0; j < spi_buffer_frames; j++){
-					analogOutBuffer[j*spi_num_channels + n] = lastAnalogOutFrame[n];
-				}
-			}
-            if(digital_enabled){
-                for(unsigned int n = 0; n < digital_buffer_frames; n++){
-                    uint16_t inputs=lastDigitalBuffer[n]&0xffff;//half-word, has 1 for inputs and 0 for outputs
-                    uint16_t outputs=~inputs; //half-word has 1 for outputs and one for inputs;
-                    digitalBuffer1[n]=(lastDigitalBuffer[digital_buffer_frames-1]&(outputs<<16))| //keep output values set in the last frame of the  previous buffer
-                                         (digitalBuffer1[n]&(inputs<<16))   | //inputs from current digitalBuffer1[n];
-                                         (lastDigitalBuffer[n]&(inputs));     //keep pin configuration from previous digitalBuffer1[n]
-//                    digitalBuffer1[n]=digitalBufferTemp[n]; //ignores inputs
-                }
-            }
-			render(spi_buffer_frames, digital_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer,
-					analogInBuffer, analogOutBuffer, digitalBuffer1);
-			//remember the content of the lastAnalogOutFrame
-			for(int n = 0; n < spi_num_channels; n++){
-				lastAnalogOutFrame[n] = analogOutBuffer[spi_buffer_frames*(spi_buffer_frames-1) + n];
-			}
-
-			for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++) {
-				int out = analogOutBuffer[n] * 65536.0;
-				if(out < 0) out = 0;
-				else if(out > 65535) out = 65535;
-				pru_buffer_spi_dac[n + spi_buffer_frames * spi_num_channels] = (uint16_t)out;
-			}
-            if(digital_enabled){ // keep track of past digital values
-                for(unsigned int n = 0; n < digital_buffer_frames; n++){ 
-                    lastDigitalBuffer[n]=digitalBuffer1[n];
-                }
-            }
-		}
-		else
-			render(0, 0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0, 0); // we still pass digitalBuffer, just it is unused
-
-		// Convert float back to short
-		for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) {
-			int out = audioOutBuffer[n] * 32768.0;
-			if(out < -32768) out = -32768;
-			else if(out > 32767) out = 32767;
-			pru_buffer_audio_dac[n + audio_buffer_frames * 2] = (int16_t)out;
-		}
-
-		if(xenomai_gpio != 0) {
-			// Set the test pin high
-			xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK;
-		}
-		renderTimer.split();
-		count+=audio_buffer_frames;
-		if((count&32767)==0){
-			scheduleAuxiliaryTask(gPRU->printIntervalsTask);
-		}
-	}
+	// FIXME: TESTING
+	// RTIME endTime = rt_timer_read();
+	// RTIME diffTime = endTime - startTime;
+	// rt_printf("%d blocks elapsed in %f seconds, %f Hz block rate\n", testCount, ((float)diffTime / 1.0e9), (float)testCount / ((float)diffTime / 1.0e9));
 
 	// Tell PRU to stop
 	pru_buffer_comm[PRU_SHOULD_STOP] = 1;
-	free(analogOutBuffer);
-	free(analogInBuffer);
-	free(audioOutBuffer);
-	free(audioInBuffer);
-	free(lastAnalogOutFrame);
-    free(lastDigitalBuffer);
+
+	// Wait two buffer lengths for the PRU to finish
+	rt_task_sleep(PRU_SAMPLE_INTERVAL_NS * context->analogFrames * 4 * 2);
+
+	// Clean up after ourselves
+	free(context->audioIn);
+	free(context->audioOut);
+
+	if(analog_enabled) {
+		free(context->analogIn);
+		free(context->analogOut);
+		free(last_analog_out_frame);
+	}
+
+	if(digital_enabled) {
+		free(last_digital_buffer);
+	}
+
+	context->audioIn = context->audioOut = 0;
+	context->analogIn = context->analogOut = 0;
+	context->digital = 0;
 }
 
 // Wait for an interrupt from the PRU indicate it is finished
@@ -648,11 +689,8 @@
 {
 	if(!running)
 		return;
-    prussdrv_pru_wait_event (pru_number == 0 ? PRU_EVTOUT_0 : PRU_EVTOUT_1);
-	if(pru_number == 0)
-		prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
-	else
-		prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_ARM_INTERRUPT);
+    prussdrv_pru_wait_event (PRU_EVTOUT_0);
+	prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
 }
 
 // Turn off the PRU when done