diff --git a/examples/xua_lite_example/Makefile b/examples/xua_lite_example/Makefile new file mode 100644 index 00000000..c52597dd --- /dev/null +++ b/examples/xua_lite_example/Makefile @@ -0,0 +1,22 @@ +APP_NAME = + +TARGET = RPI_HAT_60QFN.xn + +# The flags passed to xcc when building the application +XCC_FLAGS = -fcomment-asm -Xmapper --map -Xmapper MAPFILE -O3 -report \ + -g -Wno-unused-function -Wno-timing -DXUD_SERIES_SUPPORT=XUD_X200_SERIES -DUSB_TILE=tile[0] + +#-DSDA_HIGH=2 -DSCL_HIGH=1 -fxscope + +# The USED_MODULES variable lists other module used by the application. These +# modules will extend the SOURCE_DIRS, INCLUDE_DIRS and LIB_DIRS variables. +# Modules are expected to be in the directory above the BASE_DIR directory. +USED_MODULES = lib_xua lib_i2s lib_xud lib_i2c lib_gpio + +#============================================================================= +# The following part of the Makefile includes the common build infrastructure +# for compiling XMOS applications. You should not need to edit below here. + +XMOS_MAKE_PATH ?= ../.. +include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common + diff --git a/examples/xua_lite_example/src/AudioConfig.h b/examples/xua_lite_example/src/AudioConfig.h new file mode 100755 index 00000000..9843cb1d --- /dev/null +++ b/examples/xua_lite_example/src/AudioConfig.h @@ -0,0 +1,15 @@ +#ifndef _AUDIO_CONFIG_ +#define _AUDIO_CONFIG_ + +void ConfigAudioPorts(unsigned divide); + +void AudioHwInit(); + +void PLL_Init(void); + +/* Configures master clock and codc for passed sample freq */ +void AudioHwConfig(unsigned samFreq); + +void ConfigCodec24576(unsigned samFeq); + +#endif diff --git a/examples/xua_lite_example/src/AudioConfig.xc b/examples/xua_lite_example/src/AudioConfig.xc new file mode 100755 index 00000000..e42a09db --- /dev/null +++ b/examples/xua_lite_example/src/AudioConfig.xc @@ -0,0 +1,220 @@ +#include +#include +#include +#include +#include + + + +// TLV320DAC3101 Device I2C Address +#define DAC3101_I2C_DEVICE_ADDR 0x18 + +// TLV320DAC3101 Register Addresses +// Page 0 +#define DAC3101_PAGE_CTRL 0x00 // Register 0 - Page Control +#define DAC3101_SW_RST 0x01 // Register 1 - Software Reset +#define DAC3101_CLK_GEN_MUX 0x04 // Register 4 - Clock-Gen Muxing +#define DAC3101_PLL_P_R 0x05 // Register 5 - PLL P and R Values +#define DAC3101_PLL_J 0x06 // Register 6 - PLL J Value +#define DAC3101_PLL_D_MSB 0x07 // Register 7 - PLL D Value (MSB) +#define DAC3101_PLL_D_LSB 0x08 // Register 8 - PLL D Value (LSB) +#define DAC3101_NDAC_VAL 0x0B // Register 11 - NDAC Divider Value +#define DAC3101_MDAC_VAL 0x0C // Register 12 - MDAC Divider Value +#define DAC3101_DOSR_VAL_LSB 0x0E // Register 14 - DOSR Divider Value (LS Byte) +#define DAC3101_CLKOUT_MUX 0x19 // Register 25 - CLKOUT MUX +#define DAC3101_CLKOUT_M_VAL 0x1A // Register 26 - CLKOUT M_VAL +#define DAC3101_CODEC_IF 0x1B // Register 27 - CODEC Interface Control +#define DAC3101_DAC_DAT_PATH 0x3F // Register 63 - DAC Data Path Setup +#define DAC3101_DAC_VOL 0x40 // Register 64 - DAC Vol Control +#define DAC3101_DACL_VOL_D 0x41 // Register 65 - DAC Left Digital Vol Control +#define DAC3101_DACR_VOL_D 0x42 // Register 66 - DAC Right Digital Vol Control +#define DAC3101_GPIO1_IO 0x33 // Register 51 - GPIO1 In/Out Pin Control +// Page 1 +#define DAC3101_HP_DRVR 0x1F // Register 31 - Headphone Drivers +#define DAC3101_SPK_AMP 0x20 // Register 32 - Class-D Speaker Amp +#define DAC3101_HP_DEPOP 0x21 // Register 33 - Headphone Driver De-pop +#define DAC3101_DAC_OP_MIX 0x23 // Register 35 - DAC_L and DAC_R Output Mixer Routing +#define DAC3101_HPL_VOL_A 0x24 // Register 36 - Analog Volume to HPL +#define DAC3101_HPR_VOL_A 0x25 // Register 37 - Analog Volume to HPR +#define DAC3101_SPKL_VOL_A 0x26 // Register 38 - Analog Volume to Left Speaker +#define DAC3101_SPKR_VOL_A 0x27 // Register 39 - Analog Volume to Right Speaker +#define DAC3101_HPL_DRVR 0x28 // Register 40 - Headphone Left Driver +#define DAC3101_HPR_DRVR 0x29 // Register 41 - Headphone Right Driver +#define DAC3101_SPKL_DRVR 0x2A // Register 42 - Left Class-D Speaker Driver +#define DAC3101_SPKR_DRVR 0x2B // Register 43 - Right Class-D Speaker Driver + +// TLV320DAC3101 easy register access defines +//#define DAC3101_REGREAD(reg, data) {data[0] = 0xAA; i2c_master_read_reg(DAC3101_I2C_DEVICE_ADDR, reg, data, 1, i2c);} +//#define DAC3101_REGWRITE(reg, val) {data[0] = val; i2c_master_write_reg(DAC3101_I2C_DEVICE_ADDR, reg, data, 1, i2c);} + +//#define DAC3101_REGWRITE(reg, val) {i_i2c[0].write_reg(DAC3101_I2C_DEVICE_ADDR, reg, val);} + +// TLV320DAC3101 easy register access defines +//#define DAC3101_REGWRITE(reg, val) {data[0] = val; i2c_master_write_reg(DAC3101_I2C_DEVICE_ADDR, reg, data, 1, i2c);} +#define DAC3101_REGWRITE(reg, val) + + +void AudioHwConfigure(unsigned samFreq) +{ + + // Take DAC out of reset. + //p_gpio <: 1; + + par + { + { + unsigned char data[1] = {0}; + + // Wait for 1ms + delay_milliseconds(1); + + // Set register page to 0 + DAC3101_REGWRITE(DAC3101_PAGE_CTRL, 0x00); + // Initiate SW reset (PLL is powered off as part of reset) + DAC3101_REGWRITE(DAC3101_SW_RST, 0x01); + + // so I've got 24MHz in to PLL, I want 24.576MHz or 22.5792MHz out. + + // I will always be using fractional-N (D != 0) so we must set R = 1 + // PLL_CLKIN/P must be between 10 and 20MHz so we must set P = 2 + + // PLL_CLK = CLKIN * ((RxJ.D)/P) + // We know R = 1, P = 2. + // PLL_CLK = CLKIN * (J.D / 2) + + // For 24.576MHz: + // J = 8 + // D = 1920 + // So PLL_CLK = 24 * (8.192/2) = 24 x 4.096 = 98.304MHz + // Then: + // NDAC = 4 + // MDAC = 4 + // DOSR = 128 + // So: + // DAC_CLK = PLL_CLK / 4 = 24.576MHz. + // DAC_MOD_CLK = DAC_CLK / 4 = 6.144MHz. + // DAC_FS = DAC_MOD_CLK / 128 = 48kHz. + + // For 22.5792MHz: + // J = 7 + // D = 5264 + // So PLL_CLK = 24 * (7.5264/2) = 24 x 3.7632 = 90.3168MHz + // Then: + // NDAC = 4 + // MDAC = 4 + // DOSR = 128 + // So: + // DAC_CLK = PLL_CLK / 4 = 22.5792MHz. + // DAC_MOD_CLK = DAC_CLK / 4 = 5.6448MHz. + // DAC_FS = DAC_MOD_CLK / 128 = 44.1kHz. + + /* Sample frequency dependent register settings */ + if ((samFreq % 11025) == 0) + { + // MCLK = 22.5792MHz (44.1,88.2,176.4kHz) + // Set PLL J Value to 7 + DAC3101_REGWRITE(DAC3101_PLL_J, 0x07); + // Set PLL D to 5264 ... (0x1490) + // Set PLL D MSB Value to 0x14 + DAC3101_REGWRITE(DAC3101_PLL_D_MSB, 0x14); + // Set PLL D LSB Value to 0x90 + DAC3101_REGWRITE(DAC3101_PLL_D_LSB, 0x90); + + } + else if ((samFreq % 8000) == 0) + { + // MCLK = 24.576MHz (48,96,192kHz) + // Set PLL J Value to 8 + DAC3101_REGWRITE(DAC3101_PLL_J, 0x08); + // Set PLL D to 1920 ... (0x780) + // Set PLL D MSB Value to 0x07 + DAC3101_REGWRITE(DAC3101_PLL_D_MSB, 0x07); + // Set PLL D LSB Value to 0x80 + DAC3101_REGWRITE(DAC3101_PLL_D_LSB, 0x80); + } + else + { + //debug_printf("Unrecognised sample freq of %d in ConfigCodec\n", samFreq); + } + + delay_milliseconds(1); + + // Set PLL_CLKIN = MCLK (device pin), CODEC_CLKIN = PLL_CLK (generated on-chip) + DAC3101_REGWRITE(DAC3101_CLK_GEN_MUX, 0x03); + + // Set PLL P and R values and power up. + DAC3101_REGWRITE(DAC3101_PLL_P_R, 0xA1); + // Set NDAC clock divider to 4 and power up. + DAC3101_REGWRITE(DAC3101_NDAC_VAL, 0x84); + // Set MDAC clock divider to 4 and power up. + DAC3101_REGWRITE(DAC3101_MDAC_VAL, 0x84); + // Set OSR clock divider to 128. + DAC3101_REGWRITE(DAC3101_DOSR_VAL_LSB, 0x80); + + // Set CLKOUT Mux to DAC_CLK + DAC3101_REGWRITE(DAC3101_CLKOUT_MUX, 0x04); + // Set CLKOUT M divider to 1 and power up. + DAC3101_REGWRITE(DAC3101_CLKOUT_M_VAL, 0x81); + // Set GPIO1 output to come from CLKOUT output. + DAC3101_REGWRITE(DAC3101_GPIO1_IO, 0x10); + + // Set CODEC interface mode: I2S, 24 bit, slave mode (BCLK, WCLK both inputs). + DAC3101_REGWRITE(DAC3101_CODEC_IF, 0x20); + // Set register page to 1 + DAC3101_REGWRITE(DAC3101_PAGE_CTRL, 0x01); + // Program common-mode voltage to mid scale 1.65V. + DAC3101_REGWRITE(DAC3101_HP_DRVR, 0x14); + // Program headphone-specific depop settings. + // De-pop, Power on = 800 ms, Step time = 4 ms + DAC3101_REGWRITE(DAC3101_HP_DEPOP, 0x4E); + // Program routing of DAC output to the output amplifier (headphone/lineout or speaker) + // LDAC routed to left channel mixer amp, RDAC routed to right channel mixer amp + DAC3101_REGWRITE(DAC3101_DAC_OP_MIX, 0x44); + // Unmute and set gain of output driver + // Unmute HPL, set gain = 0 db + DAC3101_REGWRITE(DAC3101_HPL_DRVR, 0x06); + // Unmute HPR, set gain = 0 dB + DAC3101_REGWRITE(DAC3101_HPR_DRVR, 0x06); + // Unmute Left Class-D, set gain = 12 dB + DAC3101_REGWRITE(DAC3101_SPKL_DRVR, 0x0C); + // Unmute Right Class-D, set gain = 12 dB + DAC3101_REGWRITE(DAC3101_SPKR_DRVR, 0x0C); + // Power up output drivers + // HPL and HPR powered up + DAC3101_REGWRITE(DAC3101_HP_DRVR, 0xD4); + // Power-up L and R Class-D drivers + DAC3101_REGWRITE(DAC3101_SPK_AMP, 0xC6); + // Enable HPL output analog volume, set = -9 dB + DAC3101_REGWRITE(DAC3101_HPL_VOL_A, 0x92); + // Enable HPR output analog volume, set = -9 dB + DAC3101_REGWRITE(DAC3101_HPR_VOL_A, 0x92); + // Enable Left Class-D output analog volume, set = -9 dB + DAC3101_REGWRITE(DAC3101_SPKL_VOL_A, 0x92); + // Enable Right Class-D output analog volume, set = -9 dB + DAC3101_REGWRITE(DAC3101_SPKR_VOL_A, 0x92); + + delay_milliseconds(100); + + // Power up DAC + // Set register page to 0 + DAC3101_REGWRITE(DAC3101_PAGE_CTRL, 0x00); + // Power up DAC channels and set digital gain + // Powerup DAC left and right channels (soft step enabled) + DAC3101_REGWRITE(DAC3101_DAC_DAT_PATH, 0xD4); + // DAC Left gain = 0dB + DAC3101_REGWRITE(DAC3101_DACL_VOL_D, 0x00); + // DAC Right gain = 0dB + DAC3101_REGWRITE(DAC3101_DACR_VOL_D, 0x00); + // Unmute digital volume control + // Unmute DAC left and right channels + DAC3101_REGWRITE(DAC3101_DAC_VOL, 0x00); + + // Shutdown + //i_i2c[0].shutdown(); + } + } /* par */ +} + +//These are here just to silence compiler warnings +void AudioHwInit(){} +void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC){} \ No newline at end of file diff --git a/examples/xua_lite_example/src/RPI_HAT_60QFN.xn b/examples/xua_lite_example/src/RPI_HAT_60QFN.xn new file mode 100644 index 00000000..c0c3f85c --- /dev/null +++ b/examples/xua_lite_example/src/RPI_HAT_60QFN.xn @@ -0,0 +1,66 @@ + + + Device + XVF3510 Device + + + tileref tile[2] + tileref usb_tile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/xua_lite_example/src/app_xua_lite.xc b/examples/xua_lite_example/src/app_xua_lite.xc new file mode 100644 index 00000000..8e5fea8e --- /dev/null +++ b/examples/xua_lite_example/src/app_xua_lite.xc @@ -0,0 +1,106 @@ +// Copyright (c) 2017-2018, XMOS Ltd, All rights reserved + +// A very simple *example* of a USB audio application (and as such is un-verified for production) + +#include +#include + +#include "xua.h" +#include "i2s.h" +#include "i2c.h" +#include "gpio.h" + +// Port declarations. Note, the defines come from the xn file +on tile[0]: buffered out port:32 p_i2s_dac[] = {XS1_PORT_1M}; //DAC +on tile[0]: buffered in port:32 p_i2s_adc[] = {XS1_PORT_1N}; //Unused currently +on tile[0]: buffered out port:32 p_lrclk = XS1_PORT_1O; //I2S Bit-clock +on tile[0]: out port p_bclk = XS1_PORT_1P; //I2S L/R-clock + +// Master clock for the audio IO tile +on tile[0]: in port p_mclk_in = XS1_PORT_1A; + +// Resources for USB feedback +on tile[0]: in port p_for_mclk_count= XS1_PORT_16A; // Extra port for counting master clock ticks + +// [0] : DAC_RESET_N +// [1] : I2C_INTERRUPT_N +// [2] : MUTE_EN +// [3] : LED +on tile[0]: out port p_gpio = XS1_PORT_4D; + +on tile[1]: port p_scl = XS1_PORT_1C; +on tile[1]: port p_sda = XS1_PORT_1D; + + +// Clock-block declarations +clock clk_audio_bclk = on tile[0]: XS1_CLKBLK_2; // Bit clock +clock clk_audio_mclk = on tile[0]: XS1_CLKBLK_3; // Master clock + +// Endpoint type tables - informs XUD what the transfer types for each Endpoint in use and also +// if the endpoint wishes to be informed of USB bus resets + +XUD_EpType epTypeTableOut[] = {XUD_EPTYPE_CTL | XUD_STATUS_ENABLE, XUD_EPTYPE_ISO}; +XUD_EpType epTypeTableIn[] = {XUD_EPTYPE_CTL | XUD_STATUS_ENABLE, XUD_EPTYPE_ISO, XUD_EPTYPE_ISO}; + +void XUA_Buffer_lite(chanend c_aud_out, chanend c_feedback, chanend c_aud_in, chanend c_sof, chanend c_aud_ctl, in port p_for_mclk_count, chanend c_aud_host); +[[distributable]] +void AudioHub(server i2s_frame_callback_if i2s, chanend c_aud, client i2c_master_if i2c, client output_gpio_if dac_reset); + +int main() +{ + // Channels for lib_xud + chan c_ep_out[2]; + chan c_ep_in[3]; + + // Channel for communicating SOF notifications from XUD to the Buffering cores + chan c_sof; + + interface i2s_frame_callback_if i_i2s; + interface i2c_master_if i_i2c[1]; + interface output_gpio_if i_gpio[1]; + + chan c_audio; + + + // Channel for communicating control messages from EP0 to the rest of the device (via the buffering cores) + chan c_aud_ctl; + + par + { + on tile[0]: { + // Connect master-clock clock-block to clock-block pin + set_clock_src(clk_audio_mclk, p_mclk_in); // Clock clock-block from mclk pin + set_port_clock(p_for_mclk_count, clk_audio_mclk); // Clock the "count" port from the clock block + start_clock(clk_audio_mclk); // Set the clock off running + + + par { + // Low level USB device layer core + XUD_Main(c_ep_out, 2, c_ep_in, 3, + c_sof, epTypeTableOut, epTypeTableIn, + null, null, -1 , + XUD_SPEED_FS, XUD_PWR_BUS); + + // Endpoint 0 core from lib_xua + // Note, since we are not using many features we pass in null for quite a few params.. + XUA_Endpoint0(c_ep_out[0], c_ep_in[0], c_aud_ctl, null, null, null, null); + + // Buffering cores - handles audio data to/from EP's and gives/gets data to/from the audio I/O core + XUA_Buffer_lite(c_ep_out[1], c_ep_in[2], c_ep_in[1], c_sof, c_aud_ctl, p_for_mclk_count, c_audio); + + i2s_frame_master(i_i2s, p_i2s_dac, 1, p_i2s_adc, 1, p_bclk, p_lrclk, p_mclk_in, clk_audio_bclk); + [[distribute]]AudioHub(i_i2s, c_audio, i_i2c[0], i_gpio[0]); + [[distribute]]output_gpio(i_gpio, 1, p_gpio, null); + } + } + on tile[1]:{ + par{ + i2c_master(i_i2c, 1, p_scl, p_sda, 100); + } + } + } + + return 0; +} + + diff --git a/examples/xua_lite_example/src/audio_hub.xc b/examples/xua_lite_example/src/audio_hub.xc new file mode 100644 index 00000000..32b9e639 --- /dev/null +++ b/examples/xua_lite_example/src/audio_hub.xc @@ -0,0 +1,44 @@ +#include "i2s.h" +#include "i2c.h" +#include "gpio.h" +#include "xua.h" + +[[distributable]] +void AudioHub(server i2s_frame_callback_if i2s, + chanend c_aud, + client i2c_master_if i2c, + client output_gpio_if dac_reset) +{ + int32_t samples[8] = {0}; // Array used for looping back samples + while (1) { + select { + case i2s.init(i2s_config_t &?i2s_config, tdm_config_t &?tdm_config): + i2s_config.mode = I2S_MODE_I2S; + i2s_config.mclk_bclk_ratio = (MCLK_48/DEFAULT_FREQ)/64; + + // Set CODECs in reset + dac_reset.output(0); + + // Allow reset to assert + delay_milliseconds(1); + + // Take CODECs out of reset + dac_reset.output(1); + + //reset_codecs(i2c); + break; + + case i2s.receive(size_t n_chans, int32_t in_samps[n_chans]): + for (int i = 0; i < n_chans; i++) samples[i] = in_samps[i]; // copy samples + break; + + case i2s.send(size_t n_chans, int32_t out_samps[n_chans]): + for (int i = 0; i < n_chans; i++) out_samps[i] = samples[i]; // copy samples + break; + + case i2s.restart_check() -> i2s_restart_t restart: + restart = I2S_NO_RESTART; // Keep on looping + break; + } + } +} diff --git a/examples/xua_lite_example/src/xua_buffer.xc b/examples/xua_lite_example/src/xua_buffer.xc new file mode 100644 index 00000000..34508910 --- /dev/null +++ b/examples/xua_lite_example/src/xua_buffer.xc @@ -0,0 +1,274 @@ +#include +#include "xua_commands.h" +#include "xud.h" +#include "testct_byref.h" +#include "debug_print.h" +#include "xua_conf.h" +//#include "fifo_impl.h" //xua_conf.h must be included before hand so that we have FIFO sizes + +//Currently only single frequency supported +#define NOMINAL_SR_DEVICE DEFAULT_FREQ +#define NOMINAL_SR_HOST DEFAULT_FREQ + +#define DIV_ROUND_UP(n, d) (n / d + 1) //Always rounds up to the next integer. Needed for 48001Hz case etc. +#define BIGGEST(a, b) (a > b ? a : b) + +#define SOF_FREQ_HZ (8000 - ((2 - AUDIO_CLASS) * 7000) ) + +//Defines for endpoint buffer sizes. Samples is total number of samples across all channels +#define MAX_OUT_SAMPLES_PER_SOF_PERIOD (DIV_ROUND_UP(MAX_FREQ, SOF_FREQ_HZ) * NUM_USB_CHAN_OUT) +#define MAX_IN_SAMPLES_PER_SOF_PERIOD (DIV_ROUND_UP(MAX_FREQ, SOF_FREQ_HZ) * NUM_USB_CHAN_IN) +#define MAX_OUTPUT_SLOT_SIZE 4 +#define MAX_INPUT_SLOT_SIZE 4 + +#define OUT_AUDIO_BUFFER_SIZE_BYTES (MAX_OUT_SAMPLES_PER_SOF_PERIOD * MAX_OUTPUT_SLOT_SIZE) +#define IN_AUDIO_BUFFER_SIZE_BYTES (MAX_IN_SAMPLES_PER_SOF_PERIOD * MAX_INPUT_SLOT_SIZE) + +//Helper to disassemble USB packets into 32b left aligned audio samples +static inline void unpack_buff_to_samples(unsigned char input[], const unsigned n_samples, const unsigned slot_size, int output[]){ + switch(slot_size){ + case 4: + for (int i = 0; i < n_samples; i++){ + unsigned base = i * 4; + output[i] = (input[base + 3] << 24) | (input[base + 2] << 16) | (input[base + 1] << 8) | input[base + 0]; + } + break; + case 3: + for (int i = 0; i < n_samples; i++){ + unsigned base = i * 3; + output[i] = (input[base + 2] << 24) | (input[base + 1] << 16) | (input[base + 0] << 8); + } + break; + case 2: + for (int i = 0; i < n_samples; i++){ + unsigned base = i * 2; + output[i] = (input[base + 1] << 24) | (input[base + 0] << 16); + } + break; + default: + debug_printf("Invalid slot_size\n"); + break; + } +} + +//Helper to assemble USB packets from 32b left aligned audio samples +static inline void pack_samples_to_buff(int input[], const unsigned n_samples, const unsigned slot_size, unsigned char output[]){ + switch(slot_size){ + case 4: + for (int i = 0; i < n_samples; i++){ + unsigned base = i * 4; + unsigned in_word = (unsigned)input[i]; + output[base + 0] = in_word & 0xff; + output[base + 1] = (in_word & 0xff00) >> 8; + output[base + 2] = (in_word & 0xff0000) >> 16; + output[base + 3] = (in_word) >> 24; + } + break; + case 3: + for (int i = 0; i < n_samples; i++){ + unsigned base = i * 3; + unsigned in_word = (unsigned)input[i]; + output[base + 0] = (in_word & 0xff00) >> 8; + output[base + 1] = (in_word & 0xff0000) >> 16; + output[base + 2] = (in_word) >> 24; + } + break; + case 2: + for (int i = 0; i < n_samples; i++){ + unsigned base = i * 2; + unsigned in_word = (unsigned)input[i]; + output[base + 0] = (in_word & 0xff0000) >> 16; + output[base + 1] = (in_word) >> 24; + } + break; + default: + debug_printf("Invalid slot_size\n"); + break; + } +} + +//Shared memory buffers between buffer task and audio side +int asrc_to_host_sample_buffer[MAX_IN_SAMPLES_PER_SOF_PERIOD] = {0}; + +void XUA_Buffer_lite(chanend c_aud_out, chanend c_feedback, chanend c_aud_in, chanend c_sof, chanend c_aud_ctl, in port p_for_mclk_count, chanend c_aud_host){ + + debug_printf("%d\n", MAX_OUT_SAMPLES_PER_SOF_PERIOD); + + unsigned sampleFreq = DEFAULT_FREQ; + + unsigned char buffer_aud_out[OUT_AUDIO_BUFFER_SIZE_BYTES]; + unsigned char buffer_aud_in[IN_AUDIO_BUFFER_SIZE_BYTES]; + + unsigned in_subslot_size = (AUDIO_CLASS == 1) ? FS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES : HS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES; + unsigned out_subslot_size = (AUDIO_CLASS == 1) ? FS_STREAM_FORMAT_OUTPUT_1_SUBSLOT_BYTES : HS_STREAM_FORMAT_OUTPUT_1_SUBSLOT_BYTES; + + unsigned in_num_chan = NUM_USB_CHAN_IN; + unsigned out_num_chan = NUM_USB_CHAN_OUT; + + unsigned tmp; + + XUD_ep ep_aud_out = XUD_InitEp(c_aud_out); + XUD_ep ep_aud_in = XUD_InitEp(c_aud_in); + + unsigned num_samples_received_from_host = 0; + unsigned outstanding_samples_to_host = 0; + unsigned num_samples_to_send_to_host = 0; + + XUD_SetReady_OutPtr(ep_aud_out, (unsigned)buffer_aud_out); + XUD_SetReady_InPtr(ep_aud_in, (unsigned)buffer_aud_in, num_samples_to_send_to_host); + + // printintln(OUT_AUDIO_BUFFER_SIZE_BYTES); + // printintln(MAX_OUT_SAMPLES_PER_SOF_PERIOD); + + while(1){ + XUD_Result_t result; + unsigned length = 0; + + + select{ + //Handle control path from EP0 + case testct_byref(c_aud_ctl, tmp): + //ignore tmp as is used for reboot signalling only + unsigned cmd = inuint(c_aud_ctl); + + debug_printf("c_aud_ctl cmd: %d\n", cmd); + if(cmd == SET_SAMPLE_FREQ){ + unsigned receivedSampleFreq = inuint(c_aud_ctl); + debug_printf("SET_SAMPLE_FREQ: %d\n", receivedSampleFreq); + sampleFreq = receivedSampleFreq; + } + + else if(cmd == SET_STREAM_FORMAT_IN){ + unsigned formatChange_DataFormat = inuint(c_aud_ctl); + unsigned formatChange_NumChans = inuint(c_aud_ctl); + unsigned formatChange_SubSlot = inuint(c_aud_ctl); + unsigned formatChange_SampRes = inuint(c_aud_ctl); + debug_printf("SET_STREAM_FORMAT_IN: %d %d %d %d\n", formatChange_DataFormat, formatChange_NumChans, formatChange_SubSlot, formatChange_SampRes); + in_subslot_size = formatChange_SubSlot; + in_num_chan = formatChange_NumChans; + } + + else if (cmd == SET_STREAM_FORMAT_OUT) + { + XUD_BusSpeed_t busSpeed; + unsigned formatChange_DataFormat = inuint(c_aud_ctl); + unsigned formatChange_NumChans = inuint(c_aud_ctl); + unsigned formatChange_SubSlot = inuint(c_aud_ctl); + unsigned formatChange_SampRes = inuint(c_aud_ctl); + debug_printf("SET_STREAM_FORMAT_OUT: %d %d %d %d\n", formatChange_DataFormat, formatChange_NumChans, formatChange_SubSlot, formatChange_SampRes); + out_subslot_size = formatChange_SubSlot; + out_num_chan = formatChange_NumChans; + } + + else{ + debug_printf("Unhandled command\n"); + } + outct(c_aud_ctl, XS1_CT_END); + break; + + //SOF + case inuint_byref(c_sof, tmp): + unsigned mclk_port_count = 0; + asm volatile(" getts %0, res[%1]" : "=r" (mclk_port_count) : "r" (p_for_mclk_count)); + + static unsigned sof_count=0; + sof_count++; + if (sof_count > SOF_FREQ_HZ * 10){ + debug_printf("SOF\n"); + sof_count = 0; + } + + /* Assuming 48kHz from a 24.576 master clock (0.0407uS period) + * MCLK ticks per SOF = 125uS / 0.0407 = 3072 MCLK ticks per SOF. + * expected Feedback is 48000/8000 = 6 samples. so 0x60000 in 16:16 format. + * Average over 128 SOFs - 128 x 3072 = 0x60000. + */ +#if 0 + unsigned long long feedbackMul = 64ULL; + if(usb_speed != XUD_SPEED_HS) + feedbackMul = 8ULL; /* TODO Use 4 instead of 8 to avoid windows LSB issues? */ + + /* Number of MCLK ticks in this SOF period (E.g = 125 * 24.576 = 3072) */ + int count = (int) ((short)(u_tmp - lastClock)); + + unsigned long long full_result = count * feedbackMul * sampleFreq; + + clockcounter += full_result; + + /* Store MCLK for next time around... */ + lastClock = u_tmp; + + /* Reset counts based on SOF counting. Expect 16ms (128 HS SOFs/16 FS SOFS) per feedback poll + * We always count 128 SOFs, so 16ms @ HS, 128ms @ FS */ + if(sofCount == 128) + { + sofCount = 0; + + clockcounter += mod_from_last_time; + clocks = clockcounter / masterClockFreq; + mod_from_last_time = clockcounter % masterClockFreq; + + if(usb_speed == XUD_SPEED_HS) + { + clocks <<= 3; + } + else + { + clocks <<= 6; + } + + { + int usb_speed; + asm volatile("stw %0, dp[g_speed]"::"r"(clocks)); // g_speed = clocks + + GET_SHARED_GLOBAL(usb_speed, g_curUsbSpeed); + + if (usb_speed == XUD_SPEED_HS) + { + fb_clocks[0] = clocks; + } + else + { + fb_clocks[0] = clocks >> 2; + } + } + clockcounter = 0; + } +#endif + + break; + + //Receive samples from host + case XUD_GetData_Select(c_aud_out, ep_aud_out, length, result): + num_samples_received_from_host = length / out_subslot_size; + //debug_printf("out samps: %d\n", num_samples_received_from_host); + outstanding_samples_to_host += num_samples_received_from_host; + int samples[MAX_OUT_SAMPLES_PER_SOF_PERIOD]; + unpack_buff_to_samples(buffer_aud_out, num_samples_received_from_host, out_subslot_size, samples); + + //Push into fifo for ASRC + + //else debug_printf("Push\n"); + + //Tell ASRC manager what we have just sent + outuint(c_aud_host, num_samples_received_from_host); //We assume this will not block and other side always consumes + num_samples_to_send_to_host = inuint(c_aud_host); //get number of return samples for sending back to host + + //Mark EP as ready for next frame from host + XUD_SetReady_OutPtr(ep_aud_out, (unsigned)buffer_aud_out); + break; + + //Send samples to host + case XUD_SetData_Select(c_aud_in, ep_aud_in, result): + //debug_printf("sent data\n"); + + //Populate the input buffer ready for the next read + pack_samples_to_buff(asrc_to_host_sample_buffer, num_samples_to_send_to_host, in_subslot_size, buffer_aud_in); + //Use the number of samples we received last time so we are always balanced (assumes same in/out count) + unsigned input_buffer_size = num_samples_to_send_to_host * in_subslot_size; + XUD_SetReady_InPtr(ep_aud_in, (unsigned)buffer_aud_in, input_buffer_size); //loopback + num_samples_to_send_to_host = 0; + break; + } + } +} \ No newline at end of file diff --git a/examples/xua_lite_example/src/xua_conf.h b/examples/xua_lite_example/src/xua_conf.h new file mode 100644 index 00000000..d879e272 --- /dev/null +++ b/examples/xua_lite_example/src/xua_conf.h @@ -0,0 +1,25 @@ +// Copyright (c) 2017-2018, XMOS Ltd, All rights reserved + +#ifndef _XUA_CONF_H_ +#define _XUA_CONF_H_ + +#define NUM_USB_CHAN_OUT 2 /* Number of channels from host to device */ +#define NUM_USB_CHAN_IN 2 /* Number of channels from device to host */ +#define I2S_CHANS_DAC 2 /* Number of I2S channels out of xCORE */ +#define I2S_CHANS_ADC 2 /* Number of I2S channels in to xCORE */ +#define MCLK_441 (512 * 44100) /* 44.1kHz family master clock frequency */ +#define MCLK_48 (512 * 48000) /* 48kHz family master clock frequency */ +#define MIN_FREQ 48000 /* Minimum sample rate */ +#define MAX_FREQ 48000 /* Maximum sample rate */ + +#define EXCLUDE_USB_AUDIO_MAIN + +#define VENDOR_STR "XMOS" +#define VENDOR_ID 0x20B1 +#define PRODUCT_STR_A2 "XUA Example" +#define PRODUCT_STR_A1 "XUA Example" +#define PID_AUDIO_1 1 +#define PID_AUDIO_2 2 +#define XUA_DFU_EN 0 /* Disable DFU (for simplicity of example */ + +#endif diff --git a/examples/xua_lite_example/src/xud_conf.h b/examples/xua_lite_example/src/xud_conf.h new file mode 100644 index 00000000..c14d64a8 --- /dev/null +++ b/examples/xua_lite_example/src/xud_conf.h @@ -0,0 +1,7 @@ +// Copyright (c) 2017-2018, XMOS Ltd, All rights reserved + +#include "xua_conf.h" + +/* TODO */ +#define XUD_UAC_NUM_USB_CHAN_OUT NUM_USB_CHAN_OUT +#define XUD_UAC_NUM_USB_CHAN_IN NUM_USB_CHAN_IN