andrewm@0: .origin 0
andrewm@0: .entrypoint START
andrewm@0: 
andrewm@0: #define DBOX_CAPE	// Define this to use new cape hardware
andrewm@0: 	
andrewm@0: #define CLOCK_BASE  0x44E00000
andrewm@0: #define CLOCK_SPI0  0x4C
andrewm@0: #define CLOCK_SPI1  0x50
andrewm@0: #define CLOCK_L4LS  0x60
andrewm@0: 
andrewm@0: #define SPI0_BASE   0x48030100
andrewm@0: #define SPI1_BASE   0x481A0100
andrewm@0: #define SPI_BASE    SPI0_BASE
andrewm@0: 	
andrewm@0: #define SPI_SYSCONFIG 0x10
andrewm@0: #define SPI_SYSSTATUS 0x14
andrewm@0: #define SPI_MODULCTRL 0x28
andrewm@0: #define SPI_CH0CONF   0x2C
andrewm@0: #define SPI_CH0STAT   0x30
andrewm@0: #define SPI_CH0CTRL   0x34
andrewm@0: #define SPI_CH0TX     0x38
andrewm@0: #define SPI_CH0RX     0x3C
andrewm@0: #define SPI_CH1CONF   0x40
andrewm@0: #define SPI_CH1STAT   0x44
andrewm@0: #define SPI_CH1CTRL   0x48
andrewm@0: #define SPI_CH1TX     0x4C
andrewm@0: #define SPI_CH1RX     0x50
andrewm@0: 
andrewm@0: #define GPIO0 0x44E07000
andrewm@0: #define GPIO1 0x4804C000
andrewm@0: #define GPIO_CLEARDATAOUT 0x190
andrewm@0: #define GPIO_SETDATAOUT 0x194
andrewm@0: 
andrewm@45: #define PRU0_ARM_INTERRUPT 19	// Interrupt signalling we're done
andrewm@45: #define PRU1_ARM_INTERRUPT 20	// Interrupt signalling a block is ready
andrewm@0: 
andrewm@0: #define C_ADC_DAC_MEM C24     // PRU0 mem
andrewm@0: #ifdef DBOX_CAPE
andrewm@0: #define DAC_GPIO      GPIO0
andrewm@0: #define DAC_CS_PIN    (1<<5) // GPIO0:5 = P9 pin 17
andrewm@0: #else
andrewm@0: #define DAC_GPIO      GPIO1
andrewm@0: #define DAC_CS_PIN    (1<<16) // GPIO1:16 = P9 pin 15
andrewm@0: #endif
andrewm@0: #define DAC_TRM       0       // SPI transmit and receive
andrewm@0: #define DAC_WL        32      // Word length
andrewm@0: #define DAC_CLK_MODE  1       // SPI mode
andrewm@0: #define DAC_CLK_DIV   1       // Clock divider (48MHz / 2^n)
andrewm@0: #define DAC_DPE       1       // d0 = receive, d1 = transmit
andrewm@0: 
andrewm@0: #define AD5668_COMMAND_OFFSET 24
andrewm@0: #define AD5668_ADDRESS_OFFSET 20
andrewm@0: #define AD5668_DATA_OFFSET    4
andrewm@0: #define AD5668_REF_OFFSET     0
andrewm@0: 
andrewm@0: #ifdef DBOX_CAPE
andrewm@0: #define ADC_GPIO      GPIO1
andrewm@0: #define ADC_CS_PIN    (1<<16) // GPIO1:16 = P9 pin 15
andrewm@0: #else
andrewm@0: #define ADC_GPIO      GPIO1
andrewm@0: #define ADC_CS_PIN    (1<<17) // GPIO1:17 = P9 pin 23
andrewm@0: #endif
andrewm@0: #define ADC_TRM       0       // SPI transmit and receive
andrewm@0: #define ADC_WL        16      // Word length
andrewm@0: #define ADC_CLK_MODE  0       // SPI mode
andrewm@0: #define ADC_CLK_DIV   1       // Clock divider (48MHz / 2^n)
andrewm@0: #define ADC_DPE       1       // d0 = receive, d1 = transmit
andrewm@0: 
andrewm@0: #define AD7699_CFG_MASK       0xF120 // Mask for config update, unipolar, full BW
andrewm@0: #define AD7699_CHANNEL_OFFSET 9      // 7 bits offset of a 14-bit left-justified word
andrewm@0: #define AD7699_SEQ_OFFSET     3      // sequencer (0 = disable, 3 = scan all)
andrewm@0: 
andrewm@0: #define SHARED_COMM_MEM_BASE  0x00010000  // Location where comm flags are written
andrewm@0: #define COMM_SHOULD_STOP      0		  // Set to be nonzero when loop should stop
andrewm@0: #define COMM_CURRENT_BUFFER   4           // Which buffer we are on
andrewm@0: #define COMM_BUFFER_FRAMES    8           // How many frames per buffer
andrewm@0: #define COMM_SHOULD_SYNC      12          // Whether to synchronise to an external clock
andrewm@0: #define COMM_SYNC_ADDRESS     16          // Which memory address to find the GPIO on
andrewm@0: #define COMM_SYNC_PIN_MASK    20          // Which pin to read for the sync
andrewm@0: #define COMM_LED_ADDRESS      24          // Which memory address to find the status LED on
andrewm@0: #define COMM_LED_PIN_MASK     28          // Which pin to write to change LED
andrewm@0: #define COMM_FRAME_COUNT      32	  // How many frames have elapse since beginning
andrewm@0: #define COMM_USE_SPI          36          // Whether or not to use SPI ADC and DAC
andrewm@12: #define COMM_NUM_CHANNELS     40	  // Low 2 bits indicate 8 [0x3], 4 [0x1] or 2 [0x0] channels
andrewm@253: #define COMM_USE_DIGITAL      44	  // Whether or not to use DIGITAL
andrewm@253: #define COMM_PRU_NUMBER       48          // Which PRU this code is running on
andrewm@282: #define COMM_MUX_CONFIG       52          // Whether to use the mux capelet, and how many channels
andrewm@346: #define COMM_MUX_END_CHANNEL  56          // Which mux channel the last buffer ended on
andrewm@346: 	
andrewm@282: // General constants for McASP peripherals (used for audio codec)
andrewm@0: #define MCASP0_BASE 0x48038000
andrewm@0: #define MCASP1_BASE 0x4803C000
andrewm@0: 
andrewm@0: #define MCASP_PWRIDLESYSCONFIG 		0x04
andrewm@0: #define MCASP_PFUNC			0x10
andrewm@0: #define MCASP_PDIR			0x14
andrewm@0: #define MCASP_PDOUT			0x18
andrewm@0: #define MCASP_PDSET			0x1C
andrewm@0: #define MCASP_PDIN			0x1C
andrewm@0: #define MCASP_PDCLR			0x20
andrewm@0: #define MCASP_GBLCTL			0x44
andrewm@0: #define MCASP_AMUTE			0x48
andrewm@0: #define MCASP_DLBCTL			0x4C
andrewm@0: #define MCASP_DITCTL			0x50
andrewm@0: #define MCASP_RGBLCTL			0x60
andrewm@0: #define MCASP_RMASK			0x64
andrewm@0: #define MCASP_RFMT			0x68
andrewm@0: #define MCASP_AFSRCTL			0x6C
andrewm@0: #define MCASP_ACLKRCTL			0x70
andrewm@0: #define MCASP_AHCLKRCTL			0x74
andrewm@0: #define MCASP_RTDM			0x78
andrewm@0: #define MCASP_RINTCTL			0x7C
andrewm@0: #define MCASP_RSTAT			0x80
andrewm@0: #define MCASP_RSLOT			0x84
andrewm@0: #define MCASP_RCLKCHK			0x88
andrewm@0: #define MCASP_REVTCTL			0x8C
andrewm@0: #define MCASP_XGBLCTL			0xA0
andrewm@0: #define MCASP_XMASK			0xA4
andrewm@0: #define MCASP_XFMT			0xA8
andrewm@0: #define MCASP_AFSXCTL			0xAC
andrewm@0: #define MCASP_ACLKXCTL			0xB0
andrewm@0: #define MCASP_AHCLKXCTL			0xB4
andrewm@0: #define MCASP_XTDM			0xB8
andrewm@0: #define MCASP_XINTCTL			0xBC
andrewm@0: #define MCASP_XSTAT			0xC0
andrewm@0: #define MCASP_XSLOT			0xC4
andrewm@0: #define MCASP_XCLKCHK			0xC8
andrewm@0: #define MCASP_XEVTCTL			0xCC
andrewm@0: #define MCASP_SRCTL0			0x180
andrewm@0: #define MCASP_SRCTL1			0x184
andrewm@0: #define MCASP_SRCTL2			0x188
andrewm@0: #define MCASP_SRCTL3			0x18C
andrewm@0: #define MCASP_SRCTL4			0x190
andrewm@0: #define MCASP_SRCTL5			0x194
andrewm@0: #define MCASP_XBUF0			0x200
andrewm@0: #define MCASP_XBUF1			0x204
andrewm@0: #define MCASP_XBUF2			0x208
andrewm@0: #define MCASP_XBUF3			0x20C
andrewm@0: #define MCASP_XBUF4			0x210
andrewm@0: #define MCASP_XBUF5			0x214
andrewm@0: #define MCASP_RBUF0			0x280
andrewm@0: #define MCASP_RBUF1			0x284
andrewm@0: #define MCASP_RBUF2			0x288
andrewm@0: #define MCASP_RBUF3			0x28C
andrewm@0: #define MCASP_RBUF4			0x290
andrewm@0: #define MCASP_RBUF5			0x294
andrewm@0: #define MCASP_WFIFOCTL			0x1000
andrewm@0: #define MCASP_WFIFOSTS			0x1004
andrewm@0: #define MCASP_RFIFOCTL			0x1008
andrewm@0: #define MCASP_RFIFOSTS			0x100C
andrewm@0: 
giuliomoro@100: #define MCASP_XSTAT_XUNDRN_BIT          0        // Bit to test if there was an underrun
andrewm@0: #define MCASP_XSTAT_XDATA_BIT           5        // Bit to test for transmit ready
andrewm@0: #define MCASP_RSTAT_RDATA_BIT           5        // Bit to test for receive ready 
andrewm@0: 	
andrewm@0: // Constants used for this particular audio setup
andrewm@0: #define MCASP_BASE 	MCASP0_BASE
andrewm@0: #ifdef DBOX_CAPE
andrewm@0: #define MCASP_SRCTL_X	MCASP_SRCTL2	// Ser. 2 is transmitter
andrewm@0: #define MCASP_SRCTL_R	MCASP_SRCTL0	// Ser. 0 is receiver
andrewm@0: #define MCASP_XBUF	MCASP_XBUF2
andrewm@0: #define MCASP_RBUF	MCASP_RBUF0
andrewm@0: #else
andrewm@0: #define MCASP_SRCTL_X	MCASP_SRCTL3	// Ser. 3 is transmitter
andrewm@0: #define MCASP_SRCTL_R	MCASP_SRCTL2	// Ser. 2 is receiver
andrewm@0: #define MCASP_XBUF	MCASP_XBUF3
andrewm@0: #define MCASP_RBUF	MCASP_RBUF2
andrewm@0: #endif
andrewm@0: 	
andrewm@0: #define MCASP_PIN_AFSX		(1 << 28)
andrewm@0: #define MCASP_PIN_AHCLKX	(1 << 27)
andrewm@0: #define MCASP_PIN_ACLKX		(1 << 26)
andrewm@0: #define MCASP_PIN_AMUTE		(1 << 25)	// Also, 0 to 3 are XFR0 to XFR3
andrewm@0: 
andrewm@0: #ifdef DBOX_CAPE
andrewm@0: #define MCASP_OUTPUT_PINS   	MCASP_PIN_AHCLKX | (1 << 2) // AHCLKX and AXR2 outputs
andrewm@0: #else
andrewm@0: #define MCASP_OUTPUT_PINS   	(1 << 3)	// Which pins are outputs
andrewm@0: #endif
andrewm@0: 
andrewm@0: #define MCASP_DATA_MASK 	0xFFFF		// 16 bit data
andrewm@0: #define MCASP_DATA_FORMAT	0x807C		// MSB first, 0 bit delay, 16 bits, CFG bus, ROR 16bits
andrewm@0: 
andrewm@12: #define C_MCASP_MEM             C28     	// Shared PRU mem
andrewm@0: 
andrewm@0: // Flags for the flags register
andrewm@0: #define FLAG_BIT_BUFFER1	0
andrewm@0: #define FLAG_BIT_USE_SPI	1
andrewm@12: #define FLAG_BIT_MCASP_HWORD	2		// Whether we are on the high word for McASP transmission
andrewm@282: #define FLAG_BIT_USE_DIGITAL	3
andrewm@282: 	
andrewm@282: #define FLAG_BIT_MUX_CONFIG0	 8		// Mux capelet configuration:
andrewm@282: #define FLAG_BIT_MUX_CONFIG1	 9		// 00 = off, 01 = 2 ch., 10 = 4 ch., 11 = 8 ch.
andrewm@282: #define FLAG_MASK_MUX_CONFIG	 0x0300
andrewm@346: 		
andrewm@0: // Registers used throughout
andrewm@0: 
andrewm@0: // r1, r2, r3 are used for temporary storage
giuliomoro@19: #define MEM_DIGITAL_BASE 0x11000 //Base address for DIGITAL : Shared RAM + 0x400
giuliomoro@19: #define MEM_DIGITAL_BUFFER1_OFFSET 0x400 //Start pointer to DIGITAL_BUFFER1, which is 256 words after.
giuliomoro@16: // 256 is the maximum number of frames allowed
giuliomoro@16: 
giuliomoro@40: #define reg_digital_current r6  // Pointer to current storage location of DIGITAL
andrewm@12: #define reg_num_channels	r9		// Number of SPI ADC/DAC channels to use
andrewm@0: #define reg_frame_current	r10		// Current frame count in SPI ADC/DAC transfer
andrewm@0: #define reg_frame_total		r11		// Total frame count for SPI ADC/DAC
andrewm@0: #define reg_dac_data		r12		// Current dword for SPI DAC
andrewm@0: #define reg_adc_data		r13		// Current dword for SPI ADC
andrewm@0: #define reg_mcasp_dac_data	r14		// Current dword for McASP DAC
andrewm@0: #define reg_mcasp_adc_data	r15		// Current dword for McASP ADC
andrewm@0: #define reg_dac_buf0		r16		// Start pointer to SPI DAC buffer 0
andrewm@0: #define reg_dac_buf1		r17		// Start pointer to SPI DAC buffer 1
andrewm@0: #define reg_dac_current		r18		// Pointer to current storage location of SPI DAC
andrewm@0: #define reg_adc_current		r19		// Pointer to current storage location of SPI ADC
andrewm@0: #define reg_mcasp_buf0		r20		// Start pointer to McASP DAC buffer 0
andrewm@0: #define reg_mcasp_buf1		r21		// Start pointer to McASP DAC buffer 1
andrewm@0: #define reg_mcasp_dac_current	r22		// Pointer to current storage location of McASP DAC
andrewm@0: #define reg_mcasp_adc_current	r23		// Pointer to current storage location of McASP ADC
andrewm@0: #define reg_flags		r24		// Buffer ID (0 and 1) and other flags
andrewm@0: #define reg_comm_addr		r25		// Memory address for communicating with ARM
andrewm@0: #define reg_spi_addr		r26		// Base address for SPI
andrewm@0: // r27, r28 used in macros
andrewm@0: #define reg_mcasp_addr		r29		// Base address for McASP
andrewm@282: #define reg_pru1_mux_pins	r30		// Register mapped directly to P8 pins (PRU1 only)
andrewm@0: 
giuliomoro@16: //0  P8_07 36 0x890/090 66 gpio2[2]
giuliomoro@16: //1  P8_08 37 0x894/094 67 gpio2[3]
giuliomoro@16: //2  P8_09 39 0x89c/09c 69 gpio2[5]
giuliomoro@16: //3  P8_10 38 0x898/098 68 gpio2[4]
giuliomoro@16: //4  P8_11 13 0x834/034 45 gpio1[13]
giuliomoro@16: //5  P8_12 12 0x830/030 44 gpio1[12]
giuliomoro@16: //6  P9_12 30 0x878/078 60 gpio1[28]
giuliomoro@16: //7  P9_14 18 0x848/048 50 gpio1[18]
giuliomoro@16: //8  P8_15 15 0x83c/03c 47 gpio1[15]
giuliomoro@16: //9  P8_16 14 0x838/038 46 gpio1[14]
giuliomoro@16: //10 P9_16 19 0x84c/04c 51 gpio1[19]
giuliomoro@16: //11 P8_18 35 0x88c/08c 65 gpio2[1]
giuliomoro@16: //12 P8_27 56 0x8e0/0e0 86 gpio2[22]
giuliomoro@16: //13 P8_28 58 0x8e8/0e8 88 gpio2[24]
giuliomoro@16: //14 P8_29 57 0x8e4/0e4 87 gpio2[23]
giuliomoro@16: //15 P8_30 59 0x8ec/0ec 89 gpio2[25]
giuliomoro@16: 
giuliomoro@16: //generic GPIOs constants
giuliomoro@16: //#define GPIO1 0x4804c000
giuliomoro@16: #define GPIO2 0x481ac000
giuliomoro@16: //#define GPIO_CLEARDATAOUT 0x190 //SETDATAOUT is CLEARDATAOUT+4
giuliomoro@16: #define GPIO_OE 0x134 
giuliomoro@16: #define GPIO_DATAIN 0x138
giuliomoro@16: 
giuliomoro@16: .macro READ_GPIO_BITS
giuliomoro@19: .mparam gpio_data, gpio_num_bit, digital_bit, digital
giuliomoro@19:     QBBC DONE, digital, digital_bit //if the pin is set as an output, nothing to do here
giuliomoro@16:     QBBC CLEAR, gpio_data, gpio_num_bit 
giuliomoro@19:     SET digital, digital_bit+16
giuliomoro@16:     QBA DONE
giuliomoro@16:     CLEAR:
giuliomoro@19:         CLR digital, digital_bit+16
giuliomoro@16:         QBA DONE
giuliomoro@16:     DONE:
giuliomoro@16: .endm
giuliomoro@16: 
giuliomoro@16: .macro SET_GPIO_BITS
giuliomoro@19: .mparam gpio_oe, gpio_setdataout, gpio_cleardataout, gpio_num_bit, digital_bit, digital //sets the bits in GPIO_OE, GPIO_SETDATAOUT and GPIO_CLEARDATAOUT
giuliomoro@16: //Remember that the GPIO_OE Output data enable register behaves as follows for each bit:
giuliomoro@16: //0 = The corresponding GPIO pin is configured as an output.
giuliomoro@16: //1 = The corresponding GPIO pin is configured as an input.
giuliomoro@19:     QBBS SETINPUT, digital, digital_bit 
giuliomoro@16:     CLR gpio_oe, gpio_num_bit //if it is an output, configure pin as output
giuliomoro@19:     QBBC CLEARDATAOUT, digital, digital_bit+16 // check the output value. If it is 0, branch
giuliomoro@16:     SET gpio_setdataout, gpio_num_bit //if it is 1, set output to high
giuliomoro@16:     QBA DONE
giuliomoro@16: CLEARDATAOUT:
giuliomoro@16:     SET gpio_cleardataout, gpio_num_bit // set output to low
giuliomoro@16:     QBA DONE
giuliomoro@16: SETINPUT: //if it is an input, set the relevant bit
giuliomoro@16:     SET gpio_oe, gpio_num_bit
giuliomoro@16:     QBA DONE
giuliomoro@16: DONE:
giuliomoro@16: .endm
giuliomoro@16: 
giuliomoro@16: QBA START // when first starting, go to START, skipping this section.
giuliomoro@16: 
giuliomoro@19: DIGITAL:
giuliomoro@39: //IMPORTANT: do NOT use r28 in this macro, as it contains the return address for JAL
giuliomoro@39: //r27 is now the input word passed in render(), one word per frame
giuliomoro@16: //[31:16]: data(1=high, 0=low), [15:0]: direction (0=output, 1=input) )
giuliomoro@39: 
giuliomoro@39: 
giuliomoro@16: //Preparing the gpio_oe, gpio_cleardataout and gpio_setdataout for each module
giuliomoro@39: //r2 will hold GPIO1_OE
giuliomoro@39: //load current status of GPIO_OE in r2
giuliomoro@39:     MOV r2, GPIO1 | GPIO_OE 
giuliomoro@26:     //it takes 190ns to go through the next instruction
giuliomoro@39:     LBBO r2, r2, 0, 4
giuliomoro@16: //GPIO1-start
giuliomoro@16: //process oe and datain and prepare dataout for GPIO1
giuliomoro@39: //r7 will contain GPIO1_CLEARDATAOUT
giuliomoro@39: //r8 will contain GPIO1_SETDATAOUT
giuliomoro@39:     MOV r8, 0 
giuliomoro@39:     MOV r7, 0
giuliomoro@39: //map GPIO_ANALOG to gpio1 pins,
giuliomoro@39: //r2 is gpio1_oe, r8 is gpio1_setdataout, r7 is gpio1_cleardataout, r27 is the input word
giuliomoro@39: //the following operations will read from r27 and update r2,r7,r8
giuliomoro@39:     SET_GPIO_BITS r2, r8, r7, 13, 4, r27
giuliomoro@39:     SET_GPIO_BITS r2, r8, r7, 12, 5, r27
giuliomoro@39:     SET_GPIO_BITS r2, r8, r7, 28, 6, r27
giuliomoro@39:     SET_GPIO_BITS r2, r8, r7, 18, 7, r27
giuliomoro@39:     SET_GPIO_BITS r2, r8, r7, 15, 8, r27
giuliomoro@39:     SET_GPIO_BITS r2, r8, r7, 14, 9, r27
giuliomoro@39:     SET_GPIO_BITS r2, r8, r7, 19, 10, r27
giuliomoro@16: //set the output enable register for gpio1.
giuliomoro@39:     MOV r3, GPIO1 | GPIO_OE  //use r3 as a temp register
giuliomoro@39:     SBBO r2, r3, 0, 4 //takes two cycles (10ns)
giuliomoro@16: //GPIO1-end
giuliomoro@39: // r2 is now unused
giuliomoro@16: 
giuliomoro@16: //GPIO2-start
giuliomoro@39: //r3 will hold GPIO1_OE
giuliomoro@39: //load current status of GPIO_OE in r3
giuliomoro@39:     MOV r3, GPIO2 | GPIO_OE  
giuliomoro@26: //it takes 200ns to go through the next instructions
giuliomoro@39:     LBBO r3, r3, 0, 4
giuliomoro@16: //process oe and datain and prepare dataout for GPIO2
giuliomoro@39: //r4 will contain GPIO2_CLEARDATAOUT
giuliomoro@39: //r5 will contain GPIO2_SETDATAOUT
giuliomoro@39:     MOV r5, 0
giuliomoro@39:     MOV r4, 0 
giuliomoro@39: //map GPIO_ANALOG to gpio2 pins
giuliomoro@39: //r3 is gpio2_oe, r5 is gpio2_setdataout, r4 is gpio2_cleardataout, r27 is the input word
giuliomoro@39: //the following operations will read from r27 and update r3,r4,r5
giuliomoro@39:     SET_GPIO_BITS r3, r5, r4, 2, 0, r27
giuliomoro@39:     SET_GPIO_BITS r3, r5, r4, 3, 1, r27
giuliomoro@39:     SET_GPIO_BITS r3, r5, r4, 5, 2, r27
giuliomoro@39:     SET_GPIO_BITS r3, r5, r4, 4, 3, r27
giuliomoro@39:     SET_GPIO_BITS r3, r5, r4, 1, 11, r27
giuliomoro@39:     SET_GPIO_BITS r3, r5, r4, 22, 12, r27
giuliomoro@39:     SET_GPIO_BITS r3, r5, r4, 24, 13, r27
giuliomoro@39:     SET_GPIO_BITS r3, r5, r4, 23, 14, r27
giuliomoro@39:     SET_GPIO_BITS r3, r5, r4, 25, 15, r27
giuliomoro@16: //set the output enable register for gpio2.
giuliomoro@39:     MOV r2, GPIO2 | GPIO_OE  //use r2 as a temp registerp
giuliomoro@39:     SBBO r3, r2, 0, 4 //takes two cycles (10ns)
giuliomoro@16: //GPIO2-end
giuliomoro@39: //r3 is now unused
giuliomoro@16: 
giuliomoro@39: //load current inputs in r2, r3
giuliomoro@39: //r2 will contain GPIO1_DATAIN
giuliomoro@39: //r3 will contain GPIO2_DATAIN
giuliomoro@39: //load the memory locations
giuliomoro@39:     MOV r2, GPIO1 | GPIO_DATAIN  
giuliomoro@39:     MOV r3, GPIO2 | GPIO_DATAIN  
giuliomoro@26:     //takes 375 nns to go through the next two instructions
giuliomoro@39: //read the datain
giuliomoro@39:     LBBO r2, r2, 0, 4
giuliomoro@39:     LBBO r3, r3, 0, 4
giuliomoro@39: //now read from r2 and r3 only the channels that are set as input in the lower word of r27 
giuliomoro@39: // and set their value in the high word of r27
giuliomoro@39: //GPIO1
giuliomoro@39:     READ_GPIO_BITS r2, 13, 4, r27
giuliomoro@39:     READ_GPIO_BITS r2, 12, 5, r27
giuliomoro@39:     READ_GPIO_BITS r2, 28, 6, r27
giuliomoro@39:     READ_GPIO_BITS r2, 18, 7, r27
giuliomoro@39:     READ_GPIO_BITS r2, 15, 8, r27
giuliomoro@39:     READ_GPIO_BITS r2, 14, 9, r27
giuliomoro@39:     READ_GPIO_BITS r2, 19, 10, r27
giuliomoro@39: //GPIO2
giuliomoro@39:     READ_GPIO_BITS r3, 2, 0, r27
giuliomoro@39:     READ_GPIO_BITS r3, 3, 1, r27
giuliomoro@39:     READ_GPIO_BITS r3, 5, 2, r27
giuliomoro@39:     READ_GPIO_BITS r3, 4, 3, r27
giuliomoro@39:     READ_GPIO_BITS r3, 1, 11, r27
giuliomoro@39:     READ_GPIO_BITS r3, 22, 12, r27
giuliomoro@39:     READ_GPIO_BITS r3, 24, 13, r27
giuliomoro@39:     READ_GPIO_BITS r3, 23, 14, r27
giuliomoro@39:     READ_GPIO_BITS r3, 25, 15, r27
giuliomoro@39: //r2, r3 are now unused
giuliomoro@16: 
giuliomoro@16: //now all the setdataout and cleardataout are ready to be written to the GPIO register.
giuliomoro@39: //CLEARDATAOUT and SETDATAOUT are consecutive positions in memory, so we just write 8 bytes to CLEARDATAOUT.
giuliomoro@39: //We can do this because we chose cleardata and setdata registers for a given GPIO to be consecutive
giuliomoro@16: //load the memory addresses to be written to
giuliomoro@39:     MOV r2, GPIO1 | GPIO_CLEARDATAOUT //use r2 as a temp register
giuliomoro@39:     MOV r3, GPIO2 | GPIO_CLEARDATAOUT //use r3 as a temp register
giuliomoro@16: //write 8 bytes for each GPIO
giuliomoro@39: //takes 30ns in total to go through the following two instructions
giuliomoro@39:     SBBO r7, r2, 0, 8 //store r7 and r8 in GPIO1_CLEARDATAOUT and GPIO1_SETDATAOUT 
giuliomoro@39:                       //takes 145ns to be effective when going low, 185ns when going high
giuliomoro@39:     SBBO r4, r3, 0, 8 //store r4 and r5 in GPIO2_CLEARDATAOUT and GPIO2_SETDATAOUT 
giuliomoro@39:                      //takes 95ns to be effective when going low, 130ns when going high
giuliomoro@16: //reversing the order of the two lines above will swap the performances between the GPIO modules
giuliomoro@16: //i.e.: the first line will always take 145ns/185ns and the second one will always take 95ns/130ns, 
giuliomoro@16: //regardless of whether the order is gpio1-gpio2 or gpio2-gpio1
giuliomoro@39: JMP r28.w0 // go back to ADC_WRITE_AND_PROCESS_GPIO
giuliomoro@16: 
giuliomoro@39: .macro HANG //useful for debugging
giuliomoro@188: DALOOP:
giuliomoro@102:     set r30.t14
giuliomoro@102:     clr r30.t14
giuliomoro@38: QBA DALOOP
giuliomoro@38: .endm	
giuliomoro@39: 
andrewm@0: // Bring CS line low to write to DAC
andrewm@0: .macro DAC_CS_ASSERT
giuliomoro@102:      MOV r27, DAC_CS_PIN
giuliomoro@102:      MOV r28, DAC_GPIO + GPIO_CLEARDATAOUT
giuliomoro@102:      SBBO r27, r28, 0, 4
andrewm@0: .endm
andrewm@0: 
andrewm@0: // Bring CS line high at end of DAC transaction
andrewm@0: .macro DAC_CS_UNASSERT
giuliomoro@102:      MOV r27, DAC_CS_PIN
giuliomoro@102:      MOV r28, DAC_GPIO + GPIO_SETDATAOUT
giuliomoro@102:      SBBO r27, r28, 0, 4
andrewm@0: .endm
andrewm@0: 
andrewm@0: // Write to DAC TX register
andrewm@0: .macro DAC_TX
andrewm@0: .mparam data
andrewm@0:       SBBO data, reg_spi_addr, SPI_CH0TX, 4
andrewm@0: .endm
andrewm@0: 
andrewm@0: // Wait for SPI to finish (uses RXS indicator)
andrewm@0: .macro DAC_WAIT_FOR_FINISH
andrewm@0:  LOOP:
giuliomoro@102:      LBBO r27, reg_spi_addr, SPI_CH0STAT, 4
giuliomoro@102:      QBBC LOOP, r27, 0
andrewm@0: .endm
andrewm@0: 
andrewm@0: // Read the RX word to clear
andrewm@0: .macro DAC_DISCARD_RX
giuliomoro@102:      LBBO r27, reg_spi_addr, SPI_CH0RX, 4
andrewm@0: .endm
andrewm@0: 
andrewm@0: // Complete DAC write with chip select
andrewm@0: .macro DAC_WRITE
andrewm@0: .mparam reg
giuliomoro@102:      DAC_CS_ASSERT
giuliomoro@102:      DAC_TX reg
giuliomoro@102:      DAC_WAIT_FOR_FINISH
giuliomoro@102:      DAC_CS_UNASSERT
giuliomoro@102:      DAC_DISCARD_RX
andrewm@0: .endm
andrewm@0: 
andrewm@346: // Transform channel order on DAC
andrewm@346: // (in) 01234567 --> (out) 64201357
andrewm@346: // This is to make the pin order on the Bela cape
andrewm@346: // make sense
andrewm@346: .macro DAC_CHANNEL_REORDER
andrewm@346: .mparam out, in
andrewm@346:      QBBS DAC_CHANNEL_REORDER_HIGH, in, 2
andrewm@346: // Input channels 0,1,2,3 --> 6,4,2,0
andrewm@346: // out = (3 - in) << 1
andrewm@346:      XOR out, in, 0x03
andrewm@346:      LSL out, out, 1
andrewm@346:      QBA DAC_CHANNEL_REORDER_DONE
andrewm@346: DAC_CHANNEL_REORDER_HIGH:	
andrewm@346: // Input channels 4,5,6,7 --> 1,3,5,7
andrewm@346: // out = ((in & 0x03) << 1) + 1
andrewm@346:      AND out, in, 0x03
andrewm@346:      LSL out, out, 1
andrewm@346:      ADD out, out, 1
andrewm@346: DAC_CHANNEL_REORDER_DONE:	
andrewm@346: .endm
andrewm@346: 	
andrewm@0: // Bring CS line low to write to ADC
andrewm@0: .macro ADC_CS_ASSERT
giuliomoro@102:      MOV r27, ADC_CS_PIN
giuliomoro@102:      MOV r28, ADC_GPIO + GPIO_CLEARDATAOUT
giuliomoro@102:      SBBO r27, r28, 0, 4
andrewm@0: .endm
andrewm@0: 
andrewm@0: // Bring CS line high at end of ADC transaction
andrewm@0: .macro ADC_CS_UNASSERT
giuliomoro@102:      MOV r27, ADC_CS_PIN
giuliomoro@102:      MOV r28, ADC_GPIO + GPIO_SETDATAOUT
giuliomoro@102:      SBBO r27, r28, 0, 4
andrewm@0: .endm
andrewm@0: 
andrewm@0: // Write to ADC TX register
andrewm@0: .macro ADC_TX
andrewm@0: .mparam data
giuliomoro@102:      SBBO data, reg_spi_addr, SPI_CH1TX, 4
andrewm@0: .endm
andrewm@0: 
andrewm@0: // Wait for SPI to finish (uses RXS indicator)
andrewm@0: .macro ADC_WAIT_FOR_FINISH
andrewm@0:  LOOP:
giuliomoro@102:      LBBO r27, reg_spi_addr, SPI_CH1STAT, 4
giuliomoro@102:      QBBC LOOP, r27, 0
andrewm@0: .endm
andrewm@0: 
andrewm@0: // Read the RX word to clear; store output
andrewm@0: .macro ADC_RX
andrewm@0: .mparam data
giuliomoro@102:      LBBO data, reg_spi_addr, SPI_CH1RX, 4
andrewm@0: .endm
andrewm@0: 
andrewm@0: // Complete ADC write+read with chip select
andrewm@0: .macro ADC_WRITE
andrewm@0: .mparam in, out
giuliomoro@102:      ADC_CS_ASSERT
giuliomoro@102:      ADC_TX in
giuliomoro@102:      ADC_WAIT_FOR_FINISH
giuliomoro@102:      ADC_RX out
giuliomoro@102:      ADC_CS_UNASSERT
andrewm@0: .endm
andrewm@0: 
giuliomoro@19: // Complete ADC write+read with chip select and also performs IO for digital
giuliomoro@16: .macro ADC_WRITE_GPIO
giuliomoro@16: .mparam in, out, do_gpio
giuliomoro@102:      ADC_CS_ASSERT
giuliomoro@102:      ADC_TX in
giuliomoro@102:      QBBC GPIO_DONE, reg_flags, FLAG_BIT_USE_DIGITAL //skip if DIGITAL is disabled
giuliomoro@188:      QBLT CASE_4_OR_8_CHANNELS, reg_num_channels, 2
giuliomoro@188: CASE_2_CHANNELS:
giuliomoro@188:      AND r27, reg_frame_current, 0x1
giuliomoro@188:      QBNE GPIO_DONE, r27, 0
giuliomoro@188:      JMP DO_GPIO
giuliomoro@188: CASE_4_OR_8_CHANNELS:
giuliomoro@102:      AND r27, do_gpio, 0x3 // only do a DIGITAL every 2 SPI I/O
giuliomoro@102:      QBNE GPIO_DONE, r27, 0 
giuliomoro@188: DO_GPIO:
giuliomoro@16: //from here to GPIO_DONE takes 1.8us, while usually ADC_WAIT_FOR_FINISH only waits for 1.14us.
giuliomoro@19: //TODO: it would be better to split the DIGITAL stuff in two parts:
giuliomoro@16: //- one taking place during DAC_WRITE which sets the GPIO_OE
giuliomoro@16: //- and the other during ADC_WRITE which actually reads DATAIN and writes CLEAR/SET DATAOUT
giuliomoro@39:                             //r27 is actually r27, so do not use r27 from here to ...
giuliomoro@102:      LBBO r27, reg_digital_current, 0, 4 
giuliomoro@102:      JAL r28.w0, DIGITAL // note that this is not called as a macro, but with JAL. r28 will contain the return address
giuliomoro@102:      SBBO r27, reg_digital_current, 0,   4 
giuliomoro@16:                             //..here you can start using r27 again
giuliomoro@102:      ADD reg_digital_current, reg_digital_current, 4 //increment pointer
giuliomoro@16: GPIO_DONE:
giuliomoro@102:      ADC_WAIT_FOR_FINISH
giuliomoro@102:      ADC_RX out
giuliomoro@102:      ADC_CS_UNASSERT
giuliomoro@16: .endm
giuliomoro@16: 
andrewm@0: // Write a McASP register
andrewm@0: .macro MCASP_REG_WRITE
andrewm@0: .mparam reg, value
giuliomoro@102:      MOV r27, value
giuliomoro@102:      SBBO r27, reg_mcasp_addr, reg, 4
andrewm@0: .endm
andrewm@0: 
andrewm@0: // Write a McASP register beyond the 0xFF boundary
andrewm@0: .macro MCASP_REG_WRITE_EXT
andrewm@0: .mparam reg, value
giuliomoro@102:      MOV r27, value
giuliomoro@102:      MOV r28, reg
giuliomoro@102:      ADD r28, reg_mcasp_addr, r28
giuliomoro@102:      SBBO r27, r28, 0, 4
andrewm@0: .endm
andrewm@0: 
andrewm@0: // Read a McASP register
andrewm@0: .macro MCASP_REG_READ
andrewm@0: .mparam reg, value
giuliomoro@102:      LBBO value, reg_mcasp_addr, reg, 4
andrewm@0: .endm
andrewm@0: 	
andrewm@0: // Read a McASP register beyond the 0xFF boundary
andrewm@0: .macro MCASP_REG_READ_EXT
andrewm@0: .mparam reg, value
giuliomoro@102:      MOV r28, reg
giuliomoro@102:      ADD r28, reg_mcasp_addr, r28
giuliomoro@102:      LBBO value, r28, 0, 4
andrewm@0: .endm
andrewm@0: 	
andrewm@0: // Set a bit and wait for it to come up
andrewm@0: .macro MCASP_REG_SET_BIT_AND_POLL
andrewm@0: .mparam reg, mask
giuliomoro@102:      MOV r27, mask
giuliomoro@102:      LBBO r28, reg_mcasp_addr, reg, 4
giuliomoro@102:      OR r28, r28, r27
giuliomoro@102:      SBBO r28, reg_mcasp_addr, reg, 4
andrewm@0: POLL:
giuliomoro@102:      LBBO r28, reg_mcasp_addr, reg, 4
giuliomoro@102:      AND r28, r28, r27
giuliomoro@102:      QBEQ POLL, r28, 0
andrewm@0: .endm
andrewm@282: 
andrewm@282: // Multiplexer Capelet: Increment channel on muxes 0-3
andrewm@282: .macro MUX_INCREMENT_0_TO_3
andrewm@282:      MOV r28, FLAG_MASK_MUX_CONFIG
andrewm@282:      AND r28, reg_flags, r28             // Check flags
andrewm@282:      QBEQ DONE, r28, 0                   // Skip if disabled
andrewm@282:      LSR r28, r28, FLAG_BIT_MUX_CONFIG0
andrewm@282:      AND r27, reg_pru1_mux_pins, 0x07    // Current mux channel in r30 bits 2-0
andrewm@282:      ADD r27, r27, 1            // Increment channel
andrewm@282:      AND r27, r27, 0x07         // Mask to 8 channels
andrewm@282:      QBEQ UPDATE, r28, 0x03
andrewm@282:      AND r27, r27, 0x03         // Mask to 4 channels
andrewm@282:      QBEQ UPDATE, r28, 0x02
andrewm@282:      AND r27, r27, 0x01         // Mask to 2 channels
andrewm@282: UPDATE:
andrewm@282:      MOV r28, 0xFFFFFFF8
andrewm@282:      AND r28, reg_pru1_mux_pins, r28  // Mask out low 3 bits of r30
andrewm@282:      OR  r28, r28, r27                // Combine with new value
andrewm@282:      MOV reg_pru1_mux_pins, r28       // Move back to r30 to propagate to pins
andrewm@282: DONE:
andrewm@282: .endm
andrewm@283: 
andrewm@283: // Multiplexer Capelet: Increment channel on muxes 4-7
andrewm@283: .macro MUX_INCREMENT_4_TO_7
andrewm@283:      MOV r28, FLAG_MASK_MUX_CONFIG
andrewm@283:      AND r28, reg_flags, r28             // Check flags
andrewm@283:      QBEQ DONE, r28, 0                   // Skip if disabled
andrewm@283:      LSR r28, r28, FLAG_BIT_MUX_CONFIG0
andrewm@283:      AND r27, reg_pru1_mux_pins, 0x38    // Current mux channel in r30 bits 5-3
andrewm@283:      ADD r27, r27, 8            // Increment channel (+1 LSB starting at bit 3)
andrewm@283:      AND r27, r27, 0x38         // Mask to 8 channels
andrewm@283:      QBEQ UPDATE, r28, 0x03
andrewm@283:      AND r27, r27, 0x18         // Mask to 4 channels
andrewm@283:      QBEQ UPDATE, r28, 0x02
andrewm@283:      AND r27, r27, 0x08         // Mask to 2 channels
andrewm@283: UPDATE:
andrewm@283:      MOV r28, 0xFFFFFFC7
andrewm@283:      AND r28, reg_pru1_mux_pins, r28  // Mask out bits 5-3 of r30
andrewm@283:      OR  r28, r28, r27                // Combine with new value
andrewm@283:      MOV reg_pru1_mux_pins, r28       // Move back to r30 to propagate to pins
andrewm@283: DONE:
andrewm@283: .endm
andrewm@283: 
andrewm@282: 	
andrewm@0: START:
andrewm@253:      // Load useful registers for addressing SPI
andrewm@253:      MOV reg_comm_addr, SHARED_COMM_MEM_BASE
andrewm@253:      MOV reg_spi_addr, SPI_BASE
andrewm@253:      MOV reg_mcasp_addr, MCASP_BASE
andrewm@253: 
andrewm@253:      // Find out which PRU we are running on
andrewm@253:      // This affects the following offsets
andrewm@253:      MOV  r0, 0x24000      // PRU1 control register offset
andrewm@253:      LBBO r2, reg_comm_addr, COMM_PRU_NUMBER, 4
andrewm@253:      QBEQ PRU_NUMBER_CHECK_DONE, r2, 1
andrewm@253:      MOV  r0, 0x22000      // PRU0 control register offset
andrewm@253: PRU_NUMBER_CHECK_DONE:	
andrewm@253: 	
giuliomoro@102:      // Set up c24 and c25 offsets with CTBIR register
giuliomoro@102:      // Thus C24 points to start of PRU0 RAM
andrewm@253:      OR  r3, r0, 0x20      // CTBIR0
giuliomoro@102:      MOV r2, 0
giuliomoro@102:      SBBO r2, r3, 0, 4
andrewm@0: 
giuliomoro@102:      // Set up c28 pointer offset for shared PRU RAM
andrewm@253:      OR r3, r0, 0x28       // CTPPR0
giuliomoro@102:      MOV r2, 0x00000120    // To get address 0x00012000
giuliomoro@102:      SBBO r2, r3, 0, 4
andrewm@0: 	
giuliomoro@102:      // Set ARM such that PRU can write to registers
giuliomoro@102:      LBCO r0, C4, 4, 4
giuliomoro@102:      CLR r0, r0, 4
giuliomoro@102:      SBCO r0, C4, 4, 4
andrewm@0: 
giuliomoro@102:      // Clear flags
giuliomoro@102:      MOV reg_flags, 0
giuliomoro@102:      // Default number of channels in case SPI disabled
giuliomoro@102:      LDI reg_num_channels, 8
andrewm@0: 
giuliomoro@102:      // Find out whether we should use DIGITAL
giuliomoro@102:      LBBO r2, reg_comm_addr, COMM_USE_DIGITAL, 4
giuliomoro@102:      QBEQ DIGITAL_INIT_DONE, r2, 0 // if we use digital
giuliomoro@102:      SET reg_flags, reg_flags, FLAG_BIT_USE_DIGITAL 
giuliomoro@38: /* This block of code is not really needed, as the memory is initialized by ARM before the PRU is started.
giuliomoro@38: Will leave it here for future reference
giuliomoro@38: DIGITAL_INIT: //set the digital buffer to 0x0000ffff (all inputs), to prevent unwanted high outputs
giuliomoro@38:               //the loop is unrolled by a factor of four just to take advantage of the speed of SBBO on larger byte bursts, but there is no real need for it
giuliomoro@102:      MOV r2, 0x0000ffff //value to store. 0x0000ffff means all inputs
giuliomoro@102:      MOV r3, MEM_DIGITAL_BASE //start of the digital buffer
giuliomoro@102:      MOV r4, MEM_DIGITAL_BASE+2*MEM_DIGITAL_BUFFER1_OFFSET //end of the digital buffer
giuliomoro@38: DIGITAL_INIT_BUFFER_LOOP:
giuliomoro@102:      SBBO r2, r3, 0, 4 
giuliomoro@102:      ADD r3, r3, 4 //increment pointer
giuliomoro@102:      QBGT DIGITAL_INIT_BUFFER_LOOP, r3, r4 //loop until we reach the end of the buffer
giuliomoro@38: */
giuliomoro@38: DIGITAL_INIT_DONE:
andrewm@282:      // Check if we should use an external multiplexer capelet
andrewm@282:      // The valid values are 0 (off), 1 (2 ch), 2 (4 ch), 3 (8 ch)
andrewm@282:      // This can only happen on PRU1 because of how the pins are mapped
andrewm@282:      LBBO r2, reg_comm_addr, COMM_PRU_NUMBER, 4
andrewm@282:      QBNE MUX_INIT_DONE, r2, 1
andrewm@282:      LBBO r2, reg_comm_addr, COMM_MUX_CONFIG, 4
andrewm@282:      AND r2, r2, 0x03
andrewm@282:      QBEQ MUX_INIT_DONE, r2, 0
andrewm@282:      // If we get here, we are using the mux. Prepare flags and initial state.
andrewm@282:      LSL r2, r2, FLAG_BIT_MUX_CONFIG0
andrewm@282:      OR  reg_flags, reg_flags, r2
andrewm@282:      // Clear lower 6 bits of r30 which controls the mux pins
andrewm@282:      MOV r2, 0xFFFFFFC0
andrewm@282:      AND reg_pru1_mux_pins, reg_pru1_mux_pins, r2
andrewm@282: MUX_INIT_DONE:
andrewm@282: 	
giuliomoro@102:      // Find out whether we should use SPI ADC and DAC
giuliomoro@102:      LBBO r2, reg_comm_addr, COMM_USE_SPI, 4
giuliomoro@102:      QBEQ SPI_FLAG_CHECK_DONE, r2, 0
giuliomoro@102:      SET reg_flags, reg_flags, FLAG_BIT_USE_SPI
andrewm@0: SPI_FLAG_CHECK_DONE:
giuliomoro@102:      // If we don't use SPI, then skip all this init
giuliomoro@102:      QBBC SPI_INIT_DONE, reg_flags, FLAG_BIT_USE_SPI
andrewm@12: 
giuliomoro@102:      // Load the number of channels: valid values are 8, 4 or 2
giuliomoro@102:      LBBO reg_num_channels, reg_comm_addr, COMM_NUM_CHANNELS, 4
giuliomoro@102:      QBGT SPI_NUM_CHANNELS_LT8, reg_num_channels, 8 // 8 > num_channels ?
giuliomoro@102:      LDI reg_num_channels, 8		// If N >= 8, N = 8
giuliomoro@102:      QBA SPI_NUM_CHANNELS_DONE
andrewm@12: SPI_NUM_CHANNELS_LT8:	
giuliomoro@102:      QBGT SPI_NUM_CHANNELS_LT4, reg_num_channels, 4 // 4 > num_channels ?
giuliomoro@102:      LDI reg_num_channels, 4		// If N >= 4, N = 4
giuliomoro@102:      QBA SPI_NUM_CHANNELS_DONE
andrewm@12: SPI_NUM_CHANNELS_LT4:
giuliomoro@102:      LDI reg_num_channels, 2		// else N = 2
andrewm@12: SPI_NUM_CHANNELS_DONE:	
andrewm@0: 	
giuliomoro@102:      // Init SPI clock
giuliomoro@102:      MOV r2, 0x02
giuliomoro@102:      MOV r3, CLOCK_BASE + CLOCK_SPI0
giuliomoro@102:      SBBO r2, r3, 0, 4
andrewm@0: 
giuliomoro@102:      // Reset SPI and wait for finish
giuliomoro@102:      MOV r2, 0x02
giuliomoro@102:      SBBO r2, reg_spi_addr, SPI_SYSCONFIG, 4
andrewm@0: 
andrewm@0: SPI_WAIT_RESET:
giuliomoro@102:      LBBO r2, reg_spi_addr, SPI_SYSSTATUS, 4
giuliomoro@102:      QBBC SPI_WAIT_RESET, r2, 0
andrewm@0: 	
giuliomoro@102:      // Turn off SPI channels
giuliomoro@102:      MOV r2, 0
giuliomoro@102:      SBBO r2, reg_spi_addr, SPI_CH0CTRL, 4
giuliomoro@102:      SBBO r2, reg_spi_addr, SPI_CH1CTRL, 4
andrewm@0:   
giuliomoro@102:      // Set to master; chip select lines enabled (CS0 used for DAC)
giuliomoro@102:      MOV r2, 0x00
giuliomoro@102:      SBBO r2, reg_spi_addr, SPI_MODULCTRL, 4
andrewm@0:   
giuliomoro@102:      // Configure CH0 for DAC
giuliomoro@102:      MOV r2, (3 << 27) | (DAC_DPE << 16) | (DAC_TRM << 12) | ((DAC_WL - 1) << 7) | (DAC_CLK_DIV << 2) | DAC_CLK_MODE | (1 << 6)
giuliomoro@102:      SBBO r2, reg_spi_addr, SPI_CH0CONF, 4
andrewm@0: 
giuliomoro@102:      // Configure CH1 for ADC
giuliomoro@102:      MOV r2, (3 << 27) | (ADC_DPE << 16) | (ADC_TRM << 12) | ((ADC_WL - 1) << 7) | (ADC_CLK_DIV << 2) | ADC_CLK_MODE
giuliomoro@102:      SBBO r2, reg_spi_addr, SPI_CH1CONF, 4
andrewm@0:    
giuliomoro@102:      // Turn on SPI channels
giuliomoro@102:      MOV r2, 0x01
giuliomoro@102:      SBBO r2, reg_spi_addr, SPI_CH0CTRL, 4
giuliomoro@102:      SBBO r2, reg_spi_addr, SPI_CH1CTRL, 4   
andrewm@0: 
giuliomoro@102:      // DAC power-on reset sequence
giuliomoro@102:      MOV r2, (0x07 << AD5668_COMMAND_OFFSET)
giuliomoro@102:      DAC_WRITE r2
andrewm@0: 
giuliomoro@102:      // Initialise ADC
giuliomoro@102:      MOV r2, AD7699_CFG_MASK | (0 << AD7699_CHANNEL_OFFSET) | (0 << AD7699_SEQ_OFFSET)
giuliomoro@102:      ADC_WRITE r2, r2
andrewm@0: 
giuliomoro@102:      // Enable DAC internal reference
giuliomoro@102:      MOV r2, (0x08 << AD5668_COMMAND_OFFSET) | (0x01 << AD5668_REF_OFFSET)
giuliomoro@102:      DAC_WRITE r2
andrewm@0: 	
giuliomoro@102:      // Read ADC ch0 and ch1: result is always 2 samples behind so start here
giuliomoro@102:      MOV r2, AD7699_CFG_MASK | (0x00 << AD7699_CHANNEL_OFFSET)
giuliomoro@102:      ADC_WRITE r2, r2
andrewm@0: 
giuliomoro@102:      MOV r2, AD7699_CFG_MASK | (0x01 << AD7699_CHANNEL_OFFSET)
giuliomoro@102:      ADC_WRITE r2, r2
andrewm@0: SPI_INIT_DONE:	
andrewm@0: 	
giuliomoro@102:     // Prepare McASP0 for audio
giuliomoro@102:     MCASP_REG_WRITE MCASP_GBLCTL, 0			// Disable McASP
giuliomoro@102:     MCASP_REG_WRITE_EXT MCASP_SRCTL0, 0		// All serialisers off
giuliomoro@102:     MCASP_REG_WRITE_EXT MCASP_SRCTL1, 0
giuliomoro@102:     MCASP_REG_WRITE_EXT MCASP_SRCTL2, 0
giuliomoro@102:     MCASP_REG_WRITE_EXT MCASP_SRCTL3, 0
giuliomoro@102:     MCASP_REG_WRITE_EXT MCASP_SRCTL4, 0
giuliomoro@102:     MCASP_REG_WRITE_EXT MCASP_SRCTL5, 0
andrewm@0: 
giuliomoro@102:     MCASP_REG_WRITE MCASP_PWRIDLESYSCONFIG, 0x02	// Power on
giuliomoro@102:     MCASP_REG_WRITE MCASP_PFUNC, 0x00		// All pins are McASP
giuliomoro@102:     MCASP_REG_WRITE MCASP_PDIR, MCASP_OUTPUT_PINS	// Set pin direction
giuliomoro@102:     MCASP_REG_WRITE MCASP_DLBCTL, 0x00
giuliomoro@102:     MCASP_REG_WRITE MCASP_DITCTL, 0x00
giuliomoro@102:     MCASP_REG_WRITE MCASP_RMASK, MCASP_DATA_MASK	// 16 bit data receive
giuliomoro@102:     MCASP_REG_WRITE MCASP_RFMT, MCASP_DATA_FORMAT	// Set data format
giuliomoro@102:     MCASP_REG_WRITE MCASP_AFSRCTL, 0x100		// I2S mode
giuliomoro@102:     MCASP_REG_WRITE MCASP_ACLKRCTL, 0x80		// Sample on rising edge
giuliomoro@102:     MCASP_REG_WRITE MCASP_AHCLKRCTL, 0x8001		// Internal clock, not inv, /2; irrelevant?
giuliomoro@102:     MCASP_REG_WRITE MCASP_RTDM, 0x03		// Enable TDM slots 0 and 1
giuliomoro@102:     MCASP_REG_WRITE MCASP_RINTCTL, 0x00		// No interrupts
giuliomoro@102:     MCASP_REG_WRITE MCASP_XMASK, MCASP_DATA_MASK	// 16 bit data transmit
giuliomoro@102:     MCASP_REG_WRITE MCASP_XFMT, MCASP_DATA_FORMAT	// Set data format
giuliomoro@102:     MCASP_REG_WRITE MCASP_AFSXCTL, 0x100		// I2S mode
giuliomoro@102:     MCASP_REG_WRITE MCASP_ACLKXCTL, 0x00		// Transmit on rising edge, sync. xmit and recv
giuliomoro@102:     MCASP_REG_WRITE MCASP_AHCLKXCTL, 0x8001		// External clock from AHCLKX
giuliomoro@102:     MCASP_REG_WRITE MCASP_XTDM, 0x03		// Enable TDM slots 0 and 1
giuliomoro@102:     MCASP_REG_WRITE MCASP_XINTCTL, 0x00		// No interrupts
andrewm@0: 	
giuliomoro@102:     MCASP_REG_WRITE_EXT MCASP_SRCTL_R, 0x02		// Set up receive serialiser
giuliomoro@102:     MCASP_REG_WRITE_EXT MCASP_SRCTL_X, 0x01		// Set up transmit serialiser
giuliomoro@102:     MCASP_REG_WRITE_EXT MCASP_WFIFOCTL, 0x00	// Disable FIFOs
giuliomoro@102:     MCASP_REG_WRITE_EXT MCASP_RFIFOCTL, 0x00
andrewm@0: 
giuliomoro@102:     MCASP_REG_WRITE MCASP_XSTAT, 0xFF		// Clear transmit errors
giuliomoro@102:     MCASP_REG_WRITE MCASP_RSTAT, 0xFF		// Clear receive errors
andrewm@0: 
giuliomoro@102:     MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 1)	// Set RHCLKRST
giuliomoro@102:     MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 9)	// Set XHCLKRST
andrewm@0: 
andrewm@0: // The above write sequence will have temporarily changed the AHCLKX frequency
andrewm@0: // The PLL needs time to settle or the sample rate will be unstable and possibly
andrewm@0: // cause an underrun. Give it ~1ms before going on.
andrewm@0: // 10ns per loop iteration = 10^-8s --> 10^5 iterations needed
andrewm@0: 
giuliomoro@102:      MOV r2, 1 << 28
giuliomoro@102:      MOV r3, GPIO1 + GPIO_SETDATAOUT
giuliomoro@102:      SBBO r2, r3, 0, 4
andrewm@0: 
andrewm@0: MOV r2, 100000
andrewm@0: MCASP_INIT_WAIT:	
giuliomoro@102:      SUB r2, r2, 1
giuliomoro@102:      QBNE MCASP_INIT_WAIT, r2, 0
andrewm@0: 
giuliomoro@102:      MOV r2, 1 << 28
giuliomoro@102:      MOV r3, GPIO1 + GPIO_CLEARDATAOUT
giuliomoro@102:      SBBO r2, r3, 0, 4
giuliomoro@102: 
andrewm@0: MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 0)	// Set RCLKRST
andrewm@0: MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 8)	// Set XCLKRST
andrewm@0: MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 2)	// Set RSRCLR
andrewm@0: MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 10)	// Set XSRCLR
andrewm@0: MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 3)	// Set RSMRST
andrewm@0: MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 11)	// Set XSMRST
andrewm@0: 
andrewm@0: MCASP_REG_WRITE_EXT MCASP_XBUF, 0x00		// Write to the transmit buffer to prevent underflow
andrewm@0: 
andrewm@0: MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 4)	// Set RFRST
andrewm@0: MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 12)	// Set XFRST
andrewm@0: 
andrewm@0: // Initialisation
giuliomoro@38:     LBBO reg_frame_total, reg_comm_addr, COMM_BUFFER_FRAMES, 4  // Total frame count (SPI; 0.5x-2x for McASP)
giuliomoro@38:     MOV reg_dac_buf0, 0                      // DAC buffer 0 start pointer
giuliomoro@38:     LSL reg_dac_buf1, reg_frame_total, 1     // DAC buffer 1 start pointer = N[ch]*2[bytes]*bufsize
giuliomoro@38:     LMBD r2, reg_num_channels, 1		 // Returns 1, 2 or 3 depending on the number of channels
giuliomoro@38:     LSL reg_dac_buf1, reg_dac_buf1, r2	 // Multiply by 2, 4 or 8 to get the N[ch] scaling above
giuliomoro@38:     MOV reg_mcasp_buf0, 0			 // McASP DAC buffer 0 start pointer
giuliomoro@38:     LSL reg_mcasp_buf1, reg_frame_total, r2  // McASP DAC buffer 1 start pointer = 2[ch]*2[bytes]*(N/4)[samples/spi]*bufsize
giuliomoro@38:     CLR reg_flags, reg_flags, FLAG_BIT_BUFFER1  // Bit 0 holds which buffer we are on
giuliomoro@38:     MOV r2, 0
giuliomoro@38:     SBBO r2, reg_comm_addr, COMM_FRAME_COUNT, 4  // Start with frame count of 0
giuliomoro@38: /* This block of code is not really needed, as the memory is initialized by ARM before the PRU is started.
giuliomoro@38: Will leave it here for future reference
giuliomoro@38: //Initialise all SPI and audio buffers (DAC0, DAC1, ADC0, ADC1) to zero.
giuliomoro@38: //This is useful for analog outs so they do not have spikes during the first buffer.
giuliomoro@38: //This is not very useful for audio, as you still hear the initial "tumpf" when the converter starts 
giuliomoro@38: //and each sample in the DAC buffer is reset to 0 after it is written to the DAC.
giuliomoro@38: 
giuliomoro@38:     QBBC SPI_INIT_BUFFER_DONE, reg_flags, FLAG_BIT_USE_SPI
giuliomoro@38: //Initialize SPI buffers
giuliomoro@38: //compute the memory offset of the end of the audio buffer and store it in r4
giuliomoro@38:     SUB r4, reg_dac_buf1, reg_dac_buf0 // length of the buffer, assumes reg_dac_buf1>ref_dac_buf0
giuliomoro@38:     LSL r4, r4, 2 //length of four buffers (DAC0, DAC1, ADC0, ADC1)
giuliomoro@38:     ADD r4, reg_dac_buf0, r4 //total offset
giuliomoro@38:     MOV r2, 0// value to store
giuliomoro@38:     MOV r3, 0 // offset counter
giuliomoro@38: SPI_INIT_BUFFER_LOOP:
giuliomoro@38:     SBCO r2, C_ADC_DAC_MEM, r3, 4
giuliomoro@38:     ADD r3, r3, 4
giuliomoro@38:     QBGT SPI_INIT_BUFFER_LOOP, r3, r4
giuliomoro@38: SPI_INIT_BUFFER_DONE:
giuliomoro@38: 
giuliomoro@38: //Initialize audio buffers
giuliomoro@38: //compute the memory offset of the end of the audio buffer and store it in r4
giuliomoro@38:     SUB r4, reg_mcasp_buf1, reg_mcasp_buf0 // length of the buffer, assumes reg_mcasp_buf1>ref_mcasp_buf0
giuliomoro@38:     LSL r4, r4, 2 //length of four buffers (DAC0, DAC1, ADC0, ADC1)
giuliomoro@38:     ADD r4, reg_mcasp_buf0, r4 //total offset
giuliomoro@38:     MOV r2, 0 // value to store
giuliomoro@38:     MOV r3, 0 // offset counter
giuliomoro@38:     MCASP_INIT_BUFFER_LOOP:
giuliomoro@38:     SBCO r2, C_MCASP_MEM, r3, 4
giuliomoro@38:     ADD r3, r3, 4
giuliomoro@38:     QBGT MCASP_INIT_BUFFER_LOOP, r3, r4
giuliomoro@38: */
andrewm@0: // Here we are out of sync by one TDM slot since the 0 word transmitted above will have occupied
andrewm@0: // the first output slot. Send one more word before jumping into the loop.
andrewm@0: MCASP_DAC_WAIT_BEFORE_LOOP:	
giuliomoro@102:      LBBO r2, reg_mcasp_addr, MCASP_XSTAT, 4
giuliomoro@102:      QBBC MCASP_DAC_WAIT_BEFORE_LOOP, r2, MCASP_XSTAT_XDATA_BIT
andrewm@0: 
giuliomoro@102:      MCASP_REG_WRITE_EXT MCASP_XBUF, 0x00
andrewm@0: 
andrewm@0: // Likewise, read and discard the first sample we get back from the ADC. This keeps the DAC and ADC
andrewm@0: // in sync in terms of which TDM slot we are reading (empirically found that we should throw this away
andrewm@0: // rather than keep it and invert the phase)
andrewm@0: MCASP_ADC_WAIT_BEFORE_LOOP:
giuliomoro@102:      LBBO r2, reg_mcasp_addr, MCASP_RSTAT, 4
giuliomoro@102:      QBBC MCASP_ADC_WAIT_BEFORE_LOOP, r2, MCASP_RSTAT_RDATA_BIT
andrewm@0: 
giuliomoro@102:      MCASP_REG_READ_EXT MCASP_RBUF, r2
andrewm@0: 	
andrewm@0: WRITE_ONE_BUFFER:
giuliomoro@38: 
giuliomoro@102:      // Write a single buffer of DAC samples and read a buffer of ADC samples
giuliomoro@102:      // Load starting positions
giuliomoro@102:      MOV reg_dac_current, reg_dac_buf0         // DAC: reg_dac_current is current pointer
giuliomoro@102:      LMBD r2, reg_num_channels, 1		// 1, 2 or 3 for 2, 4 or 8 channels
giuliomoro@102:      LSL reg_adc_current, reg_frame_total, r2
giuliomoro@102:      LSL reg_adc_current, reg_adc_current, 2   // N * 2 * 2 * bufsize
giuliomoro@102:      ADD reg_adc_current, reg_adc_current, reg_dac_current // ADC: starts N * 2 * 2 * bufsize beyond DAC
giuliomoro@102:     MOV reg_mcasp_dac_current, reg_mcasp_buf0 // McASP: set current DAC pointer
giuliomoro@102:      LSL reg_mcasp_adc_current, reg_frame_total, r2 // McASP ADC: starts (N/2)*2*2*bufsize beyond DAC
giuliomoro@102:      LSL reg_mcasp_adc_current, reg_mcasp_adc_current, 1
giuliomoro@102:      ADC reg_mcasp_adc_current, reg_mcasp_adc_current, reg_mcasp_dac_current
giuliomoro@102:      MOV reg_frame_current, 0
giuliomoro@102:      QBBS DIGITAL_BASE_CHECK_SET, reg_flags, FLAG_BIT_BUFFER1  //check which buffer we are using for DIGITAL
giuliomoro@16:                   // if we are here, we are using buffer0 
giuliomoro@102:      MOV reg_digital_current, MEM_DIGITAL_BASE
giuliomoro@102:      QBA DIGITAL_BASE_CHECK_DONE
giuliomoro@19: DIGITAL_BASE_CHECK_SET: //if we are here, we are using buffer1 
giuliomoro@102:      MOV reg_digital_current, MEM_DIGITAL_BASE+MEM_DIGITAL_BUFFER1_OFFSET //so adjust offset appropriately
giuliomoro@19: DIGITAL_BASE_CHECK_DONE:
giuliomoro@16: 
andrewm@0: WRITE_LOOP:
giuliomoro@102:      // Write N channels to DAC from successive values in memory
giuliomoro@102:      // At the same time, read N channels from ADC
giuliomoro@102:      // Unrolled by a factor of 2 to get high and low words
giuliomoro@102:      MOV r1, 0
andrewm@0: ADC_DAC_LOOP:
giuliomoro@102:      QBBC SPI_DAC_LOAD_DONE, reg_flags, FLAG_BIT_USE_SPI
giuliomoro@102:      // Load next 2 SPI DAC samples and store zero in their place
giuliomoro@102:      LBCO reg_dac_data, C_ADC_DAC_MEM, reg_dac_current, 4
giuliomoro@102:      MOV r2, 0
giuliomoro@102:      SBCO r2, C_ADC_DAC_MEM, reg_dac_current, 4
giuliomoro@102:      ADD reg_dac_current, reg_dac_current, 4
andrewm@0: SPI_DAC_LOAD_DONE:
andrewm@0: 
giuliomoro@102:      // On even iterations, load two more samples and choose the first one
giuliomoro@102:      // On odd iterations, transmit the second of the samples already loaded
giuliomoro@102:      // QBBS MCASP_DAC_HIGH_WORD, r1, 1
giuliomoro@102:      QBBS MCASP_DAC_HIGH_WORD, reg_flags, FLAG_BIT_MCASP_HWORD
andrewm@0: MCASP_DAC_LOW_WORD:	
giuliomoro@102:      // Load next 2 Audio DAC samples and store zero in their place
giuliomoro@102:      LBCO reg_mcasp_dac_data, C_MCASP_MEM, reg_mcasp_dac_current, 4
giuliomoro@102:      MOV r2, 0
giuliomoro@102:      SBCO r2, C_MCASP_MEM, reg_mcasp_dac_current, 4
giuliomoro@102:      ADD reg_mcasp_dac_current, reg_mcasp_dac_current, 4
andrewm@0: 
giuliomoro@102:      // Mask out the low word (first in little endian)
giuliomoro@102:      MOV r2, 0xFFFF
giuliomoro@102:      AND r7, reg_mcasp_dac_data, r2
andrewm@0: 	
giuliomoro@102:      QBA MCASP_WAIT_XSTAT
andrewm@0: MCASP_DAC_HIGH_WORD:
giuliomoro@102:      // Take the high word of the previously loaded data
giuliomoro@102:      LSR r7, reg_mcasp_dac_data, 16
andrewm@0: 	
giuliomoro@102:      // Every 2 channels we send one audio sample; this loop already
giuliomoro@102:      // sends exactly two SPI channels.
giuliomoro@102:      // Wait for McASP XSTAT[XDATA] to set indicating we can write more data
andrewm@0: MCASP_WAIT_XSTAT:
giuliomoro@102:      LBBO r2, reg_mcasp_addr, MCASP_XSTAT, 4
giuliomoro@102:      QBBS START, r2, MCASP_XSTAT_XUNDRN_BIT // if underrun occurred, reset the PRU
giuliomoro@102:      QBBC MCASP_WAIT_XSTAT, r2, MCASP_XSTAT_XDATA_BIT
andrewm@0: 
giuliomoro@102:      MCASP_REG_WRITE_EXT MCASP_XBUF, r7
andrewm@0: 	
giuliomoro@102:      // Same idea with ADC: even iterations, load the sample into the low word, odd
giuliomoro@102:      // iterations, load the sample into the high word and store
giuliomoro@102:      // QBBS MCASP_ADC_HIGH_WORD, r1, 1
giuliomoro@102:      QBBS MCASP_ADC_HIGH_WORD, reg_flags, FLAG_BIT_MCASP_HWORD
andrewm@0: MCASP_ADC_LOW_WORD:	
giuliomoro@102:      // Start ADC data at 0
giuliomoro@102:      LDI reg_mcasp_adc_data, 0
andrewm@0: 	
giuliomoro@102:      // Now wait for a received word to become available from the audio ADC
andrewm@0: MCASP_WAIT_RSTAT_LOW:
giuliomoro@102:      LBBO r2, reg_mcasp_addr, MCASP_RSTAT, 4
giuliomoro@102:      QBBC MCASP_WAIT_RSTAT_LOW, r2, MCASP_RSTAT_RDATA_BIT
andrewm@0: 
giuliomoro@102:      // Mask low word and store in ADC data register
giuliomoro@102:      MCASP_REG_READ_EXT MCASP_RBUF, r3
giuliomoro@102:      MOV r2, 0xFFFF
giuliomoro@102:      AND reg_mcasp_adc_data, r3, r2
giuliomoro@102:      QBA MCASP_ADC_DONE
andrewm@0: 
andrewm@0: MCASP_ADC_HIGH_WORD:	
giuliomoro@102:      // Wait for a received word to become available from the audio ADC
andrewm@0: MCASP_WAIT_RSTAT_HIGH:
giuliomoro@102:      LBBO r2, reg_mcasp_addr, MCASP_RSTAT, 4
giuliomoro@102:      QBBC MCASP_WAIT_RSTAT_HIGH, r2, MCASP_RSTAT_RDATA_BIT
andrewm@0: 
giuliomoro@102:      // Read data and shift 16 bits to the left (into the high word)
giuliomoro@102:      MCASP_REG_READ_EXT MCASP_RBUF, r3
giuliomoro@102:      LSL r3, r3, 16
giuliomoro@102:      OR reg_mcasp_adc_data, reg_mcasp_adc_data, r3
andrewm@0: 
giuliomoro@102:      // Now store the result and increment the pointer
giuliomoro@102:      SBCO reg_mcasp_adc_data, C_MCASP_MEM, reg_mcasp_adc_current, 4
giuliomoro@102:      ADD reg_mcasp_adc_current, reg_mcasp_adc_current, 4
andrewm@0: MCASP_ADC_DONE:	
giuliomoro@102:      QBBC SPI_SKIP_WRITE, reg_flags, FLAG_BIT_USE_SPI
giuliomoro@26: 
giuliomoro@102:      // DAC: transmit low word (first in little endian)
giuliomoro@102:      MOV r2, 0xFFFF
giuliomoro@102:      AND r7, reg_dac_data, r2
giuliomoro@102:      LSL r7, r7, AD5668_DATA_OFFSET
giuliomoro@102:      MOV r8, (0x03 << AD5668_COMMAND_OFFSET)
giuliomoro@102:      OR r7, r7, r8
andrewm@346:      DAC_CHANNEL_REORDER r8, r1
andrewm@346:      LSL r8, r8, AD5668_ADDRESS_OFFSET
giuliomoro@102:      OR r7, r7, r8
giuliomoro@102:      DAC_WRITE r7
andrewm@0: 
giuliomoro@102:      // Read ADC channels: result is always 2 commands behind
giuliomoro@102:      // Start by reading channel 2 (result is channel 0) and go
giuliomoro@102:      // to N+2, but masking the channel number to be between 0 and N-1
giuliomoro@102:      LDI reg_adc_data, 0
giuliomoro@102:      ADD r8, r1, 2
giuliomoro@102:      SUB r7, reg_num_channels, 1
giuliomoro@102:      AND r8, r8, r7
giuliomoro@102:      LSL r8, r8, AD7699_CHANNEL_OFFSET
giuliomoro@102:      MOV r7, AD7699_CFG_MASK
giuliomoro@102:      OR r7, r7, r8
giuliomoro@38: 
giuliomoro@102:      ADC_WRITE_GPIO r7, r7, r1
andrewm@0: 
giuliomoro@102:      // Mask out only the relevant 16 bits and store in reg_adc_data
giuliomoro@102:      MOV r2, 0xFFFF
giuliomoro@102:      AND reg_adc_data, r7, r2
giuliomoro@102:      // Increment channel index
giuliomoro@102:      ADD r1, r1, 1
andrewm@0: 
giuliomoro@102:      // DAC: transmit high word (second in little endian)
giuliomoro@102:      LSR r7, reg_dac_data, 16
giuliomoro@102:      LSL r7, r7, AD5668_DATA_OFFSET
giuliomoro@102:      MOV r8, (0x03 << AD5668_COMMAND_OFFSET)
giuliomoro@102:      OR r7, r7, r8
andrewm@346:      DAC_CHANNEL_REORDER r8, r1
andrewm@346:      LSL r8, r8, AD5668_ADDRESS_OFFSET
giuliomoro@102:      OR r7, r7, r8
giuliomoro@102:      DAC_WRITE r7
andrewm@0: 
giuliomoro@102:      // Read ADC channels: result is always 2 commands behind
giuliomoro@102:      // Start by reading channel 2 (result is channel 0) and go
giuliomoro@102:      // to N+2, but masking the channel number to be between 0 and N-1
giuliomoro@102:      ADD r8, r1, 2
giuliomoro@102:      SUB r7, reg_num_channels, 1
giuliomoro@102:      AND r8, r8, r7
giuliomoro@102:      LSL r8, r8, AD7699_CHANNEL_OFFSET
giuliomoro@102:      MOV r7, AD7699_CFG_MASK
giuliomoro@102:      OR r7, r7, r8
giuliomoro@102:      ADC_WRITE r7, r7
andrewm@0: 
giuliomoro@102:      // Move this result up to the 16 high bits
giuliomoro@102:      LSL r7, r7, 16
giuliomoro@102:      OR reg_adc_data, reg_adc_data, r7
andrewm@0: 
giuliomoro@102:      // Store 2 ADC words in memory
giuliomoro@102:      SBCO reg_adc_data, C_ADC_DAC_MEM, reg_adc_current, 4
giuliomoro@102:      ADD reg_adc_current, reg_adc_current, 4
andrewm@0: 
andrewm@282:      // If enabled, update the multiplexer settings
andrewm@282:      QBNE MUX_0_3_DONE, r1, 3    // Change mux settings for ch0-3 after reading ch. 3
andrewm@282:      MUX_INCREMENT_0_TO_3
andrewm@282: MUX_0_3_DONE:
andrewm@283:      QBNE MUX_4_7_DONE, r1, 7    // Change mux settings for ch4-7 after reading ch. 7
andrewm@283:      MUX_INCREMENT_4_TO_7
andrewm@283: MUX_4_7_DONE:
andrewm@282: 	
giuliomoro@102:      // Toggle the high/low word for McASP control (since we send one word out of
giuliomoro@102:      // 32 bits for each pair of SPI channels)
giuliomoro@102:      XOR reg_flags, reg_flags, (1 << FLAG_BIT_MCASP_HWORD)
giuliomoro@102: 	
giuliomoro@102:      // Repeat 4 times for 8 channels (2 samples per loop, r1 += 1 already happened)
giuliomoro@102:      // For 4 or 2 channels, repeat 2 or 1 times, according to flags
giuliomoro@102:      ADD r1, r1, 1
giuliomoro@102:      QBNE ADC_DAC_LOOP, r1, reg_num_channels
giuliomoro@102:      QBA ADC_DAC_LOOP_DONE
giuliomoro@102: SPI_SKIP_WRITE:
giuliomoro@102:      // We get here only if the SPI ADC and DAC are disabled
giuliomoro@102:      // Just keep the loop going for McASP
andrewm@0: 
giuliomoro@102:      // Toggle the high/low word for McASP control (since we send one word out of
giuliomoro@102:      // 32 bits for each pair of SPI channels)
giuliomoro@102:      XOR reg_flags, reg_flags, (1 << FLAG_BIT_MCASP_HWORD)
andrewm@12: 
giuliomoro@102:      ADD r1, r1, 2
giuliomoro@102:      QBNE ADC_DAC_LOOP, r1, reg_num_channels
andrewm@0: 	
andrewm@282: ADC_DAC_LOOP_DONE:
giuliomoro@102:      // Increment number of frames, see if we have more to write
giuliomoro@102:      ADD reg_frame_current, reg_frame_current, 1
giuliomoro@102:      QBNE WRITE_LOOP, reg_frame_current, reg_frame_total
andrewm@0: 
andrewm@0: WRITE_LOOP_DONE:
giuliomoro@102:      // Now done, swap the buffers and do the next one
giuliomoro@102:      // Use r2 as a temp register
giuliomoro@102:      MOV r2, reg_dac_buf0
giuliomoro@102:      MOV reg_dac_buf0, reg_dac_buf1
giuliomoro@102:      MOV reg_dac_buf1, r2
giuliomoro@102:      MOV r2, reg_mcasp_buf0
giuliomoro@102:      MOV reg_mcasp_buf0, reg_mcasp_buf1
giuliomoro@102:      MOV reg_mcasp_buf1, r2
giuliomoro@102:      XOR reg_flags, reg_flags, (1 << FLAG_BIT_BUFFER1) //flip the buffer flag
andrewm@0: 
andrewm@346:      // If multiplexer capelet is enabled, save which channel we got to
andrewm@346:      // Muxes 0-3 change at a different time than muxes 4-7 but the first
andrewm@346:      // of these is sufficient to capture where we are
andrewm@346:      MOV r2, FLAG_MASK_MUX_CONFIG
andrewm@346:      AND r2, reg_flags, r2             
andrewm@346:      QBEQ MUX_CHANNEL_SAVE_DONE, r2, 0
andrewm@346:      AND r2, reg_pru1_mux_pins, 0x03
andrewm@346:      SBBO r2, reg_comm_addr, COMM_MUX_END_CHANNEL, 4
andrewm@346: MUX_CHANNEL_SAVE_DONE:	
andrewm@346: 	
giuliomoro@102:      // Notify ARM of buffer swap
giuliomoro@102:      AND r2, reg_flags, (1 << FLAG_BIT_BUFFER1)    // Mask out every but low bit
giuliomoro@102:      SBBO r2, reg_comm_addr, COMM_CURRENT_BUFFER, 4
giuliomoro@102:      MOV R31.b0, PRU1_ARM_INTERRUPT + 16           // Interrupt to host loop
andrewm@45: 	
giuliomoro@102:      // Increment the frame count in the comm buffer (for status monitoring)
giuliomoro@102:      LBBO r2, reg_comm_addr, COMM_FRAME_COUNT, 4
giuliomoro@102:      ADD r2, r2, reg_frame_total
giuliomoro@102:      SBBO r2, reg_comm_addr, COMM_FRAME_COUNT, 4
andrewm@0: 
giuliomoro@102:      // If LED blink enabled, toggle every 4096 frames
giuliomoro@102:      LBBO r3, reg_comm_addr, COMM_LED_ADDRESS, 4
giuliomoro@102:      QBEQ LED_BLINK_DONE, r3, 0	
giuliomoro@102:      MOV r1, 0x1000
giuliomoro@102:      AND r2, r2, r1          // Test (frame count & 4096)
giuliomoro@102:      QBEQ LED_BLINK_OFF, r2, 0
giuliomoro@102:      LBBO r2, reg_comm_addr, COMM_LED_PIN_MASK, 4	
giuliomoro@102:      MOV r1, GPIO_SETDATAOUT
giuliomoro@102:      ADD r3, r3, r1          // Address for GPIO set register
giuliomoro@102:      SBBO r2, r3, 0, 4       // Set GPIO pin
giuliomoro@102:      QBA LED_BLINK_DONE
andrewm@0: LED_BLINK_OFF:
giuliomoro@102:      LBBO r2, reg_comm_addr, COMM_LED_PIN_MASK, 4
giuliomoro@102:      MOV r1, GPIO_CLEARDATAOUT
giuliomoro@102:      ADD r3, r3, r1          // Address for GPIO clear register
giuliomoro@102:      SBBO r2, r3, 0, 4       // Clear GPIO pin	
andrewm@0: LED_BLINK_DONE:	
giuliomoro@102:      // Check if we should finish: flag is zero as long as it should run
giuliomoro@102:      LBBO r2, reg_comm_addr, COMM_SHOULD_STOP, 4
giuliomoro@102:      QBEQ WRITE_ONE_BUFFER, r2, 0
andrewm@0: 
andrewm@0: CLEANUP:
giuliomoro@102:      MCASP_REG_WRITE MCASP_GBLCTL, 0x00	// Turn off McASP
andrewm@0: 
giuliomoro@102:      // Turn off SPI if enabled
giuliomoro@102:      QBBC SPI_CLEANUP_DONE, reg_flags, FLAG_BIT_USE_SPI
andrewm@0: 	
giuliomoro@102:      MOV r3, SPI_BASE + SPI_CH0CONF
giuliomoro@102:      LBBO r2, r3, 0, 4
giuliomoro@102:      CLR r2, r2, 13
giuliomoro@102:      CLR r2, r2, 27
giuliomoro@102:      SBBO r2, r3, 0, 4
andrewm@0: 
giuliomoro@102:      MOV r3, SPI_BASE + SPI_CH0CTRL
giuliomoro@102:      LBBO r2, r3, 0, 4
giuliomoro@102:      CLR r2, r2, 1
giuliomoro@102:      SBBO r2, r3, 0, 4      
andrewm@0: SPI_CLEANUP_DONE:
giuliomoro@102:      // Signal the ARM that we have finished 
giuliomoro@102:      MOV R31.b0, PRU0_ARM_INTERRUPT + 16
giuliomoro@102:      HALT