forked from PAWPAW-Mirror/lib_xua
Remove xua_lite examples.
Requested by Oscar Bailey as part of github.com/xmos/lib_xua Pull Request #103.
This commit is contained in:
@@ -1,21 +0,0 @@
|
||||
APP_NAME =
|
||||
|
||||
TARGET = RPI_HAT_60QFN.xn
|
||||
|
||||
# The flags passed to xcc when building the application
|
||||
XCC_FLAGS = -fcomment-asm -Xmapper --map -Xmapper MAPFILE -Os -report \
|
||||
-g -Wno-unused-function -Wno-timing -DXUD_SERIES_SUPPORT=XUD_X200_SERIES -DUSB_TILE=tile[1] \
|
||||
-D MIC_ARRAY_CH0=PIN0 -D MIC_ARRAY_CH1=PIN4
|
||||
|
||||
# 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_mic_array
|
||||
|
||||
#=============================================================================
|
||||
# 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
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<xSCOPEconfig ioMode="basic" enabled="true">
|
||||
<Probe name="Value" type="CONTINUOUS" datatype="INT" units="Value" enabled="true"/>
|
||||
</xSCOPEconfig>
|
||||
@@ -1,66 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Network xmlns="http://www.xmos.com"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.xmos.com http://www.xmos.com">
|
||||
<Type>Device</Type>
|
||||
<Name>XVF3510 Device</Name>
|
||||
|
||||
<Declarations>
|
||||
<Declaration>tileref tile[2]</Declaration>
|
||||
<Declaration>tileref usb_tile</Declaration>
|
||||
</Declarations>
|
||||
|
||||
<Packages>
|
||||
<Package id="0" Type="XS2-UFnA-512-TQ128">
|
||||
<Nodes>
|
||||
<Node Id="0" InPackageId="0" Type="XS2-L16A-512" SystemFrequency="500MHz" OscillatorSrc="1">
|
||||
<Boot>
|
||||
<Source Location="bootFlash0"/>
|
||||
</Boot>
|
||||
<Tile Number="0" Reference="tile[0]">
|
||||
<Port Location="XS1_PORT_1B" Name="PORT_SQI_CS_0"/>
|
||||
<Port Location="XS1_PORT_1C" Name="PORT_SQI_SCLK_0"/>
|
||||
<Port Location="XS1_PORT_4B" Name="PORT_SQI_SIO_0"/>
|
||||
</Tile>
|
||||
<Tile Number="1" Reference="tile[1]"/>
|
||||
</Node>
|
||||
<Node Id="1" InPackageId="1" Type="periph:XS1-SU" Reference="usb_tile" Oscillator="24MHz">
|
||||
</Node>
|
||||
</Nodes>
|
||||
<Links>
|
||||
<Link Encoding="5wire">
|
||||
<LinkEndpoint NodeId="0" Link="8" Delays="52clk,52clk"/>
|
||||
<LinkEndpoint NodeId="1" Link="XL0" Delays="1clk,1clk"/>
|
||||
</Link>
|
||||
</Links>
|
||||
</Package>
|
||||
</Packages>
|
||||
|
||||
<Nodes>
|
||||
<Node Id="3" Type="device:" RoutingId="0x8000">
|
||||
<Service Id="0" Proto="xscope_host_data(chanend c);">
|
||||
<Chanend Identifier="c" end="3"/>
|
||||
</Service>
|
||||
</Node>
|
||||
</Nodes>
|
||||
|
||||
<Links>
|
||||
<Link Encoding="2wire" Delays="4,4" Flags="XSCOPE">
|
||||
<LinkEndpoint NodeId="0" Link="XL0"/>
|
||||
<LinkEndpoint NodeId="3" Chanend="1"/>
|
||||
</Link>
|
||||
</Links>
|
||||
|
||||
<ExternalDevices>
|
||||
<Device NodeId="0" Tile="0" Class="SQIFlash" Name="bootFlash0">
|
||||
<Attribute Name="PORT_SQI_CS" Value="PORT_SQI_CS_0"/>
|
||||
<Attribute Name="PORT_SQI_SCLK" Value="PORT_SQI_SCLK_0"/>
|
||||
<Attribute Name="PORT_SQI_SIO" Value="PORT_SQI_SIO_0"/>
|
||||
</Device>
|
||||
</ExternalDevices>
|
||||
|
||||
<JTAGChain>
|
||||
<JTAGDevice NodeId="0"/>
|
||||
</JTAGChain>
|
||||
|
||||
</Network>
|
||||
@@ -1,41 +0,0 @@
|
||||
// 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 Lite Class 2"
|
||||
#define PRODUCT_STR_A1 "XUA Lite Class 1"
|
||||
#define PID_AUDIO_1 1
|
||||
#define PID_AUDIO_2 2
|
||||
#define XUA_DFU_EN 0 /* Disable DFU (for simplicity of example) */
|
||||
|
||||
#define INPUT_FORMAT_COUNT 1
|
||||
#define STREAM_FORMAT_INPUT_1_RESOLUTION_BITS 16
|
||||
#define OUTPUT_FORMAT_COUNT 1
|
||||
#define STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS 16
|
||||
|
||||
#define OUTPUT_VOLUME_CONTROL 0
|
||||
#define INPUT_VOLUME_CONTROL 0
|
||||
|
||||
#define UAC_FORCE_FEEDBACK_EP 0
|
||||
#define XUA_ADAPTIVE 1
|
||||
#define XUA_LITE 1 // Use simple/optimised USB buffer tasks
|
||||
#define AUDIO_CLASS 1
|
||||
|
||||
#define XUA_NUM_PDM_MICS 4 // It's actually 2 but we run 4ch and ignore 2
|
||||
#define PDM_MAX_DECIMATION (96000/(MIN_FREQ))
|
||||
|
||||
#endif
|
||||
@@ -1,7 +0,0 @@
|
||||
// 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
|
||||
@@ -1 +0,0 @@
|
||||
grep pid: dump.txt | grep -Eo "\-?\d+" > proc.txt && gnuplot -p -e 'set term png; plot "proc.txt" with lines' > plot.png && open plot.png
|
||||
@@ -1,157 +0,0 @@
|
||||
// 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 <stdint.h>
|
||||
|
||||
#include <xs1.h>
|
||||
#include <platform.h>
|
||||
|
||||
#include "xua.h"
|
||||
#include "xud.h"
|
||||
#include "i2s.h"
|
||||
#include "i2c.h"
|
||||
#include "mic_array.h"
|
||||
#include "xua_buffer_lite.h"
|
||||
#include "xua_ep0_wrapper.h"
|
||||
#include "pdm_mic.h"
|
||||
#include "audio_config.h"
|
||||
#include "audio_hub.h"
|
||||
|
||||
#define DEBUG_UNIT XUA_APP
|
||||
#define DEBUG_PRINT_ENABLE_XUA_APP 1
|
||||
#include "debug_print.h"
|
||||
|
||||
// Port declarations. Note, the defines come from the xn file
|
||||
on tile[0]: buffered out port:32 p_i2s_dac[] = {XS1_PORT_1N}; //DAC
|
||||
on tile[0]: buffered in port:32 p_i2s_adc[] = {XS1_PORT_1F}; //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_1K;
|
||||
|
||||
// [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;
|
||||
on tile[1]: in port p_mclk_in_usb = XS1_PORT_1A;
|
||||
on tile[1]: in port p_for_mclk_count= XS1_PORT_16A; // Extra port for counting master clock ticks
|
||||
on tile[1]: clock clk_usb_mclk = XS1_CLKBLK_3; // Master clock
|
||||
|
||||
// Clock-block declarations
|
||||
on tile[0]: clock clk_audio_bclk = XS1_CLKBLK_2; // Bit clock
|
||||
on tile[0]: clock clk_audio_mclk = XS1_CLKBLK_3; // Master clock
|
||||
//XUD uses XS1_CLKBLK_4, XS1_CLKBLK_5 on tile[1]
|
||||
|
||||
//Mic array resources
|
||||
on tile[0]: out port p_pdm_clk = XS1_PORT_1L;
|
||||
on tile[0]: in buffered port:32 p_pdm_mics = XS1_PORT_4E;
|
||||
|
||||
on tile[0]: clock pdmclk = XS1_CLKBLK_4;
|
||||
on tile[0]: clock pdmclk6 = XS1_CLKBLK_5;
|
||||
|
||||
|
||||
// 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 burn_normal_priority(void){
|
||||
while(1);
|
||||
}
|
||||
|
||||
void burn_high_priority(void){
|
||||
set_core_high_priority_on();
|
||||
while(1);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Channels for lib_xud
|
||||
chan c_ep_out[XUA_ENDPOINT_COUNT_OUT];
|
||||
chan c_ep_in[XUA_ENDPOINT_COUNT_IN];
|
||||
|
||||
// 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];
|
||||
|
||||
streaming chan c_audio; //We use the channel buffering (48B across switch each way)
|
||||
streaming chan c_ds_output[1];
|
||||
|
||||
interface ep0_control_if i_ep0_ctl;
|
||||
|
||||
par
|
||||
{
|
||||
on tile[0]: {
|
||||
//Set the GPIOs needed for audio (reset and mute)
|
||||
setup_audio_gpio(p_gpio);
|
||||
c_audio <: 0; //Signal that we can now do i2c setup
|
||||
c_audio :> int _; //Now wait until i2c has finished mclk setup
|
||||
|
||||
const unsigned micDiv = MCLK_48/3072000;
|
||||
mic_array_setup_ddr(pdmclk, pdmclk6, p_mclk_in, p_pdm_clk, p_pdm_mics, micDiv);
|
||||
|
||||
par {
|
||||
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, c_ds_output);
|
||||
pdm_mic(c_ds_output[0], p_pdm_mics);
|
||||
|
||||
par (int i = 0; i < 5; i++) burn_normal_priority();
|
||||
par (int i = 0; i < 0; i++) burn_high_priority();
|
||||
}
|
||||
}
|
||||
on tile[1]:unsafe{
|
||||
// Connect master-clock input clock-block to clock-block pin for asnch feedback calculation
|
||||
set_clock_src(clk_usb_mclk, p_mclk_in_usb); // Clock clock-block from mclk pin
|
||||
set_port_clock(p_for_mclk_count, clk_usb_mclk); // Clock the "count" port from the clock block
|
||||
start_clock(clk_usb_mclk); // Set the clock off running
|
||||
|
||||
//Setup DAC over i2c and then return so we do not use a thread
|
||||
c_audio :> int _; //Wait for reset to be asserted/deasserted by other tile
|
||||
par{
|
||||
i2c_master(i_i2c, 1, p_scl, p_sda, 100);
|
||||
AudioHwConfigure(DEFAULT_FREQ, i_i2c[0]);
|
||||
}
|
||||
c_audio <: 0; //Signal to tile[0] that mclk is now good
|
||||
|
||||
par {
|
||||
// Low level USB device layer core
|
||||
XUD_Main(c_ep_out, XUA_ENDPOINT_COUNT_OUT, c_ep_in, XUA_ENDPOINT_COUNT_IN,
|
||||
c_sof, epTypeTableOut, epTypeTableIn,
|
||||
null, null, -1 ,
|
||||
(AUDIO_CLASS == 1) ? XUD_SPEED_FS : XUD_SPEED_HS, XUD_PWR_BUS);
|
||||
|
||||
// // Buffering core - handles audio and control data to/from EP's and gives/gets data to/from the audio I/O core
|
||||
// XUA_Buffer_lite(c_ep_out[0],
|
||||
// c_ep_in[0],
|
||||
// c_ep_out[1],
|
||||
// null, //c_ep_in[XUA_ENDPOINT_COUNT_IN - 2],/*feedback*/
|
||||
// c_ep_in[XUA_ENDPOINT_COUNT_IN - 1],
|
||||
// c_sof, p_for_mclk_count, c_audio);
|
||||
|
||||
//[[combine]]
|
||||
par{
|
||||
XUA_Buffer_lite2(i_ep0_ctl,
|
||||
c_ep_out[1],
|
||||
null, //c_ep_in[XUA_ENDPOINT_COUNT_IN - 2],/*feedback*/
|
||||
c_ep_in[XUA_ENDPOINT_COUNT_IN - 1],
|
||||
c_sof, p_for_mclk_count, c_audio);
|
||||
XUA_Endpoint0_select(c_ep_out[0], c_ep_in[0], i_ep0_ctl, null VENDOR_REQUESTS_PARAMS_DEC_);
|
||||
}
|
||||
par (int i = 0; i < 3; i++) burn_normal_priority();
|
||||
par (int i = 0; i < 2; i++) burn_high_priority();
|
||||
}
|
||||
}//Tile[1] par
|
||||
}//Top level par
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef _AUDIO_CONFIG_
|
||||
#define _AUDIO_CONFIG_
|
||||
|
||||
void AudioHwConfigure(unsigned samFreq, client i2c_master_if i_i2c);
|
||||
void pll_nudge(int nudge);
|
||||
void setup_audio_gpio(out port p_gpio);
|
||||
|
||||
#endif
|
||||
@@ -1,279 +0,0 @@
|
||||
#include <xs1.h>
|
||||
#include <assert.h>
|
||||
#include <platform.h>
|
||||
#include <print.h>
|
||||
#include <stdio.h>
|
||||
#include "i2c.h"
|
||||
#include "xua.h"
|
||||
#define DEBUG_UNIT AUDIO_CFG
|
||||
#define DEBUG_PRINT_ENABLE_AUDIO_CFG 1
|
||||
#include "debug_print.h"
|
||||
|
||||
|
||||
// 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_REGWRITE(reg, val) {i_i2c.write_reg(DAC3101_I2C_DEVICE_ADDR, reg, val);}
|
||||
|
||||
|
||||
// Nominal setting is ref div = 25, fb_div = 1024, op_div = 2
|
||||
// PCF Freq 0.96MHz
|
||||
|
||||
enum clock_nudge{
|
||||
PLL_SLOWER = -1,
|
||||
PLL_NOMINAL = 0,
|
||||
PLL_FASTER = 1
|
||||
};
|
||||
|
||||
//These steps provide just under +-0.1% frequency jumps
|
||||
#define PLL_LOW 0xC003FE18 // This is 3.069MHz
|
||||
#define PLL_NOM 0xC003FF18 // This is 3.072MHz
|
||||
#define PLL_HIGH 0xC0040018 // This is 3.075MHz
|
||||
|
||||
on tile[0]: out port p_leds = XS1_PORT_4F;
|
||||
|
||||
//Note use of no_ack write to prevent backpressure. There is enough buffering to
|
||||
//store both writes so we can move on without blocking
|
||||
void pll_nudge(int nudge) {
|
||||
if (nudge > 0){
|
||||
write_sswitch_reg_no_ack(get_tile_id(tile[0]), XS1_SSWITCH_PLL_CTL_NUM, PLL_HIGH);
|
||||
p_leds <: 0x02; //LED B
|
||||
}
|
||||
else if (nudge < 0){
|
||||
write_sswitch_reg_no_ack(get_tile_id(tile[0]), XS1_SSWITCH_PLL_CTL_NUM, PLL_LOW);
|
||||
p_leds <: 0x01; //LED A
|
||||
|
||||
}
|
||||
else {
|
||||
p_leds <: 0x0;
|
||||
}
|
||||
write_sswitch_reg_no_ack(get_tile_id(tile[0]), XS1_SSWITCH_PLL_CTL_NUM, PLL_NOM);
|
||||
}
|
||||
|
||||
void setup_audio_gpio(out port p_gpio){
|
||||
// Reset DAC and disable MUTE
|
||||
p_gpio <: 0x0;
|
||||
delay_milliseconds(1);
|
||||
p_gpio <: 0x1;
|
||||
delay_milliseconds(1);
|
||||
}
|
||||
|
||||
void AudioHwConfigure(unsigned samFreq, client i2c_master_if i_i2c)
|
||||
{
|
||||
|
||||
// Wait for 2ms because we apply reset for 1ms from other tile
|
||||
delay_milliseconds(2);
|
||||
|
||||
// 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.
|
||||
|
||||
#if XUA_ADAPTIVE
|
||||
//Set nominal clock speed on PLL
|
||||
write_sswitch_reg(get_tile_id(tile[0]), XS1_SSWITCH_PLL_CTL_NUM, PLL_NOM);
|
||||
|
||||
// We are assuming 48kHz family only and we generate MCLK in the DAC from BLCK supplied by XCORE
|
||||
// Set PLL J Value to 8
|
||||
DAC3101_REGWRITE(DAC3101_PLL_J, 0x08);
|
||||
// Set PLL D to 0 ...
|
||||
// Set PLL D MSB Value to 0x00
|
||||
DAC3101_REGWRITE(DAC3101_PLL_D_MSB, 0x00);
|
||||
// Set PLL D LSB Value to 0x00
|
||||
DAC3101_REGWRITE(DAC3101_PLL_D_LSB, 0x00);
|
||||
|
||||
delay_milliseconds(1);
|
||||
|
||||
// Set PLL_CLKIN = BCLK (device pin), CODEC_CLKIN = PLL_CLK (generated on-chip)
|
||||
DAC3101_REGWRITE(DAC3101_CLK_GEN_MUX, 0x07);
|
||||
|
||||
// Set PLL P=1 and R=4 values and power up.
|
||||
DAC3101_REGWRITE(DAC3101_PLL_P_R, 0x94);
|
||||
// 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);
|
||||
|
||||
#else
|
||||
/* 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);
|
||||
|
||||
#endif
|
||||
|
||||
// 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);
|
||||
|
||||
i_i2c.shutdown();
|
||||
}
|
||||
|
||||
|
||||
//These are here just to silence compiler warnings about unimplemented xua callbacks (not needed in xua lite)
|
||||
void AudioHwInit(){}
|
||||
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC){}
|
||||
@@ -1,4 +0,0 @@
|
||||
#include "i2s.h"
|
||||
|
||||
[[distributable]]
|
||||
void AudioHub(server i2s_frame_callback_if i2s, streaming chanend c_audio, streaming chanend (&?c_ds_output)[1]);
|
||||
@@ -1,86 +0,0 @@
|
||||
#include <string.h>
|
||||
#include "i2s.h"
|
||||
#include "i2c.h"
|
||||
#include "xua.h"
|
||||
#define DEBUG_UNIT XUA_AUDIO_HUB
|
||||
#define DEBUG_PRINT_ENABLE_XUA_AUDIO_HUB 1
|
||||
#include "debug_print.h"
|
||||
#include "mic_array.h"
|
||||
#include "audio_config.h"
|
||||
#include "pdm_mic.h"
|
||||
#include "xua_buffer_lite.h"
|
||||
|
||||
//Globally declared for 64b alignment
|
||||
int mic_decimator_fir_data_array[8][THIRD_STAGE_COEFS_PER_STAGE * PDM_MAX_DECIMATION] = {{0}};
|
||||
mic_array_frame_time_domain mic_audio_frame[2];
|
||||
|
||||
[[distributable]]
|
||||
void AudioHub(server i2s_frame_callback_if i2s,
|
||||
streaming chanend c_audio,
|
||||
streaming chanend (&?c_ds_output)[1])
|
||||
{
|
||||
int32_t samples_out[NUM_USB_CHAN_OUT] = {0};
|
||||
int32_t samples_in[NUM_USB_CHAN_IN] = {0};
|
||||
|
||||
int32_t clock_nudge = 0;
|
||||
|
||||
//PDM mic and decimator
|
||||
unsigned buffer;
|
||||
int raw_mics[XUA_NUM_PDM_MICS] = {0};
|
||||
const unsigned decimatorCount = 1; // Supports up to 4 mics
|
||||
mic_array_decimator_conf_common_t dcc;
|
||||
mic_array_decimator_config_t dc[1];
|
||||
mic_array_frame_time_domain * unsafe current;
|
||||
|
||||
mic_array_decimator_set_samprate(DEFAULT_FREQ, mic_decimator_fir_data_array[0], &dcc, dc);
|
||||
mic_array_decimator_configure(c_ds_output, decimatorCount, dc);
|
||||
mic_array_init_time_domain_frame(c_ds_output, decimatorCount, buffer, mic_audio_frame, dc);
|
||||
|
||||
// Used for debug
|
||||
//int saw = 0;
|
||||
|
||||
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;
|
||||
debug_printf("I2S init\n");
|
||||
delay_milliseconds(500); //Work around to ensure I2S does not start until enumeration complete so timing does not break for exchange
|
||||
//This should be ideally done by set config by the host (via xua_buffer) to know we are enumerated
|
||||
break;
|
||||
|
||||
case i2s.receive(size_t n_chans, int32_t in_samps[n_chans]):
|
||||
for (int i = 0; i < n_chans; i++) samples_in[i] = in_samps[i];
|
||||
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_out[i];
|
||||
break;
|
||||
|
||||
//Exchange samples with mics & host
|
||||
case i2s.restart_check() -> i2s_restart_t restart:
|
||||
restart = I2S_NO_RESTART; // Keep on looping
|
||||
timer tmr; int t0, t1; tmr :> t0;
|
||||
|
||||
XUA_transfer_samples(c_audio, (unsigned *) samples_out, (unsigned *) raw_mics, (clock_nudge, int));
|
||||
|
||||
//Grab mics. Takes about 200 ticks currently
|
||||
current = mic_array_get_next_time_domain_frame(c_ds_output, decimatorCount, buffer, mic_audio_frame, dc);
|
||||
//50 ticks
|
||||
unsafe {
|
||||
for (int i = 0; i < XUA_NUM_PDM_MICS; i++) raw_mics[i] = current->data[i][0];
|
||||
}
|
||||
|
||||
//memset(raw_mics, saw, sizeof(int) * XUA_NUM_PDM_MICS);
|
||||
//saw += 500;
|
||||
|
||||
//Taking about 160 ticks when adjusting, 100 when not
|
||||
tmr :> t0;
|
||||
pll_nudge(clock_nudge);
|
||||
tmr :> t1;
|
||||
if (t1-t0 > 500) debug_printf("*%d\n", t1 - t0);
|
||||
//delay_microseconds(10); //Test backpressure tolerance
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#ifndef _PDM_MIC_H_
|
||||
#define _PDM_MIC_H_
|
||||
|
||||
#include "mic_array.h"
|
||||
|
||||
void mic_array_decimator_set_samprate(const unsigned samplerate, int mic_decimator_fir_data_array[], mic_array_decimator_conf_common_t *dcc, mic_array_decimator_config_t dc[]);
|
||||
void pdm_mic(streaming chanend c_ds_output, in buffered port:32 p_pdm_mics);
|
||||
void mic_array_setup_ddr(clock pdmclk, clock pdmclk6, in port p_mclk,
|
||||
out port p_pdm_clk, buffered in port:32 p_pdm_data,
|
||||
int divide);
|
||||
|
||||
#endif
|
||||
@@ -1,112 +0,0 @@
|
||||
#include "xua.h"
|
||||
#include <platform.h>
|
||||
#include <xs1.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <xclib.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "mic_array.h"
|
||||
|
||||
|
||||
void mic_array_decimator_set_samprate(const unsigned samplerate, int mic_decimator_fir_data_array[], mic_array_decimator_conf_common_t *dcc, mic_array_decimator_config_t dc[])
|
||||
{
|
||||
unsigned decimationfactor = 96000/samplerate;
|
||||
int fir_gain_compen[7];
|
||||
int * unsafe fir_coefs[7];
|
||||
unsafe
|
||||
{
|
||||
fir_gain_compen[0] = 0;
|
||||
fir_gain_compen[1] = FIR_COMPENSATOR_DIV_2; //48kHz
|
||||
fir_gain_compen[2] = FIR_COMPENSATOR_DIV_4;
|
||||
fir_gain_compen[3] = FIR_COMPENSATOR_DIV_6; //16kHz
|
||||
fir_gain_compen[4] = FIR_COMPENSATOR_DIV_8;
|
||||
fir_gain_compen[5] = 0;
|
||||
fir_gain_compen[6] = FIR_COMPENSATOR_DIV_12;
|
||||
|
||||
fir_coefs[0] = 0;
|
||||
fir_coefs[1] = (int * unsafe)g_third_stage_div_2_fir;
|
||||
fir_coefs[2] = (int * unsafe)g_third_stage_div_4_fir;
|
||||
fir_coefs[3] = (int * unsafe)g_third_stage_div_6_fir;
|
||||
fir_coefs[4] = (int * unsafe)g_third_stage_div_8_fir;
|
||||
fir_coefs[5] = 0;
|
||||
fir_coefs[6] = (int * unsafe)g_third_stage_div_12_fir;
|
||||
|
||||
//dcc = {MIC_ARRAY_MAX_FRAME_SIZE_LOG2, 1, 0, 0, decimationfactor, fir_coefs[decimationfactor/2], 0, 0, DECIMATOR_NO_FRAME_OVERLAP, 2};
|
||||
dcc->len = MIC_ARRAY_MAX_FRAME_SIZE_LOG2;
|
||||
dcc->apply_dc_offset_removal = 1;
|
||||
dcc->index_bit_reversal = 0;
|
||||
dcc->windowing_function = null;
|
||||
dcc->output_decimation_factor = decimationfactor;
|
||||
dcc->coefs = fir_coefs[decimationfactor/2];
|
||||
dcc->apply_mic_gain_compensation = 0;
|
||||
dcc->fir_gain_compensation = fir_gain_compen[decimationfactor/2];
|
||||
dcc->buffering_type = DECIMATOR_NO_FRAME_OVERLAP;
|
||||
dcc->number_of_frame_buffers = 2;
|
||||
|
||||
//dc[0] = {&dcc, mic_decimator_fir_data[0], {0, 0, 0, 0}, 4};
|
||||
dc[0].dcc = dcc;
|
||||
dc[0].data = mic_decimator_fir_data_array;
|
||||
dc[0].mic_gain_compensation[0]=0;
|
||||
dc[0].mic_gain_compensation[1]=0;
|
||||
dc[0].mic_gain_compensation[2]=0;
|
||||
dc[0].mic_gain_compensation[3]=0;
|
||||
dc[0].channel_count = 4;
|
||||
}
|
||||
}
|
||||
|
||||
#if MAX_FREQ > 48000
|
||||
#error MAX_FREQ > 48000 NOT CURRENTLY SUPPORTED
|
||||
#endif
|
||||
|
||||
void pdm_mic(streaming chanend c_ds_output, in buffered port:32 p_pdm_mics)
|
||||
{
|
||||
streaming chan c_4x_pdm_mic_0;
|
||||
assert((MCLK_48 / 3072000) == (MCLK_441 / 2822400)); //Make sure mic clock is achievable from MCLK
|
||||
par
|
||||
{
|
||||
mic_array_pdm_rx(p_pdm_mics, c_4x_pdm_mic_0, null);
|
||||
mic_array_decimate_to_pcm_4ch(c_4x_pdm_mic_0, c_ds_output, MIC_ARRAY_NO_INTERNAL_CHANS);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void mic_array_setup_ddr(clock pdmclk,
|
||||
clock pdmclk6,
|
||||
in port p_mclk, /*used only in I2S slave case*/
|
||||
out port p_pdm_clk,
|
||||
buffered in port:32 p_pdm_data,
|
||||
int divide) {
|
||||
|
||||
|
||||
#if !XUA_ADAPTIVE //i2s slave
|
||||
//p_mclk coming from the Pi is 24.576 MHz
|
||||
configure_clock_src_divide(pdmclk, p_mclk, 4); //3.072 = 24.576 / 8
|
||||
configure_clock_src_divide(pdmclk6, p_mclk, 2); //6.144 = 24.576 / 4
|
||||
|
||||
#else
|
||||
configure_clock_xcore(pdmclk, 80); // 3.072
|
||||
configure_clock_xcore(pdmclk6, 40); // 6.144
|
||||
#endif
|
||||
|
||||
configure_port_clock_output(p_pdm_clk, pdmclk);
|
||||
configure_in_port(p_pdm_data, pdmclk6);
|
||||
|
||||
/* start the faster capture clock */
|
||||
start_clock(pdmclk6);
|
||||
/* wait for a rising edge on the capture clock */
|
||||
partin(p_pdm_data, 4);
|
||||
/* start the slower output clock */
|
||||
start_clock(pdmclk);
|
||||
|
||||
/*
|
||||
* this results in the rising edge of the capture clock
|
||||
* leading the rising edge of the output clock by one period
|
||||
* of p_mclk, which is about 40.7 ns for the typical frequency
|
||||
* of 24.576 megahertz.
|
||||
* This should fall within the data valid window.
|
||||
*/
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
# Copyright (c) 2018, XMOS Ltd, All rights reserved
|
||||
|
||||
#Note no xcommon included - we are going bareback here because xcommon not good
|
||||
#at dealing excluding/including source directories out of normal structure.
|
||||
#We want to have a test app here using some source files from sw_vocalfusion (delay estimator)
|
||||
|
||||
|
||||
BUILD_FLAGS = -O3 -g
|
||||
XCC_FLAGS = $(BUILD_FLAGS)
|
||||
|
||||
INCLUDE_DIRS = \
|
||||
-I ../src/ \
|
||||
-I . \
|
||||
|
||||
COMMON = \
|
||||
-O2 -g -report \
|
||||
-target=XCORE-200-EXPLORER \
|
||||
|
||||
SOURCES = \
|
||||
./test_fifo.xc \
|
||||
|
||||
BINARY=bin/test_fifo.xe \
|
||||
|
||||
|
||||
all:
|
||||
mkdir -p bin; \
|
||||
mv test_fifoxc test_fifo.xc; \
|
||||
xcc $(SOURCES) $(COMMON) $(INCLUDE_DIRS) -D AUDIO_DELAY_SAMPLES=$$DELAY -o $(BINARY); \
|
||||
mv test_fifo.xc test_fifoxc; \
|
||||
|
||||
sim:
|
||||
xsim $(BINARY)
|
||||
|
||||
clean:
|
||||
rm -rf bin/*
|
||||
@@ -1,139 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include "fifo_impl.h"
|
||||
|
||||
typedef enum test_t{
|
||||
FAIL=0,
|
||||
PASS=1
|
||||
}test_t;
|
||||
|
||||
#define FIFO_SIZE 4
|
||||
|
||||
unsafe test_t test_is_empty(mem_fifo_short_t * unsafe fifo){
|
||||
unsigned fill = fifo_get_fill_short(fifo);
|
||||
short data[1];
|
||||
fifo_ret_t ret = fifo_block_pop_short(fifo, data, 1);
|
||||
if (fill == 0 && ret == FIFO_EMPTY) return PASS;
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
unsafe test_t test_push_one(mem_fifo_short_t * unsafe fifo){
|
||||
unsigned fill_0 = fifo_get_fill_short(fifo);
|
||||
short data[] = {123};
|
||||
fifo_ret_t ret = fifo_block_push_short(fifo, data, 1);
|
||||
unsigned fill_1 = fifo_get_fill_short(fifo);
|
||||
|
||||
if (ret != FIFO_SUCCESS || fill_0 + 1 != fill_1) return FAIL;
|
||||
return PASS;
|
||||
}
|
||||
|
||||
unsafe test_t test_partial_fill(mem_fifo_short_t * unsafe fifo){
|
||||
unsigned fill_0 = fifo_get_fill_short(fifo);
|
||||
short data[] = {80, 90, 100};
|
||||
fifo_ret_t ret = fifo_block_push_short(fifo, data, 3);
|
||||
unsigned fill_1 = fifo_get_fill_short(fifo);
|
||||
|
||||
if (ret != FIFO_SUCCESS || fill_0 != 0 || fill_1 != 3) return FAIL;
|
||||
return PASS;
|
||||
}
|
||||
|
||||
unsafe test_t test_fill_level(mem_fifo_short_t * unsafe fifo){
|
||||
unsigned fill = fifo_get_fill_short(fifo);
|
||||
if (fill != 3) return FAIL;
|
||||
return PASS;
|
||||
}
|
||||
|
||||
unsafe test_t test_pop_one(mem_fifo_short_t * unsafe fifo, short expect){
|
||||
unsigned fill_0 = fifo_get_fill_short(fifo);
|
||||
short data[1] = {0xffff};
|
||||
fifo_ret_t ret = fifo_block_pop_short(fifo, data, 1);
|
||||
unsigned fill_1 = fifo_get_fill_short(fifo);
|
||||
if (ret != FIFO_SUCCESS || fill_0 != fill_1 + 1 || data[0] != expect) {
|
||||
printf("grr %d\n", data[0]);
|
||||
return FAIL;
|
||||
}
|
||||
return PASS;
|
||||
}
|
||||
|
||||
unsafe test_t test_pop_three_fail(mem_fifo_short_t * unsafe fifo){
|
||||
unsigned fill_0 = fifo_get_fill_short(fifo);
|
||||
short data[3] = {0xffff, 0xffff, 0xffff};
|
||||
fifo_ret_t ret = fifo_block_pop_short(fifo, data, 3);
|
||||
unsigned fill_1 = fifo_get_fill_short(fifo);
|
||||
if (ret == FIFO_SUCCESS || fill_0 != 2 || fill_1 != 2) return FAIL;
|
||||
return PASS;
|
||||
}
|
||||
|
||||
unsafe test_t test_partial_fill_fast(mem_fifo_short_t * unsafe fifo){
|
||||
unsigned fill_0 = fifo_get_fill_short(fifo);
|
||||
short data[] = {20, 30, 40};
|
||||
fifo_ret_t ret = fifo_block_push_short_fast(fifo, data, 3);
|
||||
unsigned fill_1 = fifo_get_fill_short(fifo);
|
||||
|
||||
if (ret != FIFO_SUCCESS || fill_0 != 0 || fill_1 != 3) return FAIL;
|
||||
return PASS;
|
||||
}
|
||||
|
||||
unsafe test_t test_pop_block_fast(mem_fifo_short_t * unsafe fifo, short expect[], unsigned n){
|
||||
unsigned fill_0 = fifo_get_fill_short(fifo);
|
||||
short data[10] = {0xffff};
|
||||
fifo_ret_t ret = fifo_block_pop_short_fast(fifo, data, n);
|
||||
unsigned fill_1 = fifo_get_fill_short(fifo);
|
||||
unsigned data_ok = memcmp(data, expect, n * sizeof(short)) == 0;
|
||||
if (ret != FIFO_SUCCESS || fill_0 != fill_1 + n || !data_ok) return FAIL;
|
||||
return PASS;
|
||||
}
|
||||
|
||||
unsafe void print_fifo(mem_fifo_short_t * unsafe fifo){
|
||||
for (int i = fifo->size - 1; i >= 0; i--){
|
||||
printf("FIFO[%d]: %hd %s %s\n", i, fifo->data_base_ptr[i], fifo->read_idx == i ? "RD" : "", fifo->write_idx == i ? "WR" : "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(void){
|
||||
unsafe{
|
||||
short test_fifo_storage[FIFO_SIZE];
|
||||
mem_fifo_short_t test_fifo = {sizeof(test_fifo_storage)/sizeof(test_fifo_storage[0]), test_fifo_storage, 0, 0};
|
||||
volatile mem_fifo_short_t * unsafe test_fifo_ptr = &test_fifo;
|
||||
|
||||
// print_fifo(test_fifo_ptr);
|
||||
printf("test_is_empty: %d\n", test_is_empty(test_fifo_ptr));
|
||||
printf("test_partial_fill: %d\n", test_partial_fill(test_fifo_ptr));
|
||||
// print_fifo(test_fifo_ptr);
|
||||
|
||||
printf("test_fill_level: %d\n", test_fill_level(test_fifo_ptr));
|
||||
printf("test_pop_one: %d\n", test_pop_one(test_fifo_ptr, 80));
|
||||
printf("test_pop_three_fail: %d\n", test_pop_three_fail(test_fifo_ptr));
|
||||
printf("test_pop_one: %d\n", test_pop_one(test_fifo_ptr, 90));
|
||||
printf("test_pop_one: %d\n", test_pop_one(test_fifo_ptr, 100));
|
||||
printf("test_is_empty: %d\n", test_is_empty(test_fifo_ptr));
|
||||
|
||||
printf("test_partial_fill_fast: %d\n", test_partial_fill_fast(test_fifo_ptr));
|
||||
|
||||
printf("test_pop_one: %d\n", test_pop_one(test_fifo_ptr, 20));
|
||||
// print_fifo(test_fifo_ptr);
|
||||
|
||||
short td[] = {30, 40};
|
||||
|
||||
printf("test_pop_block_fast: %d\n", test_pop_block_fast(test_fifo_ptr, td, 2)); //no wrap
|
||||
|
||||
printf("test_is_empty: %d\n", test_is_empty(test_fifo_ptr));
|
||||
printf("test_push_one: %d\n", test_push_one(test_fifo_ptr));
|
||||
printf("test_push_one: %d\n", test_push_one(test_fifo_ptr));
|
||||
printf("test_pop_three_fail: %d\n", test_pop_three_fail(test_fifo_ptr));
|
||||
printf("test_pop_one: %d\n", test_pop_one(test_fifo_ptr, 123));
|
||||
printf("test_pop_one: %d\n", test_pop_one(test_fifo_ptr, 123));
|
||||
|
||||
printf("test_partial_fill_fast: %d\n", test_partial_fill_fast(test_fifo_ptr)); //no wrap
|
||||
short te[] = {20, 30, 40};
|
||||
printf("test_pop_block_fast: %d\n", test_pop_block_fast(test_fifo_ptr, te, 3));// no wrap
|
||||
printf("test_partial_fill_fast: %d\n", test_partial_fill_fast(test_fifo_ptr)); //wrap
|
||||
printf("test_pop_block_fast: %d\n", test_pop_block_fast(test_fifo_ptr, te, 3));// wrap
|
||||
// print_fifo(test_fifo_ptr);
|
||||
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
- Bring ep0 serivice into xua_buffer select (make control mem) (DONE)
|
||||
- Tidy feedback endpoint (DONE)
|
||||
- Input path + FIFO (DONE)
|
||||
- Function prototypes into includes (DONE)
|
||||
- Single input/ouput format (DONE)
|
||||
- Get UAC1 / FS working (DONE)
|
||||
- Optimised EP buffer (either triple block or block FIFO) (DONE)
|
||||
- Fix output gain issue (IN PROGRESS)
|
||||
- Add timer to xua_buffer to prepare for exchange with audio (remove backpressure to audio) (WONT DO - use port buffer and reduce case overhead)
|
||||
- Adaptive endpoint EP and descriptors (DONE)
|
||||
- Adpative clock control (IN PROGRESS)
|
||||
- Proper control loop w/filtering (IN PROGRESS)
|
||||
- Switchable MICS using define (WONT DO - separate app)
|
||||
- DFU
|
||||
- Combinable EP0 (DONE)
|
||||
- Interrupt EP0 option
|
||||
- Control processing
|
||||
- Fix cast warning (DONE)
|
||||
- Work out why no clock drift on Android / OSX (drift seen on Linux and Win) (DONE)
|
||||
- Tidy/cut down EP0 handling
|
||||
- Broader host testing (Android, W10, MAC) (IN PROGRESS)
|
||||
- Peer review
|
||||
@@ -1,26 +0,0 @@
|
||||
def options(ctx):
|
||||
ctx.add_option('--target', action='store', default='xua_lite_example.xe')
|
||||
ctx.add_option('--debug', action='store_true')
|
||||
|
||||
|
||||
def configure(conf):
|
||||
conf.load('xwaf.compiler_xcc')
|
||||
conf.env.PROJECT_ROOT = '../../..'
|
||||
|
||||
def build(bld):
|
||||
target_path = 'bin/' + bld.options.target
|
||||
bld.env.TARGET_ARCH ='config/RPI_HAT_60QFN.xn'
|
||||
bld.env.XSCOPE = bld.path.find_resource('config.xscope')
|
||||
depends_on = ['lib_xua','lib_i2s','lib_xud','lib_mic_array', 'lib_i2c']
|
||||
#bld.env.XCC_FLAGS = ['-O2', '-g', '-Wall', '-fcmdline-buffer-bytes=512', '-report', '-DDISABLE_STAGE_C']
|
||||
bld.env.XCC_FLAGS = ['-fcomment-asm','-Xmapper','--map','-Xmapper','MAPFILE',
|
||||
'-Os','-report','-g','-Wno-unused-function','-Wno-timing',
|
||||
'-DXUD_SERIES_SUPPORT=XUD_X200_SERIES','-DUSB_TILE=tile[1]',
|
||||
'-DMIC_ARRAY_CH0=PIN0','-DMIC_ARRAY_CH1=PIN4', '-DDEBUG_PRINT_ENABLE=1']
|
||||
if bld.options.debug:
|
||||
bld.env.XCC_FLAGS.append('-DDEBUG')
|
||||
|
||||
source = bld.path.ant_glob(['src/**/*.xc', 'src/**/*.c',
|
||||
'xua_lite/**/*.xc', 'xua_lite/**/*.c'])
|
||||
includes = ['config']
|
||||
bld.program(source=source, includes=includes, depends_on=depends_on,target=target_path)
|
||||
@@ -1,206 +0,0 @@
|
||||
#ifndef __FIFO__
|
||||
#define __FIFO__
|
||||
#include <string.h> //memcpy
|
||||
#include "fifo_types.h"
|
||||
|
||||
//Asynch FIFO implementaion
|
||||
//Note these are in the include file to allow the compiler to inline for performance
|
||||
|
||||
///////////////////////////////////////
|
||||
//Shared memory FIFO (sample by sample)
|
||||
//Can be any size
|
||||
///////////////////////////////////////
|
||||
|
||||
|
||||
static inline unsigned fifo_get_fill(volatile mem_fifo_t * unsafe fifo) {
|
||||
unsafe{
|
||||
unsigned fifo_fill = 0;
|
||||
if (fifo->write_idx >= fifo->read_idx){
|
||||
fifo_fill = fifo->write_idx - fifo->read_idx;
|
||||
}
|
||||
else{
|
||||
fifo_fill = (fifo->size + fifo->write_idx) - fifo->read_idx;
|
||||
}
|
||||
return fifo_fill;
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned fifo_get_fill_short(volatile mem_fifo_short_t * unsafe fifo) {
|
||||
unsafe{
|
||||
unsigned fifo_fill = 0;
|
||||
if (fifo->write_idx >= fifo->read_idx){
|
||||
fifo_fill = fifo->write_idx - fifo->read_idx;
|
||||
}
|
||||
else{
|
||||
fifo_fill = (fifo->size + fifo->write_idx) - fifo->read_idx;
|
||||
}
|
||||
return fifo_fill;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void fifo_init_short(volatile mem_fifo_short_t * unsafe fifo) {
|
||||
unsafe{
|
||||
fifo->write_idx = 0;
|
||||
fifo->read_idx = (fifo->size * 2) / 4;
|
||||
memset(fifo->data_base_ptr , 0, fifo->size * sizeof(short));
|
||||
}
|
||||
}
|
||||
|
||||
#pragma unsafe arrays
|
||||
static inline fifo_ret_t fifo_block_push(volatile mem_fifo_t * unsafe fifo, int data[], unsigned n) {
|
||||
unsafe{
|
||||
//check there is a block of space large enough
|
||||
unsigned space_remaining = fifo->size - fifo_get_fill(fifo) - 1;
|
||||
if (n > space_remaining) {
|
||||
return FIFO_FULL;
|
||||
}
|
||||
for (int i = 0; i < n; i++){
|
||||
unsigned next_idx = fifo->write_idx + 1;
|
||||
if (next_idx == fifo->size) next_idx = 0; //Check for wrap
|
||||
fifo->data_base_ptr[fifo->write_idx] = data[i];
|
||||
fifo->write_idx = next_idx;
|
||||
}
|
||||
return FIFO_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma unsafe arrays
|
||||
static inline fifo_ret_t fifo_block_push_short(volatile mem_fifo_short_t * unsafe fifo, short data[], unsigned n) {
|
||||
unsafe{
|
||||
//check there is a block of space large enough
|
||||
unsigned space_remaining = fifo->size - fifo_get_fill_short(fifo) - 1;
|
||||
if (n > space_remaining) {
|
||||
return FIFO_FULL;
|
||||
}
|
||||
for (int i = 0; i < n; i++){
|
||||
unsigned next_idx = fifo->write_idx + 1;
|
||||
if (next_idx == fifo->size) next_idx = 0; //Check for wrap
|
||||
fifo->data_base_ptr[fifo->write_idx] = data[i];
|
||||
fifo->write_idx = next_idx;
|
||||
}
|
||||
return FIFO_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma unsafe arrays
|
||||
static inline fifo_ret_t fifo_block_push_short_fast(volatile mem_fifo_short_t * unsafe fifo, short data[], unsigned n) {
|
||||
unsafe{
|
||||
//check there is a block of space large enough
|
||||
unsigned space_remaining = fifo->size - fifo_get_fill_short(fifo) - 1;
|
||||
if (n > space_remaining) {
|
||||
return FIFO_FULL;
|
||||
}
|
||||
//We will write either one or two blocks depending on wrap
|
||||
unsigned first_block_size = 0;
|
||||
unsigned second_block_size = 0;
|
||||
|
||||
//See if we need to wrap during block writes
|
||||
unsigned space_left_at_top = fifo->size - fifo->write_idx;
|
||||
//printf("space_left_at_top %d\n", space_left_at_top);
|
||||
//Yes, we do need to wrap
|
||||
if (n > space_left_at_top){
|
||||
first_block_size = space_left_at_top;
|
||||
second_block_size = n - space_left_at_top;
|
||||
memcpy(&fifo->data_base_ptr[fifo->write_idx], &data[0], first_block_size * sizeof(short));
|
||||
memcpy(&fifo->data_base_ptr[0], &data[first_block_size], second_block_size * sizeof(short));
|
||||
fifo->write_idx = second_block_size;
|
||||
}
|
||||
//No wrap, do all in one go
|
||||
else{
|
||||
first_block_size = n;
|
||||
second_block_size = 0;
|
||||
memcpy(&fifo->data_base_ptr[fifo->write_idx], &data[0], first_block_size * sizeof(short));
|
||||
fifo->write_idx += first_block_size;
|
||||
}
|
||||
|
||||
return FIFO_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma unsafe arrays
|
||||
static inline fifo_ret_t fifo_block_pop(volatile mem_fifo_t * unsafe fifo, int data[], unsigned n) {
|
||||
unsafe{
|
||||
//Check we have a block big enough to send
|
||||
if (n > fifo_get_fill(fifo)){
|
||||
return FIFO_EMPTY;
|
||||
}
|
||||
for (int i = 0; i < n; i++){
|
||||
data[i] = fifo->data_base_ptr[fifo->read_idx];
|
||||
fifo->read_idx++;
|
||||
if (fifo->read_idx == fifo->size) fifo->read_idx = 0; //Check for wrap
|
||||
}
|
||||
return FIFO_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma unsafe arrays
|
||||
static inline fifo_ret_t fifo_block_pop_short(volatile mem_fifo_short_t * unsafe fifo, short data[], unsigned n) {
|
||||
unsafe{
|
||||
//Check we have a block big enough to send
|
||||
if (n > fifo_get_fill_short(fifo)){
|
||||
return FIFO_EMPTY;
|
||||
}
|
||||
for (int i = 0; i < n; i++){
|
||||
data[i] = fifo->data_base_ptr[fifo->read_idx];
|
||||
fifo->read_idx++;
|
||||
if (fifo->read_idx == fifo->size) fifo->read_idx = 0; //Check for wrap
|
||||
}
|
||||
return FIFO_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma unsafe arrays
|
||||
static inline fifo_ret_t fifo_block_pop_short_fast(volatile mem_fifo_short_t * unsafe fifo, short data[], unsigned n) {
|
||||
unsafe{
|
||||
//Check we have a block big enough to send
|
||||
if (n > fifo_get_fill_short(fifo)){
|
||||
return FIFO_EMPTY;
|
||||
}
|
||||
//We will read either one or two blocks depending on wrap
|
||||
unsigned first_block_size = 0;
|
||||
unsigned second_block_size = 0;
|
||||
|
||||
//See if we need to wrap during block read
|
||||
unsigned num_read_at_top = fifo->size - fifo->read_idx;
|
||||
// printf("num_read_at_top %d\n", num_read_at_top);
|
||||
//Yes, we do need to wrap
|
||||
if (n > num_read_at_top){
|
||||
first_block_size = num_read_at_top;
|
||||
second_block_size = n - num_read_at_top;
|
||||
memcpy(&data[0], &fifo->data_base_ptr[fifo->read_idx], first_block_size * sizeof(short));
|
||||
memcpy( &data[first_block_size], &fifo->data_base_ptr[0], second_block_size * sizeof(short));
|
||||
fifo->read_idx = second_block_size;
|
||||
// printf("wrap\n");
|
||||
}
|
||||
//No wrap, do all in one go
|
||||
else{
|
||||
first_block_size = n;
|
||||
second_block_size = 0;
|
||||
memcpy(&data[0], &fifo->data_base_ptr[fifo->read_idx], first_block_size * sizeof(short));
|
||||
fifo->read_idx += first_block_size;
|
||||
// printf("no wrap\n");
|
||||
|
||||
}
|
||||
|
||||
return FIFO_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
//Version of above that returns fill level relative to half full
|
||||
static inline int fifo_get_fill_relative_half(volatile mem_fifo_t * unsafe fifo){
|
||||
unsafe{
|
||||
int fifo_fill = (int)fifo_get_fill(fifo);
|
||||
fifo_fill -= (fifo->size / 2);
|
||||
return fifo_fill;
|
||||
}
|
||||
}
|
||||
|
||||
//Version of above that returns fill level relative to half full
|
||||
static inline int fifo_get_fill_relative_half_short(volatile mem_fifo_short_t * unsafe fifo){
|
||||
unsafe{
|
||||
int fifo_fill = (int)fifo_get_fill_short(fifo);
|
||||
fifo_fill -= (fifo->size / 2);
|
||||
return fifo_fill;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,37 +0,0 @@
|
||||
#ifndef __ASRC_FIFO_TYPES__
|
||||
#define __ASRC_FIFO_TYPES__
|
||||
#include <stdint.h>
|
||||
|
||||
//Shared FIFO return types
|
||||
typedef enum fifo_ret_t {
|
||||
FIFO_SUCCESS = 0,
|
||||
FIFO_FULL,
|
||||
FIFO_EMPTY
|
||||
} fifo_ret_t;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
//Shared memory FIFO (sample by sample or block)
|
||||
//Can be any size
|
||||
//
|
||||
//Note that the actual storage for the FIFO is declared externally
|
||||
//and a reference to the base address of the storage is passed in along
|
||||
//with the size of the storage. This way, multiple instances may be
|
||||
//different sizes.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef struct mem_fifo_t {
|
||||
const unsigned size; //Size in INTs
|
||||
int * const unsafe data_base_ptr; //Base of the data array - declared externally so we can have differnt sized FIFOs
|
||||
unsigned write_idx;
|
||||
unsigned read_idx;
|
||||
} mem_fifo_t;
|
||||
|
||||
typedef struct mem_fifo_short_t {
|
||||
const unsigned size; //Size in SHORTs
|
||||
short * const unsafe data_base_ptr; //Base of the data array - declared externally so we can have differnt sized FIFOs
|
||||
unsigned write_idx;
|
||||
unsigned read_idx;
|
||||
} mem_fifo_short_t;
|
||||
|
||||
#endif
|
||||
@@ -1,21 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
|
||||
typedef int32_t xua_lite_fixed_point_t;
|
||||
|
||||
typedef struct pid_state_t{
|
||||
xua_lite_fixed_point_t fifo_level_filtered_old;
|
||||
xua_lite_fixed_point_t fifo_level_accum;
|
||||
} pid_state_t;
|
||||
|
||||
|
||||
//USB Adaptive mode helper
|
||||
xua_lite_fixed_point_t do_rate_control(int fill_level, pid_state_t *pid_state);
|
||||
|
||||
//PDM modulator for clock control
|
||||
void do_clock_nudge_pdm(xua_lite_fixed_point_t controller_out, int *clock_nudge);
|
||||
|
||||
|
||||
//USB Asynch mode helper
|
||||
void do_feedback_calculation(unsigned &sof_count, const unsigned mclk_hz, unsigned mclk_port_counter,unsigned &mclk_port_counter_old
|
||||
,long long &feedback_value, unsigned &mod_from_last_time, unsigned fb_clocks[1]);
|
||||
@@ -1,201 +0,0 @@
|
||||
#include <xs1.h>
|
||||
#include "xua_buffer_lite.h"
|
||||
#include "rate_controller.h"
|
||||
#define DEBUG_UNIT XUA_RATE_CONTROL
|
||||
#define DEBUG_PRINT_ENABLE_XUA_RATE_CONTROL 1
|
||||
#include "debug_print.h"
|
||||
|
||||
#define XUA_LIGHT_FIXED_POINT_Q_BITS 10 //Including sign bit. 10b gets us to +511.999999 to -512.000000
|
||||
#define XUA_LIGHT_FIXED_POINT_TOTAL_BITS (sizeof(xua_lite_fixed_point_t) * 8)
|
||||
#define XUA_LIGHT_FIXED_POINT_FRAC_BITS (XUA_LIGHT_FIXED_POINT_TOTAL_BITS - XUA_LIGHT_FIXED_POINT_Q_BITS)
|
||||
#define XUA_LIGHT_FIXED_POINT_ONE (1 << XUA_LIGHT_FIXED_POINT_FRAC_BITS)
|
||||
#define XUA_LIGHT_FIXED_POINT_MINUS_ONE (-XUA_LIGHT_FIXED_POINT_ONE)
|
||||
|
||||
#define FIFO_LEVEL_EMA_COEFF 0.939 //Proportion of signal from y[-1].
|
||||
//0.939 gives ~10Hz 3db cutoff low pass filter for filter rate of 1kHz
|
||||
//dsp.stackexchange.com/questions/40462/exponential-moving-average-cut-off-frequency/40465
|
||||
#define FIFO_LEVEL_A_COEFF ((int32_t)(INT_MAX * FIFO_LEVEL_EMA_COEFF)) //Scale to signed 1.31 format
|
||||
#define FIFO_LEVEL_B_COEFF (INT_MAX - FIFO_LEVEL_A_COEFF)
|
||||
|
||||
#define RANDOMISATION_PERCENT 20 //How much radnom noise to inject in percent of existing signal amplitude
|
||||
#define RANDOMISATION_COEFF_A ((INT_MAX / 100) * RANDOMISATION_PERCENT)
|
||||
|
||||
#define PID_CALC_OVERHEAD_BITS 2 //Allow large P,I or D constants, up to 2^(this number)
|
||||
|
||||
|
||||
#define PID_CONTROL_P_TERM 10.0
|
||||
#define PID_CONTROL_I_TERM 150.0
|
||||
#define PID_CONTROL_D_TERM 1.0
|
||||
|
||||
#define PID_RATE_MULTIPLIER SOF_FREQ_HZ
|
||||
|
||||
#define PID_CONTROL_P_TERM_COEFF ((xua_lite_fixed_point_t)((XUA_LIGHT_FIXED_POINT_ONE >> PID_CALC_OVERHEAD_BITS) * (float)PID_CONTROL_P_TERM)) //scale to fixed point
|
||||
#define PID_CONTROL_I_TERM_COEFF ((xua_lite_fixed_point_t)((XUA_LIGHT_FIXED_POINT_ONE >> PID_CALC_OVERHEAD_BITS) * (float)PID_CONTROL_I_TERM / PID_RATE_MULTIPLIER)) //scale to fixed point
|
||||
#define PID_CONTROL_D_TERM_COEFF ((xua_lite_fixed_point_t)((XUA_LIGHT_FIXED_POINT_ONE >> PID_CALC_OVERHEAD_BITS) * (float)PID_CONTROL_D_TERM * PID_RATE_MULTIPLIER)) //scale to fixed point
|
||||
|
||||
|
||||
static inline xua_lite_fixed_point_t do_fifo_depth_lowpass_filter(xua_lite_fixed_point_t old, int fifo_depth){
|
||||
//we grow from 32b to 64b for intermediate
|
||||
int64_t intermediate = ((int64_t)(fifo_depth << XUA_LIGHT_FIXED_POINT_FRAC_BITS) * (int64_t)FIFO_LEVEL_B_COEFF) + ((int64_t)old * (int64_t)FIFO_LEVEL_A_COEFF);
|
||||
xua_lite_fixed_point_t new_fifo_depth = (xua_lite_fixed_point_t)( intermediate >> (64 - XUA_LIGHT_FIXED_POINT_TOTAL_BITS - 1)); //-1 because signed int
|
||||
return new_fifo_depth;
|
||||
}
|
||||
|
||||
static inline int32_t get_random_number(void)
|
||||
{
|
||||
static const unsigned random_poly = 0xEDB88320;
|
||||
static unsigned random = 0x12345678;
|
||||
crc32(random, -1, random_poly);
|
||||
return (int32_t) random;
|
||||
}
|
||||
|
||||
static inline xua_lite_fixed_point_t add_noise(xua_lite_fixed_point_t input){
|
||||
//Note the input number cannot be bigger than 2 ^ (FIXED_POINT_Q_BITS - 1) * (1 + PERCENT) else we could oveflow
|
||||
//Eg. if Q bits = 10 then biggest input value is 255.9999
|
||||
int32_t random = get_random_number();
|
||||
int32_t input_fraction = ((int64_t)input * (int64_t)RANDOMISATION_COEFF_A) >> (XUA_LIGHT_FIXED_POINT_TOTAL_BITS - 1);
|
||||
int64_t output_64 = ((int64_t)input << (XUA_LIGHT_FIXED_POINT_TOTAL_BITS - 1)) + ((int64_t)input_fraction * (int64_t)random);
|
||||
return (xua_lite_fixed_point_t)( output_64 >> (64 - XUA_LIGHT_FIXED_POINT_TOTAL_BITS - 1));
|
||||
}
|
||||
|
||||
//Convert the control input into a pdm output (dither) with optional noise
|
||||
void do_clock_nudge_pdm(xua_lite_fixed_point_t controller_out, int *clock_nudge){
|
||||
|
||||
//Randomise - add a proportion of rectangular probability distribution noise to spread the spectrum
|
||||
controller_out = add_noise(controller_out);
|
||||
|
||||
//Convert to pulse density modulation (sigma-delta)
|
||||
static xua_lite_fixed_point_t nudge_accumulator = 0;
|
||||
nudge_accumulator += controller_out; //Note no overflow check as if we reach XUA_LIGHT_FIXED_POINT_Q_BITS
|
||||
//something is very wrong
|
||||
//printf("co: %d ratio: %f \n", controller_out, (float)controller_out/XUA_LIGHT_FIXED_POINT_ONE);
|
||||
if (nudge_accumulator >= XUA_LIGHT_FIXED_POINT_ONE){
|
||||
*clock_nudge = 1;
|
||||
nudge_accumulator -= XUA_LIGHT_FIXED_POINT_ONE;
|
||||
}
|
||||
else if (nudge_accumulator <= XUA_LIGHT_FIXED_POINT_MINUS_ONE){
|
||||
nudge_accumulator -= XUA_LIGHT_FIXED_POINT_MINUS_ONE;
|
||||
*clock_nudge = -1;
|
||||
}
|
||||
else{
|
||||
*clock_nudge = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Do PI control and modulation for adaptive USB audio
|
||||
xua_lite_fixed_point_t do_rate_control(int fill_level, pid_state_t *pid_state){
|
||||
|
||||
//We always check the FIFO level after USB has produced a block, and total FIFO size is 2x max, so half full is at 3/4
|
||||
const int half_full = ((MAX_OUT_SAMPLES_PER_SOF_PERIOD * 2) * 3) / 4;
|
||||
int fill_level_wrt_half = fill_level - half_full; //Will be +ve for more than half full and negative for less than half full
|
||||
|
||||
//Low pass filter fill level and get error w.r.t. to set point which is depth = 0 (relative to half full)
|
||||
xua_lite_fixed_point_t fifo_level_filtered = do_fifo_depth_lowpass_filter(pid_state->fifo_level_filtered_old , fill_level_wrt_half);
|
||||
//printf("old fill_level: %f fill_level: %f\n", (float)pid_state->fifo_level_filtered_old/(1<<XUA_LIGHT_FIXED_POINT_FRAC_BITS) , (float)fifo_level_filtered/(1<<XUA_LIGHT_FIXED_POINT_FRAC_BITS) );
|
||||
|
||||
//Calculate the value for the integral term which is the accumulated fill level error
|
||||
xua_lite_fixed_point_t i_term_pre_clip = pid_state->fifo_level_accum + fifo_level_filtered;
|
||||
|
||||
//clip the I term (which can wind up) to maximum fixed point representation. Check to see if overflow (which will change sign)
|
||||
if (fifo_level_filtered >= 0){ //If it was positive before, ensure it still is else clip to positive
|
||||
if (i_term_pre_clip >= pid_state->fifo_level_accum){
|
||||
//debug_printf("grow %d %d\n", (int32_t)i_term_pre_clip, (int32_t)pid_state->fifo_level_accum);
|
||||
pid_state->fifo_level_accum = i_term_pre_clip;
|
||||
}
|
||||
else{
|
||||
pid_state->fifo_level_accum = INT_MAX;
|
||||
//debug_printf("clip+ %d\n", pid_state->fifo_level_accum);
|
||||
}
|
||||
}
|
||||
else{ //Value was negative so ensure it still is else clip negative
|
||||
if (i_term_pre_clip <= pid_state->fifo_level_accum){
|
||||
pid_state->fifo_level_accum = i_term_pre_clip;
|
||||
}
|
||||
else{
|
||||
pid_state->fifo_level_accum = INT_MIN;
|
||||
//debug_printf("clip- %d %d\n", pid_state->fifo_level_accum, fifo_level_filtered);
|
||||
}
|
||||
}
|
||||
|
||||
//Calculate D term. No clipping required because it can never be larger than the D term,
|
||||
//which is already scaled to fit within the fixed point representation
|
||||
xua_lite_fixed_point_t fifo_level_delta = fifo_level_filtered - pid_state->fifo_level_filtered_old;
|
||||
|
||||
//Save to struct for next iteration
|
||||
pid_state->fifo_level_filtered_old = fifo_level_filtered;
|
||||
|
||||
//Do PID calculation. Note there is an implicit cast back to xua_lite_fixed_point_t before assignment
|
||||
xua_lite_fixed_point_t p_term = (((int64_t) fifo_level_filtered * (int64_t)PID_CONTROL_P_TERM_COEFF)) >> XUA_LIGHT_FIXED_POINT_FRAC_BITS;
|
||||
xua_lite_fixed_point_t i_term = (((int64_t) pid_state->fifo_level_accum * (int64_t)PID_CONTROL_I_TERM_COEFF)) >> XUA_LIGHT_FIXED_POINT_FRAC_BITS;
|
||||
xua_lite_fixed_point_t d_term = (((int64_t) fifo_level_delta * (int64_t)PID_CONTROL_D_TERM_COEFF)) >> XUA_LIGHT_FIXED_POINT_FRAC_BITS;
|
||||
|
||||
//debug_printf("p: %d i: %d f: %d\n", p_term >> XUA_LIGHT_FIXED_POINT_Q_BITS, i_term >> XUA_LIGHT_FIXED_POINT_Q_BITS, fifo_level_filtered >> (XUA_LIGHT_FIXED_POINT_FRAC_BITS - 10));
|
||||
//printf("p: %f i: %f d: %f filtered: %f integrated: %f\n", (float)p_term / (1<<(XUA_LIGHT_FIXED_POINT_FRAC_BITS-PID_CALC_OVERHEAD_BITS)), (float)i_term / (1<<(XUA_LIGHT_FIXED_POINT_FRAC_BITS-PID_CALC_OVERHEAD_BITS)), (float)d_term / (1<<(XUA_LIGHT_FIXED_POINT_FRAC_BITS-PID_CALC_OVERHEAD_BITS)), (float)fifo_level_filtered/(1<<XUA_LIGHT_FIXED_POINT_FRAC_BITS), (float)pid_state->fifo_level_accum/(1<<XUA_LIGHT_FIXED_POINT_FRAC_BITS));
|
||||
|
||||
//Sum and scale to +- 1.0 (important it does not exceed these values for following step)
|
||||
xua_lite_fixed_point_t controller_out = (p_term + i_term + d_term) >> (XUA_LIGHT_FIXED_POINT_Q_BITS - 1 - PID_CALC_OVERHEAD_BITS);
|
||||
|
||||
|
||||
//debug_printf("filtered: %d raw: %d\n", fifo_level_filtered >> 22, fill_level_wrt_half);
|
||||
|
||||
//static unsigned counter; counter++; if (counter>100){counter = 0; debug_printf("pid: %d\n",i_term >> (XUA_LIGHT_FIXED_POINT_FRAC_BITS - 10));}
|
||||
debug_printf("co: %d\n", controller_out >> XUA_LIGHT_FIXED_POINT_FRAC_BITS);
|
||||
return controller_out;
|
||||
}
|
||||
|
||||
|
||||
//Calculate feedback for asynchronous USB audio
|
||||
void do_feedback_calculation(unsigned &sof_count
|
||||
,const unsigned mclk_hz
|
||||
,unsigned mclk_port_counter
|
||||
,unsigned &mclk_port_counter_old
|
||||
,long long &feedback_value
|
||||
,unsigned &mod_from_last_time
|
||||
,unsigned fb_clocks[1]){
|
||||
// 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.
|
||||
|
||||
unsigned long long feedbackMul = 64ULL;
|
||||
if(AUDIO_CLASS == 1) 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 mclk_ticks_this_sof_period = (int) ((short)(mclk_port_counter - mclk_port_counter_old));
|
||||
unsigned long long full_result = mclk_ticks_this_sof_period * feedbackMul * DEFAULT_FREQ;
|
||||
feedback_value += full_result;
|
||||
|
||||
// Store MCLK for next time around...
|
||||
mclk_port_counter_old = mclk_port_counter;
|
||||
|
||||
// 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(sof_count == 128){
|
||||
//debug_printf("fb\n");
|
||||
sof_count = 0;
|
||||
|
||||
feedback_value += mod_from_last_time;
|
||||
unsigned clocks = feedback_value / mclk_hz;
|
||||
mod_from_last_time = feedback_value % mclk_hz;
|
||||
feedback_value = 0;
|
||||
|
||||
//Scale for working out number of samps to take from device for input
|
||||
if(AUDIO_CLASS == 2){
|
||||
clocks <<= 3;
|
||||
}
|
||||
else{
|
||||
clocks <<= 6;
|
||||
}
|
||||
asm volatile("stw %0, dp[g_speed]"::"r"(clocks)); // g_speed = clocks
|
||||
|
||||
//Write to feedback EP buffer
|
||||
if (AUDIO_CLASS == 2){
|
||||
fb_clocks[0] = clocks;
|
||||
}
|
||||
else{
|
||||
fb_clocks[0] = clocks >> 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
#include <xs1.h>
|
||||
#include "xua_ep0_wrapper.h"
|
||||
#include "xua.h"
|
||||
|
||||
//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) ) //1000 for FS or 8000 for HS
|
||||
|
||||
//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)
|
||||
|
||||
unsafe void XUA_Buffer_lite(chanend c_ep0_out, chanend c_ep0_in, chanend c_aud_out, chanend ?c_feedback, chanend c_aud_in, chanend c_sof, in port p_for_mclk_count, streaming chanend c_audio_hub);
|
||||
[[combinable]]
|
||||
unsafe void XUA_Buffer_lite2(server ep0_control_if i_ep0_ctl, chanend c_aud_out, chanend ?c_feedback, chanend c_aud_in, chanend c_sof, in port p_for_mclk_count, streaming chanend c_audio_hub);
|
||||
|
||||
/** Transfer samples to/from XUA. Should be called at the current USB rate.
|
||||
* This function is non-blocking.
|
||||
*
|
||||
* \param[in,out] c_audio Channel to XUA.
|
||||
*
|
||||
* \param[out] sampsFromUsbToAudio Samples sent from host to device.
|
||||
*
|
||||
* \param[in] sampsFromAudioToUsb Samples to send from device to host.
|
||||
*
|
||||
* \param[out] clock_nudge Notification that the device is running
|
||||
* too slowly/quickly. Only used when in
|
||||
* adaptive endpoint mode.
|
||||
*/
|
||||
static inline void XUA_transfer_samples(streaming chanend c_audio,
|
||||
unsigned sampsFromUsbToAudio[],
|
||||
unsigned sampsFromAudioToUsb[],
|
||||
int &clock_nudge) {
|
||||
//Transfer samples. Takes about 25 ticks
|
||||
for (int i = 0; i < NUM_USB_CHAN_OUT; i++) c_audio :> sampsFromUsbToAudio[i];
|
||||
if (XUA_ADAPTIVE) c_audio :> clock_nudge;
|
||||
for (int i = 0; i < NUM_USB_CHAN_IN; i++) c_audio <: sampsFromAudioToUsb[i];
|
||||
}
|
||||
@@ -1,384 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <xs1.h>
|
||||
|
||||
#include "xua_commands.h"
|
||||
#include "xud.h"
|
||||
#include "testct_byref.h"
|
||||
#define DEBUG_UNIT XUA_LITE_BUFFER
|
||||
#define DEBUG_PRINT_ENABLE_XUA_LITE_BUFFER 1
|
||||
#include "debug_print.h"
|
||||
#include "xua.h"
|
||||
#include "fifo_impl.h"
|
||||
#include "xua_ep0_wrapper.h"
|
||||
#include "rate_controller.h"
|
||||
#include "xua_buffer_lite.h"
|
||||
|
||||
|
||||
extern "C"{
|
||||
void XUA_Endpoint0_lite_init(chanend c_ep0_out, chanend c_ep0_in, chanend c_audioControl,
|
||||
chanend ?c_mix_ctl, chanend ?c_clk_ctl, chanend ?c_EANativeTransport_ctrl, CLIENT_INTERFACE(i_dfu, ?dfuInterface) VENDOR_REQUESTS_PARAMS_DEC_);
|
||||
void XUA_Endpoint0_lite_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend c_ep0_out, chanend c_ep0_in, chanend c_audioControl,
|
||||
chanend ?c_mix_ctl, chanend ?c_clk_ctl, chanend ?c_EANativeTransport_ctrl, CLIENT_INTERFACE(i_dfu, ?dfuInterface) VENDOR_REQUESTS_PARAMS_DEC_, unsigned *input_interface_num, unsigned *output_interface_num);
|
||||
}
|
||||
#pragma select handler
|
||||
void XUD_GetSetupData_Select(chanend c, XUD_ep e_out, unsigned &length, XUD_Result_t &result);
|
||||
|
||||
extern XUD_ep ep0_out;
|
||||
extern XUD_ep ep0_in;
|
||||
|
||||
#if 0
|
||||
//Unsafe to allow us to use fifo API without local unsafe scope
|
||||
unsafe void XUA_Buffer_lite(chanend c_ep0_out, chanend c_ep0_in, chanend c_aud_out, chanend ?c_feedback, chanend c_aud_in, chanend c_sof, in port p_for_mclk_count, streaming chanend c_audio_hub) {
|
||||
|
||||
debug_printf("%d\n", MAX_OUT_SAMPLES_PER_SOF_PERIOD);
|
||||
|
||||
//These buffers are unions so we can access them as different types
|
||||
union buffer_aud_out{
|
||||
unsigned char bytes[OUT_AUDIO_BUFFER_SIZE_BYTES];
|
||||
short short_words[OUT_AUDIO_BUFFER_SIZE_BYTES / 2];
|
||||
long long_words[OUT_AUDIO_BUFFER_SIZE_BYTES / 4];
|
||||
}buffer_aud_out;
|
||||
union buffer_aud_in{
|
||||
unsigned char bytes[IN_AUDIO_BUFFER_SIZE_BYTES];
|
||||
short short_words[IN_AUDIO_BUFFER_SIZE_BYTES / 2];
|
||||
unsigned long long_words[IN_AUDIO_BUFFER_SIZE_BYTES / 4];
|
||||
}buffer_aud_in;
|
||||
|
||||
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;
|
||||
|
||||
//Asynch feedback calculation
|
||||
unsigned sof_count = 0;
|
||||
unsigned mclk_port_counter_old = 0;
|
||||
long long feedback_value = 0;
|
||||
unsigned mod_from_last_time = 0;
|
||||
const unsigned mclk_hz = MCLK_48;
|
||||
unsigned int fb_clocks[1] = {0};
|
||||
|
||||
//Adapative device clock control
|
||||
int clock_nudge = 0;
|
||||
pid_state_t pid_state = {0, 0};
|
||||
|
||||
//Endpoints
|
||||
XUD_ep ep_aud_out = XUD_InitEp(c_aud_out);
|
||||
XUD_ep ep_aud_in = XUD_InitEp(c_aud_in);
|
||||
XUD_ep ep_feedback = 0;
|
||||
if (!isnull(c_feedback)) ep_feedback = XUD_InitEp(c_feedback);
|
||||
|
||||
unsigned num_samples_received_from_host = 0;
|
||||
unsigned num_samples_to_send_to_host = 0;
|
||||
|
||||
short samples_in_short[NUM_USB_CHAN_IN] = {0};
|
||||
short samples_out_short[NUM_USB_CHAN_OUT] = {0};
|
||||
|
||||
#define c_audioControl null
|
||||
#define dfuInterface null
|
||||
XUA_Endpoint0_lite_init(c_ep0_out, c_ep0_in, c_audioControl, null, null, null, dfuInterface);
|
||||
unsigned char sbuffer[120]; //Raw buffer for EP0 data
|
||||
USB_SetupPacket_t sp; //Parsed setup packet from EP0
|
||||
|
||||
unsigned input_interface_num = 0;
|
||||
unsigned output_interface_num = 0;
|
||||
|
||||
//Enable all EPs
|
||||
XUD_SetReady_OutPtr(ep_aud_out, (unsigned)buffer_aud_out.long_words);
|
||||
XUD_SetReady_InPtr(ep_aud_in, (unsigned)buffer_aud_in.long_words, num_samples_to_send_to_host);
|
||||
XUD_SetReady_Out(ep0_out, sbuffer);
|
||||
if (!isnull(c_feedback)) XUD_SetReady_InPtr(ep_feedback, (unsigned)fb_clocks, (AUDIO_CLASS == 2) ? 4 : 3);
|
||||
|
||||
|
||||
//Send initial samples so audiohub is not blocked
|
||||
for (int i = 0; i < 2 * (NUM_USB_CHAN_OUT + (XUA_ADAPTIVE != 0 ? 1 : 0)); i++) c_audio_hub <: 0;
|
||||
|
||||
//FIFOs from EP buffers to audio
|
||||
short host_to_device_fifo_storage[MAX_OUT_SAMPLES_PER_SOF_PERIOD * 2];
|
||||
short device_to_host_fifo_storage[MAX_IN_SAMPLES_PER_SOF_PERIOD * 2];
|
||||
mem_fifo_short_t host_to_device_fifo = {sizeof(host_to_device_fifo_storage)/sizeof(host_to_device_fifo_storage[0]), host_to_device_fifo_storage, 0, 0};
|
||||
mem_fifo_short_t device_to_host_fifo = {sizeof(device_to_host_fifo_storage)/sizeof(device_to_host_fifo_storage[0]), device_to_host_fifo_storage, 0, 0};
|
||||
volatile mem_fifo_short_t * unsafe host_to_device_fifo_ptr = &host_to_device_fifo;
|
||||
volatile mem_fifo_short_t * unsafe device_to_host_fifo_ptr = &device_to_host_fifo;
|
||||
|
||||
//XUD transaction variables passed in by reference
|
||||
XUD_Result_t result;
|
||||
unsigned length = 0;
|
||||
unsigned u_tmp; //For select channel input by ref on EP0
|
||||
int s_tmp; //For select on channel from audiohub
|
||||
while(1){
|
||||
#pragma ordered
|
||||
select{
|
||||
//Handle EP0 requests
|
||||
case XUD_GetSetupData_Select(c_ep0_out, ep0_out, length, result):
|
||||
timer tmr; int t0, t1; tmr :> t0;
|
||||
|
||||
debug_printf("ep0, result: %d, length: %d\n", result, length); //-1 reset, 0 ok, 1 error
|
||||
USB_ParseSetupPacket(sbuffer, sp); //Parse data buffer end populate SetupPacket struct
|
||||
|
||||
XUA_Endpoint0_lite_loop(result, sp, c_ep0_out, c_ep0_in, c_audioControl, null/*mix*/, null/*clk*/, null/*EA*/, dfuInterface, &input_interface_num, &output_interface_num);
|
||||
XUD_SetReady_Out(ep0_out, sbuffer);
|
||||
tmr :> t1; debug_printf("c%d\n", t1 - t0);
|
||||
|
||||
break;
|
||||
|
||||
//SOF handling
|
||||
case inuint_byref(c_sof, u_tmp):
|
||||
timer tmr; int t0, t1; tmr :> t0;
|
||||
unsigned mclk_port_counter = 0;
|
||||
asm volatile(" getts %0, res[%1]" : "=r" (mclk_port_counter) : "r" (p_for_mclk_count));
|
||||
if (!isnull(c_feedback)) do_feedback_calculation(sof_count, mclk_hz, mclk_port_counter, mclk_port_counter_old, feedback_value, mod_from_last_time, fb_clocks);
|
||||
sof_count++;
|
||||
tmr :> t1; debug_printf("s%d\n", t1 - t0);
|
||||
|
||||
break;
|
||||
|
||||
//Receive samples from host
|
||||
case XUD_GetData_Select(c_aud_out, ep_aud_out, length, result):
|
||||
timer tmr; int t0, t1; tmr :> t0;
|
||||
|
||||
num_samples_received_from_host = length / out_subslot_size;
|
||||
|
||||
fifo_ret_t ret = fifo_block_push_short(host_to_device_fifo_ptr, buffer_aud_out.short_words, num_samples_received_from_host);
|
||||
if (ret != FIFO_SUCCESS) debug_printf("h2d full\n");
|
||||
num_samples_to_send_to_host = num_samples_received_from_host;
|
||||
|
||||
int fill_level = fifo_get_fill_short(host_to_device_fifo_ptr);
|
||||
if (isnull(c_feedback)) do_clock_nudge_pdm(do_rate_control(fill_level, &pid_state), &clock_nudge);
|
||||
|
||||
//Mark EP as ready for next frame from host
|
||||
XUD_SetReady_OutPtr(ep_aud_out, (unsigned)buffer_aud_out.long_words);
|
||||
tmr :> t1; debug_printf("o%d\n", t1 - t0);
|
||||
break;
|
||||
|
||||
//Send asynch explicit feedback value, but only if enabled
|
||||
case !isnull(c_feedback) => XUD_SetData_Select(c_feedback, ep_feedback, result):
|
||||
timer tmr; int t0, t1; tmr :> t0;
|
||||
|
||||
XUD_SetReady_In(ep_feedback, (fb_clocks, unsigned char[]), (AUDIO_CLASS == 2) ? 4 : 3);
|
||||
//debug_printf("0x%x\n", fb_clocks[0]);
|
||||
tmr :> t1; debug_printf("f%d\n", t1 - t0);
|
||||
|
||||
break;
|
||||
|
||||
//Send samples to host
|
||||
case XUD_SetData_Select(c_aud_in, ep_aud_in, result):
|
||||
timer tmr; int t0, t1; tmr :> t0;
|
||||
|
||||
if (output_interface_num == 0) num_samples_to_send_to_host = (DEFAULT_FREQ / SOF_FREQ_HZ) * NUM_USB_CHAN_IN;
|
||||
|
||||
fifo_ret_t ret = fifo_block_pop_short(device_to_host_fifo_ptr, buffer_aud_in.short_words, num_samples_received_from_host);
|
||||
if (ret != FIFO_SUCCESS) debug_printf("d2h empty\n");
|
||||
|
||||
//Populate the input buffer ready for the next read
|
||||
//pack_samples_to_buff(loopback_samples, 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.long_words, input_buffer_size); //loopback
|
||||
num_samples_to_send_to_host = 0;
|
||||
tmr :> t1; debug_printf("i%d\n", t1 - t0);
|
||||
|
||||
break;
|
||||
|
||||
//Exchange samples with audiohub. Note we are using channel buffering here to act as a FIFO
|
||||
case c_audio_hub :> s_tmp:
|
||||
timer tmr; int t0, t1; tmr :> t0;
|
||||
samples_in_short[0] = s_tmp >> 16;
|
||||
for (int i = 1; i < NUM_USB_CHAN_IN; i++){
|
||||
c_audio_hub :> s_tmp;
|
||||
samples_in_short[i] = s_tmp >> 16;
|
||||
}
|
||||
fifo_ret_t ret = fifo_block_pop_short(host_to_device_fifo_ptr, samples_out_short, NUM_USB_CHAN_OUT);
|
||||
if (ret != FIFO_SUCCESS && output_interface_num != 0) debug_printf("h2d empty\n");
|
||||
for (int i = 0; i < NUM_USB_CHAN_OUT; i++) c_audio_hub <: (int)samples_out_short[i] << 16;
|
||||
if (XUA_ADAPTIVE) c_audio_hub <: clock_nudge;
|
||||
ret = fifo_block_push_short(device_to_host_fifo_ptr, samples_in_short, NUM_USB_CHAN_IN);
|
||||
if (ret != FIFO_SUCCESS && input_interface_num != 0) debug_printf("d2h full\n");
|
||||
tmr :> t1; debug_printf("a%d\n", t1 - t0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
extern port p_sda;
|
||||
|
||||
[[combinable]]
|
||||
//Unsafe to allow us to use fifo API without local unsafe scope
|
||||
unsafe void XUA_Buffer_lite2(server ep0_control_if i_ep0_ctl, chanend c_aud_out, chanend ?c_feedback, chanend c_aud_in, chanend c_sof, in port p_for_mclk_count, streaming chanend c_audio_hub) {
|
||||
|
||||
debug_printf("%d\n", MAX_OUT_SAMPLES_PER_SOF_PERIOD);
|
||||
|
||||
//These buffers are unions so we can access them as different types
|
||||
union buffer_aud_out{
|
||||
unsigned char bytes[OUT_AUDIO_BUFFER_SIZE_BYTES];
|
||||
short short_words[OUT_AUDIO_BUFFER_SIZE_BYTES / 2];
|
||||
long long_words[OUT_AUDIO_BUFFER_SIZE_BYTES / 4];
|
||||
}buffer_aud_out;
|
||||
union buffer_aud_in{
|
||||
unsigned char bytes[IN_AUDIO_BUFFER_SIZE_BYTES];
|
||||
short short_words[IN_AUDIO_BUFFER_SIZE_BYTES / 2];
|
||||
unsigned long long_words[IN_AUDIO_BUFFER_SIZE_BYTES / 4];
|
||||
}buffer_aud_in;
|
||||
|
||||
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;
|
||||
|
||||
//Asynch feedback calculation
|
||||
unsigned sof_count = 0;
|
||||
unsigned mclk_port_counter_old = 0;
|
||||
long long feedback_value = 0;
|
||||
unsigned mod_from_last_time = 0;
|
||||
const unsigned mclk_hz = MCLK_48;
|
||||
unsigned int fb_clocks[1] = {0};
|
||||
|
||||
//Adapative device clock control
|
||||
int clock_nudge = 0;
|
||||
pid_state_t pid_state = {0, 0};
|
||||
|
||||
|
||||
//Endpoints
|
||||
XUD_ep ep_aud_out = XUD_InitEp(c_aud_out);
|
||||
XUD_ep ep_aud_in = XUD_InitEp(c_aud_in);
|
||||
XUD_ep ep_feedback = 0;
|
||||
if (!isnull(c_feedback)) ep_feedback = XUD_InitEp(c_feedback);
|
||||
|
||||
unsigned num_samples_received_from_host = 0;
|
||||
unsigned num_samples_to_send_to_host = 0;
|
||||
|
||||
unsigned input_interface_num = 0;
|
||||
unsigned output_interface_num = 0;
|
||||
|
||||
//Enable all EPs
|
||||
XUD_SetReady_OutPtr(ep_aud_out, (unsigned)buffer_aud_out.long_words);
|
||||
XUD_SetReady_InPtr(ep_aud_in, (unsigned)buffer_aud_in.long_words, num_samples_to_send_to_host);
|
||||
if (!isnull(c_feedback)) XUD_SetReady_InPtr(ep_feedback, (unsigned)fb_clocks, (AUDIO_CLASS == 2) ? 4 : 3);
|
||||
|
||||
short samples_in_short[NUM_USB_CHAN_IN] = {0};
|
||||
short samples_out_short[NUM_USB_CHAN_OUT] = {0};
|
||||
|
||||
|
||||
//Send initial samples so audiohub is not blocked
|
||||
const unsigned n_sample_periods_to_preload = 2;
|
||||
for (int i = 0; i < n_sample_periods_to_preload * (NUM_USB_CHAN_OUT + (XUA_ADAPTIVE != 0 ? 1 : 0)); i++) c_audio_hub <: 0;
|
||||
|
||||
//FIFOs from EP buffers to audio
|
||||
short host_to_device_fifo_storage[MAX_OUT_SAMPLES_PER_SOF_PERIOD * 2];
|
||||
short device_to_host_fifo_storage[MAX_IN_SAMPLES_PER_SOF_PERIOD * 2];
|
||||
mem_fifo_short_t host_to_device_fifo = {sizeof(host_to_device_fifo_storage)/sizeof(host_to_device_fifo_storage[0]), host_to_device_fifo_storage, 0, 0};
|
||||
mem_fifo_short_t device_to_host_fifo = {sizeof(device_to_host_fifo_storage)/sizeof(device_to_host_fifo_storage[0]), device_to_host_fifo_storage, 0, 0};
|
||||
volatile mem_fifo_short_t * unsafe host_to_device_fifo_ptr = &host_to_device_fifo;
|
||||
volatile mem_fifo_short_t * unsafe device_to_host_fifo_ptr = &device_to_host_fifo;
|
||||
|
||||
//XUD transaction variables passed in by reference
|
||||
XUD_Result_t result;
|
||||
unsigned length = 0;
|
||||
unsigned u_tmp; //For select channel input by ref on EP0
|
||||
int s_tmp; //For select on channel from audiohub
|
||||
while(1){
|
||||
select{
|
||||
//Handle EP0 requests
|
||||
case i_ep0_ctl.set_output_interface(unsigned num):
|
||||
//Reset output FIFO if moving from idle to streaming
|
||||
if (num != 0 && output_interface_num == 0) fifo_init_short(host_to_device_fifo_ptr);
|
||||
output_interface_num = num;
|
||||
debug_printf("output_interface_num: %d\n", num);
|
||||
break;
|
||||
|
||||
case i_ep0_ctl.set_input_interface(unsigned num):
|
||||
input_interface_num = num;
|
||||
debug_printf("input_interface_num: %d\n", num);
|
||||
break;
|
||||
|
||||
case i_ep0_ctl.set_host_active(unsigned active):
|
||||
break;
|
||||
|
||||
//SOF handling
|
||||
case inuint_byref(c_sof, u_tmp):
|
||||
timer tmr; int t0, t1; tmr :> t0;
|
||||
unsigned mclk_port_counter = 0;
|
||||
asm volatile(" getts %0, res[%1]" : "=r" (mclk_port_counter) : "r" (p_for_mclk_count));
|
||||
if (!isnull(c_feedback)) do_feedback_calculation(sof_count, mclk_hz, mclk_port_counter, mclk_port_counter_old, feedback_value, mod_from_last_time, fb_clocks);
|
||||
sof_count++;
|
||||
//tmr :> t1; debug_printf("s%d\n", t1 - t0);
|
||||
uint16_t port_counter;
|
||||
p_sda <: 1 @ port_counter;
|
||||
p_sda @ port_counter + 100 <: 0;
|
||||
break;
|
||||
|
||||
//Receive samples from host
|
||||
case XUD_GetData_Select(c_aud_out, ep_aud_out, length, result):
|
||||
timer tmr; int t0, t1; tmr :> t0;
|
||||
|
||||
num_samples_received_from_host = length / out_subslot_size;
|
||||
|
||||
if (num_samples_received_from_host != 96) debug_printf("hs: %d\n", num_samples_received_from_host);
|
||||
|
||||
fifo_ret_t ret = fifo_block_push_short_fast(host_to_device_fifo_ptr, buffer_aud_out.short_words, num_samples_received_from_host);
|
||||
if (ret != FIFO_SUCCESS) debug_printf("h2d full\n");
|
||||
num_samples_to_send_to_host = num_samples_received_from_host;
|
||||
|
||||
int fill_level = fifo_get_fill_short(host_to_device_fifo_ptr);
|
||||
if (isnull(c_feedback)) do_clock_nudge_pdm(do_rate_control(fill_level, &pid_state), &clock_nudge);
|
||||
|
||||
//Mark EP as ready for next frame from host
|
||||
XUD_SetReady_OutPtr(ep_aud_out, (unsigned)buffer_aud_out.long_words);
|
||||
//tmr :> t1; debug_printf("o%d\n", t1 - t0);
|
||||
break;
|
||||
|
||||
//Send asynch explicit feedback value, but only if enabled
|
||||
case !isnull(c_feedback) => XUD_SetData_Select(c_feedback, ep_feedback, result):
|
||||
timer tmr; int t0, t1; tmr :> t0;
|
||||
|
||||
XUD_SetReady_In(ep_feedback, (fb_clocks, unsigned char[]), (AUDIO_CLASS == 2) ? 4 : 3);
|
||||
//debug_printf("0x%x\n", fb_clocks[0]);
|
||||
//tmr :> t1; debug_printf("f%d\n", t1 - t0);
|
||||
break;
|
||||
|
||||
//Send samples to host
|
||||
case XUD_SetData_Select(c_aud_in, ep_aud_in, result):
|
||||
timer tmr; int t0, t1; tmr :> t0;
|
||||
|
||||
//If host is not streaming out, then send a fixed number of samples to host
|
||||
if (output_interface_num == 0) {
|
||||
num_samples_to_send_to_host = (DEFAULT_FREQ / SOF_FREQ_HZ) * NUM_USB_CHAN_IN;
|
||||
int fill_level = fifo_get_fill_short(device_to_host_fifo_ptr);
|
||||
if (isnull(c_feedback)) do_clock_nudge_pdm(-do_rate_control(fill_level, &pid_state), &clock_nudge);
|
||||
}
|
||||
|
||||
fifo_ret_t ret = fifo_block_pop_short_fast(device_to_host_fifo_ptr, buffer_aud_in.short_words, num_samples_received_from_host);
|
||||
if (ret != FIFO_SUCCESS) {
|
||||
memset(buffer_aud_in.short_words, 0, sizeof(buffer_aud_in.short_words));
|
||||
debug_printf("d2h empty\n");
|
||||
}
|
||||
|
||||
//Populate the input buffer ready for the next read
|
||||
//pack_samples_to_buff(loopback_samples, 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.long_words, input_buffer_size); //loopback
|
||||
num_samples_to_send_to_host = 0;
|
||||
//tmr :> t1; debug_printf("i%d\n", t1 - t0);
|
||||
break;
|
||||
|
||||
//Exchange samples with audiohub. Note we are using channel buffering here to act as a FIFO
|
||||
case c_audio_hub :> s_tmp:
|
||||
timer tmr; int t0, t1; tmr :> t0;
|
||||
samples_in_short[0] = s_tmp >> 16;
|
||||
for (int i = 1; i < NUM_USB_CHAN_IN; i++){
|
||||
c_audio_hub :> s_tmp;
|
||||
samples_in_short[i] = s_tmp >> 16;
|
||||
}
|
||||
fifo_ret_t ret = fifo_block_pop_short(host_to_device_fifo_ptr, samples_out_short, NUM_USB_CHAN_OUT);
|
||||
if (ret != FIFO_SUCCESS && output_interface_num != 0) {
|
||||
memset(samples_out_short, 0, sizeof(samples_out_short));
|
||||
debug_printf("h2d empty\n");
|
||||
}
|
||||
for (int i = 0; i < NUM_USB_CHAN_OUT; i++) c_audio_hub <: (int)samples_out_short[i] << 16;
|
||||
if (XUA_ADAPTIVE) c_audio_hub <: clock_nudge;
|
||||
ret = fifo_block_push_short(device_to_host_fifo_ptr, samples_in_short, NUM_USB_CHAN_IN);
|
||||
if (ret != FIFO_SUCCESS && input_interface_num != 0) debug_printf("d2h full\n");
|
||||
//tmr :> t1; debug_printf("a%d\n", t1 - t0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
//Helper to disassemble USB packets into 32b left aligned audio samples
|
||||
#pragma unsafe arrays
|
||||
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
|
||||
#pragma unsafe arrays
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user