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 giuliomoro@16: 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@282: andrewm@282: #define FLAG_BIT_MUX_STATE0 10 // Current state of the mux channels andrewm@282: #define FLAG_BIT_MUX_STATE1 11 andrewm@282: #define FLAG_BIT_MUX_STATE2 12 andrewm@282: #define FLAG_MASK_MUX_STATE 0x1C00 andrewm@282: 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@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 giuliomoro@102: LSL r8, r1, 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 giuliomoro@102: LSL r8, r1, 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: 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