changeset 21:0d80ff9e2227

Add float<->int macros to PRU code (still to integrate); formatting cleanups
author andrewm
date Sun, 08 Feb 2015 00:20:01 +0000
parents 901d205d1a3c
children 472e892c6e41
files pru_rtaudio.p
diffstat 1 files changed, 230 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/pru_rtaudio.p	Sat Feb 07 16:41:56 2015 +0000
+++ b/pru_rtaudio.p	Sun Feb 08 00:20:01 2015 +0000
@@ -205,6 +205,181 @@
 // r27, r28 used in macros
 #define reg_mcasp_addr		r29		// Base address for McASP
 
+// Convert float to 16-bit int, multiplying by 32768
+// Converts -1.0 to 1.0 to a full 16-bit range
+// input and output can safely be the same register
+.macro FLOAT_TO_INT16
+.mparam input, output
+      // int exponent = ((input >> 23) & 0xFF)
+      LSR r27, input, 23  // exponent goes in r27
+      AND r27, r27, 0xFF
+
+      // Actual exponent is 127 less than the above; below -15 we
+      // should return 0. So check if it is less than 112.
+      QBLE EXPONENT_GREQ_MINUS15, r27, 112
+      LDI output, 0
+      QBA FLOAT_TO_INT16_DONE
+EXPONENT_GREQ_MINUS15:	
+
+      // Next check if exponent is greater than or equal to 0 (i.e.
+      // 127 in our adjusted version. If so we return the max.
+      QBGT EXPONENT_LT_ZERO, r27, 127
+      QBBS NEGATIVE_MAX, input, 31  // Is sign negative?
+      LDI output, 32767		    // Max positive value
+      QBA FLOAT_TO_INT16_DONE
+NEGATIVE_MAX:
+      LDI output, 32768	            // Actually will be -32768 in signed
+      QBA FLOAT_TO_INT16_DONE
+EXPONENT_LT_ZERO:	
+
+      // Mask out the mantissa and shift
+      // int mantissa = (input & 0x7FFFFF) | (1 << 23)
+      MOV r28, 0x007FFFFF
+      AND r28, r28, input
+      SET r28, 23
+
+      // Shift right by -(exponent - 127 - 8) to produce an int
+      // after effectively multiplying by 2^15
+      // ---> (135 - exponent)
+      RSB r27, r27, 135
+      LSR r28, r28, r27
+
+      // Finally, check the sign bit and invert if needed
+      QBBS NEGATIVE_RESULT, input, 31
+      // Positive result: but might be 32768 so needs checking
+      LDI r27, 0x7FFF
+      MIN output, r27, r28	
+      QBA FLOAT_TO_INT16_DONE
+NEGATIVE_RESULT:
+      // Take negative: invert the bits and add 1
+      LDI r27, 0xFFFF
+      XOR r28, r28, r27
+      ADD r28, r28, 1
+      CLR output, r28, 16         // Clear carry bit if present
+FLOAT_TO_INT16_DONE:
+.endm
+
+
+// Convert float to 16-bit unsigned int, multiplying by 65536
+// Converts 0.0 to 1.0 to a full 16-bit range
+// input and output can safely be the same register
+.macro FLOAT_TO_UINT16
+.mparam input, output
+      QBBC NONNEGATIVE, input, 31   // Is sign negative?
+      LDI output, 0		    // All < 0 inputs produce 0 output
+      QBA FLOAT_TO_UINT16_DONE
+NONNEGATIVE:
+      // int exponent = ((input >> 23) & 0xFF)
+      LSR r27, input, 23  // exponent goes in r27
+      AND r27, r27, 0xFF
+
+      // Actual exponent is 127 less than the above; below -16 we
+      // should return 0. So check if it is less than 111.
+      QBLE EXPONENT_GREQ_MINUS16, r27, 111
+      LDI output, 0
+      QBA FLOAT_TO_UINT16_DONE
+EXPONENT_GREQ_MINUS16:	
+
+      // Next check if exponent is greater than or equal to 0 (i.e.
+      // 127 in our adjusted version. If so we return the max.
+      QBGT EXPONENT_LT_ZERO, r27, 127
+      LDI output, 65535		    // Max positive value
+      QBA FLOAT_TO_UINT16_DONE
+EXPONENT_LT_ZERO:	
+
+      // Mask out the mantissa and shift
+      // int mantissa = (input & 0x7FFFFF) | (1 << 23)
+      MOV r28, 0x007FFFFF
+      AND r28, r28, input
+      SET r28, 23
+
+      // Shift right by -(exponent - 127 - 7) to produce an int
+      // after effectively multiplying by 2^16
+      // ---> (134 - exponent)
+      RSB r27, r27, 134
+      LSR r28, r28, r27
+
+      // Check for 65536 and clip at 65535
+      LDI r27, 0xFFFF
+      MIN output, r27, r28	
+FLOAT_TO_UINT16_DONE:
+.endm
+
+	
+// Convert a 16-bit int to float. This macro assumes that the upper
+// 16 bits of input are 0 and may behave strangely if this is not the case.
+// input and output must be different registers
+.macro INT16_TO_FLOAT
+.mparam input, output
+      // Check edge cases first: 0 and -32768 (= 32768 in unsigned)
+      QBNE INPUT_NOT_ZERO, input, 0
+      LDI output, 0
+      QBA INT16_TO_FLOAT_DONE
+INPUT_NOT_ZERO:
+      LDI r28, 32768
+      QBNE INPUT_NOT_MIN, input, r28
+      MOV output, 0xBF800000	// -1.0
+      QBA INT16_TO_FLOAT_DONE
+INPUT_NOT_MIN:
+      // Check for negative values = values with bit 15 set
+      MOV output, input
+      QBBC NEGATIVE_DONE, output, 15
+      LDI r28, 0xFFFF
+      XOR output, output, r28
+      ADD output, output, 1
+      CLR output, 16	 // Clear any carry bit
+NEGATIVE_DONE:
+      // Now we need to find the highest bit that is 1 in order to determine
+      // the exponent
+      LMBD r28, output, 1
+
+      // Calculate exponent field: 127 + 8 + (r28 - 23) = 112 + r28
+      ADD r27, r28, 112
+	
+      // Take 23 minus the result to get the shift	
+      RSB r28, r28, 23     
+      LSL output, output, r28
+
+      // Now clear bit 23 (implicitly 1) and replace it with the exponent
+      CLR output, output, 23
+      LSL r27, r27, 23
+      OR  output, output, r27
+	
+      // Put the sign bit back in place
+      QBBC INT16_TO_FLOAT_DONE, input, 15
+      SET output, 31
+INT16_TO_FLOAT_DONE:	
+.endm
+
+// Convert a 16-bit unsigned int to float.
+.macro UINT16_TO_FLOAT
+.mparam input, output
+      MOV output, input
+	
+      // Clear upper 16 bits
+      LDI r27, 0xFFFF
+      AND output, output, r27
+
+      // If zero, we're done
+      QBEQ UINT16_TO_FLOAT_DONE, output, 0
+	
+      // Now we need to find the highest bit that is 1 in order to determine
+      // the exponent
+      LMBD r28, output, 1
+
+      // Calculate exponent field: 127 + 7 + (r28 - 23) = 111 + r28
+      ADD r27, r28, 111
+	
+      // Take 23 minus the result to get the shift	
+      RSB r28, r28, 23     
+      LSL output, output, r28
+
+      // Now clear bit 23 (implicitly 1) and replace it with the exponent
+      CLR output, output, 23
+      LSL r27, r27, 23
+      OR  output, output, r27	
+UINT16_TO_FLOAT_DONE:	
+.endm	
 	
 // Bring CS line low to write to DAC
 .macro DAC_CS_ASSERT
@@ -439,45 +614,45 @@
       ADC_WRITE r2, r2
 SPI_INIT_DONE:	
 	
-// Prepare McASP0 for audio
-MCASP_REG_WRITE MCASP_GBLCTL, 0			// Disable McASP
-MCASP_REG_WRITE_EXT MCASP_SRCTL0, 0		// All serialisers off
-MCASP_REG_WRITE_EXT MCASP_SRCTL1, 0
-MCASP_REG_WRITE_EXT MCASP_SRCTL2, 0
-MCASP_REG_WRITE_EXT MCASP_SRCTL3, 0
-MCASP_REG_WRITE_EXT MCASP_SRCTL4, 0
-MCASP_REG_WRITE_EXT MCASP_SRCTL5, 0
+      // Prepare McASP0 for audio
+      MCASP_REG_WRITE MCASP_GBLCTL, 0			// Disable McASP
+      MCASP_REG_WRITE_EXT MCASP_SRCTL0, 0		// All serialisers off
+      MCASP_REG_WRITE_EXT MCASP_SRCTL1, 0
+      MCASP_REG_WRITE_EXT MCASP_SRCTL2, 0
+      MCASP_REG_WRITE_EXT MCASP_SRCTL3, 0
+      MCASP_REG_WRITE_EXT MCASP_SRCTL4, 0
+      MCASP_REG_WRITE_EXT MCASP_SRCTL5, 0
 
-MCASP_REG_WRITE MCASP_PWRIDLESYSCONFIG, 0x02	// Power on
-MCASP_REG_WRITE MCASP_PFUNC, 0x00		// All pins are McASP
-MCASP_REG_WRITE MCASP_PDIR, MCASP_OUTPUT_PINS	// Set pin direction
-MCASP_REG_WRITE MCASP_DLBCTL, 0x00
-MCASP_REG_WRITE MCASP_DITCTL, 0x00
-MCASP_REG_WRITE MCASP_RMASK, MCASP_DATA_MASK	// 16 bit data receive
-MCASP_REG_WRITE MCASP_RFMT, MCASP_DATA_FORMAT	// Set data format
-MCASP_REG_WRITE MCASP_AFSRCTL, 0x100		// I2S mode
-MCASP_REG_WRITE MCASP_ACLKRCTL, 0x80		// Sample on rising edge
-MCASP_REG_WRITE MCASP_AHCLKRCTL, 0x8001		// Internal clock, not inv, /2; irrelevant?
-MCASP_REG_WRITE MCASP_RTDM, 0x03		// Enable TDM slots 0 and 1
-MCASP_REG_WRITE MCASP_RINTCTL, 0x00		// No interrupts
-MCASP_REG_WRITE MCASP_XMASK, MCASP_DATA_MASK	// 16 bit data transmit
-MCASP_REG_WRITE MCASP_XFMT, MCASP_DATA_FORMAT	// Set data format
-MCASP_REG_WRITE MCASP_AFSXCTL, 0x100		// I2S mode
-MCASP_REG_WRITE MCASP_ACLKXCTL, 0x00		// Transmit on rising edge, sync. xmit and recv
-MCASP_REG_WRITE MCASP_AHCLKXCTL, 0x8001		// External clock from AHCLKX
-MCASP_REG_WRITE MCASP_XTDM, 0x03		// Enable TDM slots 0 and 1
-MCASP_REG_WRITE MCASP_XINTCTL, 0x00		// No interrupts
+      MCASP_REG_WRITE MCASP_PWRIDLESYSCONFIG, 0x02	// Power on
+      MCASP_REG_WRITE MCASP_PFUNC, 0x00			// All pins are McASP
+      MCASP_REG_WRITE MCASP_PDIR, MCASP_OUTPUT_PINS	// Set pin direction
+      MCASP_REG_WRITE MCASP_DLBCTL, 0x00
+      MCASP_REG_WRITE MCASP_DITCTL, 0x00
+      MCASP_REG_WRITE MCASP_RMASK, MCASP_DATA_MASK	// 16 bit data receive
+      MCASP_REG_WRITE MCASP_RFMT, MCASP_DATA_FORMAT	// Set data format
+      MCASP_REG_WRITE MCASP_AFSRCTL, 0x100		// I2S mode
+      MCASP_REG_WRITE MCASP_ACLKRCTL, 0x80		// Sample on rising edge
+      MCASP_REG_WRITE MCASP_AHCLKRCTL, 0x8001		// Internal clock, not inv, /2; irrelevant?
+      MCASP_REG_WRITE MCASP_RTDM, 0x03			// Enable TDM slots 0 and 1
+      MCASP_REG_WRITE MCASP_RINTCTL, 0x00		// No interrupts
+      MCASP_REG_WRITE MCASP_XMASK, MCASP_DATA_MASK	// 16 bit data transmit
+      MCASP_REG_WRITE MCASP_XFMT, MCASP_DATA_FORMAT	// Set data format
+      MCASP_REG_WRITE MCASP_AFSXCTL, 0x100		// I2S mode
+      MCASP_REG_WRITE MCASP_ACLKXCTL, 0x00		// Transmit on rising edge, sync. xmit and recv
+      MCASP_REG_WRITE MCASP_AHCLKXCTL, 0x8001		// External clock from AHCLKX
+      MCASP_REG_WRITE MCASP_XTDM, 0x03			// Enable TDM slots 0 and 1
+      MCASP_REG_WRITE MCASP_XINTCTL, 0x00		// No interrupts
 	
-MCASP_REG_WRITE_EXT MCASP_SRCTL_R, 0x02		// Set up receive serialiser
-MCASP_REG_WRITE_EXT MCASP_SRCTL_X, 0x01		// Set up transmit serialiser
-MCASP_REG_WRITE_EXT MCASP_WFIFOCTL, 0x00	// Disable FIFOs
-MCASP_REG_WRITE_EXT MCASP_RFIFOCTL, 0x00
+      MCASP_REG_WRITE_EXT MCASP_SRCTL_R, 0x02		// Set up receive serialiser
+      MCASP_REG_WRITE_EXT MCASP_SRCTL_X, 0x01		// Set up transmit serialiser
+      MCASP_REG_WRITE_EXT MCASP_WFIFOCTL, 0x00		// Disable FIFOs
+      MCASP_REG_WRITE_EXT MCASP_RFIFOCTL, 0x00
 
-MCASP_REG_WRITE MCASP_XSTAT, 0xFF		// Clear transmit errors
-MCASP_REG_WRITE MCASP_RSTAT, 0xFF		// Clear receive errors
+      MCASP_REG_WRITE MCASP_XSTAT, 0xFF		// Clear transmit errors
+      MCASP_REG_WRITE MCASP_RSTAT, 0xFF		// Clear receive errors
 
-MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 1)	// Set RHCLKRST
-MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 9)	// Set XHCLKRST
+      MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 1)	// Set RHCLKRST
+      MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 9)	// Set XHCLKRST
 
 // The above write sequence will have temporarily changed the AHCLKX frequency
 // The PLL needs time to settle or the sample rate will be unstable and possibly
@@ -488,7 +663,7 @@
       MOV r3, GPIO1 + GPIO_SETDATAOUT
       SBBO r2, r3, 0, 4
 
-MOV r2, 100000
+      MOV r2, 100000
 MCASP_INIT_WAIT:	
       SUB r2, r2, 1
       QBNE MCASP_INIT_WAIT, r2, 0
@@ -497,29 +672,29 @@
       MOV r3, GPIO1 + GPIO_CLEARDATAOUT
       SBBO r2, r3, 0, 4
 	
-MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 0)	// Set RCLKRST
-MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 8)	// Set XCLKRST
-MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 2)	// Set RSRCLR
-MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 10)	// Set XSRCLR
-MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 3)	// Set RSMRST
-MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 11)	// Set XSMRST
+      MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 0)	// Set RCLKRST
+      MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 8)	// Set XCLKRST
+      MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 2)	// Set RSRCLR
+      MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 10)	// Set XSRCLR
+      MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 3)	// Set RSMRST
+      MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 11)	// Set XSMRST
 
-MCASP_REG_WRITE_EXT MCASP_XBUF, 0x00		// Write to the transmit buffer to prevent underflow
+      MCASP_REG_WRITE_EXT MCASP_XBUF, 0x00		// Write to the transmit buffer to prevent underflow
 
-MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 4)	// Set RFRST
-MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 12)	// Set XFRST
+      MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 4)	// Set RFRST
+      MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 12)	// Set XFRST
 
 // Initialisation
-LBBO reg_frame_total, reg_comm_addr, COMM_BUFFER_FRAMES, 4  // Total frame count (SPI; 0.5x-2x for McASP)
-MOV reg_dac_buf0, 0                      // DAC buffer 0 start pointer
-LSL reg_dac_buf1, reg_frame_total, 1     // DAC buffer 1 start pointer = N[ch]*2[bytes]*bufsize
-LMBD r2, reg_num_channels, 1		 // Returns 1, 2 or 3 depending on the number of channels
-LSL reg_dac_buf1, reg_dac_buf1, r2	 // Multiply by 2, 4 or 8 to get the N[ch] scaling above
-MOV reg_mcasp_buf0, 0			 // McASP DAC buffer 0 start pointer
-LSL reg_mcasp_buf1, reg_frame_total, r2  // McASP DAC buffer 1 start pointer = 2[ch]*2[bytes]*(N/4)[samples/spi]*bufsize
-CLR reg_flags, reg_flags, FLAG_BIT_BUFFER1  // Bit 0 holds which buffer we are on
-MOV r2, 0
-SBBO r2, reg_comm_addr, COMM_FRAME_COUNT, 4  // Start with frame count of 0
+      LBBO reg_frame_total, reg_comm_addr, COMM_BUFFER_FRAMES, 4  // Total frame count (SPI; 0.5x-2x for McASP)
+      MOV reg_dac_buf0, 0                      // DAC buffer 0 start pointer
+      LSL reg_dac_buf1, reg_frame_total, 1     // DAC buffer 1 start pointer = N[ch]*2[bytes]*bufsize
+      LMBD r2, reg_num_channels, 1		 // Returns 1, 2 or 3 depending on the number of channels
+      LSL reg_dac_buf1, reg_dac_buf1, r2	 // Multiply by 2, 4 or 8 to get the N[ch] scaling above
+      MOV reg_mcasp_buf0, 0			 // McASP DAC buffer 0 start pointer
+      LSL reg_mcasp_buf1, reg_frame_total, r2    // McASP DAC buffer 1 start pointer = 2[ch]*2[bytes]*(N/4)[samples/spi]*bufsize
+      CLR reg_flags, reg_flags, FLAG_BIT_BUFFER1 // Bit 0 holds which buffer we are on
+      MOV r2, 0
+      SBBO r2, reg_comm_addr, COMM_FRAME_COUNT, 4  // Start with frame count of 0
 	
 // Here we are out of sync by one TDM slot since the 0 word transmitted above will have occupied
 // the first output slot. Send one more word before jumping into the loop.