forked from PAWPAW-Mirror/lib_xua
Refactor: Separate XUA lite and app code
This commit is contained in:
@@ -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>
|
||||
@@ -8,6 +8,7 @@
|
||||
#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}};
|
||||
@@ -35,8 +36,6 @@ void AudioHub(server i2s_frame_callback_if i2s,
|
||||
mic_array_decimator_configure(c_ds_output, decimatorCount, dc);
|
||||
mic_array_init_time_domain_frame(c_ds_output, decimatorCount, buffer, mic_audio_frame, dc);
|
||||
|
||||
UserBufferManagementInit();
|
||||
|
||||
// Used for debug
|
||||
//int saw = 0;
|
||||
|
||||
@@ -63,12 +62,7 @@ void AudioHub(server i2s_frame_callback_if i2s,
|
||||
restart = I2S_NO_RESTART; // Keep on looping
|
||||
timer tmr; int t0, t1; tmr :> t0;
|
||||
|
||||
UserBufferManagement((unsigned *) raw_mics, (unsigned *) samples_out);
|
||||
|
||||
//Transfer samples. Takes about 25 ticks
|
||||
for (int i = 0; i < NUM_USB_CHAN_OUT; i++) c_audio :> samples_out[i];
|
||||
if (XUA_ADAPTIVE) c_audio :> clock_nudge;
|
||||
for (int i = 0; i < NUM_USB_CHAN_IN; i++) c_audio <: raw_mics[i];
|
||||
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);
|
||||
|
||||
@@ -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,25 +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);
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user