Compare commits

18 Commits

Author SHA1 Message Date
Ross Owen
dd8d2675d3 App PLL sync mode improvements:
- Added IIR to reduce jitter into DCO
- Added PID for tracking external clock
- Added basic “fast lock” based on an initial estimate
2023-08-15 11:58:32 +01:00
Ross Owen
7930a5d59c Sync mode 2nd order delta sigma replaced with third order, level output. 2023-08-08 18:19:04 +01:00
Ross Owen
158d79335c Sync mode related build fix 2023-08-02 12:43:04 +01:00
Ross Owen
ebc09e1c4f Fixes for sync mode related build warnings/errors 2023-08-02 12:14:21 +01:00
Ross Owen
a7943a8859 - Improved audio performance in synchronous mode when using the App PLL
- Software PLL code now uses a core rather than running entirely in XUA_Buffer_EP()
2023-08-01 15:56:00 +01:00
Ross Owen
77fad35497 Documentation updates 2023-07-17 14:23:09 +01:00
Ross Owen
50966dda90 Build fixes related to the use of i_pll_ref 2023-07-14 16:07:38 +01:00
Ross Owen
e7b5faed66 Documentation updates and fixes 2023-07-14 15:58:54 +01:00
Ross Owen
7f72c4b842 - Moved audiohw functions from xua_audiohub into user folder in line with other user functions 2023-07-14 15:58:03 +01:00
Ross Owen
d119755313 Do not use sync out pin when using app PLL for sync mode 2023-07-14 11:59:15 +01:00
Ross Owen
2228575e09 Documentation updates 2023-07-14 11:59:15 +01:00
Ross Owen
b6972295e9 Merge pull request #339 from xross/experimetal/sync_app_pll
Experimental synchronous mode using xs3 app pll
2023-07-13 16:15:26 +01:00
Ross Owen
39802f3620 Example app note code now use app pll code from lib_xua 2023-07-13 15:09:29 +01:00
Ross Owen
5bf53fa453 Fix to test_sync_clk_basic 2023-07-13 14:12:52 +01:00
Ross Owen
2f11eda7d8 - Added AudioHwConfig_Mute() and AudioHwConfig_UnMute()
- Added default (empty) implementations of AudioHW.. functions
2023-07-13 14:12:41 +01:00
Ross Owen
1574137dda - Update sofCount type
- Fix sync clock tests
2023-07-13 13:01:25 +01:00
Ross Owen
c7e782179c Added copyright/licence comments 2023-07-12 15:36:00 +01:00
Ross Owen
badcb71c80 - Added app PLL config functions - previously were in sw_usb_audio
- Use app PLL for sync mode
- Use app PLL on xcore.ai devices by default (unless ADAT or SPDIF receive enabled) 
- Added XUA_USE_APP_PLL (which is default 1)
- Some tidy of xua_conf_default.h
- Added DEFAULT_MCLK define
2023-07-11 13:57:14 +01:00
81 changed files with 2155 additions and 3285 deletions

4
.gitignore vendored
View File

@@ -48,7 +48,3 @@ host_usb_mixer_control/xmos_mixer
**/.vscode/**
**.egg-info
*tests/logs/*
midi_tx_cmds.txt
midi_rx_cmds.txt
trace.txt
tests/xua_unit_tests/src.runners

View File

@@ -1,72 +1,6 @@
lib_xua change log
lib_xua Change Log
==================
4.1.0
-----
* ADDED: MIDI unit and sub-system tests
* CHANGED: Only the minimum number of ADAT input formats are enabled based
on the supported sample rates
* CHANGED: Enabling ADAT tx enables different channel count interface alts,
based on sample rate
* CHANGED: Input audio buffer size and the exit condition underflow modified
to to fix buffer underflow in some configurations
* CHANGED: CT_END token based handshake in MIDI channels transactions,
reducing opportuninity for deadlock
* FIXED: Device fails to enumerate when ADAT and S/PDIF transmit are
enabled
* FIXED: Update software PLL at the correct rate for ADAT S/MUX
* FIXED: Incorrect internal input EP count for input only devices
* FIXED: Samples transferred to ADAT tx too frequently in TDM mode
* FIXED: S/MUX not initialised to a value based on DEFAULT_FREQ in
clockgen
* FIXED: Trap when moving to DSD mode on XS3A based devices
* Changes to dependencies:
- lib_adat: 1.0.1 -> 1.2.0
- lib_locks: 2.1.0 -> 2.2.0
- lib_logging: 3.1.1 -> 3.2.0
- lib_sw_pll: 2.1.0 -> 2.2.0
- lib_xassert: 4.1.0 -> 4.2.0
4.0.0
-----
* ADDED: Support for XCommon CMake build system
* FIXED: Output volume control not enabled by default when MIXER disabled
* FIXED: Full 32bit result of volume processing not calculated when
required
* FIXED: Input stream sending an erroneous zero-length packet when exiting
underflow state
* FIXED Build failures when XUA_USB_EN = 0
* FIXED: Clock configuration issues when ADAT and S/PDIF receive are
enabled (#352)
* FIXED: Repeated old S/PDIF and ADAT samples when entering underflow
state
* CHANGED: QUAD_SPI_FLASH replaced by XUA_QUAD_SPI_FLASH (default: 1)
* CHANGED: UserBufferManagementInit() now takes a sample rate parameter
* CHANGED: xcore.ai targets use sigma-delta software PLL for clock recovery
of digital Rx streams and synchronous USB audio by default
* CHANGED: Windows host mixer control application now requires driver GUID
option
* Changes to dependencies:
- lib_dsp: 6.2.1 -> 6.3.0
- lib_mic_array: 4.5.0 -> 4.6.0
- lib_spdif: 5.0.1 -> 6.1.0
- lib_sw_pll: Added dependency 2.1.0
- lib_xud: 2.2.3 -> 2.3.1
3.5.1
-----

22
Jenkinsfile vendored
View File

@@ -1,4 +1,4 @@
@Library('xmos_jenkins_shared_library@v0.27.0') _
@Library('xmos_jenkins_shared_library@v0.24.0') _
getApproval()
@@ -7,7 +7,6 @@ pipeline {
environment {
REPO = 'lib_xua'
VIEW = getViewName(REPO)
TOOLS_VERSION = "15.2.1" // For unit tests
}
options {
skipDefaultCheckout()
@@ -36,8 +35,7 @@ pipeline {
dir("${REPO}/tests"){
viewEnv(){
withVenv{
sh "xmake -C test_midi -j" // Xdist does not like building so do here
runPytest('--numprocesses=auto -vvv')
runPytest('--numprocesses=4')
}
}
}
@@ -45,14 +43,14 @@ pipeline {
}
stage('Unity tests') {
steps {
dir("${REPO}/tests/xua_unit_tests") {
withTools("${env.TOOLS_VERSION}") {
withVenv {
withEnv(["XMOS_CMAKE_PATH=${WORKSPACE}/xcommon_cmake"]) {
sh "cmake -G 'Unix Makefiles' -B build"
sh 'xmake -C build -j'
runPython("pytest -s --junitxml=pytest_unity.xml")
junit "pytest_unity.xml"
dir("${REPO}") {
dir('tests') {
dir('xua_unit_tests') {
withVenv {
runWaf('.', "configure clean build --target=xcore200")
viewEnv() {
runPython("TARGET=XCORE200 pytest -s")
}
}
}
}

View File

@@ -1,9 +1,8 @@
lib_xua
#######
:Version: 4.1.0
:Version: 3.5.0
:Vendor: XMOS
:Scope: General Use
Summary
@@ -82,15 +81,14 @@ The following application notes use this library:
Required Software (dependencies)
================================
* lib_adat (www.github.com/xmos/lib_adat)
* lib_locks (www.github.com/xmos/lib_locks)
* lib_logging (www.github.com/xmos/lib_logging)
* lib_mic_array (www.github.com/xmos/lib_mic_array)
* lib_xassert (www.github.com/xmos/lib_xassert)
* lib_dsp (www.github.com/xmos/lib_dsp)
* lib_spdif (www.github.com/xmos/lib_spdif)
* lib_sw_pll (www.github.com/xmos/lib_sw_pll)
* lib_xud (www.github.com/xmos/lib_xud)
* lib_adat (www.github.com/xmos/lib_adat)
Documentation
=============
@@ -101,3 +99,4 @@ Support
=======
This package is supported by XMOS Ltd. Issues can be raised against the software at: http://www.xmos.com/support

View File

@@ -14,7 +14,7 @@ Required hardware
.................
The example code provided with the application has been implemented
and tested on the xCORE.ai Multi-channel Audio Board
and tested on the xCORE-200 Multi-channel Audio Board
Prerequisites
.............

View File

@@ -1,11 +1,8 @@
// Copyright 2017-2024 XMOS LIMITED.
// Copyright 2017-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h>
#include <platform.h>
#include "xua.h"
extern "C"{
#include "sw_pll.h"
}
on tile[0]: out port p_ctrl = XS1_PORT_8D;
@@ -37,29 +34,20 @@ void ctrlPort()
void AudioHwInit()
{
/* Wait for power supply to come up */
delay_milliseconds(100);
/* Use xCORE Secondary PLL to generate *fixed* master clock */
if(DEFAULT_FREQ % 22050 == 0)
{
sw_pll_fixed_clock(MCLK_441);
}
else
{
sw_pll_fixed_clock(MCLK_48);
}
delay_milliseconds(100);
/* DAC setup: For basic I2S input we don't need any register setup. DACs will clock auto detect etc.
* It holds DAC in reset until it gets clocks anyway.
* Note, this example doesn't use the ADCs
* Note, this example doesn't use the ADC's
*/
}
/* Configures the external audio hardware for the required sample frequency */
/* Configures the external audio hardware for the required sample frequency
* Note, the application PLL in xcore.ai will be configured to the correct master clock frequency
* by lib_xua
*/
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC)
{
sw_pll_fixed_clock(mClk);
/* Nothing required since the DAC's will auto detect the sample rate from the clocks */
}

View File

@@ -1,12 +1,8 @@
// Copyright 2017-2024 XMOS LIMITED.
// Copyright 2017-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h>
#include <platform.h>
#include "xua.h"
#include "xassert.h"
extern "C"{
#include "sw_pll.h"
}
on tile[0]: out port p_ctrl = XS1_PORT_8D;
@@ -38,29 +34,20 @@ void ctrlPort()
void AudioHwInit()
{
/* Wait for power supply to come up */
delay_milliseconds(100);
/* Use xCORE Secondary PLL to generate *fixed* master clock */
if(DEFAULT_FREQ % 22050 == 0)
{
sw_pll_fixed_clock(MCLK_441);
}
else
{
sw_pll_fixed_clock(MCLK_48);
}
delay_milliseconds(100);
/* DAC setup: For basic I2S input we don't need any register setup. DACs will clock auto detect etc.
* It holds DAC in reset until it gets clocks anyway.
* Note, this example doesn't use the ADCs
* Note, this example doesn't use the ADC's
*/
}
/* Configures the external audio hardware for the required sample frequency */
/* Configures the external audio hardware for the required sample frequency
* Note, the application PLL in xcore.ai will be configured to the correct master clock frequency
* by lib_xua
*/
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC)
{
sw_pll_fixed_clock(mClk);
/* Nothing required since the DAC's will auto detect the sample rate from the clocks */
}

View File

@@ -1,4 +1,4 @@
// Copyright 2022-2024 XMOS LIMITED.
// Copyright 2022-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdlib.h>
@@ -14,20 +14,10 @@
// TODO
// res, min, max
#ifdef _WIN32
int mixer_init(TCHAR guid[GUID_STR_LEN])
#else
int mixer_init(void)
#endif
{
#ifdef _WIN32
int ret = usb_mixer_connect(guid);
#else
int ret = usb_mixer_connect();
#endif
/* Open the connection to the USB mixer */
if (ret == USB_MIXER_FAILURE)
if (usb_mixer_connect() == USB_MIXER_FAILURE)
{
return USB_MIXER_FAILURE;
}
@@ -391,15 +381,8 @@ void print_levels(const char* levelTitle, unsigned char* levels, int levelBytes)
void mixer_display_usage(void) {
fprintf(stderr, "Usage: xmos_mixer "
#ifdef _WIN32
"-g<GUID> "
#endif
"<options>\n");
fprintf(stderr,
#ifdef _WIN32
" -g<GUID> Driver GUID string, eg. -g{E5A2658B-817D-4A02-A1DE-B628A93DDF5D}\n"
#endif
fprintf(stderr, "Usage :\n");
fprintf(stderr,
" --display-info\n"
" --display-mixer-nodes mixer_id\n"
" --display-min mixer_id\n"
@@ -438,124 +421,97 @@ int main (int argc, char **argv) {
unsigned int mixer_index = 0;
unsigned int result = 0;
int min_argc;
// arg_idx is the position in the arguments to start parsing to skip the "-g" GUID option on Windows
int arg_idx;
#ifdef _WIN32
// Driver GUID string is required on Windows
min_argc = 3;
arg_idx = 2;
#else
min_argc = 2;
arg_idx = 1;
#endif
if (argc < min_argc) {
if (argc < 2) {
fprintf(stderr, "ERROR :: No options passed to mixer application\n");
mixer_display_usage();
return -1;
}
#ifdef _WIN32
TCHAR driver_guid[GUID_STR_LEN];
if (strncmp(argv[1], "-g", 2) == 0) {
swprintf(driver_guid, GUID_STR_LEN, L"%hs", argv[1]+2);
} else {
fprintf(stderr, "ERROR :: First option must be driver GUID\n");
return -1;
}
#endif
if (strcmp(argv[1], "--help") == 0) {
mixer_display_usage();
return 0;
}
#ifdef _WIN32
int ret = mixer_init(driver_guid);
#else
int ret = mixer_init();
#endif
if (ret != USB_MIXER_SUCCESS) {
if (mixer_init() != USB_MIXER_SUCCESS) {
fprintf(stderr, "ERROR :: Cannot connect\n");
return -1;
}
if (strcmp(argv[arg_idx], "--display-info") == 0)
if (strcmp(argv[1], "--display-info") == 0)
{
mixer_display_info();
}
else if (strcmp(argv[arg_idx], "--display-mixer-nodes") == 0)
else if (strcmp(argv[1], "--display-mixer-nodes") == 0)
{
if (argv[arg_idx+1])
if (argv[2])
{
mixer_index = atoi(argv[arg_idx+1]);
mixer_index = atoi(argv[2]);
} else {
fprintf(stderr, "ERROR :: No mixer index supplied\n");
return -1;
}
mixer_display(mixer_index, MIXER_UNIT_DISPLAY_VALUE);
} else if (strcmp(argv[arg_idx], "--display-mixer-nodes") == 0) {
} else if (strcmp(argv[1], "--display-mixer-nodes") == 0) {
if (argv[2]) {
mixer_index = atoi(argv[arg_idx+1]);
mixer_index = atoi(argv[2]);
} else {
fprintf(stderr, "ERROR :: No mixer index supplied\n");
return -1;
}
mixer_display(mixer_index, MIXER_UNIT_DISPLAY_VALUE);
} else if (strcmp(argv[arg_idx], "--display-min") == 0) {
if (argv[arg_idx+1]) {
mixer_index = atoi(argv[arg_idx+1]);
} else if (strcmp(argv[1], "--display-min") == 0) {
if (argv[2]) {
mixer_index = atoi(argv[2]);
} else {
fprintf(stderr, "ERROR :: No mixer index supplied\n");
return -1;
}
mixer_display(mixer_index, MIXER_UNIT_DISPLAY_MIN);
} else if (strcmp(argv[arg_idx], "--display-max") == 0) {
if (argv[arg_idx+1]) {
mixer_index = atoi(argv[arg_idx+1]);
} else if (strcmp(argv[1], "--display-max") == 0) {
if (argv[2]) {
mixer_index = atoi(argv[2]);
} else {
fprintf(stderr, "ERROR :: No mixer index supplied\n");
return -1;
}
mixer_display(mixer_index, MIXER_UNIT_DISPLAY_MAX);
} else if (strcmp(argv[arg_idx], "--display-res") == 0) {
if (argv[arg_idx+1]) {
mixer_index = atoi(argv[arg_idx+1]);
} else if (strcmp(argv[1], "--display-res") == 0) {
if (argv[2]) {
mixer_index = atoi(argv[2]);
} else {
fprintf(stderr, "ERROR :: No mixer index supplied\n");
return -1;
}
mixer_display(mixer_index, MIXER_UNIT_DISPLAY_RES);
}
else if (strcmp(argv[arg_idx], "--set-value") == 0) {
else if (strcmp(argv[1], "--set-value") == 0) {
unsigned int mixer_unit = 0;
double value = 0;
if (argc - arg_idx < 4) {
if (argc < 5) {
fprintf(stderr, "ERROR :: incorrect number of arguments passed\n");
return -1;
}
mixer_index = atoi(argv[arg_idx+1]);
mixer_unit = atoi(argv[arg_idx+2]);
if (strcmp(argv[arg_idx+3],"-inf")==0)
mixer_index = atoi(argv[2]);
mixer_unit = atoi(argv[3]);
if (strcmp(argv[4],"-inf")==0)
value = -128;
else
value = atof(argv[arg_idx+3]);
value = atof(argv[4]);
usb_mixer_set_value(mixer_index, mixer_unit, value);
} else if (strcmp(argv[arg_idx], "--get-value") == 0) {
} else if (strcmp(argv[1], "--get-value") == 0) {
unsigned int mixer_unit = 0;
double result = 0;
if (argc - arg_idx < 3) {
if (argc < 4) {
fprintf(stderr, "ERROR :: incorrect number of arguments passed\n");
return -1;
}
mixer_index = atoi(argv[arg_idx+1]);
mixer_unit = atoi(argv[arg_idx+2]);
mixer_index = atoi(argv[2]);
mixer_unit = atoi(argv[3]);
result = usb_mixer_get_value(mixer_index, mixer_unit);
if (result <= -127.996)
@@ -563,99 +519,99 @@ int main (int argc, char **argv) {
else
printf("%g\n",result);
}
else if (strcmp(argv[arg_idx], "--display-current-mixer-sources") == 0)
else if (strcmp(argv[1], "--display-current-mixer-sources") == 0)
{
if(argc - arg_idx < 2)
if(argc < 3)
{
usage_error();
return -1;
}
display_mixer_sources(atoi(argv[arg_idx+1]));
display_mixer_sources(atoi(argv[2]));
}
else if (strcmp(argv[arg_idx], "--display-available-mixer-sources") == 0)
else if (strcmp(argv[1], "--display-available-mixer-sources") == 0)
{
if(argc - arg_idx < 2)
if(argc < 3)
{
usage_error();
return -1;
}
display_available_mixer_sources(atoi(argv[arg_idx+1]));
display_available_mixer_sources(atoi(argv[2]));
}
else if(strcmp(argv[arg_idx], "--set-mixer-source") == 0)
else if(strcmp(argv[1], "--set-mixer-source") == 0)
{
if(argc - arg_idx < 4)
if(argc < 5)
{
usage_error();
return -1;
}
set_mixer_source(atoi(argv[arg_idx+1]), atoi(argv[arg_idx+2]), atoi(argv[arg_idx+3]));
set_mixer_source(atoi(argv[2]), atoi(argv[3]), atoi(argv[4]));
}
else if (strcmp(argv[arg_idx], "--display-aud-channel-map") == 0)
else if (strcmp(argv[1], "--display-aud-channel-map") == 0)
{
/* Display the channel mapping to the devices audio outputs */
display_aud_channel_map();
}
else if (strcmp(argv[arg_idx], "--display-aud-channel-map-sources") == 0)
else if (strcmp(argv[1], "--display-aud-channel-map-sources") == 0)
{
display_aud_channel_map_sources();
}
else if (strcmp(argv[arg_idx], "--display-daw-channel-map") == 0)
else if (strcmp(argv[1], "--display-daw-channel-map") == 0)
{
/* Display the channel mapping to the devices DAW output to host */
display_daw_channel_map();
}
else if (strcmp(argv[arg_idx], "--display-daw-channel-map-sources") == 0)
else if (strcmp(argv[1], "--display-daw-channel-map-sources") == 0)
{
display_daw_channel_map_sources();
}
else if (strcmp(argv[arg_idx], "--set-aud-channel-map") == 0)
else if (strcmp(argv[1], "--set-aud-channel-map") == 0)
{
unsigned int dst = 0;
unsigned int src = 0;
if (argc - arg_idx != 3)
if (argc != 4)
{
usage_error();
return -1;
}
dst = atoi(argv[arg_idx+1]);
src = atoi(argv[arg_idx+2]);
dst = atoi(argv[2]);
src = atoi(argv[3]);
usb_set_aud_channel_map(dst, src);
}
else if (strcmp(argv[arg_idx], "--set-daw-channel-map") == 0)
else if (strcmp(argv[1], "--set-daw-channel-map") == 0)
{
unsigned int dst = 0;
unsigned int src = 0;
if (argc - arg_idx != 3)
if (argc != 4)
{
usage_error();
return -1;
}
dst = atoi(argv[arg_idx+1]);
src = atoi(argv[arg_idx+2]);
dst = atoi(argv[2]);
src = atoi(argv[3]);
usb_set_usb_channel_map(dst, src);
}
else if(strcmp(argv[arg_idx], "--get-mixer-levels-input") == 0 ||
strcmp(argv[arg_idx],"--get-mixer-levels-output") == 0)
else if(strcmp(argv[1], "--get-mixer-levels-input") == 0 ||
strcmp(argv[1],"--get-mixer-levels-output") == 0)
{
unsigned int dst = 0;
unsigned char levels[64];
int datalength = 0;
int offset = 0;
if (argc - arg_idx < 2) {
if (argc < 3) {
fprintf(stderr, "ERROR :: incorrect number of arguments passed\n");
return -1;
}
if(strcmp(argv[arg_idx],"--get-mixer-levels-output") == 0)
if(strcmp(argv[1],"--get-mixer-levels-output") == 0)
offset = 1;
for(int i = 0; i < 64; i++)
levels[i] = 0;
dst = atoi(argv[arg_idx+1]);
dst = atoi(argv[2]);
/* Mem request to mixer with offset of 0 gives input levels */
datalength = usb_mixer_mem_get(dst, offset, levels);
@@ -672,7 +628,7 @@ int main (int argc, char **argv) {
print_levels("Mixer Input", levels, datalength);
}
else if(strcmp(argv[arg_idx], "--vendor-audio-request-get") == 0)
else if(strcmp(argv[1], "--vendor-audio-request-get") == 0)
{
unsigned int bRequest = 0;
unsigned int cs = 0;
@@ -681,7 +637,7 @@ int main (int argc, char **argv) {
int datalength = 0;
unsigned char data[64];
if(argc - arg_idx < 5)
if(argc < 6)
{
fprintf(stderr, "ERROR :: incorrect number of arguments passed\n");
return -1;
@@ -690,10 +646,10 @@ int main (int argc, char **argv) {
for(int i = 0; i < 64; i++)
data[i] = 0;
bRequest = atoi(argv[arg_idx+1]);
cs = atoi(argv[arg_idx+2]);
cn = atoi(argv[arg_idx+3]);
unitId = atoi(argv[arg_idx+4]);
bRequest = atoi(argv[2]);
cs = atoi(argv[3]);
cn = atoi(argv[4]);
unitId = atoi(argv[5]);
/* Do request */
datalength = usb_audio_request_get(bRequest, cs, cn, unitId, data);
@@ -710,7 +666,7 @@ int main (int argc, char **argv) {
printf("0x%02x\n" ,data[i]);
}
}
else if(strcmp(argv[arg_idx], "--vendor-audio-request-set") == 0)
else if(strcmp(argv[1], "--vendor-audio-request-set") == 0)
{
unsigned int bRequest = 0;
@@ -724,23 +680,23 @@ int main (int argc, char **argv) {
data[i] = 0;
}
if(argc - arg_idx < 6)
if(argc < 7)
{
fprintf(stderr, "ERROR :: incorrect number of arguments passed - no data passed\n");
return -1;
}
bRequest = atoi(argv[arg_idx+1]);
cs = atoi(argv[arg_idx+2]);
cn = atoi(argv[arg_idx+3]);
unitId = atoi(argv[arg_idx+4]);
bRequest = atoi(argv[2]);
cs = atoi(argv[3]);
cn = atoi(argv[4]);
unitId = atoi(argv[5]);
/* Get data */
for(int i=0; i < argc-arg_idx-5; i++)
for(int i=0; i < argc-6; i++)
{
data[i] = atoi(argv[i+arg_idx+5]);
data[i] = atoi(argv[i+6]);
}
result = usb_audio_request_set(bRequest, cs, cn, unitId, data, argc-arg_idx-5);
result = usb_audio_request_set(bRequest, cs, cn, unitId, data, argc-6);
if(result < 0)
{

View File

@@ -1,4 +1,4 @@
// Copyright 2022-2024 XMOS LIMITED.
// Copyright 2022-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdlib.h>
@@ -800,18 +800,14 @@ static int find_xmos_device(unsigned int id)
// End of libusb interface functions
#ifdef _WIN32
int usb_mixer_connect(TCHAR guid[GUID_STR_LEN])
#else
int usb_mixer_connect()
#endif
int usb_mixer_connect()
{
// Allocate internal storage
usb_mixers = (usb_mixer_handle *)malloc(sizeof(usb_mixer_handle));
memset(usb_mixers, 0, sizeof(usb_mixer_handle));
#if defined(_WIN32)
gDrvApi.LoadByGUID(guid);
gDrvApi.LoadByGUID(_T("{E5A2658B-817D-4A02-A1DE-B628A93DDF5D}"));
TUsbAudioStatus st = gDrvApi.TUSBAUDIO_EnumerateDevices();
#endif

View File

@@ -1,4 +1,4 @@
// Copyright 2022-2024 XMOS LIMITED.
// Copyright 2022-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#define USB_MIXER_SUCCESS 0
@@ -22,14 +22,7 @@ enum usb_chan_type {
#define RANGE (2)
#define MEM (3)
#ifdef _WIN32
#include <tchar.h>
// GUID strings are 36 characters, plus a pair of braces and NUL-termination
#define GUID_STR_LEN (36+2+1)
int usb_mixer_connect(TCHAR guid[GUID_STR_LEN]);
#else
int usb_mixer_connect();
#endif
int usb_mixer_disconnect();
/* MIXER UNIT(s) INTERFACE */

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2024 XMOS LIMITED.
// Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _XUA_AUDIOHUB_H_
#define _XUA_AUDIOHUB_H_
@@ -19,35 +19,29 @@
* This function drives I2S ports and handles samples to/from other digital
* I/O threads.
*
* \param c_aud Audio sample channel connected to the mixer() thread or the
* decouple() thread
* \param c_aud Audio sample channel connected to the mixer() thread or the
* decouple() thread
*
* \param clk_audio_mclk Nullable clockblock to be clocked from master clock
* \param clk_audio_mclk Nullable clockblock to be clocked from master clock
*
* \param clk_audio_bclk Nullable clockblock to be clocked from i2s bit clock
* \param clk_audio_bclk Nullable clockblock to be clocked from i2s bit clock
*
* \param p_mclk_in Master clock inport port (must be 1-bit)
* \param p_mclk_in Master clock inport port (must be 1-bit)
*
* \param p_lrclk Nullable port for I2S sample clock
* \param p_lrclk Nullable port for I2S sample clock
*
* \param p_bclk Nullable port for I2S bit clock
* \param p_bclk Nullable port for I2S bit
*
* \param p_i2s_dac Nullable array of ports for I2S data output lines
* \param p_i2s_dac Nullable array of ports for I2S data output lines
*
* \param p_i2s_adc Nullable array of ports for I2S data input lines
* \param p_i2s_adc Nullable array of ports for I2S data input lines
*
* \param i_SoftPll Interface to software PLL task
* \param i_SoftPll Interface to software PLL task
*
* \param c_spdif_tx Channel connected to S/PDIF transmitter core from lib_spdif
* \param c_spdif_tx Channel connected to S/PDIF transmiter core from lib_spdif
*
* \param c_dig Channel connected to the clockGen() thread for
* receiving/transmitting samples
*
* \param c_audio_rate_change Channel notifying ep_buffer of an mclk frequency change and sync for stable clock
*
* \param dfuInterface Interface supporting DFU methods
*
* \param c_pdm_in Channel for receiving decimated PDM samples
* \param c_dig Channel connected to the clockGen() thread for
* receiving/transmitting samples
*/
void XUA_AudioHub(chanend ?c_aud,
clock ?clk_audio_mclk,
@@ -57,36 +51,25 @@ void XUA_AudioHub(chanend ?c_aud,
buffered _XUA_CLK_DIR port:32 ?p_bclk,
buffered out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC],
buffered in port:32 (&?p_i2s_adc)[I2S_WIRES_ADC]
#if (XUA_USE_APP_PLL) || defined(__DOXYGEN__)
, client interface SoftPll_if i_SoftPll
#endif
#if (XUA_SPDIF_TX_EN) || defined(__DOXYGEN__)
, chanend c_spdif_tx
#endif
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN || defined(__DOXYGEN__))
, chanend c_dig
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN || defined(__DOXYGEN__))
, chanend c_audio_rate_change
#endif
#if (((XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1)) || defined(__DOXYGEN__))
#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1)
, server interface i_dfu ?dfuInterface
#endif
#if (XUA_NUM_PDM_MICS > 0 || defined(__DOXYGEN__))
#if (XUA_NUM_PDM_MICS > 0)
, chanend c_pdm_in
#endif
);
void SpdifTxWrapper(chanend c_spdif_tx);
/* These functions must be implemented for the CODEC/ADC/DAC arrangement of a specific design */
/* Any required clocking and CODEC initialisation - run once at start up */
/* TODO Provide default implementation of this */
void AudioHwInit();
/* Configure audio hardware (clocking, CODECs etc) for a specific mClk/Sample frquency - run on every sample frequency change */
/* TODO Provide default implementation of this */
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode,
unsigned sampRes_DAC, unsigned sampRes_ADC);
#endif // __XC__
/**
@@ -108,10 +91,7 @@ void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudi
*
* This function is called once, before the first call to UserBufferManagement(), and can be used to initialise any
* related user state
*
* \param sampFreq The initial sample frequency
*
*/
void UserBufferManagementInit(unsigned sampFreq);
void UserBufferManagementInit();
#endif // _XUA_AUDIOHUB_H_

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2024 XMOS LIMITED.
// Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _XUA_BUFFER_H_
#define _XUA_BUFFER_H_
@@ -13,21 +13,20 @@
* Most of the chanend parameters to the function should be connected to
* XUD_Manager(). The uses two cores.
*
* \param c_aud_out Audio OUT endpoint channel connected to the XUD
* \param c_aud_in Audio IN endpoint channel connected to the XUD
* \param c_aud_fb Audio feedback endpoint channel connected to the XUD
* \param c_midi_from_host MIDI OUT endpoint channel connected to the XUD
* \param c_midi_to_host MIDI IN endpoint channel connected to the XUD
* \param c_midi Channel connected to MIDI core
* \param c_int Audio clocking interrupt endpoint channel connected to the XUD
* \param c_clk_int Optional chanend connected to the clockGen() thread if present
* \param c_sof Start of frame channel connected to the XUD
* \param c_aud_ctl Audio control channel connected to Endpoint0()
* \param p_off_mclk A port that is clocked of the MCLK input (not the MCLK input itself)
* \param c_aud Channel connected to XUA_AudioHub() core
* \param c_audio_rate_change Channel to notify and synchronise on audio rate change
* \param i_pll_ref Interface to task that toggles reference pin to CS2100
* \param c_swpll_update Channel connected to software PLL task. Expects master clock counts based on USB frames.
* \param c_aud_out Audio OUT endpoint channel connected to the XUD
* \param c_aud_in Audio IN endpoint channel connected to the XUD
* \param c_aud_fb Audio feedback endpoint channel connected to the XUD
* \param c_midi_from_host MIDI OUT endpoint channel connected to the XUD
* \param c_midi_to_host MIDI IN endpoint channel connected to the XUD
* \param c_midi Channel connected to MIDI core
* \param c_int Audio clocking interrupt endpoint channel connected to the XUD
* \param c_clk_int Optional chanend connected to the clockGen() thread if present
* \param c_sof Start of frame channel connected to the XUD
* \param c_aud_ctl Audio control channel connected to Endpoint0()
* \param p_off_mclk A port that is clocked of the MCLK input (not the MCLK input itself)
* \param c_aud Channel connected to XUA_AudioHub() core
* \param i_pll_ref Interface to task that toggles reference pin to CS2100
* \param c_swpll_update Channel connected to software PLL task. Expects master clock counts based on USB frames.
*/
void XUA_Buffer(
chanend c_aud_out,
@@ -40,7 +39,7 @@ void XUA_Buffer(
#if defined(MIDI) || defined(__DOXYGEN__)
chanend c_midi_from_host,
chanend c_midi_to_host,
chanend c_midi,
chanend c_midi,
#endif
#if XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN || defined(__DOXYGEN__)
chanend ?c_int,
@@ -54,11 +53,10 @@ void XUA_Buffer(
#endif
, chanend c_aud
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || defined(__DOYXGEN__)
, chanend c_audio_rate_change
#if (!XUA_USE_SW_PLL) || defined(__DOXYGEN__)
#if (!XUA_USE_APP_PLL) || defined(__DOXYGEN__)
, client interface pll_ref_if i_pll_ref
#endif
#if (XUA_USE_SW_PLL) || defined(__DOXYGEN__)
#if (XUA_USE_APP_PLL) || defined(__DOXYGEN__)
, chanend c_swpll_update
#endif
#endif
@@ -74,7 +72,7 @@ void XUA_Buffer_Ep(chanend c_aud_out,
#ifdef MIDI
chanend c_midi_from_host,
chanend c_midi_to_host,
chanend c_midi,
chanend c_midi,
#endif
#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN)
chanend ?c_int,
@@ -90,11 +88,10 @@ void XUA_Buffer_Ep(chanend c_aud_out,
, chanend c_buff_ctrl
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || defined(__DOYXGEN__)
, chanend c_audio_rate_change
#if (!XUA_USE_SW_PLL) || defined(__DOXYGEN__)
#if (!XUA_USE_APP_PLL) || defined(__DOXYGEN__)
, client interface pll_ref_if i_pll_ref
#endif
#if (XUA_USE_SW_PLL) || defined(__DOXYGEN__)
#if (XUA_USE_APP_PLL) || defined(__DOXYGEN__)
, chanend c_swpll_update
#endif
#endif

View File

@@ -1,12 +1,11 @@
// Copyright 2011-2024 XMOS LIMITED.
// Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _CLOCKING_H_
#define _CLOCKING_H_
#include <xs1.h>
#include "sw_pll_wrapper.h"
#include "xua.h"
interface pll_ref_if
{
@@ -20,31 +19,56 @@ void PllRefPinTask(server interface pll_ref_if i_pll_ref, out port p_sync);
/** Clock generation and digital audio I/O handling.
*
* \param c_spdif_rx channel connected to S/PDIF receive thread
* \param c_adat_rx channel connect to ADAT receive thread
* \param i_pll_ref interface to taslk that outputs clock signal to drive external frequency synthesizer
* \param c_audio channel connected to the audio() thread
* \param c_clk_ctl channel connected to Endpoint0() for configuration of the
* clock
* \param c_clk_int channel connected to the decouple() thread for clock
* interrupts
* \param c_audio_rate_change channel to notify of master clock change
* \param p_for_mclk_count_aud port used for counting mclk and providing a timestamp
* \param c_sw_pll channel used to communicate with software PLL task
*
* \param c_spdif_rx channel connected to S/PDIF receive thread
* \param c_adat_rx channel connect to ADAT receive thread
* \param i_pll_ref interface to taslk that outputs clock signal to drive external frequency synthesizer
* \param c_audio channel connected to the audio() thread
* \param c_clk_ctl channel connected to Endpoint0() for configuration of the
* clock
* \param c_clk_int channel connected to the decouple() thread for clock
interrupts
*/
void clockGen( streaming chanend ?c_spdif_rx,
chanend ?c_adat_rx,
client interface pll_ref_if i_pll_ref,
chanend c_audio,
chanend c_clk_ctl,
chanend c_clk_int,
chanend c_audio_rate_change
#if XUA_USE_SW_PLL
, port p_for_mclk_count_aud
, chanend c_sw_pll
void clockGen(streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interface pll_ref_if i_pll_ref, chanend c_audio, chanend c_clk_ctl, chanend c_clk_int);
#if (XUA_USE_APP_PLL)
struct SoftPllState
{
// Count we expect on MCLK port timer at SW PLL check point.
// Note, we expect wrapping so this is essentiually a modulus
unsigned expectedClkMod;
unsigned initialSetting;
unsigned initialErrorMult;
unsigned setting;
int phaseError;
/* Integrated phase error */
int phaseErrorInt;
/* IIR filter */
int iir_y;
/* Delta sigma modulator */
unsigned ds_in;
int ds_x1;
int ds_x2;
int ds_x3;
};
void AppPllEnable(tileref tile, int mclkFreq_hz);
void AppPllGetSettings(int clkFreq_hz, struct SoftPllState &pllState);
void AppPllUpdate(tileref tile, unsigned short mclk_pt, struct SoftPllState &pllState);
interface SoftPll_if
{
void init(int mclk_hz);
};
#if (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC)
[[distributable]]
#endif
);
void XUA_SoftPll(tileref tile, server interface SoftPll_if i_softPll, chanend c_update);
#endif
#endif

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2024 XMOS LIMITED.
// Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/*
* @brief Defines relating to device configuration and customisation of lib_xua
@@ -265,16 +265,21 @@
#endif
/**
* @brief Enable/disable the use of the secondary/application PLL for generating and recovering master-clocks.
* @brief Enable/disable the use of the secondary/application PLL for generating master-clocks.
* Only available on xcore.ai devices.
*
* Default: Enabled (for xcore.ai devices)
*/
#ifndef XUA_USE_SW_PLL
#ifndef XUA_USE_APP_PLL
#if defined(__XS3A__)
#define XUA_USE_SW_PLL (1)
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
/* Currently must use an external CS2100 device for syncing to external digital streams */
#define XUA_USE_APP_PLL (0)
#else
#define XUA_USE_APP_PLL (1)
#endif
#else
#define XUA_USE_SW_PLL (0)
#define XUA_USE_APP_PLL (0)
#endif
#endif
@@ -412,28 +417,6 @@
#define SPDIF_TX_INDEX (0)
#endif
/**
* @brief Enables SPDIF Rx. Default: 0 (Disabled)
*/
#ifndef XUA_SPDIF_RX_EN
#define XUA_SPDIF_RX_EN (0)
#endif
/**
* @brief S/PDIF Rx first channel index, defines which channels S/PDIF will be input on.
* Note, indexed from 0.
*
* Default: NONE (Must be defined by app when SPDIF_RX enabled)
*/
#if (XUA_SPDIF_RX_EN) || defined (__DOXYGEN__)
#ifndef SPDIF_RX_INDEX
#error SPDIF_RX_INDEX not defined and XUA_SPDIF_RX_EN defined
#define SPDIF_RX_INDEX 0 /* Default define for doxygen */
#endif
#endif
/**
* @brief Enables ADAT Tx. Default: 0 (Disabled)
*/
@@ -441,35 +424,20 @@
#define XUA_ADAT_TX_EN (0)
#endif
/* Calculate max ADAT channels based on sample rate range. Used for Tx and Rx */
#if (MIN_FREQ < 88200)
#define ADAT_MAX_CHANS (8)
#elif (MIN_FREQ < 176400)
#define ADAT_MAX_CHANS (4)
#else
#define ADAT_MAX_CHANS (2)
#endif
/* Set the maximum number of channels for ADAT */
#if XUA_ADAT_TX_EN
#define ADAT_TX_MAX_CHANS ADAT_MAX_CHANS
#else
#define ADAT_TX_MAX_CHANS (0)
#endif
/**
* @brief Defines which output channels (8) should be output on ADAT. Note, Output channels indexed from 0.
*
* Default: 0 (i.e. channels [0:7])
* */
#if (XUA_ADAT_TX_EN) || defined(__DOXYGEN__)
#ifndef ADAT_TX_INDEX
#define ADAT_TX_INDEX (0)
#endif
#ifndef ADAT_TX_INDEX
#define ADAT_TX_INDEX (0)
#endif
#if (ADAT_TX_INDEX + ADAT_TX_MAX_CHANS > NUM_USB_CHAN_OUT)
#error Not enough channels for ADAT Tx
#endif
/**
* @brief Enables SPDIF Rx. Default: 0 (Disabled)
*/
#ifndef XUA_SPDIF_RX_EN
#define XUA_SPDIF_RX_EN (0)
#endif
/**
@@ -479,11 +447,17 @@
#define XUA_ADAT_RX_EN (0)
#endif
/* Set the maximum number of channels for ADAT */
#if XUA_ADAT_RX_EN
#define ADAT_RX_MAX_CHANS ADAT_MAX_CHANS
#else
#define ADAT_RX_MAX_CHANS (0)
/**
* @brief S/PDIF Rx first channel index, defines which channels S/PDIF will be input on.
* Note, indexed from 0.
*
* Default: NONE (Must be defined by app when SPDIF_RX enabled)
*/
#if (XUA_SPDIF_RX_EN) || defined (__DOXYGEN__)
#ifndef SPDIF_RX_INDEX
#error SPDIF_RX_INDEX not defined and XUA_SPDIF_RX_EN defined
#define SPDIF_RX_INDEX 0 /* Default define for doxygen */
#endif
#endif
/**
@@ -493,80 +467,30 @@
* Default: NONE (Must be defined by app when XUA_ADAT_RX_EN is true)
*/
#if (XUA_ADAT_RX_EN) || defined(__DOXYGEN__)
#ifndef ADAT_RX_INDEX
#error ADAT_RX_INDEX not defined and XUA_ADAT_RX_EN is true
#define ADAT_RX_INDEX (0) /* Default define for doxygen */
#endif
#if (ADAT_RX_INDEX + ADAT_RX_MAX_CHANS > NUM_USB_CHAN_IN)
#error Not enough channels for ADAT Rx
#endif
#ifndef ADAT_RX_INDEX
#error ADAT_RX_INDEX not defined and XUA_ADAT_RX_EN is true
#define ADAT_RX_INDEX (0) /* Default define for doxygen */
#endif
#if (ADAT_RX_INDEX + 8 > NUM_USB_CHAN_IN)
#error Not enough channels for ADAT
#endif
#endif
#if (XUA_ADAT_RX_EN)
/* Setup input stream formats for ADAT */
#if (XUA_ADAT_RX_EN)
#if (MAX_FREQ > 96000)
#if (MIN_FREQ > 96000)
#define INPUT_FORMAT_COUNT 1
#define HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT NUM_USB_CHAN_IN
#elif (MIN_FREQ > 48000)
#define INPUT_FORMAT_COUNT 2
#define HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT NUM_USB_CHAN_IN
#define HS_STREAM_FORMAT_INPUT_2_CHAN_COUNT (NUM_USB_CHAN_IN - 2)
#else
#define INPUT_FORMAT_COUNT 3
#define HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT NUM_USB_CHAN_IN
#define HS_STREAM_FORMAT_INPUT_2_CHAN_COUNT (NUM_USB_CHAN_IN - 4)
#define HS_STREAM_FORMAT_INPUT_3_CHAN_COUNT (NUM_USB_CHAN_IN - 6)
#endif
#elif (MAX_FREQ > 48000)
#if (MIN_FREQ > 48000)
#define INPUT_FORMAT_COUNT 1
#define HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT NUM_USB_CHAN_IN
#else
#define INPUT_FORMAT_COUNT 2
#define HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT NUM_USB_CHAN_IN
#define HS_STREAM_FORMAT_INPUT_2_CHAN_COUNT (NUM_USB_CHAN_IN - 4)
#endif
#else
#define INPUT_FORMAT_COUNT 1
#define HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT NUM_USB_CHAN_IN
#endif
#if(MAX_FREQ > 96000)
#define INPUT_FORMAT_COUNT 3
#elif(MAX_FREQ > 48000)
#define INPUT_FORMAT_COUNT 2
#else
#define INPUT_FORMAT_COUNT 1
#endif
/* Setup output stream formats for ADAT */
#if (XUA_ADAT_TX_EN)
#if (MAX_FREQ > 96000)
#if (MIN_FREQ > 96000)
#define OUTPUT_FORMAT_COUNT 1
#define HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT NUM_USB_CHAN_OUT
#elif (MIN_FREQ > 48000)
#define OUTPUT_FORMAT_COUNT 2
#define HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT NUM_USB_CHAN_OUT
#define HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT (NUM_USB_CHAN_OUT - 2)
#else
#define OUTPUT_FORMAT_COUNT 3
#define HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT NUM_USB_CHAN_OUT
#define HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT (NUM_USB_CHAN_OUT - 4)
#define HS_STREAM_FORMAT_OUTPUT_3_CHAN_COUNT (NUM_USB_CHAN_OUT - 6)
#endif
#elif (MAX_FREQ > 48000)
#if (MIN_FREQ > 48000)
#define OUTPUT_FORMAT_COUNT 1
#define HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT NUM_USB_CHAN_OUT
#else
#define OUTPUT_FORMAT_COUNT 2
#define HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT NUM_USB_CHAN_OUT
#define HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT (NUM_USB_CHAN_OUT - 4)
#endif
#else
#define OUTPUT_FORMAT_COUNT 1
#define HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT NUM_USB_CHAN_OUT
#endif
#define STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS 24
#define STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS 24
#define STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS 24
#define HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT NUM_USB_CHAN_IN
#define HS_STREAM_FORMAT_INPUT_2_CHAN_COUNT (NUM_USB_CHAN_IN - 4)
#define HS_STREAM_FORMAT_INPUT_3_CHAN_COUNT (NUM_USB_CHAN_IN - 6)
#endif
/**
@@ -1043,18 +967,7 @@
#define HS_STREAM_FORMAT_INPUT_3_CHAN_COUNT NUM_USB_CHAN_IN
#endif
/* Channel count defines for output streams */
#ifndef HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT
#define HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT NUM_USB_CHAN_OUT
#endif
#ifndef HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT
#define HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT NUM_USB_CHAN_OUT
#endif
#ifndef HS_STREAM_FORMAT_OUTPUT_3_CHAN_COUNT
#define HS_STREAM_FORMAT_OUTPUT_3_CHAN_COUNT NUM_USB_CHAN_OUT
#endif
/**
* @brief Sample sub-slot size (bytes) of input stream Alternate 1 when running in high-speed
@@ -1136,7 +1049,7 @@
* Default: 1 (Enabled)
*/
#ifndef OUTPUT_VOLUME_CONTROL
#define OUTPUT_VOLUME_CONTROL (1)
#define OUTPUT_VOLUME_CONTROL (1)
#endif
/**
@@ -1145,7 +1058,7 @@
* Default: 1 (Enabled)
*/
#ifndef INPUT_VOLUME_CONTROL
#define INPUT_VOLUME_CONTROL (1)
#define INPUT_VOLUME_CONTROL (1)
#endif
/* Power */
@@ -1197,7 +1110,7 @@
*/
#if (MIXER)
#ifndef MAX_MIX_COUNT
#define MAX_MIX_COUNT (8)
#define MAX_MIX_COUNT (8)
#endif
#else
#ifndef MAX_MIX_COUNT
@@ -1277,11 +1190,7 @@
/* Handle out volume control in the mixer - enabled by default */
#ifndef OUT_VOLUME_IN_MIXER
#if MIXER
#define OUT_VOLUME_IN_MIXER (1)
#else
#define OUT_VOLUME_IN_MIXER (0)
#endif
#define OUT_VOLUME_IN_MIXER (1)
#endif
/* Apply out volume controls after the mix. Only relevant when OUT_VOLUME_IN_MIXER enabled. Enabled by default */
@@ -1294,7 +1203,7 @@
#define IN_VOLUME_IN_MIXER (0)
#endif
/* Apply in volume controls after the mix. Only relevant when IN_VOLUMNE_IN MIXER enabled. Enabled by default */
/* Apply in volume controls after the mix. Only relebant when IN_VOLUMNE_IN MIXER enabled. Enabled by default */
#ifndef IN_VOLUME_AFTER_MIX
#define IN_VOLUME_AFTER_MIX (1)
#endif
@@ -1318,7 +1227,7 @@
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
#if (XUA_SPDIF_RX_EN|| XUA_ADAT_RX_EN)
#if (XUA_SPDIF_RX_EN|| ADAT_RX)
#error "Digital input streams not supported in Sync mode"
#endif
#endif
@@ -1336,9 +1245,7 @@ enum USBEndpointNumber_In
#if (NUM_USB_CHAN_IN == 0) || defined (UAC_FORCE_FEEDBACK_EP)
ENDPOINT_NUMBER_IN_FEEDBACK,
#endif
#if (NUM_USB_CHAN_IN != 0)
ENDPOINT_NUMBER_IN_AUDIO,
#endif
#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN)
ENDPOINT_NUMBER_IN_INTERRUPT, /* Audio interrupt/status EP */
#endif
@@ -1497,7 +1404,7 @@ enum USBEndpointNumber_Out
/* Some defines that allow us to remove unused code */
/* Useful for dropping lower part of macs in volume processing... */
#if (FS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS > 24) || (HS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS > 24) || \
#if (FS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS > 24) || (HS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS > 24) || \
(((FS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS > 24) || (HS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS > 24)) && (OUTPUT_FORMAT_COUNT > 1)) || \
(((FS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS > 24) || (HS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS > 24)) && (OUTPUT_FORMAT_COUNT > 2))
#define STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED 1
@@ -1531,29 +1438,29 @@ enum USBEndpointNumber_Out
#endif
/* Useful for dropping lower part of macs in volume processing... */
#if (FS_STREAM_FORMAT_INPUT_1_RESOLUTION_BITS > 24) || (HS_STREAM_FORMAT_INPUT_1_RESOLUTION_BITS > 24)
#define STREAM_FORMAT_INPUT_RESOLUTION_32BIT_USED 1
#else
#define STREAM_FORMAT_INPUT_RESOLUTION_32BIT_USED 0
#endif
#if (FS_STREAM_FORMAT_INPUT_1_RESOLUTION_BITS > 24) || (FS_STREAM_FORMAT_INPUT_2_RESOLUTION_BITS > 24)
#define STREAM_FORMAT_INPUT_RESOLUTION_32BIT_USED 1
#else
#define STREAM_FORMAT_INPUT_RESOLUTION_32BIT_USED 0
#endif
#if((FS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES == 4) || (HS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES == 4))
#define STREAM_FORMAT_INPUT_SUBSLOT_4_USED 1
#else
#define STREAM_FORMAT_INPUT_SUBSLOT_4_USED 0
#endif
#if((FS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES == 4) || (HS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES == 4))
#define STREAM_FORMAT_INPUT_SUBSLOT_4_USED 1
#else
#define STREAM_FORMAT_INPUT_SUBSLOT_4_USED 0
#endif
#if((FS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES == 3) || (HS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES == 3))
#define STREAM_FORMAT_INPUT_SUBSLOT_3_USED 1
#else
#define STREAM_FORMAT_INPUT_SUBSLOT_3_USED 0
#endif
#if((FS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES == 3) || (HS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES == 3))
#define STREAM_FORMAT_INPUT_SUBSLOT_3_USED 1
#else
#define STREAM_FORMAT_INPUT_SUBSLOT_3_USED 0
#endif
#if((FS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES == 2) || (HS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES == 2))
#define STREAM_FORMAT_INPUT_SUBSLOT_2_USED 1
#else
#define STREAM_FORMAT_INPUT_SUBSLOT_2_USED 0
#endif
#if((FS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES == 2) || (HS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES == 2))
#define STREAM_FORMAT_INPUT_SUBSLOT_2_USED 1
#else
#define STREAM_FORMAT_INPUT_SUBSLOT_2_USED 0
#endif
#if MAX_FREQ < MIN_FREQ
#error MAX_FREQ should be >= MIN_FREQ!!

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2024 XMOS LIMITED.
// Copyright 2011-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _XUA_MIDI_H_
#define _XUA_MIDI_H_
@@ -57,25 +57,24 @@ void midi_get_ack_or_data(chanend c, int &is_ack, unsigned int &datum);
INLINE void midi_get_ack_or_data(chanend c, int &is_ack, unsigned int &datum) {
if (testct(c)) {
is_ack = 1;
chkct(c, XS1_CT_END);
(void) inct(c); // read 1-bytes control token
(void) inuchar(c);
(void) inuchar(c);
(void) inuchar(c);
}
else {
is_ack = 0;
datum = inuint(c);
chkct(c, XS1_CT_END);
}
}
#endif
INLINE void midi_send_ack(chanend c) {
outct(c, XS1_CT_END);
outct(c, MIDI_ACK);
outuchar(c, 0);
outuchar(c, 0);
outuchar(c, 0);
}
INLINE void midi_send_data(chanend c, unsigned int datum) {
outuint(c, datum);
outct(c, XS1_CT_END);
}
#define MIDI_RATE (31250)
#define MIDI_BITTIME (XS1_TIMER_MHZ * 1000000 / MIDI_RATE)
#define MIDI_BITTIME_2 (MIDI_BITTIME>>1)

View File

@@ -38,7 +38,7 @@ Frequencies and Clocks
.. doxygendefine:: DEFAULT_FREQ
.. doxygendefine:: MCLK_441
.. doxygendefine:: MCLK_48
.. doxygendefine:: XUA_USE_SW_PLL
.. doxygendefine:: XUA_USE_APP_PLL
Audio Class
-----------

View File

@@ -54,9 +54,7 @@ In addition :ref:`usb_audio_optional_components` shows optional components that
* - Clockgen
- Drives an external frequency generator (PLL) and manages
changes between internal clocks and external clocks arising
from digital input. On xcore.ai Clockgen may also work in
conjunction with lib_sw_pll to produce a local clock from
the XCORE which is locked to the incoming digital stream.
from digital input.
* - MIDI
- Outputs and inputs MIDI over a serial UART interface.

View File

@@ -29,11 +29,10 @@ The S/PDIF receiver should be called on the appropriate tile::
With the steps above an S/PDIF stream can be captured by the xCORE. To be functionally useful the audio
master clock must be able to synchronise to this external digital stream. Additionally, the host can be
notified regarding changes in the validity of this stream, it's frequency etc. To synchronise to external
streams the codebase assumes the use of an external Cirrus Logic CS2100 device or lib_sw_pll on xcore.ai designs.
streams the codebase assumes the use of an external Cirrus Logic CS2100 device.
The ``ClockGen()`` task from ``lib_xua`` provides the reference signal to the CS2100 device or timing information
to lib_sw_pll and also handles recording of clock validity etc.
See :ref:`usb_audio_sec_clock_recovery` for full details regarding ``ClockGen()``.
The ``ClockGen()`` task from ``lib_xua`` provides the reference signal to the CS2100 device and also handles
recording of clock validity etc. See :ref:`usb_audio_sec_clock_recovery` for full details regarding ``ClockGen()``.
It also provides a small FIFO for S/PDIF samples before they are forwarded to the ``AudioHub`` core.
As such it requires to be inserted in the communication path between the S/PDIF receiver and the

View File

@@ -52,11 +52,11 @@ Three methods of generating an audio master clock are provided on the board:
* A Skyworks Si5351B PLL device. The Si5351 is an I2C configurable clock generator that is ideally suited for replacing crystals, crystal oscillators, VCXOs, phase-locked loops (PLLs), and fanout buffers.
* xCORE.ai devices are equipped with a secondary (or 'application') PLL which can be used to generate fixed audio clocks or recover external clocks using lib_sw_pll.
* xCORE.ai devices are equipped with a secondary (or 'application') PLL which can be used to generate audio clocks
Selection between these methods is done via writing to bits 6 and 7 of PORT 8D on tile[0].
Either the locally generated clock (from the PL611) or the recovered low jitter clock (from the CS2100) may be selected to clock the audio stages; the xcore.ai, the ADC/DAC and Digital output stages. Selection is controlled via an additional I/O, bit 5 of PORT 8C, see :ref:`hw_316_ctrlport`.
Either the locally generated clock (from the PL611) or the recovered low jitter clock (from the CS2100) may be selected to clock the audio stages; the xCORE-200, the ADC/DAC and Digital output stages. Selection is controlled via an additional I/O, bit 5 of PORT 8C, see :ref:`hw_316_ctrlport`.
.. _hw_316_ctrlport:

View File

@@ -33,8 +33,8 @@ This must be a 1-bit port, for example::
<Port Location="XS1_PORT_1A" Name="PORT_SPDIF_IN"/>
When S/PDIF receive is enabled the codebase expects to either drive a synchronisation signal to an external
Cirrus Logic CS2100 device or use lib_swp_pll (xcore.ai only) for master-clock generation.
When S/PDIF receive is enabled the codebase expects to drive a synchronisation signal to an external
Cirrus Logic CS2100 device for master-clock generation.
The programmer should ensure the define in :ref:`opt_spdif_rx_ref_defines` is set appropriately.

View File

@@ -1,27 +1,27 @@
|newpage|
Synchronisation
===============
Synchronisation & Clocking
==========================
The codebase supports "Synchronous" and "Asynchronous" modes for USB transfer as defined by the
The codebase supports "Synchronous" and "Asynchronous" modes for USB transfer as defined by the
USB specification(s).
Asynchronous mode (``XUA_SYNCMODE_ASYNC``) has the advantage that the device is clock-master. This means that
a high-quality local master-clock source can be utilised. It also has the benefit that the device may
synchronise it's master clock to an external digital input stream e.g. S/PDIF and thus avoiding sample-rate
Asynchronous mode (``XUA_SYNCMODE_ASYNC``) has the advantage that the device is clock-master. This means that
a high-quality local master-clock source can be utilised. It also has the benefit that the device may
synchronise it's master clock to an external digital input stream e.g. S/PDIF thus avoiding sample-rate
conversion.
The drawback of this mode is that it burdens the host with syncing to the device which some hosts
The drawback of this mode is that it burdens the host with syncing to the device which some hosts
may not support. This is especially pertinent to embedded hosts, however, most PC's and mobile devices
will indeed support this mode.
Synchronous mode (``XUA_SYNCMODE_SYNC``) is an option if the target host does not support asynchronous mode
or if it is desirable to synchronise many devices to a single host. It should be noted, however, that input
or if it is desirable to synchronise many devices to a single host. It should be noted, however, that input
from digital streams, such as S/PDIF, are not currently supported in this mode.
.. note::
The selection of synchronisation mode is done at build time and cannot be changed dynamically.
Setting the synchronisation mode of the device is done using the define in :ref:`opt_sync_defines`
@@ -39,13 +39,17 @@ Setting the synchronisation mode of the device is done using the define in :ref:
- USB synchronisation mode
- ``XUA_SYNCMODE_ASYNC``
When operating in synchronous mode a local master clock must be generated that is synchronised to the incoming
SoF rate from USB. Either an external Cirrus Logic CS2100 device is required for this purpose
or, on xcore.ai devices, the on-chip application PLL may be used via lib_sw_pll.
In the case of using the CS2100, the codebase expects to drive a synchronisation signal to this external device
as a reference.
When operating in asynchronous mode xcore.ai based devices will be configured, by default, to use their internal
"Applications" PLL to generated an appropriate master-clock signal. To disable this ``XUA_USE_APP_PLL`` should be
set to ``0``. For all other devices the developer is expected to supply external master-clock generation circuitry.
The programmer should ensure the define in :ref:`opt_sync_ref_defines` is set appropriately.
When operating in synchronous mode an xcore.ai based device, by default, will be configured to used it's internal
"application" PLL to generate a master-clock synchronised to the USB host.
xcore-200 based devices do not have this application PLL and so an external Cirrus Logic CS2100 device is required
for master clock generation. The codebase expects to drive a synchronisation signal to this external device.
In this case the developer should ensure the define in :ref:`opt_sync_ref_defines` is set appropriately.
.. _opt_sync_ref_defines:
@@ -59,14 +63,34 @@ The programmer should ensure the define in :ref:`opt_sync_ref_defines` is set ap
* - ``PLL_REF_TILE``
- Tile location of reference to CS2100 device
- ``AUDIO_IO_TILE``
* - ``XUA_USE_SW_PLL``
- Whether or not to use sw_pll to recover the clock (xcore.ai only)
- 1 for xcore.ai targets. May be overridden to 0 in ``xua_conf.h``
The codebase expects the CS2100 reference signal port to be defined in the application XN file as ``PORT_PLL_REF``.
The codebase expects this reference signal port to be defined in the application XN file as ``PORT_PLL_REF``.
This may be a port of any bit-width, however, connection to bit[0] is assumed::
<Port Location="XS1_PORT_1A" Name="PORT_PLL_REF"/>
Configuration of the external CS2100 device (typically via I2C) is beyond the scope of this document.
Note, in all cases the master-clocks are generated (when using the xcore.ai Application PLL) or should be generated
(if using external circuitry) to match the defines in :ref:`opt_sync_mclk_defines`.
.. _opt_sync_mclk_defines:
.. list-table:: Master clock frequencies
:header-rows: 1
:widths: 20 80 20
* - Define
- Description
- Default
* - ``MCLK_48``
- Master clock frequency (in Hz)used for sample-rates related to 48KHz
- NOTE
* - ``MCLK_441``
- Master clock frequency (in Hz) used for sample-rates related to 44.1KHz
- NONE
.. note::
The master clock defines above are critical for proper operation and default values are not provided.
If they are not defined by the devloper a build error will be emmited.

View File

@@ -15,35 +15,32 @@ the xCORE.
Using an external PLL/Clock Multiplier allows an Asynchronous mode design to lock to an external
clock source from a digital stream (e.g. S/PDIF or ADAT input). The codebase supports the Cirrus
Logic CS2100 device or use of lib_sw_pll (xcore.ai only) for this purpose. Other devices may be
supported via code modification.
Logic CS2100 device for this purpose. Other devices may be supported via code modification.
The Clock Recovery core (Clock Gen) is responsible for either generating the reference frequency
to the CS2100 device or driving lib_sw_pll from time measurements based on the local master clock
and the time of received samples. Clock Gen (via CS2100 or lib_sw_pll) generates the master clock
used over the whole design. This core also serves as a smaller buffer between ADAT and S/PDIF
receiving cores and the Audio Hub core.
.. note::
It is expected that in a future release the secondary PLL in xCORE.ai devices, coupled with
associated software changes, will be capable of replacing the CS2100 part for most designs.
When using lib_sw_pll (xcore.ai only) an further core is instantiated which performs the sigma-delta
modulation of the xCORE PLL to ensure the lowest jitter over the audio band. See lib_sw_pll
documentation for further details.
The Clock Recovery core (Clock Gen) is responsible for generating the reference frequency
to the CS2100 device. This, in turn, generates the master clock used over the whole design.
This core also serves as a smaller buffer between ADAT and S/PDIF receiving cores and the Audio Hub
core.
When running in *Internal Clock* mode this core simply generates this clock using a local
timer, based on the XMOS reference clock.
When running in an external clock mode (i.e. S/PDIF Clock" or "ADAT Clock" mode) samples are
received from the S/PDIF and/or ADAT receive core. The external frequency is calculated through
counting samples in a given period. Either the reference clock to the CS2100 is then generated based on
the reception of these samples or the timing information is provided to lib_sw_pll to generate
the phase-locked clock on-chip (xcore.ai only).
received from the S/PDIF and/or ADAT receive core. The external frequency is calculated through
counting samples in a given period. The reference clock to the CS2100 is then generated based on
the reception of these samples.
If an external stream becomes invalid, the *Internal Clock* timer event will fire to ensure that
valid master clock generation continues regardless of cable unplugs etc. Efforts are made to
ensure the transition between these clocks are relatively seamless. Additionally efforts are also
made to try and keep the jitter on the reference clock as low as possible, regardless of activity
made to try and keep the jitter on the reference clock as low as possibly, regardless of activity
level of the Clock Gen core. The is achieved though the use of port times to schedule pin toggling
rather than directly outputting to the port in the case of using the CS2100. For lib_sw_pll cases the
last setting is kept for the sigma-delta modulator ensuring clock continuity.
rather than directly outputting to the port.
The Clock Gen core gets clock selection Get/Set commands from Endpoint 0 via the ``c_clk_ctl``
channel. This core also records the validity of external clocks, which is also queried

View File

@@ -3,26 +3,11 @@
MIDI
====
The MIDI core implements a 31250 baud UART (8-N-1) for both input and output. It uses a single dedicated thread which performs multiple functions:
- UART Tx peripheral.
- UART Tx FIFO of 1024 bytes (may be configured by the user).
- Decoding of USB MIDI message to bytes.
- UART Rx peripheral.
- Packing of received MIDI bytes into USB MIDI messages/events.
It is connected via a channel to the Endpoint Buffer core meaning that it can be placed on any XCORE tile in the system subject to resource availability. The channel uses an optimised low level protocol meaning that it always occupies a switch path.
The Endpoint Buffer core implements the two Bulk endpoints (one In and one Out) as well as interacting with small, shared-memory, FIFOs for each endpoint.
On receiving 32-bit USB MIDI events from the Endpoint Buffer core over the channel, the MIDI core parses these and translates them to 8-bit MIDI messages which are sent
out over the UART. Up to 1024 bytes may be buffered by the MIDI task for outgoing messages in the default configuration. If the outgoing buffer is full then it will cause the USB endpoint to be NACKed which provides flow control in the case that the host application sends messages faster than the UART can transmit them. This is important because the USB bandwidth far exceeds the MIDI UART bandwidth by many orders of magnitude. The combination of buffering and flow control ensures outgoing messages are not dropped during normal operation.
Incoming 8-bit MIDI messages from the UART receiver are packed into 32-bit USB MIDI events and passed on to the Endpoint Buffer core. Since the rate of ingress
to the MIDI port is tiny in comparison to the host USB bandwidth, no buffering is required in the MIDI core and the MIDI events are always forwarded on directly to USB immediately.
All MIDI message types are supported including `Sysex` (MIDI System Exclusive) strings allowing custom function such as bank updates and patches, backup and device firmware upgrade (DFU) where supported by the MIDI device.
The MIDI core is implemented in the file ``usb_midi.xc`` and the USB buffering is handled in the file ``ep_buffer.xc``.
The MIDI core implements a 31250 baud UART for both input and output. On receiving 32=bit USB MIDI events
from the Endpoint Buffer core, it parses these and translates them to 8-bit MIDI messages which are sent
over UART. Similarly, incoming 8-bit MIDI messages are aggregated into 32-bit USB MIDI events and
passed on to the Endpoint Buffer core. The MIDI core is implemented in the file ``usb_midi.xc``.
The Endpoint Buffer core implements the two Bulk endpoints (one In and one Out) as well as interacting
with small, shared-memory, FIFOs for each endpoint.

View File

@@ -6,4 +6,4 @@
xmosdfu: xmosdfu.cpp
mkdir -p bin
g++ -D_GNU_SOURCE -Wall -g -o bin/xmosdfu -Ilibusb/Rasp -x c xmosdfu.cpp -std=c99 -lusb-1.0
g++ -D_GNU_SOURCE -Wall -g -o bin/xmosdfu -Ilibusb/Rasp -lusb-1.0 -x c xmosdfu.cpp -std=c99

View File

@@ -1,53 +0,0 @@
set(LIB_NAME lib_xua)
set(LIB_VERSION 4.1.0)
set(LIB_INCLUDES api
src/core
src/core/audiohub
src/core/buffer/ep
src/core/endpoint0
src/dfu
src/core/buffer/decouple
src/core/clocking
src/core/mixer
src/core/pdm_mics
src/core/ports
src/core/support
src/core/user
src/core/user/audiostream
src/core/user/audiohw
src/core/user/hid
src/core/user/hostactive
src/hid
src/midi)
set(LIB_OPTIONAL_HEADERS xua_conf.h static_hid_report.h)
set(LIB_DEPENDENT_MODULES "lib_adat(1.2.0)"
"lib_locks(2.2.0)"
"lib_logging(3.2.0)"
"lib_mic_array(4.6.0)"
"lib_spdif(6.1.0)"
"lib_sw_pll(2.2.0)"
"lib_xassert(4.2.0)"
"lib_xud(2.3.1)")
set(LIB_COMPILER_FLAGS -O3 -DREF_CLK_FREQ=100 -fasm-linenum -fcomment-asm)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
list(APPEND LIB_COMPILER_FLAGS -DXASSERT_ENABLE_ASSERTIONS=1
-DXASSERT_ENABLE_DEBUG=1
-DXASSERT_ENBALE_LINE_NUMBERS=1)
else()
list(APPEND LIB_COMPILER_FLAGS -DXASSERT_ENABLE_ASSERTIONS=0
-DXASSERT_ENABLE_DEBUG=0
-DXASSERT_ENABLE_LINE_NUMBERS=0)
endif()
set(LIB_COMPILER_FLAGS_xua_endpoint0.c ${LIB_COMPILER_FLAGS} -Os -mno-dual-issue)
set(LIB_COMPILER_FLAGS_xua_ep0_uacreqs.xc ${LIB_COMPILER_FLAGS} -Os -mno-dual-issue)
set(LIB_COMPILER_FLAGS_dbcalc.xc ${LIB_COMPILER_FLAGS} -Os -mno-dual-issue)
set(LIB_COMPILER_FLAGS_audioports.c ${LIB_COMPILER_FLAGS} -Os -mno-dual-issue)
set(LIB_COMPILER_FLAGS_audioports.xc ${LIB_COMPILER_FLAGS} -Os -mno-dual-issue)
set(LIB_COMPILER_FLAGS_dfu.xc ${LIB_COMPILER_FLAGS} -Os -mno-dual-issue)
set(LIB_COMPILER_FLAGS_flash_interface.c ${LIB_COMPILER_FLAGS} -Os -mno-dual-issue)
set(LIB_COMPILER_FLAGS_flashlib_user.c ${LIB_COMPILER_FLAGS} -Os -mno-dual-issue)
XMOS_REGISTER_MODULE()

View File

@@ -1,4 +1,4 @@
VERSION = 4.1.0
VERSION = 3.5.1
DEBUG ?= 0
@@ -8,14 +8,13 @@ else
DEBUG_FLAGS = -DXASSERT_ENABLE_ASSERTIONS=0 -DXASSERT_ENABLE_DEBUG=0 -DXASSERT_ENABLE_LINE_NUMBERS=0
endif
DEPENDENT_MODULES = lib_adat(>=1.2.0) \
lib_locks(>=2.2.0) \
lib_logging(>=3.2.0) \
lib_mic_array(>=4.6.0) \
lib_spdif(>=6.1.0) \
lib_sw_pll(>=2.2.0) \
lib_xassert(>=4.2.0) \
lib_xud(>=2.3.1)
DEPENDENT_MODULES = lib_locks(>=2.1.0) \
lib_logging(>=3.1.1) \
lib_mic_array(>=4.5.0) \
lib_spdif(>=5.0.0) \
lib_xassert(>=4.1.0) \
lib_xud(>=2.2.3) \
lib_adat(>=1.0.0)
MODULE_XCC_FLAGS = $(XCC_FLAGS) \
-O3 \
@@ -54,9 +53,9 @@ INCLUDE_DIRS = $(EXPORT_INCLUDE_DIRS) \
src/core/support \
src/core/user \
src/core/user/audiostream \
src/core/user/audiohw \
src/core/user/hid \
src/core/user/hostactive \
src/core/user/audiohw \
src/hid \
src/midi
@@ -71,8 +70,8 @@ SOURCE_DIRS = src/core \
src/core/ports \
src/core/support \
src/core/user/audiostream \
src/core/user/audiohw \
src/core/user/hostactive \
src/core/user/audiohw \
src/core/xuduser \
src/dfu \
src/hid \

View File

@@ -1,11 +1,13 @@
// Copyright 2018-2023 XMOS LIMITED.
// Copyright 2018-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
unsigned adatCounter = 0;
unsigned adatSamples[8];
#pragma unsafe arrays
static inline void TransferAdatTxSamples(chanend c_adat_out, const unsigned samplesFromHost[], int smux, int handshake)
{
/* Do some re-arranging for SMUX.. */
unsafe
{
@@ -27,6 +29,7 @@ static inline void TransferAdatTxSamples(chanend c_adat_out, const unsigned samp
if(adatCounter == smux)
{
#ifdef ADAT_TX_USE_SHARED_BUFF
unsafe
{

View File

@@ -38,12 +38,12 @@ void InitPorts_master(buffered _XUA_CLK_DIR port:32 p_lrclk, buffered _XUA_CLK_D
#endif
unsigned tmp;
if(XUA_I2S_N_BITS == 32)
p_lrclk <: 0 @ tmp;
else
tmp = partout_timestamped(p_lrclk, XUA_I2S_N_BITS, 0);
tmp += 100;
/* Since BCLK is free-running, setup outputs/inputs at a known point in the future */
@@ -72,7 +72,7 @@ void InitPorts_master(buffered _XUA_CLK_DIR port:32 p_lrclk, buffered _XUA_CLK_D
for(int i = 0; i < I2S_WIRES_ADC; i++)
{
asm("setpt res[%0], %1"::"r"(p_i2s_adc[i]),"r"(tmp-1));
if(XUA_I2S_N_BITS != 32)
set_port_shift_count(p_i2s_adc[i], XUA_I2S_N_BITS);
}
@@ -127,13 +127,13 @@ void InitPorts_slave(buffered _XUA_CLK_DIR port:32 p_lrclk, buffered _XUA_CLK_DI
for(int i = 0; i < I2S_WIRES_ADC; i++)
{
asm("setpt res[%0], %1"::"r"(p_i2s_adc[i]),"r"(tmp-1));
if(XUA_I2S_N_BITS != 32)
if(XUA_I2S_N_BITS != 32)
set_port_shift_count(p_i2s_adc[i], XUA_I2S_N_BITS);
}
#endif
asm("setpt res[%0], %1"::"r"(p_lrclk),"r"(tmp-1));
if(XUA_I2S_N_BITS != 32)
if(XUA_I2S_N_BITS != 32)
set_port_shift_count(p_lrclk, XUA_I2S_N_BITS);
#endif /* (I2S_CHANS_ADC != 0 || I2S_CHANS_DAC != 0) */
}

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2024 XMOS LIMITED.
// Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/**
* @file xua_audiohub.xc
@@ -17,10 +17,10 @@
#include <string.h>
#include <xassert.h>
#include "xua.h"
#include "audiohw.h"
#include "audioports.h"
#include "mic_array_conf.h"
#if (XUA_SPDIF_TX_EN)
@@ -242,12 +242,12 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out
}
#endif // ((DEBUG_MIC_ARRAY == 1) && (XUA_NUM_PDM_MICS > 0))
UserBufferManagementInit(curSamFreq);
UserBufferManagementInit();
unsigned command = DoSampleTransfer(c_out, readBuffNo, underflowWord);
// Reinitialise user state before entering the main loop
UserBufferManagementInit(curSamFreq);
UserBufferManagementInit();
#if (XUA_ADAT_TX_EN)
unsafe{
@@ -375,11 +375,12 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out
}
#endif // (I2S_CHANS_DAC != 0)
#if (XUA_ADAT_TX_EN)
TransferAdatTxSamples(c_adat_out, samplesOut, adatSmuxMode, 1);
#endif
if(frameCount == 0)
{
#if (XUA_ADAT_TX_EN)
TransferAdatTxSamples(c_adat_out, samplesOut, adatSmuxMode, 1);
#endif
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
/* Sync with clockgen */
@@ -635,15 +636,15 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
buffered _XUA_CLK_DIR port:32 ?p_bclk,
buffered out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC],
buffered in port:32 (&?p_i2s_adc)[I2S_WIRES_ADC]
#if (XUA_USE_APP_PLL)
, client interface SoftPll_if i_softPll
#endif
#if (XUA_SPDIF_TX_EN) //&& (SPDIF_TX_TILE != AUDIO_IO_TILE)
, chanend c_spdif_out
#endif
#if (XUA_ADAT_RX_EN || XUA_SPDIF_RX_EN)
, chanend c_dig_rx
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
, chanend c_audio_rate_change
#endif
#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1)
, server interface i_dfu ?dfuInterface
#endif
@@ -665,10 +666,17 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
unsigned divide;
unsigned firstRun = 1;
#if (XUA_USE_APP_PLL)
/* Use xCORE.ai Secondary PLL to generate master clock
* This could be "fixed" for async mode or adjusted if in sync mode */
i_softPll.init(DEFAULT_MCLK);
#endif
/* Clock master clock-block from master-clock port */
/* Note, marked unsafe since other cores may be using this mclk port */
configure_clock_src(clk_audio_mclk, p_mclk_in);
start_clock(clk_audio_mclk);
#if (DSD_CHANS_DAC > 0)
/* Make sure the DSD ports are on and buffered - just in case they are not shared with I2S */
@@ -680,11 +688,16 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
#endif
#if (XUA_ADAT_TX_EN)
configure_out_port_no_ready(p_adat_tx, clk_audio_mclk, 0);
set_clock_fall_delay(clk_audio_mclk, 7);
/* Share SPDIF clk blk */
configure_clock_src(clk_mst_spd, p_mclk_in);
configure_out_port_no_ready(p_adat_tx, clk_mst_spd, 0);
set_clock_fall_delay(clk_mst_spd, 7);
#if (XUA_SPDIF_TX_EN == 0)
start_clock(clk_mst_spd);
#endif
#endif
start_clock(clk_audio_mclk);
/* Perform required CODEC/ADC/DAC initialisation */
AudioHwInit();
@@ -797,21 +810,16 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
curFreq *= 16;
}
#endif
/* Configure Clocking/CODEC/DAC/ADC for SampleFreq/MClk */
/* User should mute audio hardware */
AudioHwConfig_Mute();
#if (XUA_USE_APP_PLL)
i_softPll.init(mClk);
#endif
/* User code should configure audio harware for SampleFreq/MClk etc */
AudioHwConfig(curFreq, mClk, dsdMode, curSamRes_DAC, curSamRes_ADC);
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
/* Notify clockgen of new mCLk */
c_audio_rate_change <: mClk;
c_audio_rate_change <: curFreq;
/* Wait for ACK back from clockgen or ep_buffer to signal clocks all good */
c_audio_rate_change :> int _;
#endif
/* User should unmute audio hardware */
AudioHwConfig_UnMute();
@@ -821,7 +829,7 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
{
/* TODO wait for good mclk instead of delay */
/* No delay for DFU modes */
if (((curSamFreq / AUD_TO_USB_RATIO) != AUDIO_STOP_FOR_DFU) && command)
if (((curSamFreq / AUD_TO_USB_RATIO) != AUDIO_REBOOT_FROM_DFU) && ((curSamFreq / AUD_TO_USB_RATIO) != AUDIO_STOP_FOR_DFU) && command)
{
#if 0
/* User should ensure MCLK is stable in AudioHwConfig */
@@ -931,9 +939,13 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
#else
dummy_deliver(c_aud, command);
#endif
/* Note, we do not expect to reach here */
curSamFreq = inuint(c_aud);
outct(c_aud, XS1_CT_END);
if (curSamFreq == AUDIO_START_FROM_DFU)
{
outct(c_aud, XS1_CT_END);
break;
}
}
}
#endif

View File

@@ -1,11 +1,11 @@
// Copyright 2016-2023 XMOS LIMITED.
// Copyright 2016-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include "xccompat.h"
#include "xua_audiohub.h"
/* Default implementation for UserBufferManagementInit() */
void __attribute__ ((weak)) UserBufferManagementInit(unsigned sampFreq)
void __attribute__ ((weak)) UserBufferManagementInit()
{
/* Do nothing */
}

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2024 XMOS LIMITED.
// Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include "xua.h"
@@ -42,13 +42,8 @@
#define MAX_DEVICE_AUD_PACKET_SIZE_OUT (MAX(MAX_DEVICE_AUD_PACKET_SIZE_OUT_FS, MAX_DEVICE_AUD_PACKET_SIZE_OUT_HS))
/*** BUFFER SIZES ***/
/* How many packets too allow for in buffer - minimum is 5.
2 for having in the aud_to_host buffer when it comes out of underflow, space available for 2 more for to accomodate cases when
2 pkts from audio hub get written into the aud_to_host buffer within 1 SOF period, and space for 1 extra packet to ensure that
when the 4th packet gets written to the buffer, there's space to accomodate the next packet, otherwise handle_audio_request() will
drop packets after writing the 4th packet in the buffer
*/
#define BUFFER_PACKET_COUNT (5)
#define BUFFER_PACKET_COUNT (4) /* How many packets too allow for in buffer - minimum is 4 */
#define BUFF_SIZE_OUT_HS MAX_DEVICE_AUD_PACKET_SIZE_OUT_HS * BUFFER_PACKET_COUNT
#define BUFF_SIZE_OUT_FS MAX_DEVICE_AUD_PACKET_SIZE_OUT_FS * BUFFER_PACKET_COUNT
@@ -155,65 +150,68 @@ unsigned unpackData = 0;
unsigned packState = 0;
unsigned packData = 0;
static inline void _send_sample_4(chanend c_mix_out, int ch)
{
int sample;
read_via_xc_ptr(sample, g_aud_from_host_rdptr);
g_aud_from_host_rdptr+=4;
#if (OUTPUT_VOLUME_CONTROL == 1) && (!OUT_VOLUME_IN_MIXER)
int mult;
int h;
unsigned l;
unsafe
{
mult = multOutPtr[ch];
}
{h, l} = macs(mult, sample, 0, 0);
h <<= 3;
#if (STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED == 1)
h |= (l >>29) & 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit)
// Note: We need all 32bits for Native DSD
#endif
outuint(c_mix_out, h);
#else
outuint(c_mix_out, sample);
#endif
}
static inline void SendSamples4(chanend c_mix_out)
{
/* Doing this allows us to unroll */
if(g_numUsbChan_Out == HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT)
/* Doing this checking allows us to unroll */
if(g_numUsbChan_Out == NUM_USB_CHAN_OUT)
{
#pragma loop unroll
for(int i = 0; i < HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT; i++)
/* Buffering not underflow condition send out some samples...*/
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
_send_sample_4(c_mix_out, i);
}
}
else if(g_numUsbChan_Out == HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT)
{
#pragma loop unroll
for(int i = 0; i < HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT; i++)
{
_send_sample_4(c_mix_out, i);
}
}
else if(g_numUsbChan_Out == HS_STREAM_FORMAT_OUTPUT_3_CHAN_COUNT)
{
#pragma loop unroll
for(int i = 0; i < HS_STREAM_FORMAT_OUTPUT_3_CHAN_COUNT; i++)
{
_send_sample_4(c_mix_out, i);
int sample;
int mult;
int h;
unsigned l;
read_via_xc_ptr(sample, g_aud_from_host_rdptr);
g_aud_from_host_rdptr+=4;
#if (OUTPUT_VOLUME_CONTROL == 1) && (!OUT_VOLUME_IN_MIXER)
unsafe
{
mult = multOutPtr[i];
}
{h, l} = macs(mult, sample, 0, 0);
h <<= 3;
#if (STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED == 1)
h |= (l >>29) & 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit)
// Note: We need all 32bits for Native DSD
#endif
outuint(c_mix_out, h);
#else
outuint(c_mix_out, sample);
#endif
}
}
else
{
#pragma loop unroll
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_OUT_FS; i++)
{
_send_sample_4(c_mix_out, i);
int sample;
int mult;
int h;
unsigned l;
read_via_xc_ptr(sample, g_aud_from_host_rdptr);
g_aud_from_host_rdptr+=4;
#if (OUTPUT_VOLUME_CONTROL == 1) && (!OUT_VOLUME_IN_MIXER)
unsafe
{
mult = multOutPtr[i];
}
{h, l} = macs(mult, sample, 0, 0);
h <<= 3;
#if (STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED == 1)
h |= (l >>29) & 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit)
// Note: We need all 32bits for Native DSD
#endif
outuint(c_mix_out, h);
#else
outuint(c_mix_out, sample);
#endif
}
}
}
@@ -516,7 +514,7 @@ __builtin_unreachable();
{
/* Finished creating packet - commit it to the FIFO */
/* Total samps to write could start at 0 (i.e. no MCLK) so need to check for < 0) */
if(sampsToWrite <= 0)
if (sampsToWrite <= 0)
{
int speed, wrPtr;
packState = 0;
@@ -653,7 +651,6 @@ __builtin_unreachable();
#if (NUM_USB_CHAN_IN > 0)
/* Mark Endpoint (IN) ready with an appropriately sized zero buffer */
/* TODO We should properly size zeros packet rather than using "mid" */
static inline void SetupZerosSendBuffer(XUD_ep aud_to_host_usb_ep, unsigned sampFreq, unsigned slotSize,
xc_ptr aud_to_host_zeros)
{
@@ -662,8 +659,8 @@ static inline void SetupZerosSendBuffer(XUD_ep aud_to_host_usb_ep, unsigned samp
/* Set IN stream packet size to something sensible. We expect the buffer to
* over flow and this to be reset */
SET_SHARED_GLOBAL(sampsToWrite, mid);
SET_SHARED_GLOBAL(totalSampsToWrite, mid);
SET_SHARED_GLOBAL(sampsToWrite, 0);
SET_SHARED_GLOBAL(totalSampsToWrite, 0);
mid *= g_numUsbChan_In * slotSize;
@@ -824,7 +821,6 @@ void XUA_Buffer_Decouple(chanend c_mix_out
SetupZerosSendBuffer(aud_to_host_usb_ep, sampFreq, g_curSubSlot_In, aud_to_host_zeros);
#endif
#if (NUM_USB_CHAN_OUT > 0)
/* Reset OUT buffer state */
outUnderflow = 1;
SET_SHARED_GLOBAL(g_aud_from_host_rdptr, aud_from_host_fifo_start);
@@ -834,10 +830,9 @@ void XUA_Buffer_Decouple(chanend c_mix_out
if(outOverflow)
{
/* If we were previously in overflow we wont have marked as ready */
XUD_SetReady_OutPtr(aud_from_host_usb_ep, aud_from_host_fifo_start + 4);
XUD_SetReady_OutPtr(aud_from_host_usb_ep, aud_from_host_fifo_start+4);
outOverflow = 0;
}
#endif
}
/* Wait for handshake back and pass back up */
@@ -911,7 +906,6 @@ void XUA_Buffer_Decouple(chanend c_mix_out
GET_SHARED_GLOBAL(dataFormat, g_formatChange_DataFormat);
GET_SHARED_GLOBAL(sampRes, g_formatChange_SampRes);
#if (NUM_USB_CHAN_OUT > 0)
/* Reset OUT buffer state */
SET_SHARED_GLOBAL(g_aud_from_host_rdptr, aud_from_host_fifo_start);
SET_SHARED_GLOBAL(g_aud_from_host_wrptr, aud_from_host_fifo_start);
@@ -927,7 +921,6 @@ void XUA_Buffer_Decouple(chanend c_mix_out
XUD_SetReady_OutPtr(aud_from_host_usb_ep, aud_from_host_fifo_start+4);
outOverflow = 0;
}
#endif
#ifdef NATIVE_DSD
if(dataFormat == UAC_FORMAT_TYPEI_RAW_DATA)
@@ -1048,7 +1041,7 @@ void XUA_Buffer_Decouple(chanend c_mix_out
DISABLE_INTERRUPTS();
if(inUnderflow)
if (inUnderflow)
{
int fillLevel;
GET_SHARED_GLOBAL(fillLevel, g_aud_to_host_fill_level);
@@ -1056,21 +1049,7 @@ void XUA_Buffer_Decouple(chanend c_mix_out
assert(fillLevel <= BUFF_SIZE_IN);
/* Check if we have come out of underflow */
unsigned sampFreq;
GET_SHARED_GLOBAL(sampFreq, g_freqChange_sampFreq);
int min, mid, max;
GetADCCounts(sampFreq, min, mid, max);
const int min_pkt_size = ((min * g_curSubSlot_In * g_numUsbChan_In + 3) & ~0x3) + 4;
/*
Come out of underflow if there are exactly 2 packets in the buffer.
This ensures that handle_audio_request() does not drop packets when writing packets into the aud_to_host buffer
when aud_to_host buffer is not in underflow.
For example, coming out of underflow with 3 packets in the buffer would mean handle_audio_request()
drops packets if 2 pkts are received from audio hub in 1 SOF period. Coming out of underflow with 4
packets would mean handle_audio_request would drop packets after writing 1 packet to the aud_to_host buffer.
*/
if ((fillLevel >= (min_pkt_size*2)) && (fillLevel < (min_pkt_size*3)))
if (fillLevel >= IN_BUFFER_PREFILL)
{
int aud_to_host_rdptr;
GET_SHARED_GLOBAL(aud_to_host_rdptr, g_aud_to_host_rdptr);

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2024 XMOS LIMITED.
// Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include "xua.h"
#if XUA_USB_EN
@@ -105,9 +105,8 @@ void XUA_Buffer(
#endif
, chanend c_aud
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
, chanend c_audio_rate_change
#if(XUA_USE_SW_PLL)
, chanend c_sw_pll
#if(XUA_USE_APP_PLL)
, chanend c_swpll_update
#else
, client interface pll_ref_if i_pll_ref
#endif
@@ -146,9 +145,8 @@ void XUA_Buffer(
, c_buff_ctrl
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
, c_audio_rate_change
#if(XUA_USE_SW_PLL)
, c_sw_pll
#if(XUA_USE_APP_PLL)
, c_swpll_update
#else
, i_pll_ref
#endif
@@ -201,9 +199,8 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
, chanend c_buff_ctrl
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
, chanend c_audio_rate_change
#if (XUA_USE_SW_PLL)
, chanend c_sw_pll
#if (XUA_USE_APP_PLL)
, chanend c_swpll_update
#else
, client interface pll_ref_if i_pll_ref
#endif
@@ -263,6 +260,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
unsigned bufferIn = 1;
#endif
int sofCount = 0;
int pllUpdate = 0;
unsigned mod_from_last_time = 0;
#ifdef FB_TOLERANCE_TEST
@@ -372,23 +370,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
#define LOCAL_CLOCK_MARGIN (1000)
#endif
#if (XUA_USE_SW_PLL)
/* Setup the phase frequency detector */
const unsigned controller_rate_hz = 100;
const unsigned pfd_ppm_max = 2000; /* PPM range before we assume unlocked */
sw_pll_pfd_state_t sw_pll_pfd;
sw_pll_pfd_init(&sw_pll_pfd,
1, /* How often the PFD is invoked per call */
masterClockFreq / controller_rate_hz, /* pll ratio integer */
0, /* Assume precise timing of sampling */
pfd_ppm_max);
outuint(c_sw_pll, masterClockFreq);
outct(c_sw_pll, XS1_CT_END);
inuint(c_sw_pll); /* receive ACK */
inct(c_sw_pll);
#else /* XUA_USE_SW_PLL */
#if (!XUA_USE_APP_PLL)
timer t_sofCheck;
unsigned timeLastEdge;
unsigned timeNextEdge;
@@ -397,7 +379,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
i_pll_ref.toggle();
#endif
#endif /* (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) */
#endif
while(1)
{
@@ -462,6 +444,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
/* Note, Endpoint 0 will hold off host for a sufficient period to allow our feedback
* to stabilise (i.e. sofCount == 128 to fire) */
sofCount = 0;
pllUpdate = 0;
clocks = 0;
clockcounter = 0;
mod_from_last_time = 0;
@@ -484,7 +467,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
masterClockFreq = MCLK_441;
}
}
#endif /* (MAX_FREQ != MIN_FREQ) */
#endif
/* Ideally we want to wait for handshake (and pass back up) here. But we cannot keep this
* core locked, it must stay responsive to packets (MIDI etc) and SOFs. So, set a flag and check for
* handshake elsewhere */
@@ -542,7 +525,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
}
break;
}
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) && (!XUA_USE_SW_PLL)
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) && (!XUA_USE_APP_PLL)
case t_sofCheck when timerafter(timeNextEdge) :> void:
i_pll_ref.toggle();
timeLastEdge = timeNextEdge;
@@ -557,60 +540,41 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
/* SOF notification from XUD_Manager() */
case inuint_byref(c_sof, u_tmp):
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
/* This really could (should) be done in decouple. However, for a quick demo this is okay
* Decouple expects a 16:16 number in fixed point stored in the global g_speed */
unsigned usbSpeed;
int framesPerSec;
GET_SHARED_GLOBAL(usbSpeed, g_curUsbSpeed);
static int sofCount = 0;
#if (XUA_USE_SW_PLL)
/* Run PFD and sw_pll controller at 100Hz */
const int sofFreqDivider = (usbSpeed == XUD_SPEED_HS) ? (8000 / controller_rate_hz) : (1000 / controller_rate_hz);
#else /* (XUA_USE_SW_PLL) */
/* 1000 toggles per second for CS2100 reference -> 500 Hz */
const int toggleRateHz = 1000;
const int sofFreqDivider = (usbSpeed == XUD_SPEED_HS) ? (8000 / toggleRateHz) : (1000 / toggleRateHz);
#endif /* (XUA_USE_SW_PLL) */
sofCount++;
if (sofCount == sofFreqDivider)
framesPerSec = (usbSpeed == XUD_SPEED_HS) ? 8000 : 1000;
clocks = ((int64_t) sampleFreq << 16) / framesPerSec;
asm volatile("stw %0, dp[g_speed]"::"r"(clocks));
sofCount += 1000;
if (sofCount == framesPerSec)
{
#if (XUA_USE_SW_PLL)
/* Grab port timer count, run through PFD and send to sw_pll */
unsigned short mclk_pt;
asm volatile("getts %0, res[%1]" : "=r" (mclk_pt) : "r" (p_off_mclk));
uint8_t first_loop = 0;
unsafe{
sw_pll_calc_error_from_port_timers(&sw_pll_pfd, &first_loop, mclk_pt, 0);
}
int error = 0;
if(!first_loop)
{
error = sw_pll_pfd.mclk_diff;
}
sw_pll_pfd.mclk_pt_last = mclk_pt;
/* Send error to sw_pll */
outuint(c_sw_pll, error);
outct(c_sw_pll, XS1_CT_END);
#else /* (XUA_USE_SW_PLL) */
/* Do toggle for CS2100 reference clock */
sofCount = 0;
pllUpdate++;
#if (!XUA_USE_APP_PLL)
/* Port is accessed via interface to allow flexibilty with location */
i_pll_ref.toggle();
t_sofCheck :> timeLastEdge;
timeNextEdge = timeLastEdge + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN;
#endif /* (XUA_USE_SW_PLL) */
sofCount = 0;
#endif
}
/* This really could (should) be done in decouple. However, for a quick demo this is okay
* Decouple expects a 16:16 number in fixed point stored in the global g_speed */
const int framesPerSec = (usbSpeed == XUD_SPEED_HS) ? 8000 : 1000;
clocks = ((int64_t) sampleFreq << 16) / framesPerSec;
asm volatile("stw %0, dp[g_speed]"::"r"(clocks));
#if (XUA_USE_APP_PLL)
// Update PLL @ 100Hz
if(pllUpdate == 10)
{
pllUpdate = 0;
unsigned short mclk_pt;
asm volatile("getts %0, res[%1]" : "=r" (mclk_pt) : "r" (p_off_mclk));
outuint(c_swpll_update, mclk_pt);
outct(c_swpll_update, XS1_CT_END);
}
#endif
#elif (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC)
@@ -757,6 +721,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
clocks < (expected_fb + FB_TOLERANCE))
#endif
{
int usb_speed;
asm volatile("stw %0, dp[g_speed]"::"r"(clocks)); // g_speed = clocks
GET_SHARED_GLOBAL(usb_speed, g_curUsbSpeed);
@@ -839,7 +804,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
if (midi_data_remaining_to_device)
{
read_via_xc_ptr(datum, midi_from_host_rdptr);
midi_send_data(c_midi, datum);
outuint(c_midi, datum);
midi_from_host_rdptr += 4;
midi_data_remaining_to_device -= 4;
}
@@ -992,7 +957,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
{
/* Read another word from the fifo and output it to MIDI thread */
read_via_xc_ptr(datum, midi_from_host_rdptr);
midi_send_data(c_midi, datum);
outuint(c_midi, datum);
midi_from_host_rdptr += 4;
midi_data_remaining_to_device -= 4;
}
@@ -1028,33 +993,6 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
break;
#endif /* ifdef MIDI */
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
case c_audio_rate_change :> u_tmp:
unsigned selected_mclk_rate = u_tmp;
c_audio_rate_change :> u_tmp; /* Sample rate is discarded as only care about mclk */
#if (XUA_USE_SW_PLL)
sw_pll_pfd_init(&sw_pll_pfd,
1, /* How often the PFD is invoked per call */
selected_mclk_rate / controller_rate_hz, /* pll muliplication ratio integer */
0, /* Assume precise timing of sampling */
pfd_ppm_max);
restart_sigma_delta(c_sw_pll, selected_mclk_rate);
/* Delay ACK until sw_pll says it is ready */
#else
c_audio_rate_change <: 0; /* ACK back to audio to release I2S immediately */
#endif /* XUA_USE_SW_PLL */
break;
#if (XUA_USE_SW_PLL)
/* This is fired when sw_pll has completed initialising a new mclk_rate */
case inuint_byref(c_sw_pll, u_tmp):
inct(c_sw_pll);
c_audio_rate_change <: 0; /* ACK back to audio to release */
break;
#endif /* (XUA_USE_SW_PLL) */
#endif /* (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) */
#ifdef IAP
/* Received word from iap thread - Check for ACK or Data */
case iap_get_ack_or_reset_or_data(c_iap, is_ack_iap, is_reset, datum_iap):

View File

@@ -0,0 +1,457 @@
// Copyright 2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <print.h>
#include <platform.h>
#include "xua.h"
#include "xassert.h"
#include <stdio.h>
#if (XUA_USE_APP_PLL)
/*
* Functions for interacting with the secondary/application PLL
*/
#ifndef __XS3A__
#error App PLL not included in device
#endif
/*
* App PLL settings used for syncing to external clocks
*/
// Define the PLL settings to generate the required frequencies.
// All settings allow greater than +-1000ppm lock range.
// Comment out the following line for 2us update.
//#define FAST_FRAC_REG_WRITE
// OPTION 1 - 1us register update rate - Lowest jitter
// 10ps jitter 100Hz-40kHz. Low freq noise floor -100dBc
#ifdef FAST_FRAC_REG_WRITE
#define FRAC_REG_WRITE_DLY (100)
// Found solution: IN 24.000MHz, OUT 22.578947MHz, VCO 3251.37MHz, RD 1, FD 135.474 (m = 9, n = 19), OD 6, FOD 6, ERR -11.189ppm
#define APP_PLL_CTL_SYNC_22M (0x0A808600)
#define APP_PLL_DIV_SYNC_22M (0x80000005)
#define APP_PLL_FRAC_SYNC_22M (0x80000812)
#define APP_PLL_ERR_MULT_22M (627) // round(135(divider)*100Hz*1048576/22579200)
#define APP_PLL_MOD_INIT_22M (498283)
// Fout = Fin*divider/(2*2*6*6) = (fin/144) * divider = (24/144) * divider. = 1/6 * divider.
// To achieve frequency f, Fraction Setting = (6*f) - 135
// So to achieve 22.5792MHz, Fraction Setting = (6*22.5792) - 135 = 0.4752
// Numerical input = round((Fraction setting * 2^20) = 0.4752 * 1048576 = 498283
//Found solution: IN 24.000MHz, OUT 24.575758MHz, VCO 3538.91MHz, RD 1, FD 147.455 (m = 5, n = 11), OD 6, FOD 6, ERR -9.864ppm
#define APP_PLL_CTL_SYNC_24M (0x0A809200)
#define APP_PLL_DIV_SYNC_24M (0x80000005)
#define APP_PLL_FRAC_SYNC_24M (0x8000040A)
#define APP_PLL_ERR_MULT_24M (627) // round(147(divider)*100Hz*1048576/24576000)
#define APP_PLL_MOD_INIT_24M (478151)
// Fout = Fin*divider/(2*2*6*6) = (fin/144) * divider = (24/144) * divider. = 1/6 * divider.
// To achieve frequency f, Fraction Setting = (6*f) - 147
// So to achieve 24.576MHz, Fraction Setting = (6*24.576) - 147 = 0.456
// Numerical input = round((Fraction setting * 2^20) = 0.456 * 1048576 = 478151
#else
// OPTION 2 - 2us register update rate - Higher jitter
// 50ps jitter 100Hz-40kHz. Low freq noise floor -93dBc
#define FRAC_REG_WRITE_DLY (200)
//Found solution: IN 24.000MHz, OUT 22.579186MHz, VCO 3522.35MHz, RD 2, FD 293.529 (m = 9, n = 17), OD 3, FOD 13, ERR -0.641ppm
#define APP_PLL_CTL_SYNC_22M (0x09012401)
#define APP_PLL_DIV_SYNC_22M (0x8000000C)
#define APP_PLL_FRAC_SYNC_22M (0x80000810)
#define APP_PLL_ERR_MULT_22M (1361) // round(293(divider)*100Hz*1048576/22579200)
#define APP_PLL_MOD_INIT_22M (555326)
// Fout = (Fin/2)*divider/(2*2*3*13) = (fin/312) * divider = (24/312) * divider. = 1/13 * divider.
// To achieve frequency f, Fraction Setting = (13*f) - 293
// So to achieve 22.5792MHz, Fraction Setting = (13*22.5792) - 293 = 0.5296
// Numerical input = round((Fraction setting * 2^20) = 0.5296 * 1048576 = 555326
//Found solution: IN 24.000MHz, OUT 24.576125MHz, VCO 3342.35MHz, RD 2, FD 278.529 (m = 9, n = 17), OD 2, FOD 17, ERR 5.069ppm - Runs VCO out fractionally out of spec at 835MHz
#define APP_PLL_CTL_SYNC_24M (0x08811501)
#define APP_PLL_DIV_SYNC_24M (0x80000010)
#define APP_PLL_FRAC_SYNC_24M (0x80000810)
#define APP_PLL_ERR_MULT_24M (1186) // round(278(divider)*100Hz*1048576/24576000)
#define APP_PLL_MOD_INIT_24M (553648)
// Fout = (Fin/2)*divider/(2*2*2*17) = (fin/272) * divider = (24/272) * divider. = 3/34 * divider.
// To achieve frequency f, Fraction Setting = ((34/3)*f) - 278
// So to achieve 24.576MHz, Fraction Setting = ((34/3)*24.576) - 278 = 0.528
// Numerical input = round((Fraction setting * 2^20) = 0.528 * 1048576 = 553648
#endif
/*
* App PLL settings used for low jitter fixed local clocks
*/
//Found solution: IN 24.000MHz, OUT 49.151786MHz, VCO 3145.71MHz, RD 1, FD 131.071 (m = 1, n = 14), OD 8, FOD 2, ERR -4.36ppm
// Measure: 100Hz-40kHz: ~7ps
// 100Hz-1MHz: 70ps.
// 100Hz high pass: 118ps.
#define APP_PLL_CTL_FIXED_49M (0x0B808200)
#define APP_PLL_DIV_FIXED_49M (0x80000001)
#define APP_PLL_FRAC_FIXED_49M (0x8000000D)
//Found solution: IN 24.000MHz, OUT 45.157895MHz, VCO 2709.47MHz, RD 1, FD 112.895 (m = 17, n = 19), OD 5, FOD 3, ERR -11.19ppm
// Measure: 100Hz-40kHz: 6.5ps
// 100Hz-1MHz: 67ps.
// 100Hz high pass: 215ps.
#define APP_PLL_CTL_FIXED_45M (0x0A006F00)
#define APP_PLL_DIV_FIXED_45M (0x80000002)
#define APP_PLL_FRAC_FIXED_45M (0x80001012)
// Found solution: IN 24.000MHz, OUT 24.576000MHz, VCO 2457.60MHz, RD 1, FD 102.400 (m = 2, n = 5), OD 5, FOD 5, ERR 0.0ppm
// Measure: 100Hz-40kHz: ~8ps
// 100Hz-1MHz: 63ps.
// 100Hz high pass: 127ps.
#define APP_PLL_CTL_FIXED_24M (0x0A006500)
#define APP_PLL_DIV_FIXED_24M (0x80000004)
#define APP_PLL_FRAC_FIXED_24M (0x80000104)
// Found solution: IN 24.000MHz, OUT 22.579186MHz, VCO 3522.35MHz, RD 1, FD 146.765 (m = 13, n = 17), OD 3, FOD 13, ERR -0.641ppm
// Measure: 100Hz-40kHz: 7ps
// 100Hz-1MHz: 67ps.
// 100Hz high pass: 260ps.
#define APP_PLL_CTL_FIXED_22M (0x09009100)
#define APP_PLL_DIV_FIXED_22M (0x8000000C)
#define APP_PLL_FRAC_FIXED_22M (0x80000C10)
#define APP_PLL_CTL_FIXED_12M (0x0A006500)
#define APP_PLL_DIV_FIXED_12M (0x80000009)
#define APP_PLL_FRAC_FIXED_12M (0x80000104)
#define APP_PLL_CTL_FIXED_11M (0x09009100)
#define APP_PLL_DIV_FIXED_11M (0x80000009)
#define APP_PLL_FRAC_FIXED_11M (0x80000C10)
#define APP_PLL_CTL_ENABLE (1 << 27)
#define APP_PLL_CLK_OUTPUT_ENABLE (1 << 16)
static void set_app_pll_init(tileref tile, int app_pll_ctrl)
{
// Disable the PLL
write_node_config_reg(tile, XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (app_pll_ctrl & ~APP_PLL_CTL_ENABLE));
// Enable the PLL to invoke a reset on the appPLL.
write_node_config_reg(tile, XS1_SSWITCH_SS_APP_PLL_CTL_NUM, app_pll_ctrl);
// Must write the CTL register twice so that the F and R divider values are captured using a running clock.
write_node_config_reg(tile, XS1_SSWITCH_SS_APP_PLL_CTL_NUM, app_pll_ctrl);
// Now disable and re-enable the PLL so we get the full 5us reset time with the correct F and R values.
write_node_config_reg(tile, XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (app_pll_ctrl & 0xF7FFFFFF));
write_node_config_reg(tile, XS1_SSWITCH_SS_APP_PLL_CTL_NUM, app_pll_ctrl);
// Wait for PLL to lock.
delay_microseconds(500);
}
void AppPllEnable(tileref tile, int clkFreq_hz)
{
unsigned app_pll_ctrl, app_pll_div, app_pll_frac;
/* Decide on App PLL settings */
if(XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
{
switch(clkFreq_hz)
{
case 44100 * 512:
app_pll_ctrl = APP_PLL_CTL_SYNC_22M;
app_pll_div = APP_PLL_DIV_SYNC_22M;
app_pll_frac = APP_PLL_FRAC_SYNC_22M;
break;
case 48000 * 512:
app_pll_ctrl = APP_PLL_CTL_SYNC_24M;
app_pll_div = APP_PLL_DIV_SYNC_24M;
app_pll_frac = APP_PLL_FRAC_SYNC_24M;
break;
default:
assert(0);
break;
}
}
else
{
switch(clkFreq_hz)
{
case 44100 * 256:
app_pll_ctrl = APP_PLL_CTL_FIXED_11M;
app_pll_div = APP_PLL_DIV_FIXED_11M;
app_pll_frac = APP_PLL_FRAC_FIXED_11M;
break;
case 48000 * 256:
app_pll_ctrl = APP_PLL_CTL_FIXED_12M;
app_pll_div = APP_PLL_DIV_FIXED_12M;
app_pll_frac = APP_PLL_FRAC_FIXED_12M;
break;
case 44100 * 512:
app_pll_ctrl = APP_PLL_CTL_FIXED_22M;
app_pll_div = APP_PLL_DIV_FIXED_22M;
app_pll_frac = APP_PLL_FRAC_FIXED_22M;
break;
case 48000 * 512:
app_pll_ctrl = APP_PLL_CTL_FIXED_24M;
app_pll_div = APP_PLL_DIV_FIXED_24M;
app_pll_frac = APP_PLL_FRAC_FIXED_24M;
break;
case 44100 * 1024:
app_pll_ctrl = APP_PLL_CTL_FIXED_45M;
app_pll_div = APP_PLL_DIV_FIXED_45M;
app_pll_frac = APP_PLL_FRAC_FIXED_45M;
break;
case 48000 * 1024:
app_pll_ctrl = APP_PLL_CTL_FIXED_49M;
app_pll_div = APP_PLL_DIV_FIXED_49M;
app_pll_frac = APP_PLL_FRAC_FIXED_49M;
break;
default:
assert(0);
break;
}
}
// Initialise the AppPLL and get it running.
set_app_pll_init(tile, app_pll_ctrl);
// Write the fractional-n register, note, the top bit is set to enable the frac-n block.
write_node_config_reg(tile, XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, app_pll_frac);
// And then write the clock divider register to enable the output
write_node_config_reg(tile, XS1_SSWITCH_SS_APP_CLK_DIVIDER_NUM, app_pll_div);
// Wait for PLL output frequency to stabilise due to fractional divider enable
delay_microseconds(100);
}
void SoftPllInit(int clkFreq_hz, struct SoftPllState &pllState)
{
switch(clkFreq_hz)
{
case 44100 * 512:
pllState.expectedClkMod = 29184; // Count we expect on MCLK port timer at SW PLL check point. For 100Hz, 10ms.
pllState.initialSetting = APP_PLL_MOD_INIT_22M;
pllState.initialErrorMult = APP_PLL_ERR_MULT_22M;
break;
case 48000 * 512:
pllState.expectedClkMod = 49152;
pllState.initialSetting = APP_PLL_MOD_INIT_24M;
pllState.initialErrorMult = APP_PLL_ERR_MULT_24M;
break;
default:
assert(0);
break;
}
pllState.ds_in = pllState.initialSetting;
pllState.ds_x1 = 0;
pllState.ds_x2 = 0;
pllState.ds_x3 = 0;
pllState.iir_y = 0;
pllState.phaseError = 0;
pllState.phaseErrorInt = 0;
}
int SoftPllUpdate(tileref tile, unsigned short mclk_pt, unsigned short mclk_pt_last, struct SoftPllState &pllState, int fastLock)
{
int freq_error, error_p, error_i;
unsigned expectedClksMod = pllState.expectedClkMod;
unsigned initialSetting = pllState.initialSetting;
unsigned init_err_mult = pllState.initialErrorMult;
int newSetting;
unsigned short expectedPt;
int set = -1;
int diff;
// expectedClkMod is the value of the port counter that we expect given the desired MCLK in the 10ms time period we are running at.
expectedPt = mclk_pt_last + expectedClksMod;
// Handle wrapping
if (porttimeafter(mclk_pt, expectedPt))
{
diff = -(short)(expectedPt - mclk_pt);
}
else
{
diff = (short)(mclk_pt - expectedPt);
}
// TODO Add a bounds checker on diff to make sure it's roughly where we expect.
// If it isn't we should ignore it as it's either a glitch or from clock start/stop.
if(fastLock) // Fast lock - set DCO based on measured frequency error in first cycle
{
initialSetting = initialSetting - (diff * init_err_mult); // init_err_mult is the dco input change to cause an output frequency offset equating to a measured input freq error of 1.
diff = 0; // reset diff to zero so following code does not see any error in this cycle.
}
// Absolute frequency error for last measurement cycle. If diff is positive, port timer was beyond where it should have been, so MCLK was too fast. So this needs to describe a negative error.
freq_error = -diff;
// Phase error is the integral of frequency error.
pllState.phaseError += freq_error;
// Integral of phase error for use in PI loop below.
pllState.phaseErrorInt += pllState.phaseError;
error_p = (pllState.phaseError << 5); // << 5 => Kp = 32
error_i = (pllState.phaseErrorInt >> 2); // >> 2 => Ki = 0.25
// input to filter (x) is output of PI controller
int x = (error_p + error_i);
// Filter some noise into DCO to reduce jitter
// First order IIR, make A=0.125
// y = y + A(x-y)
pllState.iir_y += ((x-pllState.iir_y)>>3);
newSetting = pllState.iir_y;
// Only output new frequency tune value if different to the previous setting
if (newSetting != pllState.setting)
{
set = (initialSetting + newSetting); // init_set is our calculation of the setting required after measuring one cycle, should be accurate to +- 1MCLK.
if (set < 0)
set = 0;
else if (set > 0xFFFFF)
set = 0xFFFFF;
}
pllState.setting = newSetting;
// Return the setting to the NCO thread. -1 means no update
return set;
}
#if (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC)
[[distributable]]
#endif
void XUA_SoftPll(tileref tile, server interface SoftPll_if i_softPll, chanend c_update)
{
#if (XUA_SYNCMODE != XUA_SYNCMODE_ASYNC)
unsigned frac_val;
int ds_out;
timer tmr;
int time;
unsigned mclk_pt;
unsigned short mclk_pt_last;
tmr :> time;
#endif
struct SoftPllState pllState;
int running = 0;
int firstUpdate = 1;
int fastLock = 1;
while(1)
{
select
{
/* Interface used for basic frequency setting such that it can be distributed
* when the update code is not required */
case i_softPll.init(int mclk_hz):
AppPllEnable(tile, mclk_hz);
SoftPllInit(mclk_hz, pllState);
running = 1;
firstUpdate = 1;
fastLock = 1;
break;
#if (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC)
}
}
}
#else
/* Channel used for update such that other side is not blocked */
/* TODO add CT handshake before opening route */
case inuint_byref(c_update, mclk_pt):
inct(c_update);
if(firstUpdate)
{
firstUpdate = 0;
}
else
{
int setting = SoftPllUpdate(tile, (unsigned short) mclk_pt, mclk_pt_last, pllState, fastLock);
fastLock = 0;
if(setting != -1)
{
pllState.ds_in = setting;
// Limit input range for modulator stability.
if(pllState.ds_in > 980000)
pllState.ds_in = 980000;
if(pllState.ds_in < 60000)
pllState.ds_in = 60000;
}
}
mclk_pt_last = (unsigned short) mclk_pt;
break;
default :
break;
}
// Third order, 9 level output delta sigma. 20 bit unsigned input.
ds_out = ((pllState.ds_x3<<4) + (pllState.ds_x3<<1)) >> 13;
if (ds_out > 8)
ds_out = 8;
if (ds_out < 0)
ds_out = 0;
pllState.ds_x3 += (pllState.ds_x2>>5) - (ds_out<<9) - (ds_out<<8);
pllState.ds_x2 += (pllState.ds_x1>>5) - (ds_out<<14);
pllState.ds_x1 += pllState.ds_in - (ds_out<<17);
if (ds_out == 0)
frac_val = 0x00000007; // 0/8
else
frac_val = ((ds_out - 1) << 8) | 0x80000007; // 1/8 to 8/8
// Now write the register.
// We need to write the register at a specific period at a fast rate.
// This period needs to be (div ref clk period (ns) * how many times we repeat same value)
// In this case, div ref clk = 24/3 = 8MHz. So div ref clk period = 125ns.
// We're using fraction denominators of 8, so these repeat every 8*125ns = 1us.
// So minimum period we could use is 1us and multiples thereof.
// The slower we write, the higher our jitter will be.
time += FRAC_REG_WRITE_DLY; // Time the reg write.
tmr when timerafter(time) :> void;
// Write the register. Because we are timing the reg writes accurately we do not need to use reg write with ack.
// This saves a lot of time. Additionally, apparently we can shorten the time for this reg write by only setting up the channel once and just doing a few instructions to do the write each time.
// We can hard code this in assembler.
if(running)
{
write_node_config_reg_no_ack(tile, XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, frac_val);
}
}
}
#endif
#endif

View File

@@ -1,5 +1,6 @@
// Copyright 2011-2024 XMOS LIMITED.
// Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h>
#include <assert.h>
#include <print.h>
@@ -25,11 +26,11 @@ unsigned g_digData[10];
typedef struct
{
int receivedSamples; /* Uses by clockgen to count number of dig rx samples to ascertain clock specs */
int samples; /* Raw sample count - rolling int and never reset */
int savedSamples; /* Used by validSamples() to store state of last raw sample count */
int lastDiff; /* Used by validSamples() to store state of last sample count diff */
unsigned identicaldiffs; /* Used by validSamples() to store state of number of identical diffs */
int receivedSamples;
int samples;
int savedSamples;
int lastDiff;
unsigned identicaldiffs;
int samplesPerTick;
} Counter;
@@ -38,7 +39,6 @@ static int clockValid[NUM_CLOCKS]; /* Store current val
static int clockInt[NUM_CLOCKS]; /* Interupt flag for clocks */
static int clockId[NUM_CLOCKS];
[[distributable]]
void PllRefPinTask(server interface pll_ref_if i_pll_ref, out port p_pll_ref)
{
@@ -88,10 +88,27 @@ static int abs(int x)
return x;
}
static int channelContainsControlToken(chanend x)
{
unsigned char tmpc;
select
{
case inct_byref(x, tmpc):
return 1;
default:
return 0;
}
}
static void outInterrupt(chanend c_interruptControl, int value)
{
outuint(c_interruptControl, value);
outct(c_interruptControl, XS1_CT_END);
/* Non-blocking check for control token */
//if (channelContainsControlToken(c_interruptControl))
{
outuint(c_interruptControl, value);
outct(c_interruptControl, XS1_CT_END);
}
}
#endif
@@ -124,6 +141,9 @@ static inline void setClockValidity(chanend c_interruptControl, int clkIndex, in
}
}
/* Returns 1 for valid clock found else 0 */
static inline int validSamples(Counter &counter, int clockIndex)
{
@@ -190,7 +210,7 @@ static inline int validSamples(Counter &counter, int clockIndex)
}
}
}
else /* No valid frequency found - reset state */
else
{
counter.identicaldiffs = 0;
counter.lastDiff = diff;
@@ -199,11 +219,16 @@ static inline int validSamples(Counter &counter, int clockIndex)
}
#endif
#if XUA_USE_SW_PLL
unsafe
#if (XUA_SPDIF_RX_EN)
//:badParity
/* Returns 1 for bad parity, else 0 */
static inline int badParity(unsigned x)
{
unsigned * unsafe selected_mclk_rate_ptr = NULL;
unsigned X = (x>>4);
crc32(X, 0, 1);
return X & 1;
}
//:
#endif
#ifdef LEVEL_METER_LEDS
@@ -216,42 +241,15 @@ extern int samples_to_host_inputs_buff[NUM_USB_CHAN_IN];
int VendorAudCoreReqs(unsigned cmd, chanend c);
#pragma unsafe arrays
void clockGen ( streaming chanend ?c_spdif_rx,
chanend ?c_adat_rx,
client interface pll_ref_if i_pll_ref,
chanend c_dig_rx,
chanend c_clk_ctl,
chanend c_clk_int,
chanend c_audio_rate_change
#if XUA_USE_SW_PLL
, port p_for_mclk_count_aud
, chanend c_sw_pll
#endif
)
void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interface pll_ref_if i_pll_ref, chanend c_dig_rx, chanend c_clk_ctl, chanend c_clk_int)
{
timer t_local;
unsigned timeNextEdge, timeLastEdge, timeNextClockDetection;
unsigned clkMode = CLOCK_INTERNAL; /* Current clocking mode in operation */
unsigned tmp;
/* Start in no-SMUX (8-channel) mode */
int smux;
// Initialise smux based based on the DEFAULT_FREQ
if(DEFAULT_FREQ < 88200)
{
/* No SMUX */
smux = 0;
}
else if(DEFAULT_FREQ < 176400)
{
/* SMUX */
smux = 1;
}
else
{
/* SMUX II */
smux = 2;
}
/* start in no-SMUX (8-channel) mode */
int smux = 0;
#ifdef LEVEL_METER_LEDS
timer t_level;
@@ -260,31 +258,20 @@ void clockGen ( streaming chanend ?c_spdif_rx,
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
timer t_external;
unsigned selected_mclk_rate = MCLK_48; // Assume 24.576MHz initial clock
unsigned selected_sample_rate = 0;
#if XUA_USE_SW_PLL
unsigned mclks_per_sample = 0;
unsigned short mclk_time_stamp = 0;
/* Get MCLK count */
asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud));
#endif
#endif
#if (XUA_SPDIF_RX_EN)
/* S/PDIF buffer state */
int spdifSamples[MAX_SPDIF_SAMPLES]; /* S/PDIF sample buffer */
int spdifWr = 0; /* Write index */
int spdifRd = 0; /* Read index */ //(spdifWriteIndex ^ (MAX_SPDIF_SAMPLES >> 1)) & ~1; // Start in middle
int spdifOverflow = 0; /* Overflow/undeflow flags */
int spdifSamples[MAX_SPDIF_SAMPLES]; /* S/PDIF sample buffer */
int spdifWr = 0; /* Write index */
int spdifRd = 0; /* Read index */ //(spdifWriteIndex ^ (MAX_SPDIF_SAMPLES >> 1)) & ~1; // Start in middle
int spdifOverflow = 0; /* Overflow/undeflow flags */
int spdifUnderflow = 1;
int spdifSamps = 0; /* Number of samples in buffer */
Counter spdifCounters;
int spdifRxTime;
int spdifReceivedTime;
unsigned tmp2;
unsigned spdifLeft = 0;
unsigned spdifRxData;
#endif
#if (XUA_ADAT_RX_EN)
@@ -309,21 +296,21 @@ void clockGen ( streaming chanend ?c_spdif_rx,
}
/* Init clock unit state */
clockFreq[CLOCK_INTERNAL] = 0;
clockId[CLOCK_INTERNAL] = ID_CLKSRC_INT;
clockValid[CLOCK_INTERNAL] = 0;
clockInt[CLOCK_INTERNAL] = 0;
#if (XUA_SPDIF_RX_EN)
clockFreq[CLOCK_SPDIF] = 0;
clockValid[CLOCK_SPDIF] = 0;
clockInt[CLOCK_SPDIF] = 0;
clockId[CLOCK_SPDIF] = ID_CLKSRC_SPDIF;
clockFreq[CLOCK_SPDIF_INDEX] = 0;
clockValid[CLOCK_SPDIF_INDEX] = 0;
clockInt[CLOCK_SPDIF_INDEX] = 0;
clockId[CLOCK_SPDIF_INDEX] = ID_CLKSRC_SPDIF;
#endif
clockFreq[CLOCK_INTERNAL_INDEX] = 0;
clockId[CLOCK_INTERNAL_INDEX] = ID_CLKSRC_INT;
clockValid[CLOCK_INTERNAL_INDEX] = 0;
clockInt[CLOCK_INTERNAL_INDEX] = 0;
#if (XUA_ADAT_RX_EN)
clockFreq[CLOCK_ADAT] = 0;
clockInt[CLOCK_ADAT] = 0;
clockValid[CLOCK_ADAT] = 0;
clockId[CLOCK_ADAT] = ID_CLKSRC_ADAT;
clockFreq[CLOCK_ADAT_INDEX] = 0;
clockInt[CLOCK_ADAT_INDEX] = 0;
clockValid[CLOCK_ADAT_INDEX] = 0;
clockId[CLOCK_ADAT_INDEX] = ID_CLKSRC_ADAT;
#endif
#if (XUA_SPDIF_RX_EN)
spdifCounters.receivedSamples = 0;
@@ -343,6 +330,7 @@ void clockGen ( streaming chanend ?c_spdif_rx,
adatCounters.samplesPerTick = 0;
#endif
t_local :> timeNextEdge;
timeLastEdge = timeNextEdge;
timeNextClockDetection = timeNextEdge + (LOCAL_CLOCK_INCREMENT / 2);
@@ -361,12 +349,6 @@ void clockGen ( streaming chanend ?c_spdif_rx,
/* Initial ref clock output and get timestamp */
i_pll_ref.init();
#if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL)
int reset_sw_pll_pfd = 1;
int require_ack_to_audio = 0;
restart_sigma_delta(c_sw_pll, MCLK_48); /* default to 48kHz - this will be reset shortly when host selects rate */
#endif
while(1)
{
select
@@ -408,8 +390,8 @@ void clockGen ( streaming chanend ?c_spdif_rx,
break;
#endif
/* Updates to clock settings from endpoint 0 */
case inuint_byref(c_clk_ctl, tmp):
/* Updates to clock settings from endpoint 0 */
case inuint_byref(c_clk_ctl, tmp):
switch(tmp)
{
case GET_SEL:
@@ -423,9 +405,13 @@ void clockGen ( streaming chanend ?c_spdif_rx,
case SET_SEL:
/* Update clock mode */
clkMode = inuint(c_clk_ctl);
tmp = inuint(c_clk_ctl);
chkct(c_clk_ctl, XS1_CT_END);
if(tmp!=0)
{
clkMode = tmp;
}
#ifdef CLOCK_VALIDITY_CALL
switch(clkMode)
{
@@ -434,12 +420,12 @@ void clockGen ( streaming chanend ?c_spdif_rx,
break;
#if (XUA_ADAT_RX_EN)
case CLOCK_ADAT:
VendorClockValidity(clockValid[CLOCK_ADAT]);
VendorClockValidity(clockValid[CLOCK_ADAT_INDEX]);
break;
#endif
#if (XUA_SPDIF_RX_EN)
case CLOCK_SPDIF:
VendorClockValidity(clockValid[CLOCK_SPDIF]);
VendorClockValidity(clockValid[CLOCK_SPDIF_INDEX]);
break;
#endif
}
@@ -479,17 +465,14 @@ void clockGen ( streaming chanend ?c_spdif_rx,
break;
}
break;
break;
/* Generate local clock from timer */
case t_local when timerafter(timeNextEdge) :> void:
#if XUA_USE_SW_PLL
/* Do nothing - hold the most recent sw_pll setting */
#else
/* Setup next local clock edge */
i_pll_ref.toggle_timed(0);
#endif
/* Record time of edge */
timeLastEdge = timeNextEdge;
@@ -516,89 +499,58 @@ void clockGen ( streaming chanend ?c_spdif_rx,
#endif
break;
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
case t_external when timerafter(timeNextClockDetection) :> void:
{
int valid;
timeNextClockDetection += (LOCAL_CLOCK_INCREMENT);
timeNextClockDetection += (LOCAL_CLOCK_INCREMENT);
#if (XUA_SPDIF_RX_EN)
/* Returns 1 if valid clock found */
valid = validSamples(spdifCounters, CLOCK_SPDIF);
setClockValidity(c_clk_int, CLOCK_SPDIF, valid, clkMode);
tmp = spdifCounters.samplesPerTick;
/* Returns 1 if valid clock found */
tmp = validSamples(spdifCounters, CLOCK_SPDIF_INDEX);
setClockValidity(c_clk_int, CLOCK_SPDIF_INDEX, tmp, clkMode);
#endif
#if (XUA_ADAT_RX_EN)
/* Returns 1 if valid clock found */
valid = validSamples(adatCounters, CLOCK_ADAT);
setClockValidity(c_clk_int, CLOCK_ADAT, valid, clkMode);
#endif
}
break;
tmp = validSamples(adatCounters, CLOCK_ADAT_INDEX);
setClockValidity(c_clk_int, CLOCK_ADAT_INDEX, tmp, clkMode);
#endif
#if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL)
case inuint_byref(c_sw_pll, tmp):
inct(c_sw_pll);
/* Send ACK back to audiohub to allow I2S to start
This happens only on SDM restart and only once */
if(require_ack_to_audio)
{
c_audio_rate_change <: tmp;
require_ack_to_audio = 0;
}
break;
#endif
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
/* Receive notification of audio streaming settings change and store */
case c_audio_rate_change :> selected_mclk_rate:
c_audio_rate_change :> selected_sample_rate;
#if XUA_USE_SW_PLL
mclks_per_sample = selected_mclk_rate / selected_sample_rate;
restart_sigma_delta(c_sw_pll, selected_mclk_rate);
reset_sw_pll_pfd = 1;
/* We will shedule an ACK when sigma delta is up and running */
require_ack_to_audio = 1;
#else
/* Send ACK immediately as we are good to go if not using SW_PLL */
c_audio_rate_change <: 0;
#endif
break;
#endif
#if (XUA_SPDIF_RX_EN)
/* Receive sample from S/PDIF RX thread (streaming chan) */
case c_spdif_rx :> spdifRxData:
/* Receive sample from S/PDIF RX thread (steaming chan) */
case c_spdif_rx :> tmp:
#if XUA_USE_SW_PLL
/* Record time of sample */
asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud));
#endif
t_local :> spdifRxTime;
t_local :> spdifReceivedTime;
/* Check parity and ignore if bad */
if(spdif_rx_check_parity(spdifRxData))
if(badParity(tmp))
continue;
/* Get preamble */
unsigned preamble = spdifRxData & SPDIF_RX_PREAMBLE_MASK;
switch(preamble)
/* Get pre-amble */
tmp2 = tmp & 0xF;
switch(tmp2)
{
/* LEFT */
case SPDIF_FRAME_X:
case SPDIF_FRAME_Z:
spdifLeft = SPDIF_RX_EXTRACT_SAMPLE(spdifRxData);
spdifLeft = tmp << 4;
break;
/* RIGHT */
case SPDIF_FRAME_Y:
/* Only store sample if not in overflow and stream is reasonably valid */
if(!spdifOverflow && clockValid[CLOCK_SPDIF])
if(!spdifOverflow && clockValid[CLOCK_SPDIF_INDEX])
{
/* Store left and right sample pair to buffer */
spdifSamples[spdifWr] = spdifLeft;
spdifSamples[spdifWr+1] = SPDIF_RX_EXTRACT_SAMPLE(spdifRxData);
spdifSamples[spdifWr+1] = tmp << 4;
spdifWr = (spdifWr + 2) & (MAX_SPDIF_SAMPLES - 1);
@@ -626,7 +578,7 @@ void clockGen ( streaming chanend ?c_spdif_rx,
spdifCounters.samples += 1;
if(clkMode == CLOCK_SPDIF && clockValid[CLOCK_SPDIF])
if(clkMode == CLOCK_SPDIF && clockValid[CLOCK_SPDIF_INDEX])
{
spdifCounters.receivedSamples+=1;
@@ -634,24 +586,17 @@ void clockGen ( streaming chanend ?c_spdif_rx,
if((spdifCounters.receivedSamples >= spdifCounters.samplesPerTick))
{
/* Check edge is about right... S/PDIF may have changed freq... */
if(timeafter(spdifRxTime, (timeLastEdge + LOCAL_CLOCK_INCREMENT - LOCAL_CLOCK_MARGIN)))
if(timeafter(spdifReceivedTime, (timeLastEdge + LOCAL_CLOCK_INCREMENT - LOCAL_CLOCK_MARGIN)))
{
/* Record edge time */
timeLastEdge = spdifRxTime;
timeLastEdge = spdifReceivedTime;
/* Setup for next edge */
timeNextEdge = spdifRxTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN;
timeNextEdge = spdifReceivedTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN;
#if XUA_USE_SW_PLL
do_sw_pll_phase_frequency_detector_dig_rx( mclk_time_stamp,
mclks_per_sample,
c_sw_pll,
spdifCounters.receivedSamples,
reset_sw_pll_pfd);
#else
/* Toggle edge */
i_pll_ref.toggle_timed(1);
#endif
/* Reset counters */
spdifCounters.receivedSamples = 0;
}
@@ -662,11 +607,7 @@ void clockGen ( streaming chanend ?c_spdif_rx,
#if (XUA_ADAT_RX_EN)
/* receive sample from ADAT rx thread (streaming channel with CT_END) */
case inuint_byref(c_adat_rx, tmp):
#if XUA_USE_SW_PLL
/* record time of sample */
asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud));
#endif
t_local :> adatReceivedTime;
/* Sync is: 1 | (user_byte << 4) */
@@ -686,7 +627,7 @@ void clockGen ( streaming chanend ?c_spdif_rx,
if (adatChannel == 8)
{
/* only store left samples if not in overflow and stream is reasonably valid */
if (!adatOverflow && clockValid[CLOCK_ADAT])
if (!adatOverflow && clockValid[CLOCK_ADAT_INDEX])
{
/* Unpick the SMUX.. */
if(smux == 2)
@@ -739,18 +680,11 @@ void clockGen ( streaming chanend ?c_spdif_rx,
}
}
}
/* An edge needs to be recorded/toggled in the following cases:
* smux = 0: adatChannel = 4, 8
* smux = 1: adatChannel = 2, 4, 6, 8
* smux = 2: adatChannel = 1, 2, 3, 4, 5, 6, 7, 8
* This is simplified to a shift-and-mask in the if-condition below.
*/
if ((adatChannel != 0) && ((adatChannel << smux) & 3) == 0)
if(adatChannel == 4 || adatChannel == 8)
{
adatCounters.samples += 1;
if (clkMode == CLOCK_ADAT && clockValid[CLOCK_ADAT])
if (clkMode == CLOCK_ADAT && clockValid[CLOCK_ADAT_INDEX])
{
adatCounters.receivedSamples += 1;
@@ -766,19 +700,12 @@ void clockGen ( streaming chanend ?c_spdif_rx,
/* Setup for next edge */
timeNextEdge = adatReceivedTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN;
#if XUA_USE_SW_PLL
do_sw_pll_phase_frequency_detector_dig_rx( mclk_time_stamp,
mclks_per_sample,
c_sw_pll,
adatCounters.receivedSamples,
reset_sw_pll_pfd);
#else
/* Toggle edge */
i_pll_ref.toggle_timed(1);
#endif
/* Reset counters */
adatCounters.receivedSamples = 0;
}
}
}
@@ -787,11 +714,12 @@ void clockGen ( streaming chanend ?c_spdif_rx,
adatChannel = 0;
}
break;
#endif // XUA_ADAT_RX_EN
#endif
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
/* AudioHub requests data */
case inuint_byref(c_dig_rx, tmp):
/* AudioHub requests data */
case inuint_byref(c_dig_rx, tmp):
#if (XUA_SPDIF_RX_EN)
if(spdifUnderflow)
{
@@ -806,7 +734,7 @@ void clockGen ( streaming chanend ?c_spdif_rx,
tmp2 = spdifSamples[spdifRd + 1];
spdifRd += 2;
spdifRd &= (MAX_SPDIF_SAMPLES - 1);
spdifRd &= (MAX_SPDIF_SAMPLES - 1);
g_digData[0] = tmp;
g_digData[1] = tmp2;
@@ -814,7 +742,7 @@ void clockGen ( streaming chanend ?c_spdif_rx,
spdifSamps -= 2;
/* spdifSamps could go to -1 */
if(spdifSamps <= 0)
if(spdifSamps < 0)
{
/* We're out of S/PDIF samples, mark underflow condition */
spdifUnderflow = 1;
@@ -828,6 +756,7 @@ void clockGen ( streaming chanend ?c_spdif_rx,
spdifOverflow = 0;
}
}
#endif
#if (XUA_ADAT_RX_EN)
if (adatUnderflow)
@@ -895,7 +824,7 @@ void clockGen ( streaming chanend ?c_spdif_rx,
}
/* adatSamps could go to -1 */
if (adatSamps <= 0)
if (adatSamps < 0)
{
/* we're out of ADAT samples, mark underflow condition */
adatUnderflow = 1;
@@ -910,9 +839,11 @@ void clockGen ( streaming chanend ?c_spdif_rx,
}
#endif
outuint(c_dig_rx, 1);
break;
break;
#endif
} /* select */
} /* while(1) */
} /* clkgen task scope */
}
}
}

View File

@@ -0,0 +1,398 @@
// Copyright 2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
// Header file listing fraction options searched
// These values to go in the bottom 16 bits of the secondary PLL fractional-n divider register.
short frac_values_80[391] = {
0x3C4B, // Index: 0 Fraction: 61/76 = 0.8026
0x3846, // Index: 1 Fraction: 57/71 = 0.8028
0x3441, // Index: 2 Fraction: 53/66 = 0.8030
0x303C, // Index: 3 Fraction: 49/61 = 0.8033
0x2C37, // Index: 4 Fraction: 45/56 = 0.8036
0x2832, // Index: 5 Fraction: 41/51 = 0.8039
0x242D, // Index: 6 Fraction: 37/46 = 0.8043
0x2028, // Index: 7 Fraction: 33/41 = 0.8049
0x3D4C, // Index: 8 Fraction: 62/77 = 0.8052
0x1C23, // Index: 9 Fraction: 29/36 = 0.8056
0x3542, // Index: 10 Fraction: 54/67 = 0.8060
0x181E, // Index: 11 Fraction: 25/31 = 0.8065
0x2D38, // Index: 12 Fraction: 46/57 = 0.8070
0x1419, // Index: 13 Fraction: 21/26 = 0.8077
0x3A48, // Index: 14 Fraction: 59/73 = 0.8082
0x252E, // Index: 15 Fraction: 38/47 = 0.8085
0x3643, // Index: 16 Fraction: 55/68 = 0.8088
0x1014, // Index: 17 Fraction: 17/21 = 0.8095
0x3F4E, // Index: 18 Fraction: 64/79 = 0.8101
0x2E39, // Index: 19 Fraction: 47/58 = 0.8103
0x1D24, // Index: 20 Fraction: 30/37 = 0.8108
0x2A34, // Index: 21 Fraction: 43/53 = 0.8113
0x3744, // Index: 22 Fraction: 56/69 = 0.8116
0x0C0F, // Index: 23 Fraction: 13/16 = 0.8125
0x3C4A, // Index: 24 Fraction: 61/75 = 0.8133
0x2F3A, // Index: 25 Fraction: 48/59 = 0.8136
0x222A, // Index: 26 Fraction: 35/43 = 0.8140
0x3845, // Index: 27 Fraction: 57/70 = 0.8143
0x151A, // Index: 28 Fraction: 22/27 = 0.8148
0x3440, // Index: 29 Fraction: 53/65 = 0.8154
0x1E25, // Index: 30 Fraction: 31/38 = 0.8158
0x2730, // Index: 31 Fraction: 40/49 = 0.8163
0x303B, // Index: 32 Fraction: 49/60 = 0.8167
0x3946, // Index: 33 Fraction: 58/71 = 0.8169
0x080A, // Index: 34 Fraction: 9/11 = 0.8182
0x3A47, // Index: 35 Fraction: 59/72 = 0.8194
0x313C, // Index: 36 Fraction: 50/61 = 0.8197
0x2831, // Index: 37 Fraction: 41/50 = 0.8200
0x1F26, // Index: 38 Fraction: 32/39 = 0.8205
0x3642, // Index: 39 Fraction: 55/67 = 0.8209
0x161B, // Index: 40 Fraction: 23/28 = 0.8214
0x3B48, // Index: 41 Fraction: 60/73 = 0.8219
0x242C, // Index: 42 Fraction: 37/45 = 0.8222
0x323D, // Index: 43 Fraction: 51/62 = 0.8226
0x404E, // Index: 44 Fraction: 65/79 = 0.8228
0x0D10, // Index: 45 Fraction: 14/17 = 0.8235
0x3C49, // Index: 46 Fraction: 61/74 = 0.8243
0x2E38, // Index: 47 Fraction: 47/57 = 0.8246
0x2027, // Index: 48 Fraction: 33/40 = 0.8250
0x333E, // Index: 49 Fraction: 52/63 = 0.8254
0x1216, // Index: 50 Fraction: 19/23 = 0.8261
0x3D4A, // Index: 51 Fraction: 62/75 = 0.8267
0x2A33, // Index: 52 Fraction: 43/52 = 0.8269
0x171C, // Index: 53 Fraction: 24/29 = 0.8276
0x343F, // Index: 54 Fraction: 53/64 = 0.8281
0x1C22, // Index: 55 Fraction: 29/35 = 0.8286
0x3E4B, // Index: 56 Fraction: 63/76 = 0.8289
0x2128, // Index: 57 Fraction: 34/41 = 0.8293
0x262E, // Index: 58 Fraction: 39/47 = 0.8298
0x2B34, // Index: 59 Fraction: 44/53 = 0.8302
0x303A, // Index: 60 Fraction: 49/59 = 0.8305
0x3540, // Index: 61 Fraction: 54/65 = 0.8308
0x3A46, // Index: 62 Fraction: 59/71 = 0.8310
0x3F4C, // Index: 63 Fraction: 64/77 = 0.8312
0x0405, // Index: 64 Fraction: 5/6 = 0.8333
0x414E, // Index: 65 Fraction: 66/79 = 0.8354
0x3C48, // Index: 66 Fraction: 61/73 = 0.8356
0x3742, // Index: 67 Fraction: 56/67 = 0.8358
0x323C, // Index: 68 Fraction: 51/61 = 0.8361
0x2D36, // Index: 69 Fraction: 46/55 = 0.8364
0x2830, // Index: 70 Fraction: 41/49 = 0.8367
0x232A, // Index: 71 Fraction: 36/43 = 0.8372
0x424F, // Index: 72 Fraction: 67/80 = 0.8375
0x1E24, // Index: 73 Fraction: 31/37 = 0.8378
0x3843, // Index: 74 Fraction: 57/68 = 0.8382
0x191E, // Index: 75 Fraction: 26/31 = 0.8387
0x2E37, // Index: 76 Fraction: 47/56 = 0.8393
0x1418, // Index: 77 Fraction: 21/25 = 0.8400
0x3944, // Index: 78 Fraction: 58/69 = 0.8406
0x242B, // Index: 79 Fraction: 37/44 = 0.8409
0x343E, // Index: 80 Fraction: 53/63 = 0.8413
0x0F12, // Index: 81 Fraction: 16/19 = 0.8421
0x3A45, // Index: 82 Fraction: 59/70 = 0.8429
0x2A32, // Index: 83 Fraction: 43/51 = 0.8431
0x1A1F, // Index: 84 Fraction: 27/32 = 0.8438
0x404C, // Index: 85 Fraction: 65/77 = 0.8442
0x252C, // Index: 86 Fraction: 38/45 = 0.8444
0x3039, // Index: 87 Fraction: 49/58 = 0.8448
0x3B46, // Index: 88 Fraction: 60/71 = 0.8451
0x0A0C, // Index: 89 Fraction: 11/13 = 0.8462
0x3C47, // Index: 90 Fraction: 61/72 = 0.8472
0x313A, // Index: 91 Fraction: 50/59 = 0.8475
0x262D, // Index: 92 Fraction: 39/46 = 0.8478
0x424E, // Index: 93 Fraction: 67/79 = 0.8481
0x1B20, // Index: 94 Fraction: 28/33 = 0.8485
0x2C34, // Index: 95 Fraction: 45/53 = 0.8491
0x3D48, // Index: 96 Fraction: 62/73 = 0.8493
0x1013, // Index: 97 Fraction: 17/20 = 0.8500
0x3842, // Index: 98 Fraction: 57/67 = 0.8507
0x272E, // Index: 99 Fraction: 40/47 = 0.8511
0x3E49, // Index: 100 Fraction: 63/74 = 0.8514
0x161A, // Index: 101 Fraction: 23/27 = 0.8519
0x333C, // Index: 102 Fraction: 52/61 = 0.8525
0x1C21, // Index: 103 Fraction: 29/34 = 0.8529
0x3F4A, // Index: 104 Fraction: 64/75 = 0.8533
0x2228, // Index: 105 Fraction: 35/41 = 0.8537
0x282F, // Index: 106 Fraction: 41/48 = 0.8542
0x2E36, // Index: 107 Fraction: 47/55 = 0.8545
0x343D, // Index: 108 Fraction: 53/62 = 0.8548
0x3A44, // Index: 109 Fraction: 59/69 = 0.8551
0x404B, // Index: 110 Fraction: 65/76 = 0.8553
0x0506, // Index: 111 Fraction: 6/7 = 0.8571
0x424D, // Index: 112 Fraction: 67/78 = 0.8590
0x3C46, // Index: 113 Fraction: 61/71 = 0.8592
0x363F, // Index: 114 Fraction: 55/64 = 0.8594
0x3038, // Index: 115 Fraction: 49/57 = 0.8596
0x2A31, // Index: 116 Fraction: 43/50 = 0.8600
0x242A, // Index: 117 Fraction: 37/43 = 0.8605
0x434E, // Index: 118 Fraction: 68/79 = 0.8608
0x1E23, // Index: 119 Fraction: 31/36 = 0.8611
0x3740, // Index: 120 Fraction: 56/65 = 0.8615
0x181C, // Index: 121 Fraction: 25/29 = 0.8621
0x444F, // Index: 122 Fraction: 69/80 = 0.8625
0x2B32, // Index: 123 Fraction: 44/51 = 0.8627
0x3E48, // Index: 124 Fraction: 63/73 = 0.8630
0x1215, // Index: 125 Fraction: 19/22 = 0.8636
0x323A, // Index: 126 Fraction: 51/59 = 0.8644
0x1F24, // Index: 127 Fraction: 32/37 = 0.8649
0x2C33, // Index: 128 Fraction: 45/52 = 0.8654
0x3942, // Index: 129 Fraction: 58/67 = 0.8657
0x0C0E, // Index: 130 Fraction: 13/15 = 0.8667
0x3A43, // Index: 131 Fraction: 59/68 = 0.8676
0x2D34, // Index: 132 Fraction: 46/53 = 0.8679
0x2025, // Index: 133 Fraction: 33/38 = 0.8684
0x343C, // Index: 134 Fraction: 53/61 = 0.8689
0x1316, // Index: 135 Fraction: 20/23 = 0.8696
0x424C, // Index: 136 Fraction: 67/77 = 0.8701
0x2E35, // Index: 137 Fraction: 47/54 = 0.8704
0x1A1E, // Index: 138 Fraction: 27/31 = 0.8710
0x3C45, // Index: 139 Fraction: 61/70 = 0.8714
0x2126, // Index: 140 Fraction: 34/39 = 0.8718
0x282E, // Index: 141 Fraction: 41/47 = 0.8723
0x2F36, // Index: 142 Fraction: 48/55 = 0.8727
0x363E, // Index: 143 Fraction: 55/63 = 0.8730
0x3D46, // Index: 144 Fraction: 62/71 = 0.8732
0x444E, // Index: 145 Fraction: 69/79 = 0.8734
0x0607, // Index: 146 Fraction: 7/8 = 0.8750
0x3F48, // Index: 147 Fraction: 64/73 = 0.8767
0x3840, // Index: 148 Fraction: 57/65 = 0.8769
0x3138, // Index: 149 Fraction: 50/57 = 0.8772
0x2A30, // Index: 150 Fraction: 43/49 = 0.8776
0x2328, // Index: 151 Fraction: 36/41 = 0.8780
0x4049, // Index: 152 Fraction: 65/74 = 0.8784
0x1C20, // Index: 153 Fraction: 29/33 = 0.8788
0x3239, // Index: 154 Fraction: 51/58 = 0.8793
0x1518, // Index: 155 Fraction: 22/25 = 0.8800
0x3A42, // Index: 156 Fraction: 59/67 = 0.8806
0x2429, // Index: 157 Fraction: 37/42 = 0.8810
0x333A, // Index: 158 Fraction: 52/59 = 0.8814
0x424B, // Index: 159 Fraction: 67/76 = 0.8816
0x0E10, // Index: 160 Fraction: 15/17 = 0.8824
0x434C, // Index: 161 Fraction: 68/77 = 0.8831
0x343B, // Index: 162 Fraction: 53/60 = 0.8833
0x252A, // Index: 163 Fraction: 38/43 = 0.8837
0x3C44, // Index: 164 Fraction: 61/69 = 0.8841
0x1619, // Index: 165 Fraction: 23/26 = 0.8846
0x353C, // Index: 166 Fraction: 54/61 = 0.8852
0x1E22, // Index: 167 Fraction: 31/35 = 0.8857
0x454E, // Index: 168 Fraction: 70/79 = 0.8861
0x262B, // Index: 169 Fraction: 39/44 = 0.8864
0x2E34, // Index: 170 Fraction: 47/53 = 0.8868
0x363D, // Index: 171 Fraction: 55/62 = 0.8871
0x3E46, // Index: 172 Fraction: 63/71 = 0.8873
0x464F, // Index: 173 Fraction: 71/80 = 0.8875
0x0708, // Index: 174 Fraction: 8/9 = 0.8889
0x4048, // Index: 175 Fraction: 65/73 = 0.8904
0x383F, // Index: 176 Fraction: 57/64 = 0.8906
0x3036, // Index: 177 Fraction: 49/55 = 0.8909
0x282D, // Index: 178 Fraction: 41/46 = 0.8913
0x2024, // Index: 179 Fraction: 33/37 = 0.8919
0x3940, // Index: 180 Fraction: 58/65 = 0.8923
0x181B, // Index: 181 Fraction: 25/28 = 0.8929
0x424A, // Index: 182 Fraction: 67/75 = 0.8933
0x292E, // Index: 183 Fraction: 42/47 = 0.8936
0x3A41, // Index: 184 Fraction: 59/66 = 0.8939
0x1012, // Index: 185 Fraction: 17/19 = 0.8947
0x3B42, // Index: 186 Fraction: 60/67 = 0.8955
0x2A2F, // Index: 187 Fraction: 43/48 = 0.8958
0x444C, // Index: 188 Fraction: 69/77 = 0.8961
0x191C, // Index: 189 Fraction: 26/29 = 0.8966
0x3C43, // Index: 190 Fraction: 61/68 = 0.8971
0x2226, // Index: 191 Fraction: 35/39 = 0.8974
0x2B30, // Index: 192 Fraction: 44/49 = 0.8980
0x343A, // Index: 193 Fraction: 53/59 = 0.8983
0x3D44, // Index: 194 Fraction: 62/69 = 0.8986
0x464E, // Index: 195 Fraction: 71/79 = 0.8987
0x0809, // Index: 196 Fraction: 9/10 = 0.9000
0x3F46, // Index: 197 Fraction: 64/71 = 0.9014
0x363C, // Index: 198 Fraction: 55/61 = 0.9016
0x2D32, // Index: 199 Fraction: 46/51 = 0.9020
0x2428, // Index: 200 Fraction: 37/41 = 0.9024
0x4047, // Index: 201 Fraction: 65/72 = 0.9028
0x1B1E, // Index: 202 Fraction: 28/31 = 0.9032
0x2E33, // Index: 203 Fraction: 47/52 = 0.9038
0x4148, // Index: 204 Fraction: 66/73 = 0.9041
0x1214, // Index: 205 Fraction: 19/21 = 0.9048
0x4249, // Index: 206 Fraction: 67/74 = 0.9054
0x2F34, // Index: 207 Fraction: 48/53 = 0.9057
0x1C1F, // Index: 208 Fraction: 29/32 = 0.9062
0x434A, // Index: 209 Fraction: 68/75 = 0.9067
0x262A, // Index: 210 Fraction: 39/43 = 0.9070
0x3035, // Index: 211 Fraction: 49/54 = 0.9074
0x3A40, // Index: 212 Fraction: 59/65 = 0.9077
0x444B, // Index: 213 Fraction: 69/76 = 0.9079
0x090A, // Index: 214 Fraction: 10/11 = 0.9091
0x464D, // Index: 215 Fraction: 71/78 = 0.9103
0x3C42, // Index: 216 Fraction: 61/67 = 0.9104
0x3237, // Index: 217 Fraction: 51/56 = 0.9107
0x282C, // Index: 218 Fraction: 41/45 = 0.9111
0x474E, // Index: 219 Fraction: 72/79 = 0.9114
0x1E21, // Index: 220 Fraction: 31/34 = 0.9118
0x3338, // Index: 221 Fraction: 52/57 = 0.9123
0x484F, // Index: 222 Fraction: 73/80 = 0.9125
0x1416, // Index: 223 Fraction: 21/23 = 0.9130
0x3439, // Index: 224 Fraction: 53/58 = 0.9138
0x1F22, // Index: 225 Fraction: 32/35 = 0.9143
0x2A2E, // Index: 226 Fraction: 43/47 = 0.9149
0x353A, // Index: 227 Fraction: 54/59 = 0.9153
0x4046, // Index: 228 Fraction: 65/71 = 0.9155
0x0A0B, // Index: 229 Fraction: 11/12 = 0.9167
0x4248, // Index: 230 Fraction: 67/73 = 0.9178
0x373C, // Index: 231 Fraction: 56/61 = 0.9180
0x2C30, // Index: 232 Fraction: 45/49 = 0.9184
0x2124, // Index: 233 Fraction: 34/37 = 0.9189
0x383D, // Index: 234 Fraction: 57/62 = 0.9194
0x1618, // Index: 235 Fraction: 23/25 = 0.9200
0x393E, // Index: 236 Fraction: 58/63 = 0.9206
0x2225, // Index: 237 Fraction: 35/38 = 0.9211
0x2E32, // Index: 238 Fraction: 47/51 = 0.9216
0x3A3F, // Index: 239 Fraction: 59/64 = 0.9219
0x464C, // Index: 240 Fraction: 71/77 = 0.9221
0x0B0C, // Index: 241 Fraction: 12/13 = 0.9231
0x484E, // Index: 242 Fraction: 73/79 = 0.9241
0x3C41, // Index: 243 Fraction: 61/66 = 0.9242
0x3034, // Index: 244 Fraction: 49/53 = 0.9245
0x2427, // Index: 245 Fraction: 37/40 = 0.9250
0x3D42, // Index: 246 Fraction: 62/67 = 0.9254
0x181A, // Index: 247 Fraction: 25/27 = 0.9259
0x3E43, // Index: 248 Fraction: 63/68 = 0.9265
0x2528, // Index: 249 Fraction: 38/41 = 0.9268
0x3236, // Index: 250 Fraction: 51/55 = 0.9273
0x3F44, // Index: 251 Fraction: 64/69 = 0.9275
0x0C0D, // Index: 252 Fraction: 13/14 = 0.9286
0x4146, // Index: 253 Fraction: 66/71 = 0.9296
0x3438, // Index: 254 Fraction: 53/57 = 0.9298
0x272A, // Index: 255 Fraction: 40/43 = 0.9302
0x4247, // Index: 256 Fraction: 67/72 = 0.9306
0x1A1C, // Index: 257 Fraction: 27/29 = 0.9310
0x4348, // Index: 258 Fraction: 68/73 = 0.9315
0x282B, // Index: 259 Fraction: 41/44 = 0.9318
0x363A, // Index: 260 Fraction: 55/59 = 0.9322
0x4449, // Index: 261 Fraction: 69/74 = 0.9324
0x0D0E, // Index: 262 Fraction: 14/15 = 0.9333
0x464B, // Index: 263 Fraction: 71/76 = 0.9342
0x383C, // Index: 264 Fraction: 57/61 = 0.9344
0x2A2D, // Index: 265 Fraction: 43/46 = 0.9348
0x474C, // Index: 266 Fraction: 72/77 = 0.9351
0x1C1E, // Index: 267 Fraction: 29/31 = 0.9355
0x484D, // Index: 268 Fraction: 73/78 = 0.9359
0x2B2E, // Index: 269 Fraction: 44/47 = 0.9362
0x3A3E, // Index: 270 Fraction: 59/63 = 0.9365
0x494E, // Index: 271 Fraction: 74/79 = 0.9367
0x0E0F, // Index: 272 Fraction: 15/16 = 0.9375
0x3C40, // Index: 273 Fraction: 61/65 = 0.9385
0x2D30, // Index: 274 Fraction: 46/49 = 0.9388
0x1E20, // Index: 275 Fraction: 31/33 = 0.9394
0x2E31, // Index: 276 Fraction: 47/50 = 0.9400
0x3E42, // Index: 277 Fraction: 63/67 = 0.9403
0x0F10, // Index: 278 Fraction: 16/17 = 0.9412
0x4044, // Index: 279 Fraction: 65/69 = 0.9420
0x3033, // Index: 280 Fraction: 49/52 = 0.9423
0x2022, // Index: 281 Fraction: 33/35 = 0.9429
0x3134, // Index: 282 Fraction: 50/53 = 0.9434
0x4246, // Index: 283 Fraction: 67/71 = 0.9437
0x1011, // Index: 284 Fraction: 17/18 = 0.9444
0x4448, // Index: 285 Fraction: 69/73 = 0.9452
0x3336, // Index: 286 Fraction: 52/55 = 0.9455
0x2224, // Index: 287 Fraction: 35/37 = 0.9459
0x3437, // Index: 288 Fraction: 53/56 = 0.9464
0x464A, // Index: 289 Fraction: 71/75 = 0.9467
0x1112, // Index: 290 Fraction: 18/19 = 0.9474
0x484C, // Index: 291 Fraction: 73/77 = 0.9481
0x3639, // Index: 292 Fraction: 55/58 = 0.9483
0x2426, // Index: 293 Fraction: 37/39 = 0.9487
0x373A, // Index: 294 Fraction: 56/59 = 0.9492
0x4A4E, // Index: 295 Fraction: 75/79 = 0.9494
0x1213, // Index: 296 Fraction: 19/20 = 0.9500
0x393C, // Index: 297 Fraction: 58/61 = 0.9508
0x2628, // Index: 298 Fraction: 39/41 = 0.9512
0x3A3D, // Index: 299 Fraction: 59/62 = 0.9516
0x1314, // Index: 300 Fraction: 20/21 = 0.9524
0x3C3F, // Index: 301 Fraction: 61/64 = 0.9531
0x282A, // Index: 302 Fraction: 41/43 = 0.9535
0x3D40, // Index: 303 Fraction: 62/65 = 0.9538
0x1415, // Index: 304 Fraction: 21/22 = 0.9545
0x3F42, // Index: 305 Fraction: 64/67 = 0.9552
0x2A2C, // Index: 306 Fraction: 43/45 = 0.9556
0x4043, // Index: 307 Fraction: 65/68 = 0.9559
0x1516, // Index: 308 Fraction: 22/23 = 0.9565
0x4245, // Index: 309 Fraction: 67/70 = 0.9571
0x2C2E, // Index: 310 Fraction: 45/47 = 0.9574
0x4346, // Index: 311 Fraction: 68/71 = 0.9577
0x1617, // Index: 312 Fraction: 23/24 = 0.9583
0x4548, // Index: 313 Fraction: 70/73 = 0.9589
0x2E30, // Index: 314 Fraction: 47/49 = 0.9592
0x4649, // Index: 315 Fraction: 71/74 = 0.9595
0x1718, // Index: 316 Fraction: 24/25 = 0.9600
0x484B, // Index: 317 Fraction: 73/76 = 0.9605
0x3032, // Index: 318 Fraction: 49/51 = 0.9608
0x494C, // Index: 319 Fraction: 74/77 = 0.9610
0x1819, // Index: 320 Fraction: 25/26 = 0.9615
0x4B4E, // Index: 321 Fraction: 76/79 = 0.9620
0x3234, // Index: 322 Fraction: 51/53 = 0.9623
0x4C4F, // Index: 323 Fraction: 77/80 = 0.9625
0x191A, // Index: 324 Fraction: 26/27 = 0.9630
0x3436, // Index: 325 Fraction: 53/55 = 0.9636
0x1A1B, // Index: 326 Fraction: 27/28 = 0.9643
0x3638, // Index: 327 Fraction: 55/57 = 0.9649
0x1B1C, // Index: 328 Fraction: 28/29 = 0.9655
0x383A, // Index: 329 Fraction: 57/59 = 0.9661
0x1C1D, // Index: 330 Fraction: 29/30 = 0.9667
0x3A3C, // Index: 331 Fraction: 59/61 = 0.9672
0x1D1E, // Index: 332 Fraction: 30/31 = 0.9677
0x3C3E, // Index: 333 Fraction: 61/63 = 0.9683
0x1E1F, // Index: 334 Fraction: 31/32 = 0.9688
0x3E40, // Index: 335 Fraction: 63/65 = 0.9692
0x1F20, // Index: 336 Fraction: 32/33 = 0.9697
0x4042, // Index: 337 Fraction: 65/67 = 0.9701
0x2021, // Index: 338 Fraction: 33/34 = 0.9706
0x4244, // Index: 339 Fraction: 67/69 = 0.9710
0x2122, // Index: 340 Fraction: 34/35 = 0.9714
0x4446, // Index: 341 Fraction: 69/71 = 0.9718
0x2223, // Index: 342 Fraction: 35/36 = 0.9722
0x4648, // Index: 343 Fraction: 71/73 = 0.9726
0x2324, // Index: 344 Fraction: 36/37 = 0.9730
0x484A, // Index: 345 Fraction: 73/75 = 0.9733
0x2425, // Index: 346 Fraction: 37/38 = 0.9737
0x4A4C, // Index: 347 Fraction: 75/77 = 0.9740
0x2526, // Index: 348 Fraction: 38/39 = 0.9744
0x4C4E, // Index: 349 Fraction: 77/79 = 0.9747
0x2627, // Index: 350 Fraction: 39/40 = 0.9750
0x2728, // Index: 351 Fraction: 40/41 = 0.9756
0x2829, // Index: 352 Fraction: 41/42 = 0.9762
0x292A, // Index: 353 Fraction: 42/43 = 0.9767
0x2A2B, // Index: 354 Fraction: 43/44 = 0.9773
0x2B2C, // Index: 355 Fraction: 44/45 = 0.9778
0x2C2D, // Index: 356 Fraction: 45/46 = 0.9783
0x2D2E, // Index: 357 Fraction: 46/47 = 0.9787
0x2E2F, // Index: 358 Fraction: 47/48 = 0.9792
0x2F30, // Index: 359 Fraction: 48/49 = 0.9796
0x3031, // Index: 360 Fraction: 49/50 = 0.9800
0x3132, // Index: 361 Fraction: 50/51 = 0.9804
0x3233, // Index: 362 Fraction: 51/52 = 0.9808
0x3334, // Index: 363 Fraction: 52/53 = 0.9811
0x3435, // Index: 364 Fraction: 53/54 = 0.9815
0x3536, // Index: 365 Fraction: 54/55 = 0.9818
0x3637, // Index: 366 Fraction: 55/56 = 0.9821
0x3738, // Index: 367 Fraction: 56/57 = 0.9825
0x3839, // Index: 368 Fraction: 57/58 = 0.9828
0x393A, // Index: 369 Fraction: 58/59 = 0.9831
0x3A3B, // Index: 370 Fraction: 59/60 = 0.9833
0x3B3C, // Index: 371 Fraction: 60/61 = 0.9836
0x3C3D, // Index: 372 Fraction: 61/62 = 0.9839
0x3D3E, // Index: 373 Fraction: 62/63 = 0.9841
0x3E3F, // Index: 374 Fraction: 63/64 = 0.9844
0x3F40, // Index: 375 Fraction: 64/65 = 0.9846
0x4041, // Index: 376 Fraction: 65/66 = 0.9848
0x4142, // Index: 377 Fraction: 66/67 = 0.9851
0x4243, // Index: 378 Fraction: 67/68 = 0.9853
0x4344, // Index: 379 Fraction: 68/69 = 0.9855
0x4445, // Index: 380 Fraction: 69/70 = 0.9857
0x4546, // Index: 381 Fraction: 70/71 = 0.9859
0x4647, // Index: 382 Fraction: 71/72 = 0.9861
0x4748, // Index: 383 Fraction: 72/73 = 0.9863
0x4849, // Index: 384 Fraction: 73/74 = 0.9865
0x494A, // Index: 385 Fraction: 74/75 = 0.9867
0x4A4B, // Index: 386 Fraction: 75/76 = 0.9868
0x4B4C, // Index: 387 Fraction: 76/77 = 0.9870
0x4C4D, // Index: 388 Fraction: 77/78 = 0.9872
0x4D4E, // Index: 389 Fraction: 78/79 = 0.9873
0x4E4F, // Index: 390 Fraction: 79/80 = 0.9875
};

View File

@@ -1,57 +0,0 @@
// Copyright 2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _SW_PLL_WRAPPPER_H_
#define _SW_PLL_WRAPPPER_H_
#include "xua.h"
#if XUA_USE_SW_PLL
extern "C"
{
#include "sw_pll.h"
}
/* Special control value to disable SDM. Outside of normal range which is less than 16b.*/
#define DISABLE_SDM 0x10000000
/** Task that receives an error term, passes it through a PI controller and periodically
* calclulates a sigma delta output value and sends it to the PLL fractional register.
*
* \param c_sw_pll Channel connected to the clocking thread to pass raw error terms.
*/
void sw_pll_task(chanend c_sw_pll);
/** Helper function that sends a special restart command. It causes the SDM task
* to quit and restart using the new mclk.
*
* \param c_sw_pll Channel connected to the clocking thread to pass raw error terms.
* \param mclk_Rate The mclk frequency in Hz.
*/
void restart_sigma_delta(chanend c_sw_pll, unsigned mclk_rate);
/** Performs a frequency comparsion between the incoming digital Rx stream and the local mclk.
*
* \param mclk_time_stamp The captured mclk count (using port timer) at the time of sample Rx.
* \param mclks_per_sample The nominal number of mclks per audio sample.
* \param c_sw_pll Channel connected to the sigma delta and controller thread.
* \param receivedSamples The number of received samples since tha last call to this function.
* \param reset_sw_pll_pfd Reference to a flag which will be used to signal reset of this function's state.
*/
void do_sw_pll_phase_frequency_detector_dig_rx( unsigned short mclk_time_stamp,
unsigned mclks_per_sample,
chanend c_sw_pll,
int receivedSamples,
int &reset_sw_pll_pfd);
/** Initilaises the software PLL both hardware and state. Sets the mclk frequency to a nominal point.
*
* \param sw_pll Reference to a software pll state struct to be initialised.
* \param mClk The current nominal mClk frequency.
*
* returns The SDM update interval in ticks and the initial DCO setting for nominal frequency */
{unsigned, unsigned} InitSWPLL(sw_pll_state_t &sw_pll, unsigned mClk);
#endif /* XUA_USE_SW_PLL */
#endif /* _SW_PLL_WRAPPPER_H_ */

View File

@@ -1,202 +0,0 @@
// Copyright 2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h>
#include <assert.h>
#include <print.h>
#include "sw_pll_wrapper.h"
#include "xua.h"
#if XUA_USE_SW_PLL
{unsigned, unsigned} init_sw_pll(sw_pll_state_t &sw_pll, unsigned mClk)
{
/* Autogenerated SDM App PLL setup by dco_model.py using 22.5792_1M profile */
/* Input freq: 24000000
F: 134
R: 0
f: 8
p: 18
OD: 5
ACD: 5
*/
#define APP_PLL_CTL_REG_22 0x0A808600
#define APP_PLL_DIV_REG_22 0x80000005
#define APP_PLL_FRAC_REG_22 0x80000812
#define SW_PLL_SDM_CTRL_MID_22 498283
#define SW_PLL_SDM_RATE_22 1000000
/* Autogenerated SDM App PLL setup by dco_model.py using 24.576_1M profile */
/* Input freq: 24000000
F: 146
R: 0
f: 4
p: 10
OD: 5
ACD: 5
*/
#define APP_PLL_CTL_REG_24 0x0A809200
#define APP_PLL_DIV_REG_24 0x80000005
#define APP_PLL_FRAC_REG_24 0x8000040A
#define SW_PLL_SDM_CTRL_MID_24 478151
#define SW_PLL_SDM_RATE_24 1000000
const uint32_t app_pll_ctl_reg[2] = {APP_PLL_CTL_REG_22, APP_PLL_CTL_REG_24};
const uint32_t app_pll_div_reg[2] = {APP_PLL_DIV_REG_22, APP_PLL_DIV_REG_24};
const uint32_t app_pll_frac_reg[2] = {APP_PLL_FRAC_REG_22, APP_PLL_FRAC_REG_24};
const uint32_t sw_pll_sdm_ctrl_mid[2] = {SW_PLL_SDM_CTRL_MID_22, SW_PLL_SDM_CTRL_MID_24};
const uint32_t sw_pll_sdm_rate[2] = {SW_PLL_SDM_RATE_22, SW_PLL_SDM_RATE_24};
const int clkIndex = mClk == MCLK_48 ? 1 : 0;
sw_pll_sdm_init(&sw_pll,
SW_PLL_15Q16(0.0),
SW_PLL_15Q16(32.0),
SW_PLL_15Q16(0.25),
0, /* LOOP COUNT Don't care for this API */
0, /* PLL_RATIO Don't care for this API */
0, /* No jitter compensation needed */
app_pll_ctl_reg[clkIndex],
app_pll_div_reg[clkIndex],
app_pll_frac_reg[clkIndex],
sw_pll_sdm_ctrl_mid[clkIndex],
3000 /* PPM_RANGE (FOR PFD) Don't care for this API*/ );
/* Reset SDM too */
sw_pll_init_sigma_delta(&sw_pll.sdm_state);
return {XS1_TIMER_HZ / sw_pll_sdm_rate[clkIndex], sw_pll_sdm_ctrl_mid[clkIndex]};
}
void do_sw_pll_phase_frequency_detector_dig_rx( unsigned short mclk_time_stamp,
unsigned mclks_per_sample,
chanend c_sw_pll,
int receivedSamples,
int &reset_sw_pll_pfd)
{
const unsigned control_loop_rate_divider = 6; /* 300Hz * 2 edges / 6 -> 100Hz loop rate */
static unsigned control_loop_counter = 0;
static unsigned total_received_samples = 0;
/* Keep a store of the last mclk time stamp so we can work out the increment */
static unsigned short last_mclk_time_stamp = 0;
control_loop_counter++;
total_received_samples += receivedSamples;
if(control_loop_counter == control_loop_rate_divider)
{
/* Calculate what the zero-error mclk count increment should be for this many samples */
const unsigned expected_mclk_inc = mclks_per_sample * total_received_samples / 2; /* divide by 2 because this fn is called per edge */
/* Calculate actualy time-stamped mclk count increment is */
const unsigned short actual_mclk_inc = mclk_time_stamp - last_mclk_time_stamp;
/* The difference is the raw error in terms of mclk counts */
short f_error = (int)actual_mclk_inc - (int)expected_mclk_inc;
if(reset_sw_pll_pfd)
{
f_error = 0; /* Skip first measurement as it will likely be very out */
reset_sw_pll_pfd = 0;
}
/* send PFD output to the sigma delta thread */
outuint(c_sw_pll, (int) f_error);
outct(c_sw_pll, XS1_CT_END);
last_mclk_time_stamp = mclk_time_stamp;
control_loop_counter = 0;
total_received_samples = 0;
}
}
void sw_pll_task(chanend c_sw_pll){
/* Zero is an invalid number and the SDM will not write the frac reg until
the first control value has been received. This avoids issues with
channel lockup if two tasks (eg. init and SDM) try to write at the same time. */
while(1)
{
unsigned selected_mclk_rate = inuint(c_sw_pll);
inct(c_sw_pll);
int f_error = 0;
int dco_setting = 0; /* gets set at init_sw_pll */
unsigned sdm_interval = 0; /* gets set at init_sw_pll */
sw_pll_state_t sw_pll;
/* initialse the SDM and gather SDM initial settings */
{sdm_interval, dco_setting} = init_sw_pll(sw_pll, selected_mclk_rate);
tileref_t this_tile = get_local_tile_id();
timer tmr;
int32_t time_trigger;
tmr :> time_trigger;
time_trigger += sdm_interval; /* ensure first loop has correct delay */
int running = 1;
outuint(c_sw_pll, 0); /* Signal back via clockgen to audio to start I2S */
outct(c_sw_pll, XS1_CT_END);
unsigned rx_word = 0;
while(running)
{
/* Poll for new SDM control value */
select
{
case inuint_byref(c_sw_pll, rx_word):
inct(c_sw_pll);
if(rx_word == DISABLE_SDM)
{
f_error = 0;
running = 0;
}
else
{
f_error = (int32_t)rx_word;
unsafe
{
sw_pll_sdm_do_control_from_error(&sw_pll, -f_error);
dco_setting = sw_pll.sdm_state.current_ctrl_val;
}
}
break;
/* Do nothing & fall-through. Above case polls only once per loop */
default:
break;
}
/* Wait until the timer value has been reached
This implements a timing barrier and keeps
the loop rate constant. */
select
{
case tmr when timerafter(time_trigger) :> int _:
time_trigger += sdm_interval;
break;
}
unsafe {
sw_pll_do_sigma_delta(&sw_pll.sdm_state, this_tile, dco_setting);
}
} /* while running */
} /* while(1) */
}
void restart_sigma_delta(chanend c_sw_pll, unsigned selected_mclk_rate)
{
outuint(c_sw_pll, DISABLE_SDM); /* Resets SDM */
outct(c_sw_pll, XS1_CT_END);
outuint(c_sw_pll, selected_mclk_rate);
outct(c_sw_pll, XS1_CT_END);
}
#endif /* XUA_USE_SW_PLL */

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2024 XMOS LIMITED.
// Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/**
* @brief Implements endpoint zero for an USB Audio 1.0/2.0 device
@@ -245,6 +245,7 @@ const unsigned g_dataFormat_In[INPUT_FORMAT_COUNT] = {STREAM_FORMAT_INPUT_1
};
/* Channel count */
/* Note, currently only input changes.. */
const unsigned g_chanCount_In_HS[INPUT_FORMAT_COUNT] = {HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT,
#if(INPUT_FORMAT_COUNT > 1)
HS_STREAM_FORMAT_INPUT_2_CHAN_COUNT,
@@ -254,15 +255,6 @@ const unsigned g_chanCount_In_HS[INPUT_FORMAT_COUNT] = {HS_STREAM_FORMAT_I
#endif
};
const unsigned g_chanCount_Out_HS[OUTPUT_FORMAT_COUNT] = {HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT,
#if(OUTPUT_FORMAT_COUNT > 1)
HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT,
#endif
#if(OUTPUT_FORMAT_COUNT > 2)
HS_STREAM_FORMAT_OUTPUT_3_CHAN_COUNT
#endif
};
XUD_ep ep0_out;
XUD_ep ep0_in;
@@ -588,7 +580,7 @@ void XUA_Endpoint0_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend c_ep0
if(g_curUsbSpeed == XUD_SPEED_HS)
{
outuint(c_audioControl, g_chanCount_Out_HS[sp.wValue-1]); /* Channel count */
outuint(c_audioControl, NUM_USB_CHAN_OUT); /* Channel count */
outuint(c_audioControl, g_subSlot_Out_HS[sp.wValue-1]); /* Subslot */
outuint(c_audioControl, g_sampRes_Out_HS[sp.wValue-1]); /* Resolution */
}
@@ -822,14 +814,7 @@ void XUA_Endpoint0_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend c_ep0
{
unsigned epNum = sp.wIndex & 0xff;
// Ensure we only check for AUDIO EPs if enabled
#if (NUM_USB_CHAN_IN != 0 && NUM_USB_CHAN_OUT == 0)
if (epNum == ENDPOINT_ADDRESS_IN_AUDIO)
#elif (NUM_USB_CHAN_IN == 0 && NUM_USB_CHAN_OUT != 0)
if (epNum == ENDPOINT_ADDRESS_OUT_AUDIO)
#elif (NUM_USB_CHAN_IN != 0 && NUM_USB_CHAN_OUT != 0)
if ((epNum == ENDPOINT_ADDRESS_IN_AUDIO) || (epNum == ENDPOINT_ADDRESS_OUT_AUDIO))
#endif
if ((epNum == ENDPOINT_ADDRESS_OUT_AUDIO) || (epNum == ENDPOINT_ADDRESS_IN_AUDIO))
{
#if (AUDIO_CLASS == 2) && (AUDIO_CLASS_FALLBACK)
if(g_curUsbSpeed == XUD_SPEED_FS)
@@ -976,20 +961,20 @@ void XUA_Endpoint0_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend c_ep0
cfgDesc_Audio2.Audio_Out_Format.bSubslotSize = HS_STREAM_FORMAT_OUTPUT_1_SUBSLOT_BYTES;
cfgDesc_Audio2.Audio_Out_Format.bBitResolution = HS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS;
cfgDesc_Audio2.Audio_Out_Endpoint.wMaxPacketSize = HS_STREAM_FORMAT_OUTPUT_1_MAXPACKETSIZE;
cfgDesc_Audio2.Audio_Out_ClassStreamInterface.bNrChannels = HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT;
cfgDesc_Audio2.Audio_Out_ClassStreamInterface.bNrChannels = NUM_USB_CHAN_OUT;
#endif
#if (OUTPUT_FORMAT_COUNT > 1)
cfgDesc_Audio2.Audio_Out_Format_2.bSubslotSize = HS_STREAM_FORMAT_OUTPUT_2_SUBSLOT_BYTES;
cfgDesc_Audio2.Audio_Out_Format_2.bBitResolution = HS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS;
cfgDesc_Audio2.Audio_Out_Endpoint_2.wMaxPacketSize = HS_STREAM_FORMAT_OUTPUT_2_MAXPACKETSIZE;
cfgDesc_Audio2.Audio_Out_ClassStreamInterface_2.bNrChannels = HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT;
cfgDesc_Audio2.Audio_Out_ClassStreamInterface_2.bNrChannels = NUM_USB_CHAN_OUT;
#endif
#if (OUTPUT_FORMAT_COUNT > 2)
cfgDesc_Audio2.Audio_Out_Format_3.bSubslotSize = HS_STREAM_FORMAT_OUTPUT_3_SUBSLOT_BYTES;
cfgDesc_Audio2.Audio_Out_Format_3.bBitResolution = HS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS;
cfgDesc_Audio2.Audio_Out_Endpoint_3.wMaxPacketSize = HS_STREAM_FORMAT_OUTPUT_3_MAXPACKETSIZE;
cfgDesc_Audio2.Audio_Out_ClassStreamInterface_3.bNrChannels = HS_STREAM_FORMAT_OUTPUT_3_CHAN_COUNT;
cfgDesc_Audio2.Audio_Out_ClassStreamInterface_3.bNrChannels = NUM_USB_CHAN_OUT;
#endif
#endif
#if (NUM_USB_CHAN_IN > 0)

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2024 XMOS LIMITED.
// Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/**
* @file xua_ep0_descriptors.h
@@ -772,11 +772,6 @@ typedef struct
unsigned char configDesc_DFU[DFU_LENGTH];
#endif
#ifdef USB_CONTROL_DESCS
/* Inferface descriptor for control */
unsigned char itfDesc_control[9];
#endif
#ifdef IAP
USB_Descriptor_Interface_t iAP_Interface;
USB_Descriptor_Endpoint_t iAP_Out_Endpoint;
@@ -1122,9 +1117,9 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2=
.wTerminalType = UAC_TT_OUTPUT_TERMTYPE_SPEAKER,
0x00, /* 6 bAssocTerminal */
#if (OUTPUT_VOLUME_CONTROL == 1)
FU_USBOUT, /* 7 bSourceID Connect to analog output feature unit */
FU_USBOUT, /* 7 bSourceID Connect to analog input feature unit*/
#else
ID_IT_USB, /* 7 bSourceID Connect to USB streaming input term */
ID_IT_USB, /* 7 bSourceID Connect to analog input feature unit*/
#endif
ID_CLKSEL, /* 8 bCSourceUD */
0x0000, /* 9 bmControls */
@@ -1454,15 +1449,15 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2=
/* Class Specific AS Interface Descriptor */
.Audio_Out_ClassStreamInterface =
{
0x10, /* 0 bLength: 16 */
UAC_CS_DESCTYPE_INTERFACE, /* 1 bDescriptorType: 0x24 */
0x10, /* 0 bLength: 16 */
UAC_CS_DESCTYPE_INTERFACE, /* 1 bDescriptorType: 0x24 */
UAC_CS_AS_INTERFACE_SUBTYPE_AS_GENERAL, /* 2 bDescriptorSubType */
ID_IT_USB, /* 3 bTerminalLink (Linked to USB input terminal) */
0x00, /* 4 bmControls */
UAC_FORMAT_TYPE_I, /* 5 bFormatType */
STREAM_FORMAT_OUTPUT_1_DATAFORMAT, /* 6:10 bmFormats (note this is a bitmap) */
HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT, /* 11 bNrChannels */
0x00000000, /* 12:14: bmChannelConfig */
ID_IT_USB, /* 3 bTerminalLink (Linked to USB input terminal) */
0x00, /* 4 bmControls */
UAC_FORMAT_TYPE_I, /* 5 bFormatType */
STREAM_FORMAT_OUTPUT_1_DATAFORMAT,/* 6:10 bmFormats (note this is a bitmap) */
NUM_USB_CHAN_OUT, /* 11 bNrChannels */
0x00000000, /* 12:14: bmChannelConfig */
.iChannelNames = offsetof(StringDescTable_t, outputChanStr_1)/sizeof(char *),
},
@@ -1545,15 +1540,15 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2=
/* Class Specific AS Interface Descriptor */
.Audio_Out_ClassStreamInterface_2 =
{
0x10, /* 0 bLength: 16 */
UAC_CS_DESCTYPE_INTERFACE, /* 1 bDescriptorType: 0x24 */
0x10, /* 0 bLength: 16 */
UAC_CS_DESCTYPE_INTERFACE, /* 1 bDescriptorType: 0x24 */
UAC_CS_AS_INTERFACE_SUBTYPE_AS_GENERAL, /* 2 bDescriptorSubType */
ID_IT_USB, /* 3 bTerminalLink (Linked to USB input terminal) */
0x00, /* 4 bmControls */
UAC_FORMAT_TYPE_I, /* 5 bFormatType */
STREAM_FORMAT_OUTPUT_2_DATAFORMAT, /* 6:10 bmFormats (note this is a bitmap) */
HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT, /* 11 bNrChannels */
0x00000000, /* 12:14: bmChannelConfig */
ID_IT_USB, /* 3 bTerminalLink (Linked to USB input terminal) */
0x00, /* 4 bmControls */
UAC_FORMAT_TYPE_I, /* 5 bFormatType */
STREAM_FORMAT_OUTPUT_2_DATAFORMAT,/* 6:10 bmFormats (note this is a bitmap) */
NUM_USB_CHAN_OUT, /* 11 bNrChannels */
0x00000000, /* 12:14: bmChannelConfig */
.iChannelNames = (offsetof(StringDescTable_t, outputChanStr_1)/sizeof(char *)),
},
@@ -1636,15 +1631,15 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2=
/* Class Specific AS Interface Descriptor */
.Audio_Out_ClassStreamInterface_3 =
{
0x10, /* 0 bLength: 16 */
UAC_CS_DESCTYPE_INTERFACE, /* 1 bDescriptorType: 0x24 */
0x10, /* 0 bLength: 16 */
UAC_CS_DESCTYPE_INTERFACE, /* 1 bDescriptorType: 0x24 */
UAC_CS_AS_INTERFACE_SUBTYPE_AS_GENERAL, /* 2 bDescriptorSubType */
ID_IT_USB, /* 3 bTerminalLink (Linked to USB input terminal) */
0x00, /* 4 bmControls */
UAC_FORMAT_TYPE_I, /* 5 bFormatType */
STREAM_FORMAT_OUTPUT_3_DATAFORMAT, /* 6:10 bmFormats (note this is a bitmap) */
HS_STREAM_FORMAT_OUTPUT_3_CHAN_COUNT, /* 11 bNrChannels */
0x00000000, /* 12:14: bmChannelConfig */
ID_IT_USB, /* 3 bTerminalLink (Linked to USB input terminal) */
0x00, /* 4 bmControls */
UAC_FORMAT_TYPE_I, /* 5 bFormatType */
STREAM_FORMAT_OUTPUT_3_DATAFORMAT,/* 6:10 bmFormats (note this is a bitmap) */
NUM_USB_CHAN_OUT, /* 11 bNrChannels */
0x00000000, /* 12:14: bmChannelConfig */
.iChannelNames = offsetof(StringDescTable_t, outputChanStr_1)/sizeof(char *),
},
@@ -2109,21 +2104,6 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2=
#endif
#endif /* (XUA_DFU_EN == 1) */
#ifdef USB_CONTROL_DESCS
{
/* Control interface descriptor */
0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
0x04, /* 1 bDescriptorType : INTERFACE descriptor. (field size 1 bytes) */
(INTERFACE_NUMBER_MISC_CONTROL), /* 2 bInterfaceNumber */
0x00, /* 3 bAlternateSetting : Index of this setting. (field size 1 bytes) */
0x00, /* 4 bNumEndpoints : 0 endpoints. (field size 1 bytes) */
USB_CLASS_VENDOR_SPECIFIC, /* 5 bInterfaceClass : Vendor specific. (field size 1 bytes) */
0xFF, /* 6 bInterfaceSubclass : (field size 1 bytes) */
0xFF, /* 7 bInterfaceProtocol : Unused. (field size 1 bytes) */
offsetof(StringDescTable_t, ctrlStr)/sizeof(char *), /* 8 iInterface */
},
#endif
#ifdef IAP
/* Interface descriptor */
.iAP_Interface =

View File

@@ -145,7 +145,7 @@ static void updateMasterVol(int unitID, chanend ?c_mix_ctl)
outuint(c_mix_ctl, x);
outct(c_mix_ctl, XS1_CT_END);
}
#else
#else
unsafe
{
unsigned int * unsafe multOutPtr = multOut;
@@ -456,35 +456,34 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
(buffer, unsigned char[])[0] = 1;
return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), 1, sp.wLength);
break;
#if (XUA_SPDIF_RX_EN)
case ID_CLKSRC_SPDIF:
/* Interogate clockgen thread for validity */
if (!isnull(c_clk_ctl))
{
outuint(c_clk_ctl, GET_VALID);
outuint(c_clk_ctl, CLOCK_SPDIF);
outuint(c_clk_ctl, CLOCK_SPDIF_INDEX);
outct(c_clk_ctl, XS1_CT_END);
(buffer, unsigned char[])[0] = inuint(c_clk_ctl);
chkct(c_clk_ctl, XS1_CT_END);
return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), 1, sp.wLength);
}
break;
#endif
#if (XUA_ADAT_RX_EN)
case ID_CLKSRC_ADAT:
if (!isnull(c_clk_ctl))
{
outuint(c_clk_ctl, GET_VALID);
outuint(c_clk_ctl, CLOCK_ADAT);
outuint(c_clk_ctl, CLOCK_ADAT_INDEX);
outct(c_clk_ctl, XS1_CT_END);
(buffer, unsigned char[])[0] = inuint(c_clk_ctl);
chkct(c_clk_ctl, XS1_CT_END);
return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), 1, sp.wLength);
}
break;
#endif
default:
//Unknown Unit ID in Clock Valid Control Request
@@ -514,23 +513,19 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
return result;
}
/* Check for correct datalength for clock sel */
if(datalength == 1)
{
int clockIndex = (int) (buffer, unsigned char[])[0];
clockIndex -= 1; /* Index to/from host is 1-based */
if((clockIndex >= 0) && (clockIndex < CLOCK_COUNT))
if (!isnull(c_clk_ctl))
{
if(!isnull(c_clk_ctl))
{
outuint(c_clk_ctl, SET_SEL);
outuint(c_clk_ctl, clockIndex);
outct(c_clk_ctl, XS1_CT_END);
}
/* Send 0 Length as status stage */
return XUD_DoSetRequestStatus(ep0_in);
outuint(c_clk_ctl, SET_SEL);
outuint(c_clk_ctl, (buffer, unsigned char[])[0]);
outct(c_clk_ctl, XS1_CT_END);
}
/* Send 0 Length as status stage */
return XUD_DoSetRequestStatus(ep0_in);
}
}
else
{
@@ -538,15 +533,13 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
(buffer, unsigned char[])[0] = 1;
if (!isnull(c_clk_ctl))
{
int clockIndex;
outuint(c_clk_ctl, GET_SEL);
outct(c_clk_ctl, XS1_CT_END);
clockIndex = inuint(c_clk_ctl);
clockIndex += 1; /* Index to/from host is 1-based */
(buffer, unsigned char[])[0] = (unsigned char) clockIndex;
(buffer, unsigned char[])[0] = inuint(c_clk_ctl);
chkct(c_clk_ctl, XS1_CT_END);
}
return XUD_DoGetRequest( ep0_out, ep0_in, (buffer, unsigned char[]), 1, sp.wLength);
return XUD_DoGetRequest( ep0_out, ep0_in, (buffer, unsigned char[]), 1, sp.wLength );
}
}
break;
@@ -923,20 +916,17 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c
{
storeFreq((buffer, unsigned char[]), i, currentFreq44);
num_freqs++;
currentFreq44*=2;
}
currentFreq44*=2;
if((currentFreq48 <= maxFreq) && (currentFreq48 >= MIN_FREQ))
if((currentFreq48 <= maxFreq))
{
/* Note i passed byref here */
storeFreq((buffer, unsigned char[]), i, currentFreq48);
num_freqs++;
currentFreq48*=2;
}
currentFreq48*=2;
if((currentFreq48 > MAX_FREQ) && (currentFreq44 > MAX_FREQ))
else
{
break;
}

View File

@@ -1,4 +1,4 @@
// Copyright 2012-2024 XMOS LIMITED.
// Copyright 2012-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include "xua.h" /* Device specific defines */
@@ -30,8 +30,8 @@
#include "iap.h"
#endif
#if (XUA_SPDIF_RX_EN || XUA_SPDIF_TX_EN)
#include "spdif.h" /* From lib_spdif */
#if (XUA_SPDIF_RX_EN)
#include "spdif.h"
#endif
#if (XUA_ADAT_RX_EN)
@@ -42,6 +42,10 @@
#include "xua_pdm_mic.h"
#endif
#if (XUA_SPDIF_TX_EN)
#include "spdif.h" /* From lib_spdif */
#endif
#if (XUA_DFU_EN == 1)
[[distributable]]
void DFUHandler(server interface i_dfu i, chanend ?c_user_cmd);
@@ -141,14 +145,9 @@ on stdcore[XUD_TILE] : buffered in port:32 p_adat_rx = PORT_ADAT_IN;
on tile[XUD_TILE] : in port p_spdif_rx = PORT_SPDIF_IN;
#endif
#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) || (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) || ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) && (!XUA_USE_APP_PLL))
/* Reference to external clock multiplier */
on tile[PLL_REF_TILE] : out port p_pll_ref = PORT_PLL_REF;
#ifdef __XS3A__
on tile[AUDIO_IO_TILE] : port p_for_mclk_count_audio = PORT_MCLK_COUNT_2;
#else /* __XS3A__ */
#define p_for_mclk_count_audio null
#endif /* __XS3A__ */
#endif
#ifdef MIDI
@@ -315,12 +314,8 @@ void usb_audio_io(chanend ?c_aud_in,
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
, client interface pll_ref_if i_pll_ref
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
, chanend c_audio_rate_change
#endif
#if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL)
, port p_for_mclk_count_aud
, chanend c_sw_pll
#if (XUA_USE_APP_PLL)
, client interface SoftPll_if i_softPll
#endif
)
{
@@ -330,14 +325,9 @@ void usb_audio_io(chanend ?c_aud_in,
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
chan c_dig_rx;
chan c_audio_rate_change; /* Notification of new mclk freq to clockgen and synch */
#if XUA_USE_SW_PLL
/* Connect p_for_mclk_count_aud to clk_audio_mclk so we can count mclks/timestamp in digital rx*/
unsigned x = 0;
asm("ldw %0, dp[clk_audio_mclk]":"=r"(x));
asm("setclk res[%0], %1"::"r"(p_for_mclk_count_aud), "r"(x));
#endif /* XUA_USE_SW_PLL */
#endif /* (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) */
#else
#define c_dig_rx null
#endif
#if (XUA_NUM_PDM_MICS > 0) && (PDM_TILE == AUDIO_IO_TILE)
/* Configure clocks ports - sharing mclk port with I2S */
@@ -353,7 +343,7 @@ void usb_audio_io(chanend ?c_aud_in,
par
{
#if (MIXER && XUA_USB_EN)
#if (MIXER)
/* Mixer cores(s) */
{
thread_speed();
@@ -377,15 +367,15 @@ void usb_audio_io(chanend ?c_aud_in,
#define AUDIO_CHANNEL c_aud_in
#endif
XUA_AudioHub(AUDIO_CHANNEL, clk_audio_mclk, clk_audio_bclk, p_mclk_in, p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc
#if (XUA_USE_APP_PLL)
, i_softPll
#endif
#if (XUA_SPDIF_TX_EN) //&& (SPDIF_TX_TILE != AUDIO_IO_TILE)
, c_spdif_tx
#endif
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
, c_dig_rx
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
, c_audio_rate_change
#endif
#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1)
, dfuInterface
#endif
@@ -405,22 +395,12 @@ void usb_audio_io(chanend ?c_aud_in,
* However, due to the use of an interface the pll reference signal port can be on another tile
*/
thread_speed();
clockGen( c_spdif_rx,
c_adat_rx,
i_pll_ref,
c_dig_rx,
c_clk_ctl,
c_clk_int,
c_audio_rate_change
#if XUA_USE_SW_PLL
, p_for_mclk_count_aud
, c_sw_pll
#endif
);
clockGen(c_spdif_rx, c_adat_rx, i_pll_ref, c_dig_rx, c_clk_ctl, c_clk_int);
}
#endif
} // par
//:
}
}
#ifndef USER_MAIN_DECLARATIONS
@@ -467,7 +447,7 @@ int main()
#define c_adat_rx null
#endif
#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE)
#if (XUA_SPDIF_TX_EN) //&& (SPDIF_TX_TILE != AUDIO_IO_TILE)
chan c_spdif_tx;
#endif
@@ -493,15 +473,13 @@ int main()
#endif
#endif
#if (((XUA_SYNCMODE == XUA_SYNCMODE_SYNC && !XUA_USE_SW_PLL) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) )
#if (((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) && !XUA_USE_APP_PLL) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
interface pll_ref_if i_pll_ref;
#endif
#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL)
chan c_sw_pll;
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
chan c_audio_rate_change; /* Notification of new mclk freq to ep_buffer */
#if (XUA_USE_APP_PLL)
interface SoftPll_if i_softPll;
chan c_swpll_update;
#endif
chan c_sof;
chan c_xud_out[ENDPOINT_COUNT_OUT]; /* Endpoint channels for XUD */
@@ -524,18 +502,20 @@ int main()
{
USER_MAIN_CORES
#if (((XUA_SYNCMODE == XUA_SYNCMODE_SYNC && !XUA_USE_SW_PLL) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN))
#if (((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) && XYA_USE_APP_PLL) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
on tile[PLL_REF_TILE]: PllRefPinTask(i_pll_ref, p_pll_ref);
#endif
on tile[XUD_TILE]:
par
{
#if XUA_USB_EN
#if ((XUD_TILE == 0) && (XUA_DFU_EN == 1))
#if (XUD_TILE == 0)
/* Check if USB is on the flash tile (tile 0) */
#if (XUA_DFU_EN == 1)
[[distribute]]
DFUHandler(dfuInterface, null);
#endif
#endif
#if XUA_USB_EN
/* Core USB task, buffering, USB etc */
{
@@ -553,6 +533,10 @@ int main()
c_sof, epTypeTableOut, epTypeTableIn, usbSpeed, xudPwrCfg);
}
#if (XUA_USE_APP_PLL)
//XUA_SoftPll(tile[0], i_softPll, c_swpll_update);
#endif
/* Core USB audio task, buffering, USB etc */
{
unsigned x;
@@ -595,11 +579,10 @@ int main()
#endif
, c_mix_out
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
, c_audio_rate_change
#if (!XUA_USE_SW_PLL)
#if (!XUA_USE_APP_PLL)
, i_pll_ref
#else
, c_sw_pll
, c_swpll_update
#endif
#endif
);
@@ -615,13 +598,11 @@ int main()
#endif /* XUA_USB_EN */
}
#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL)
on tile[AUDIO_IO_TILE]: sw_pll_task(c_sw_pll);
#if(XUA_USE_APP_PLL)
on tile[AUDIO_IO_TILE]: XUA_SoftPll(tile[0], i_softPll, c_swpll_update);
#endif
on tile[AUDIO_IO_TILE]:
{
/* Audio I/O task, includes mixing etc */
usb_audio_io(c_mix_out
#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE)
@@ -635,16 +616,16 @@ int main()
, dfuInterface
#endif
#if (XUA_NUM_PDM_MICS > 0)
#if (PDM_TILE == AUDIO_IO_TILE)
, c_ds_output
#endif
, c_pdm_pcm
#endif
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
, i_pll_ref
#endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
, c_audio_rate_change
#endif
#if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL)
, p_for_mclk_count_audio
, c_sw_pll
#if (XUA_USE_APP_PLL)
, i_softPll
#endif
);
}
@@ -687,7 +668,7 @@ int main()
on tile[XUD_TILE]:
{
thread_speed();
spdif_rx(c_spdif_rx, p_spdif_rx, clk_spd_rx, 192000);
spdif_rx(c_spdif_rx,p_spdif_rx,clk_spd_rx,192000);
}
#endif

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2024 XMOS LIMITED.
// Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h>
#include <platform.h>
@@ -36,10 +36,7 @@ void ConfigAudioPorts(
#ifdef __XS3A__
/* Increase drive strength of clock ports to 8mA */
asm volatile ("setc res[%0], %1" :: "r" (p_bclk), "r" (0x200006));
if(!isnull(p_lrclk))
{
asm volatile ("setc res[%0], %1" :: "r" (p_lrclk), "r" (0x200006));
}
asm volatile ("setc res[%0], %1" :: "r" (p_lrclk), "r" (0x200006));
#endif
/* Note this call to stop_clock() will pause forever if the port clocking the clock-block is not low.

View File

@@ -1,4 +1,4 @@
// Copyright 2023-2024 XMOS LIMITED.
// Copyright 2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/* Default implementations of AudioHwInit(), AudioHwConfig(), AudioHwConfig_Mute() and AudioHwConfig_UnMute() */

View File

@@ -1,4 +1,4 @@
// Copyright 2023-2024 XMOS LIMITED.
// Copyright 2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _AUDIO_HW_H_
#define _AUDIO_HW_H_

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2024 XMOS LIMITED.
// Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _AUDIOSTREAM_H_
#define _AUDIOSTREAM_H_

View File

@@ -1,4 +1,4 @@
// Copyright 2013-2024 XMOS LIMITED.
// Copyright 2013-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/**
@@ -51,7 +51,7 @@ typedef struct hidEvent_t {
size_t UserHIDGetData( const unsigned id, unsigned char hidData[ HID_MAX_DATA_BYTES ]);
/**
* \brief Initialize HID processing
* \brief Initialize HID processing
*/
void UserHIDInit( void );

View File

@@ -1,4 +1,4 @@
// Copyright 2013-2024 XMOS LIMITED.
// Copyright 2013-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/**

View File

@@ -1,7 +1,8 @@
// Copyright 2011-2023 XMOS LIMITED.
// Copyright 2011-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _XUA_COMMANDS_H_
#define _XUA_COMMANDS_H_
#ifndef __XUA_INTERNAL_CMDS_H__
#define __XUA_INTERNAL_CMDS_H__
#include "xua.h"
@@ -10,19 +11,20 @@
#define SET_SEL 1 /* Set value of clock selector */
#define GET_FREQ 2 /* Get current freq */
#define GET_VALID 3 /* Get current validity */
#define SET_SMUX 7 /* Set SMUX mode (ADAT) */
enum
{
CLOCK_INTERNAL = 0,
#if XUA_SPDIF_RX_EN
CLOCK_SPDIF,
#define CLOCK_INTERNAL 1
#define CLOCK_SPDIF 2
#if SPDIF_RX
#define CLOCK_ADAT 3
#else
#define CLOCK_ADAT 2
#endif
#if XUA_ADAT_RX_EN
CLOCK_ADAT,
#endif
CLOCK_COUNT
};
#define CLOCK_INTERNAL_INDEX (CLOCK_INTERNAL - 1)
#define CLOCK_ADAT_INDEX (CLOCK_ADAT - 1)
#define CLOCK_SPDIF_INDEX (CLOCK_SPDIF - 1)
#define SET_SMUX 7
/* c_audioControl */
#define SET_SAMPLE_FREQ 4

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2023 XMOS LIMITED.
// Copyright 2011-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h>
#include <flash.h>
@@ -72,7 +72,7 @@ int flash_cmd_init(void)
return 1;
}
#if (!XUA_QUAD_SPI_FLASH)
#ifndef QUAD_SPI_FLASH
// Disable flash protection
fl_setProtection(0);
#endif

View File

@@ -1,11 +1,11 @@
// Copyright 2012-2023 XMOS LIMITED.
// Copyright 2012-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include "xua.h"
#if (XUA_DFU_EN == 1)
#include "uac_hwresources.h"
#include <xs1.h>
#include <xclib.h>
#if (XUA_QUAD_SPI_FLASH)
#ifdef QUAD_SPI_FLASH
#include <quadflashlib.h>
#else
#include <flashlib.h>
@@ -20,7 +20,7 @@
#ifdef DFU_FLASH_DEVICE
#if (XUA_QUAD_SPI_FLASH)
#ifdef QUAD_SPI_FLASH
/* Using specified flash device rather than all supported in tools */
fl_QuadDeviceSpec flash_devices[] = {DFU_FLASH_DEVICE};
#else
@@ -29,7 +29,7 @@ fl_DeviceSpec flash_devices[] = {DFU_FLASH_DEVICE};
#endif
#endif
#if (XUA_QUAD_SPI_FLASH)
#ifdef QUAD_SPI_FLASH
/*
typedef struct {
out port qspiCS;
@@ -60,7 +60,7 @@ fl_PortHolderStruct p_flash =
int flash_cmd_enable_ports()
{
int result = 0;
#if (XUA_QUAD_SPI_FLASH)
#ifdef QUAD_SPI_FLASH
/* Ports not shared */
#else
setc(p_flash.spiMISO, XS1_SETC_INUSE_OFF);
@@ -89,14 +89,14 @@ int flash_cmd_enable_ports()
#endif
#ifdef DFU_FLASH_DEVICE
#if (XUA_QUAD_SPI_FLASH)
#ifdef QUAD_SPI_FLASH
result = fl_connectToDevice(&p_qflash, flash_devices, sizeof(flash_devices) / sizeof(fl_QuadDeviceSpec));
#else
result = fl_connectToDevice(&p_flash, flash_devices, sizeof(flash_devices) / sizeof(fl_DeviceSpec));
#endif
#else
/* Use default flash list */
#if (XUA_QUAD_SPI_FLASH)
#ifdef QUAD_SPI_FLASH
result = fl_connect(&p_qflash);
#else
result = fl_connect(&p_flash);
@@ -117,7 +117,7 @@ int flash_cmd_disable_ports()
{
fl_disconnect();
#if (!XUA_QUAD_SPI_FLASH)
#ifndef QUAD_SPI_FLASH
setc(p_flash.spiMISO, XS1_SETC_INUSE_OFF);
setc(p_flash.spiCLK, XS1_SETC_INUSE_OFF);
setc(p_flash.spiMOSI, XS1_SETC_INUSE_OFF);

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2024 XMOS LIMITED.
// Copyright 2011-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef MIDIINPARSE_XH
#define MIDIINPARSE_XH
@@ -19,11 +19,8 @@ struct midi_in_parse_state {
unsigned codeIndexNumber;
};
#ifdef __XC__
void reset_midi_state(struct midi_in_parse_state &mips);
void dump_midi_in_parse_state(struct midi_in_parse_state &s);
void reset_midi_state(struct midi_in_parse_state &mips);
{unsigned int , unsigned int} midi_in_parse(struct midi_in_parse_state &mips, unsigned cable_number, unsigned char b);
#endif
#endif

View File

@@ -1,14 +1,8 @@
// Copyright 2011-2024 XMOS LIMITED.
// Copyright 2011-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef MIDIOUTPARSE_XH
#define MIDIOUTPARSE_XH
// If for any reason we pop a message when not needed (should never happen) this will cause midiparse out to send a size of 0 (drops packet)
#define MIDI_OUT_NULL_MESSAGE 0x00000000
#ifdef __XC__
// Takes a MIDI packet and decomoses it into up to 3 data bytes followed by a byte count.
{unsigned, unsigned, unsigned, unsigned} midi_out_parse(unsigned event);
#endif
#endif

View File

@@ -1,11 +1,9 @@
// Copyright 2013-2024 XMOS LIMITED.
// Copyright 2013-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef QUEUE_H_
#define QUEUE_H_
#include "midioutparse.h"
#include "xassert.h"
#define assert(x) asm("ecallf %0"::"r"(x));
typedef struct queue_t {
/// Read index.
@@ -16,14 +14,12 @@ typedef struct queue_t {
unsigned mask;
} queue_t;
#ifdef __XC__
inline int is_power_of_2(unsigned x) {
return x != 0 && (x & (x - 1)) == 0;
}
inline void queue_init(queue_t &q, unsigned size) {
xassert(is_power_of_2(size) && "MIDI FIFO size must be a power of 2"); // Keep this enabled as will be discovered duirng dev time
assert(is_power_of_2(size));
q.rdptr = 0;
q.wrptr = 0;
q.size = size;
@@ -40,26 +36,25 @@ inline int queue_is_full(const queue_t &q) {
inline void queue_push_word(queue_t &q, unsigned array[], unsigned data)
{
if(queue_is_full(q)) {
xassert(0 && "Unexpected push to MIDI queue when full");
// Silently drop message if asserts not enabled
return;
}
assert(!queue_is_full(q));
array[q.wrptr++ & q.mask] = data;
}
inline unsigned queue_pop_word(queue_t &q, unsigned array[]) {
if(queue_is_empty(q)){
xassert(0 && "Unexpected pop from MIDI queue when empty");
// Return NULL messaqe if asserts not enabled
return MIDI_OUT_NULL_MESSAGE;
}
assert(!queue_is_empty(q));
return array[q.rdptr++ & q.mask];
}
inline void queue_push_byte(queue_t &q, unsigned char array[], unsigned data)
{
assert(!queue_is_full(q));
array[q.wrptr++ & q.mask] = data;
}
inline unsigned queue_pop_byte(queue_t &q, unsigned char array[]) {
assert(!queue_is_empty(q));
return array[q.rdptr++ & q.mask];
}
inline unsigned queue_items(const queue_t &q) {
return q.wrptr - q.rdptr;
@@ -69,6 +64,4 @@ inline unsigned queue_space(const queue_t &q) {
return q.size - queue_items(q);
}
#endif // __XC__
#endif /* QUEUE_H_ */

View File

@@ -1,4 +1,4 @@
// Copyright 2013-2024 XMOS LIMITED.
// Copyright 2013-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include "queue.h"
@@ -9,5 +9,7 @@ extern inline int queue_is_empty(const queue_t &q);
extern inline int queue_is_full(const queue_t &q);
extern inline void queue_push_word(queue_t &q, unsigned array[], unsigned data);
extern inline unsigned queue_pop_word(queue_t &q, unsigned array[]);
extern inline void queue_push_byte(queue_t &q, unsigned char array[], unsigned data);
extern inline unsigned queue_pop_byte(queue_t &q, unsigned char array[]);
extern inline unsigned queue_space(const queue_t &q);
extern inline unsigned queue_items(const queue_t &q);

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2024 XMOS LIMITED.
// Copyright 2011-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h>
#include <xclib.h>
@@ -13,7 +13,7 @@
#include "iap_user.h"
#include "coprocessor_user.h"
#endif
//#define MIDI_LOOPBACK 1
int icount = 0;
static unsigned makeSymbol(unsigned data)
{
@@ -89,9 +89,9 @@ void usb_midi(
struct midi_in_parse_state mips;
// the symbol fifo (to go out of uart).
// the symbol fifo (to go out of uart)
queue_t symbol_fifo;
unsigned symbol_fifo_arr[USB_MIDI_DEVICE_OUT_FIFO_SIZE]; // Used for outgoing UART symbols (which include the start and stop bit)
unsigned symbol_fifo_arr[USB_MIDI_DEVICE_OUT_FIFO_SIZE]; // Used for 32bit USB MIDI events
unsigned rxPT, txPT;
int midi_from_host_overflow = 0;
@@ -197,7 +197,7 @@ void usb_midi(
{
// send data
// printstr("uart->decouple: ");
midi_send_data(c_midi, event);
outuint(c_midi, event);
waiting_for_ack = 1;
th_count++;
}
@@ -264,7 +264,7 @@ void usb_midi(
}
break;
#endif
// Received as packet from USB
case !authenticating => midi_get_ack_or_data(c_midi, is_ack, datum):
if (is_ack)
@@ -272,7 +272,7 @@ void usb_midi(
// have we got more data to send
if (!queue_is_empty(midi_to_host_fifo))
{
midi_send_data(c_midi, queue_pop_word(midi_to_host_fifo, midi_to_host_fifo_arr));
outuint(c_midi, queue_pop_word(midi_to_host_fifo, midi_to_host_fifo_arr));
th_count++;
}
else
@@ -281,7 +281,6 @@ void usb_midi(
}
}
else
// A midi packet from the host
{
unsigned midi[3];
unsigned size;
@@ -296,7 +295,7 @@ void usb_midi(
{
// send data
event = byterev(event);
midi_send_data(c_midi, event);
outuint(c_midi, event);
th_count++;
waiting_for_ack = 1;
}
@@ -328,7 +327,7 @@ void usb_midi(
midi_from_host_overflow = 1;
}
// Drop through to the isTX guarded case
if (!isTX && size > 0) // do not start tx'ing if this packet has no size
if (!isTX)
{
t :> txT; // Should be enough to trigger the other case
isTX = 1;

View File

@@ -1,11 +1,8 @@
# Copyright 2022-2024 XMOS LIMITED.
# Copyright 2022-2023 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import pytest
import time
import Pyxsim
from pathlib import Path
from midi_test_helpers import MIDI_TEST_CONFIGS
import subprocess
@pytest.fixture()
def test_file(request):
@@ -42,16 +39,3 @@ def pytest_addoption(parser):
@pytest.fixture
def options(request):
yield request.config.option
# We use the same binary multiple times so just build once for all MIDI tests
@pytest.fixture(scope="session")
def build_midi():
cmd = "xmake -C test_midi -j"
# result = subprocess.run(cmd, capture_output=True, text=True, shell=True)
# return_code = result.returncode
return_code = 0
assert return_code == 0, f"{result.stderr}\n{result.stdout}"
return str(Path(__file__).parent / f"test_midi/bin/")

View File

@@ -1,81 +0,0 @@
# Copyright 2024 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import contextlib
import os
import shutil
import tempfile
MIDI_TEST_CONFIGS = ["xs2", "xs3"]
MIDI_RATE = 31250
@contextlib.contextmanager
def cd(newdir, cleanup=lambda: True):
prevdir = os.getcwd()
os.chdir(os.path.expanduser(newdir))
try:
yield
finally:
os.chdir(prevdir)
cleanup()
@contextlib.contextmanager
def tempdir():
dirpath = tempfile.mkdtemp(dir=os.getcwd())
def cleanup():
shutil.rmtree(dirpath)
with cd(dirpath, cleanup):
yield dirpath
class midi_expect_tx:
def __init(self):
pass
def expect(self, commands):
expected = ""
for command in commands:
while len(command) < 3:
command.append(0)
expected += "uart_tx_checker: " + " ".join([f"0x{byte:02x}" for byte in command]) + "\n"
return expected + "\n"
class midi_expect_rx:
def __init(self):
pass
def expect(self, commands):
expected = ""
for command in commands:
while len(command) < 3:
command.append(0)
expected += "dut_midi_rx: " + " ".join([f"{byte}" for byte in command]) + "\n"
return expected + "\n"
midi_tx_file = "midi_tx_cmds.txt"
midi_rx_file = "midi_rx_cmds.txt"
def create_midi_tx_file(commands=None):
with open(midi_tx_file, "wt") as mt:
if commands is None:
return
for command in commands:
while len(command) < 3:
command.append(0)
text = " ".join([str(byte) for byte in command]) + "\n"
mt.write(text)
def create_midi_rx_file(num_commands=0):
with open(midi_rx_file, "wt") as mr:
text = f"{num_commands}\n"
mr.write(text)
# Test/dev only
if __name__ == "__main__":
with tempdir() as td:
print(td)
create_midi_tx_file()
input("PRESS ENTER TO CONTINUE")

View File

@@ -1,4 +1,4 @@
// Copyright 2016-2024 XMOS LIMITED.
// Copyright 2016-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifdef HARDWARE
@@ -19,7 +19,7 @@
/* CS2100 lists typical lock time as 100 * input period */
#define AUDIO_PLL_LOCK_DELAY (40000000)
#if defined(XUA_SPDIF_RX_EN) || defined(XUA_ADAT_RX_EN)
#if defined(SPDIF_RX) || defined(ADAT_RX)
#define USE_FRACTIONAL_N 1
#endif
@@ -32,7 +32,7 @@ port p_i2c = on tile[0]:PORT_I2C;
#ifdef USE_FRACTIONAL_N
#if !(defined(XUA_SPDIF_RX_EN) || defined(XUA_ADAT_RX_EN))
#if !(defined(SPDIF_RX) || defined(ADAT_RX))
/* Choose a frequency the xcore can easily generate internally */
#define PLL_SYNC_FREQ 1000000
#else
@@ -95,7 +95,7 @@ void PllMult(unsigned output, unsigned ref, client interface i2c_master_if i2c)
}
#endif
#if !(defined(XUA_SPDIF_RX_EN) || defined(XUA_ADAT_RX_EN)) && defined(USE_FRACTIONAL_N)
#if !(defined(SPDIF_RX) || defined(ADAT_RX)) && defined(USE_FRACTIONAL_N)
on tile[AUDIO_IO_TILE] : out port p_pll_clk = PORT_PLL_REF;
on tile[AUDIO_IO_TILE] : clock clk_pll_sync = XS1_CLKBLK_5;
#endif
@@ -111,7 +111,7 @@ void wait_us(int microseconds)
void AudioHwInit(chanend ?c_codec)
{
#if !(defined(XUA_SPDIF_RX_EN) || defined(XUA_ADAT_RX_EN)) && defined(USE_FRACTIONAL_N)
#if !(defined(SPDIF_RX) || defined(ADAT_RX)) && defined(USE_FRACTIONAL_N)
/* Output a fixed sync clock to the pll */
configure_clock_rate(clk_pll_sync, 100, 100/(PLL_SYNC_FREQ/1000000));
configure_port_clock_output(p_pll_clk, clk_pll_sync);

View File

@@ -1,37 +0,0 @@
# The TARGET variable determines what target system the application is
# compiled for. It either refers to an XN file in the source directories
# or a valid argument for the --target option when compiling.
ifeq ($(CONFIG), xs2)
TARGET = XCORE-200-EXPLORER
else
TARGET = XCORE-AI-EXPLORER #for xs3 and also loopback test
endif
# The APP_NAME variable determines the name of the final .xe file. It should
# not include the .xe postfix. If left blank the name will default to
# the project name
APP_NAME =
# The flags passed to xcc when building the application
# You can also set the following to override flags for a particular language:
#
# XCC_XC_FLAGS, XCC_C_FLAGS, XCC_ASM_FLAGS, XCC_CPP_FLAGS
#
# If the variable XCC_MAP_FLAGS is set it overrides the flags passed to
# xcc for the final link (mapping) stage.
XCC_FLAGS_xs2 = $(EXTRA_BUILD_FLAGS) -O2 -g
XCC_FLAGS_xs3 = $(EXTRA_BUILD_FLAGS) -O2 -g
XCC_FLAGS_LOOPBACK = $(EXTRA_BUILD_FLAGS) -O2 -g -DMIDI_LOOPBACK=1
# The USED_MODULES variable lists other module used by the application.
USED_MODULES = lib_xua
#=============================================================================
# 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

View File

@@ -1,182 +0,0 @@
// Copyright 2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h>
#include <platform.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <xclib.h>
#include "xua.h"
#include "xud_device.h"
#include "midiinparse.h"
#include "midioutparse.h"
on tile[MIDI_TILE] : port p_midi_tx = XS1_PORT_4C;
#if(MIDI_RX_PORT_WIDTH == 4)
on tile[MIDI_TILE] : buffered in port:4 p_midi_rx = XS1_PORT_1F;
#elif(MIDI_RX_PORT_WIDTH == 1)
on tile[MIDI_TILE] : buffered in port:1 p_midi_rx = XS1_PORT_1F;
#endif
#define CLKBLK_MIDI XS1_CLKBLK_2
on tile[MIDI_TILE] : clock clk_midi = CLKBLK_MIDI;
#define MAX_TEST_COMMANDS 100
#define TEST_COMMAND_FILE_TX "midi_tx_cmds.txt"
#define TEST_COMMAND_FILE_RX "midi_rx_cmds.txt"
#define DEBUG 0 // Prints for debugging. Turn off for actual test
#if DEBUG
#define dprintf(...) printf(__VA_ARGS__)
#else
#define dprintf(...)
#endif
/* See hwsupport.xc */
void board_setup();
#ifndef CABLE_NUM
#define CABLE_NUM 0
#endif
unsigned midi_in_parse_helper(unsigned midi[3], struct midi_in_parse_state &m_state){
unsigned valid = 0;
unsigned packed = 0;
for(int i = 0; i < 3; i++){
dprintf("Packing byte %d 0x%x\n", i, midi[i]);
{valid, packed} = midi_in_parse(m_state, CABLE_NUM, midi[i]);
if(valid){
return packed;
}
}
return 0;
}
{unsigned, unsigned} read_config_file(uint8_t commands[MAX_TEST_COMMANDS][3])
{
unsigned tx_line_count = 0;
FILE * movable fptr_tx = fopen(TEST_COMMAND_FILE_TX,"rt");
if (fptr_tx == NULL) {
dprintf("WARNING: TX command file %s not found or unable to open.\n", TEST_COMMAND_FILE_TX);
} else {
unsigned a,b,c;
while (fscanf(fptr_tx, "%u %u %u\n", &a, &b, &c) == 3) {
commands[tx_line_count][0] = a;
commands[tx_line_count][1] = b;
commands[tx_line_count][2] = c;
//printf("Line %u params: 0x%x 0x%x 0x%x\n", tx_line_count, commands[tx_line_count][0], commands[tx_line_count][1], commands[tx_line_count][2]);
tx_line_count++;
if(tx_line_count > MAX_TEST_COMMANDS){
printf("ERROR: Too many lines in TX command file\n");
tx_line_count = MAX_TEST_COMMANDS;
}
}
}
fclose(move(fptr_tx));
unsigned rx_cmd_count = 0;
FILE * movable fptr_rx = fopen(TEST_COMMAND_FILE_RX,"rt");
if (fptr_rx == NULL) {
dprintf("WARNING: RX command file %s not found or unable to open.\n", TEST_COMMAND_FILE_RX);
} else {
if(fscanf(fptr_rx, "%u\n", &rx_cmd_count) != 1){
printf("ERROR: Not enough or too many items in RX command file line\n");
}
}
fclose(move(fptr_rx));
return {tx_line_count, rx_cmd_count};
}
void test(chanend c_midi){
uint8_t commands[MAX_TEST_COMMANDS][3] = {{0}};
unsigned num_to_tx = 0;
unsigned num_to_rx = 0;
{num_to_tx, num_to_rx} = read_config_file(commands);
dprintf("Sending %u MIDI command line(s) and receiving %u MIDI command(s)\n", num_to_tx, num_to_rx);
// For MIDI Rx
int is_ack;
unsigned rx_packet;
// Midi in parse state
struct midi_in_parse_state m_state;
reset_midi_state(m_state);
// Counters for Rx and Tx
unsigned tx_cmd_count = 0;
unsigned rx_cmd_count = 0;
timer tmr;
int t_tx; // Used for delay between Txs
int tx_end; // Used to wait for packet to have fully left
tmr :> t_tx;
tmr :> tx_end;
const int max_tx_time = XS1_TIMER_HZ / 31250 * 3 * (8 + 1 + 1); // 30 bits at 31.25 kbps is 0.96ms
const int tx_interval = XS1_TIMER_HZ / 8000; // SoF rate on HS
tx_end += max_tx_time; // One whole packet
while(tx_cmd_count < num_to_tx || rx_cmd_count < num_to_rx ){
select{
case midi_get_ack_or_data(c_midi, is_ack, rx_packet):
if(is_ack){
dprintf("ACK from Tx\n");
tx_cmd_count++;
} else {
unsigned midi_data[3] = {0};
unsigned byte_count = 0;
// Even though this is an Rx from MIDI we use midi_out_parse so we can decode the packet
{midi_data[0], midi_data[1], midi_data[2], byte_count} = midi_out_parse(byterev(rx_packet));
dprintf("Got packet from MIDI: 0x%8x\n", rx_packet);
// Note this needs to always print for capfd in pytest to pick it up
printf("dut_midi_rx: %u %u %u\n", midi_data[0], midi_data[1], midi_data[2]);
rx_cmd_count++;
midi_send_ack(c_midi);
}
break;
case tx_cmd_count < num_to_tx => tmr when timerafter(t_tx) :> int _:
unsigned midi[] = {commands[tx_cmd_count][0], commands[tx_cmd_count][1], commands[tx_cmd_count][2]};
// Even though this is a Tx to MIDI we use midi_in_parse_helper to form the packet from bytes
unsigned tx_packet = midi_in_parse_helper(midi, m_state);
midi_send_data(c_midi, byterev(tx_packet));
dprintf("Sent packet to midi: %u %u %u (0x%8x)\n", commands[tx_cmd_count][0], commands[tx_cmd_count][1], commands[tx_cmd_count][2], tx_packet);
t_tx += tx_interval;
tx_end += max_tx_time;
break;
}
}
dprintf("Tx and Rx count met - exiting after last tx complete.\n");
tmr when timerafter(tx_end) :> int _; // wait until packet definitely departed
exit(0);
}
int main(void)
{
chan c_midi;
par
{
on tile[0]: test(c_midi);
on tile[1]: usb_midi(p_midi_rx, p_midi_tx, clk_midi, c_midi, 0);
// Setup HW so we can run this on the MC board
on tile[0]: board_setup();
}
return 0;
}

View File

@@ -1,47 +0,0 @@
// Copyright 2017-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h>
#include <platform.h>
#include "xua.h"
on tile[0]: out port p_ctrl = XS1_PORT_8D; /* p_ctrl:
* [0:3] - Unused
* [4] - EN_3v3_N (1v0 hardware only)
* [5] - EN_3v3A
* [6] - EXT_PLL_SEL (CS2100:0, SI: 1)
* [7] - MCLK_DIR (Out:0, In: 1)
*/
#define USE_FRACTIONAL_N (0)
#if (USE_FRACTIONAL_N)
#define EXT_PLL_SEL__MCLK_DIR (0x00)
#else
#define EXT_PLL_SEL__MCLK_DIR (0x80)
#endif
/* Board setup for XU316 MC Audio (1v1) */
void board_setup()
{
/* "Drive high mode" - drive high for 1, non-driving for 0 */
set_port_drive_high(p_ctrl);
/* Drive control port to turn on 3V3 and mclk direction appropriately.
* Bits set to low will be high-z, pulled down */
p_ctrl <: EXT_PLL_SEL__MCLK_DIR | 0x20;
/* Wait for power supplies to be up and stable */
delay_milliseconds(10);
}
/* Configures the external audio hardware at startup. Note this runs on Tile[1] */
void AudioHwInit()
{
}
/* Configures the external audio hardware for the required sample frequency */
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC)
{
}

View File

@@ -1,28 +0,0 @@
// Copyright 2017-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#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 0 /* Number of channels from device to host */
#define I2S_CHANS_DAC 2 /* Number of I2S channels out of xCORE */
#define I2S_CHANS_ADC 0 /* 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 MIDI 1
#define MIDI_TILE 1
#define VENDOR_STR "XMOS"
#define VENDOR_ID 0x20B1
#define PRODUCT_STR_A2 "XUA Example"
#define PRODUCT_STR_A1 "XUA Example"
#define PID_AUDIO_1 1
#define PID_AUDIO_2 2
#define XUA_DFU_EN 0 /* Disable DFU (for simplicity of example */
#endif

View File

@@ -1,8 +0,0 @@
// Copyright 2017-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#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

View File

@@ -1,62 +0,0 @@
# Copyright 2014-2024 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import pytest
import Pyxsim
from Pyxsim import testers
from pathlib import Path
from uart_rx_checker import UARTRxChecker
from midi_test_helpers import midi_expect_rx, create_midi_rx_file, create_midi_tx_file, tempdir, MIDI_RATE
from distutils.dir_util import copy_tree # we're using python 3.7 and dirs_exist_ok=True isn't available until 3.8 :(
MAX_CYCLES = 15000000
#####
# This test takes the built binary, copies it to a tmp dir and runs the midi loopback test which sends some commands
# the firmware receives them, prints and compares with the expected output
#####
def test_midi_loopback(capfd, build_midi):
# Need tempdir as we use the same config files and this causes issues when using xdist
with tempdir() as tmpdirname:
config = "LOOPBACK"
copy_tree(build_midi, tmpdirname)
xe = str(Path(tmpdirname) / f"{config}/test_midi_{config}.xe")
midi_commands = [
[0x90, 60, 81], #note on
[0xc0, 15], #instr select
[0xe0, 0, 96], #pitch bend
[0xff], #MIDI reset
[0x80, 60, 81], #note off
[0xf0, 0x00, 0x21], [0x1D, 0x0b, 0x33], [0x3f, 0x1e, 0xf7], # Sysex, Ableton, 0b33f1e, terminator
[0xc0, 17], #instr select
]
create_midi_rx_file(len(midi_commands))
create_midi_tx_file(midi_commands)
expected = midi_expect_rx().expect(midi_commands)
tester = testers.ComparisonTester(expected, ordered = True)
simthreads = []
simargs = ["--max-cycles", str(MAX_CYCLES)]
#This is just for local debug so we can capture the traces if needed. It slows xsim down a lot
# simargs.extend(["--trace-to", "trace.txt", "--vcd-tracing", "-tile tile[1] -ports -o trace.vcd"])
Pyxsim.run_with_pyxsim(
xe,
simthreads=simthreads,
timeout=180,
simargs=simargs,
)
capture = capfd.readouterr().out
result = tester.run(capture.split("\n"))
# Print to console
with capfd.disabled():
print("CAPTURE:", capture)
print("EXPECTED:", expected)
assert result

View File

@@ -1,71 +0,0 @@
# Copyright 2014-2024 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import pytest
import Pyxsim
from Pyxsim import testers
from pathlib import Path
from uart_rx_checker import UARTRxChecker
from midi_test_helpers import midi_expect_rx, create_midi_rx_file, create_midi_tx_file, tempdir, MIDI_TEST_CONFIGS, MIDI_RATE
from distutils.dir_util import copy_tree # we're using python 3.7 and dirs_exist_ok=True isn't available until 3.8 :(
MAX_CYCLES = 15000000
#####
# This test takes the built binary, copies it to a tmp dir and runs the midi Rx test which sends some commands
# to using the UARTRX checker and the firmware receives them
#####
@pytest.mark.parametrize("config", MIDI_TEST_CONFIGS)
def test_rx(capfd, config, build_midi):
# Need tempdir as we use the same config files and this causes issues when using xdist
with tempdir() as tmpdirname:
copy_tree(build_midi, tmpdirname)
xe = str(Path(tmpdirname) / f"{config}/test_midi_{config}.xe")
midi_commands = [[0x90, 0x91, 0x90],# Invalid and should be discarded
[0x90, 60, 81], # Note on
[0x80, 60, 81]] # Note off
midi_command_expected = midi_commands[1:] # should skip invalid first message
create_midi_rx_file(len(midi_command_expected))
create_midi_tx_file()
expected = midi_expect_rx().expect(midi_command_expected)
tester = testers.ComparisonTester(expected, ordered = True)
rx_port = "tile[1]:XS1_PORT_1F"
tx_port = "tile[1]:XS1_PORT_4C" # Needed so that UARTRxChecker (a transmitter) knows when to start
baud = MIDI_RATE
bpb = 8
parity = 0
stop = 1
midi_commands_flattened = [item for row in midi_commands for item in row]
simthreads = [
UARTRxChecker(tx_port, rx_port, parity, baud, stop, bpb, midi_commands_flattened, debug=False)
]
simargs = ["--max-cycles", str(MAX_CYCLES)]
#This is just for local debug so we can capture the traces if needed. It slows xsim down so not good for Jenkins
# simargs.extend(["--trace-to", "trace.txt", "--vcd-tracing", "-tile tile[1] -ports -o trace.vcd"])
# with capfd.disabled(): # use to see xsim and tester output
Pyxsim.run_with_pyxsim(
xe,
simthreads=simthreads,
timeout=120,
simargs=simargs,
)
capture = capfd.readouterr().out
result = tester.run(capture.split("\n"))
# Print to console
with capfd.disabled():
print("CAPTURE:", capture)
print("EXPECTED:", expected)
assert result, f"expected: {expected}\n capture: {capture}"

View File

@@ -1,80 +0,0 @@
# Copyright 2014-2024 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import pytest
import Pyxsim
from Pyxsim import testers
from pathlib import Path
from uart_tx_checker import UARTTxChecker
from midi_test_helpers import midi_expect_tx, create_midi_tx_file, create_midi_rx_file, tempdir, MIDI_TEST_CONFIGS, MIDI_RATE
from distutils.dir_util import copy_tree # we're using python 3.7 and dirs_exist_ok=True isn't available until 3.8 :(
MAX_CYCLES = 15000000
#####
# This test takes the built binary, copies it to a tmp dir and runs the midi Tx test which sends some commands
# to the firmware and then receives them using the UARTTX checker
#####
@pytest.mark.parametrize("config", MIDI_TEST_CONFIGS)
def test_tx(capfd, config, build_midi):
# Need tempdir as we use the same config files and this causes issues when using xdist
with tempdir() as tmpdirname:
copy_tree(build_midi, tmpdirname)
xe = str(Path(tmpdirname) / f"{config}/test_midi_{config}.xe")
midi_commands = [[0x90, 0x91, 0x90],# Invalid and should be discarded
[0x90, 60, 81], # Note on
[0x80, 60, 81]] # Note off
# Make a 1D list from the 2D list [1:] because first is invalid and we expect to skip it
midi_command_expected = [[item for row in midi_commands[1:] for item in row]]
create_midi_tx_file(midi_commands)
create_midi_rx_file()
expected = midi_expect_tx().expect(midi_command_expected)
tester = testers.ComparisonTester(expected, ordered = True)
tx_port = "tile[1]:XS1_PORT_4C"
baud = MIDI_RATE
bpb = 8
parity = 0
stop = 1
length_of_test = sum(len(cmd) for cmd in midi_command_expected)
simthreads = [
UARTTxChecker(tx_port, parity, baud, length_of_test, stop, bpb, debug=False)
]
simargs = ["--max-cycles", str(MAX_CYCLES), "--trace-to", "trace.txt"]
#This is just for local debug so we can capture the traces if needed. It slows xsim down so not needed
# simargs.extend(["--vcd-tracing", "-tile tile[1] -ports -o trace.vcd"])
# with capfd.disabled(): # use to see xsim and tester output
Pyxsim.run_with_pyxsim(
xe,
simthreads=simthreads,
timeout=120,
simargs=simargs,
)
capture = capfd.readouterr().out
result = tester.run(capture.split("\n"))
# Print to console
with capfd.disabled():
print("CAPTURE:", capture)
print("EXPECTED:", expected)
# Show tail of trace if there is an error
if not result:
with capfd.disabled():
print("Simulator trace tail:")
with open("trace.txt") as trace:
output = trace.readlines()
print("".join(output[-25:]))
assert result, f"expected: {expected}\n capture: {capture}"

View File

@@ -3,12 +3,12 @@ TEST_FLAGS ?=
XCC_FLAGS_HS = -O3 -g -DXUD_CORE_CLOCK=600 -save-temps -DUSB_TILE=tile[0] -DLOCAL_CLOCK_INCREMENT=10000 -DLOCAL_CLOCK_MARGIN=100 \
-DBUS_SPEED=2 \
-DXUA_USE_SW_PLL=0 \
-DXUA_USE_APP_PLL=0 \
$(TEST_FLAGS)
XCC_FLAGS_FS = -O3 -g -DXUD_CORE_CLOCK=600 -save-temps -DUSB_TILE=tile[0] -DLOCAL_CLOCK_INCREMENT=10000 -DLOCAL_CLOCK_MARGIN=100 \
-DBUS_SPEED=1 \
-DXUA_USE_SW_PLL=0 \
-DXUA_USE_APP_PLL=0 \
$(TEST_FLAGS)
TARGET = test_xs3_600.xn

View File

@@ -1,4 +1,4 @@
// Copyright 2022-2024 XMOS LIMITED.
// Copyright 2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/* Simple test to ensure reference clock to CS2100 device continues when SOF clock not available
@@ -173,7 +173,6 @@ int main()
chan c_in[EP_COUNT_IN];
chan c_sof;
chan c_aud_ctl;
chan c_audio_rate_change;
interface pll_ref_if i_pll_ref;
@@ -186,7 +185,7 @@ int main()
XUA_Buffer_Ep(c_out[1], /* USB Audio Out*/
c_in[1], /* USB Audio In */
c_sof, c_aud_ctl, p_off_mclk, c_audio_rate_change, i_pll_ref
c_sof, c_aud_ctl, p_off_mclk, i_pll_ref
);
}
@@ -194,6 +193,4 @@ int main()
checker();
}
return 0;
}

View File

@@ -1,178 +0,0 @@
# Copyright 2022-2024 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import Pyxsim as px
from typing import Sequence
from functools import partial
# We need to disable output buffering for this test to work on MacOS; this has
# no effect on Linux systems. Let's redefine print once to avoid putting the
# same argument everywhere.
print = partial(print, flush=True)
Parity = dict(
UART_PARITY_EVEN=0,
UART_PARITY_ODD=1,
UART_PARITY_NONE=2,
UART_PARITY_BAD=3
)
# From tools 15.2.1 we need to add an extra factor to go from ps to fs
time_scaling_factor = 1000
class DriveHigh(px.SimThread):
def __init__(self, p):
self._p = p
def run(self):
xsi = self.xsi
xsi.drive_port_pins(self._p, 1);
class UARTRxChecker(px.SimThread):
def __init__(self, tx_port, rx_port, parity, baud, stop_bits, bpb, data=[0x7f, 0x00, 0x2f, 0xff],
intermittent=False, debug=False):
"""
Create a UARTRxChecker instance.
:param rx_port: Receive port of the UART device under test.
:param parity: Parity of the UART connection.
:param baud: BAUD rate of the UART connection.
:param stop_bits: Number of stop_bits for each UART byte.
:param bpb: Number of data bits per "byte" of UART data.
:param data: A list of bytes to send (default: [0x7f, 0x00, 0x2f, 0xff])
:param intermittent: Add a random delay between sent bytes.
"""
self._tx_port = tx_port
self._rx_port = rx_port
self._parity = parity
self._baud = baud
self._stop_bits = stop_bits
self._bits_per_byte = bpb
self._data = data
self._intermittent = intermittent
# Hex value of stop bits, as MSB 1st char, e.g. 0b11 : 0xC0
def send_byte(self, xsi, byte):
"""
Send a byte to the rx_port
:param xsi: XMOS Simulator Instance.
:param byte: Byte to send
"""
# Send start bit
self.send_start(xsi)
# Send data
self.send_data(xsi, byte)
# Send parity
self.send_parity(xsi, byte)
# Send stop bit(s)
self.send_stop(xsi)
def send_start(self, xsi):
"""
Send a start bit.
:param xsi: XMOS Simulator Instance.
"""
xsi.drive_port_pins(self._rx_port, 0)
self.wait_baud_time(xsi)
def send_data(self, xsi, byte):
"""
Write the data bits to the rx_port
:param xsi: XMOS Simulator Instance.
:param byte: Data to send.
"""
# print(f"Checker sent 0x{byte:02x}")
for x in range(self._bits_per_byte):
# print(f" Sending bit {x}")
xsi.drive_port_pins(self._rx_port, (byte & (0x01 << x)) >= 1)
# print(f" (x): {((byte & (0x01 << x))>=1)}")
self.wait_baud_time(xsi)
def send_parity(self, xsi, byte):
"""
Send the parity bit to the rx_port
:param xsi: XMOS Simulator Instance.
:param byte: Data to send parity of.
"""
parity = (self._parity - 1) % 3 #parity enum in lib_uart (old XC) different from SDK
if parity < 2:
crc_sum = 0
for x in range(self._bits_per_byte):
crc_sum += ((byte & (0x01 << x)) >= 1)
crc_sum += parity
# print "Parity for 0x%02x: %d" % (byte, crc_sum%2)
xsi.drive_port_pins(self._rx_port, crc_sum % 2)
self.wait_baud_time(xsi)
elif parity == Parity['UART_PARITY_BAD']:
# print "Sending bad parity bit"
self.send_bad_parity(xsi)
def send_stop(self, xsi):
"""
Send the stop bit(s) to the rx_port
:param xsi: XMOS Simulator Instance.
"""
for x in range(self._stop_bits):
xsi.drive_port_pins(self._rx_port, 1)
self.wait_baud_time(xsi)
def send_bad_parity(self, xsi):
"""
Send a parity bit of 1 to simulate an incorrect parity state.
:param xsi: XMOS Simulator Instance.
"""
# Always send a parity bit of 1
xsi.drive_port_pins(self._rx_port, 0)
self.wait_baud_time(xsi)
def get_bit_time(self):
"""
Returns the expected time between bits for the currently set BAUD rate.
Returns float value in nanoseconds.
"""
# Return float value in ps
return (1.0 / self._baud) * 1e12 * time_scaling_factor
def wait_baud_time(self, xsi):
"""
Wait for 1 bit time, as determined by the baud rate.
"""
self.wait_until(xsi.get_time() + self.get_bit_time())
def wait_half_baud_time(self, xsi):
"""
Wait for half a bit time, as determined by the baud rate.
"""
self.wait_until(xsi.get_time() + (self.get_bit_time() / 2))
def run(self):
xsi = self.xsi
# Drive the uart line high.
xsi.drive_port_pins(self._rx_port, 1)
# Wait for the device to bring up it's tx port, indicating it is ready
self.wait((lambda _x: self.xsi.is_port_driving(self._tx_port)))
# If we're doing an intermittent send, add a delay between each byte
# sent. Delay is in ns. 20,000ns = 20ms, 100,000ns = 100ms. Delays could
# be more variable, but it hurts test time substantially.
if self._intermittent:
for x in self._data:
k = randint(20000, 100000)
self.wait_until(xsi.get_time() + k)
self.send_byte(xsi, x)
else:
for x in self._data:
self.send_byte(xsi, x)

View File

@@ -1,248 +0,0 @@
# Copyright 2022-2024 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import Pyxsim as px
from typing import Sequence
from functools import partial
# We need to disable output buffering for this test to work on MacOS; this has
# no effect on Linux systems. Let's redefine print once to avoid putting the
# same argument everywhere.
print = partial(print, flush=True)
# From tools 15.2.1 we need to add an extra factor to go from ps to fs
time_scaling_factor = 1000
class UARTTxChecker(px.SimThread):
"""
This simulator thread will act as a UART device, and will check sent and
transations caused by the device, by looking at the tx pins.
"""
def __init__(self, tx_port, parity, baud, length, stop_bits, bpb, debug=False):
"""
Create a UARTTxChecker instance.
:param tx_port: Transmit port of the UART device under test.
:param parity: Parity of the UART connection.
:param baud: BAUD rate of the UART connection.
:param length: Length of transmission to check.
:param stop_bits: Number of stop_bits for each UART byte.
:param bpb: Number of data bits per "byte" of UART data.
"""
self._tx_port = tx_port
self._parity = parity
self._baud = baud
self._length = length
self._stop_bits = stop_bits
self._bits_per_byte = bpb
# Hex value of stop bits, as MSB 1st char, e.g. 0b11 : 0xC0
self.debug = debug
def get_port_val(self, xsi, port):
"""
Sample the state of a port
:rtype: int
:param xsi: XMOS Simulator Instance.
:param port: Port to sample.
"""
is_driving = xsi.is_port_driving(port)
if not is_driving:
return 1
else:
return xsi.sample_port_pins(port)
def get_bit_time(self):
"""
Returns the expected time between bits for the currently set BAUD rate.
Returns float value in nanoseconds.
:rtype: float
"""
# Return float value in ps
return (1.0/self._baud) * 1e12 * time_scaling_factor
def wait_baud_time(self, xsi):
"""
Wait for 1 bit time, as determined by the baud rate.
"""
self.wait_until(xsi.get_time() + self.get_bit_time())
return True
def wait_half_baud_time(self, xsi):
"""
Wait for half a bit time, as determined by the baud rate.
"""
self.wait_until(xsi.get_time() + (self.get_bit_time() / 2))
def read_packet(self, xsi, parity, length=4):
"""
Read a given number of bytes of UART traffic sent by the device.
Returns a list of bytes sent by the device.
:rtype: list
:param xsi: XMOS Simulator Instance.
:param parity: The UART partiy setting. See Parity.
:param length: The number of bytes to read. Defaults to 4.
"""
packet = []
start_time = 0
got_start_bit = False
initial_port_val = self.get_port_val(xsi, self._tx_port)
if self.debug: print("tx starts high: %s" % ("True" if initial_port_val else "False"))
for x in range(length):
byte = self.read_byte(xsi, parity)
if self.debug: print(f"Checker got byte: {byte}")
packet.append(chr(byte))
return packet
def read_byte(self, xsi, parity):
"""
Read 1 byte of UART traffic sent by the device
Returns an int, representing a byte read from the uart. Should be in the range 0 <= x < 2^bits_per_byte
:rtype: int
:param xsi: XMOS Simulator Instance.
:param parity: The UART partiy setting. See Parity.
"""
byte = 0
val = 0
# Recv start bit
initial_port_val = self.get_port_val(xsi, self._tx_port)
if initial_port_val == 1:
self.wait_for_port_pins_change([self._tx_port])
#else go for it as assume tx has just fallen with no interframe gap
# The tx line should go low for 1 bit time
if self.get_val_timeout(xsi, self._tx_port) == 0:
if self.debug: print("Start bit recv'd")
else:
print("Start bit issue")
return False
# recv the byte
crc_sum = 0
for j in range(self._bits_per_byte):
val = self.get_val_timeout(xsi, self._tx_port)
byte += (val << j)
crc_sum += val
if self.debug: print(f"Sampled {self._bits_per_byte} data bits: 0x{hex(byte)}")
# Check the parity if needs be
self.check_parity(xsi, crc_sum, parity)
# Get the stop bit
self.check_stopbit(xsi)
# Print a new line to split bytes in output
if self.debug: print()
return byte
def check_parity(self, xsi, crc_sum, parity):
"""
Read the parity bit and check it against a crc sum. Print correctness.
:param xsi: XMOS Simulator Instance.
:param crc_sum: The checksum to test parity against.
:param parity: The UART partiy setting. See Parity.
"""
if parity > 0:
parity_val = 0 if parity == 1 else 1
read = self.get_val_timeout(xsi, self._tx_port)
if read == (crc_sum + parity_val) % 2:
print("Parity bit correct")
else:
print("Parity bit incorrect. Got %d, expected %d" % (read, (crc_sum + parity_val) % 2))
else:
if self.debug: print("Parity bit correct")
def check_stopbit(self, xsi):
"""
Read the stop bit(s) of a UART transmission and print correctness.
:param xsi: XMOS Simulator Instance.
"""
stop_bits_correct = True
for i in range(self._stop_bits):
# The stop bits should stay high for this time
if self.get_val_timeout(xsi, self._tx_port) == 0:
stop_bits_correct = False
if self.debug: print("Stop bit correct: %s" % ("True" if stop_bits_correct else "False"))
def get_val_timeout(self, xsi, port):
"""
Get a value from a given port of the device, with a timeout determined
by the BAUD rate.
Returns whether the pin is high (True) or low (False)
:rtype: bool
:param xsi: XMOS Simulator Instance.
:param port: The port to sample.
"""
# This intentionally has a 0.3% slop. It is per-byte and gives some
# wiggle-room if the clock doesn't divide into ns nicely.
timeout = self.get_bit_time() * 0.5
short_timeout = self.get_bit_time() * 0.2485
# Allow for "rise" time
self.wait_until(xsi.get_time() + short_timeout)
# Get val
K = self.wait_time_or_pin_change(xsi, timeout, port)
# Allow for "fall" time
self.wait_until(xsi.get_time() + short_timeout)
return K
def wait_time_or_pin_change(self, xsi, timeout, port):
"""
Waits for a given timeout, or until a port changes state. Which ever
occurs 1st. Prints an error if the former causes the function to break.
Returns whether the pin is high (True) or low (False)
:rtype: bool
:param xsi: XMOS Simulator Instance.
:param timeout: Time to wait.
:param port: Port to sample.
"""
start_time = xsi.get_time()
start_val = self.get_port_val(xsi, port)
transitioned_during_wait = False
def _continue(_timeout, _start_time, _start_val):
if xsi.get_time() >= _start_time + _timeout:
return True
if self.get_port_val(xsi, port) != _start_val:
transitioned_during_wait = True
return True
return False
wait_fun = (lambda x: _continue(timeout, start_time, start_val))
self.wait(wait_fun)
# Start value should *not* have changed during timeout
if transitioned_during_wait:
print("FAIL :: Unexpected Transition.")
return start_val
def run(self):
# Wait for the xcore to bring the uart tx port up
self.wait((lambda x: self.xsi.is_port_driving(self._tx_port)))
self.wait((lambda x: self.get_port_val(self.xsi, self._tx_port) == 1))
K = self.read_packet(self.xsi, self._parity, self._length)
# Print each member of K as a hex byte
# inline lambda function mapped over a list? awh yiss.
print("uart_tx_checker:", " ".join(map((lambda x: "0x%02x" % ord(x)), K)))

View File

@@ -1,97 +1,103 @@
cmake_minimum_required(VERSION 3.21)
include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake)
cmake_minimum_required(VERSION 3.13)
# Auto-generate schedule and top level config files
if( NOT ${Python3_FOUND} )
message(FATAL_ERROR "Python3 not found for running . ")
set(XMOS_TOOLS_PATH $ENV{XMOS_TOOL_PATH}/bin)
#**********************
# Setup XMOS toolchain
#**********************
if(NOT DEFINED ENV{XUA_PATH})
message(FATAL_ERROR "XUA_PATH environment variable not defined")
# some more commands
endif()
include("$ENV{XUA_PATH}/cmake_utils/xmos_toolchain.cmake")
#copy conftest.py in the build directory since pytest_collect_file only collects tests from the directory tree where conftest.py is present
configure_file( conftest.py conftest.py COPYONLY )
## executable output directory
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
# Set unity runner generate script
set(GEN_RUNNER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/generate_unity_runner.py)
# Create directory for runner files
set(RUNNERS_DIR ${CMAKE_CURRENT_LIST_DIR}/src.runners )
file(MAKE_DIRECTORY ${RUNNERS_DIR} )
# Find unit test files
file(GLOB_RECURSE TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/test_*/*.c)
# For every source file in xua_unit_tests/
foreach(TESTFILE ${TEST_SOURCES})
set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..)
# Get test name from C file stem
cmake_path(GET TESTFILE STEM TESTNAME)
project(${TESTNAME})
message(STATUS "Processing unit test: ${TESTNAME}")
# Create runner file directory
file(MAKE_DIRECTORY ${RUNNERS_DIR}/${TESTNAME})
#####################
## Create runner file
#####################
set( RUNNER_FILE ${RUNNERS_DIR}/${TESTNAME}/${TESTNAME}_Runner.c )
set( GEN_RUNNER_SCRIPT_BYPRODUCTS ${RUNNER_FILE})
unset(GEN_RUNNER_SCRIPT_ARGS)
list(APPEND GEN_RUNNER_SCRIPT_ARGS --project-root ${XMOS_SANDBOX_DIR})
list(APPEND GEN_RUNNER_SCRIPT_ARGS --source-file ${TESTFILE})
list(APPEND GEN_RUNNER_SCRIPT_ARGS --runner-file ${RUNNER_FILE})
## Add command to generate runner file
add_custom_command(
OUTPUT ${RUNNER_FILE}
COMMAND python ${GEN_RUNNER_SCRIPT} ${GEN_RUNNER_SCRIPT_ARGS}
COMMENT "Generate XUA Unit Test Runner" )
##########################
## Do xcommon cmake build
##########################
set(APP_HW_TARGET XK-EVK-XU316)
set(APP_DEPENDENT_MODULES "lib_xua"
"lib_unity(2.5.2)")
# set(APP_PCA_ENABLE ON)
set(APP_COMPILER_FLAGS ${EXTRA_BUILD_FLAGS} -fcomment-asm
-Wall
-O2
-report
-g
-fxscope
-DUSB_TILE=tile[0]
-DUNITY_SUPPORT_64
-DUNITY_INCLUDE_DOUBLE
-DXUD_CORE_CLOCK=600
-DXUD_SERIES_SUPPORT=4
-DXASSERT_ENABLE_ASSERTIONS=0
)
# For HID tests only enable HID
if(${TESTFILE} MATCHES ".+hid.*")
list(APPEND APP_COMPILER_FLAGS "-DHID_CONTROLS=1")
endif()
#**********************
# Project
#**********************
# Disable in-source build.
#if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
# message(FATAL_ERROR "In-source build is not allowed! Please specify a build folder.\n\tex:cmake -B build")
#endif()
# Workaround for xcommon cmake pre-pending CMAKE_CURRENT_LIST_DIR
string(REPLACE ${CMAKE_CURRENT_LIST_DIR} "" UNIT_TEST_SOURCE_RELATIVE ${TESTFILE})
string(REPLACE ${CMAKE_CURRENT_LIST_DIR} "" RUNNER_FILE_RELATIVE ${RUNNER_FILE})
## Define project
project(xua_unit_tests VERSION 0.1.0)
set(APP_C_SRCS ${RUNNER_FILE_RELATIVE}
${UNIT_TEST_SOURCE_RELATIVE}
)
## Enable languages for project
enable_language(CXX XC C ASM)
message(STATUS "CAME HERE")
add_custom_target("runners" ALL)
add_custom_command(
TARGET runners
COMMAND python generate_unity_runners.py
COMMENT "generate unity runners"
)
get_filename_component(TEST_FILE_DIR ${TESTFILE} DIRECTORY)
set(APP_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/src
${TEST_FILE_DIR}
${XMOS_SANDBOX_DIR}/lib_xud/lib_xud/src/user/class)
message(STATUS "CAME HERE 1")
file( GLOB APP_SOURCES src/test*.xc )
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
foreach( testsourcefile ${APP_SOURCES} )
get_filename_component(ITEM_NAME ${testsourcefile} NAME_WE)
message(STATUS "item_name " ${ITEM_NAME})
add_executable(${ITEM_NAME})
set(APP_COMPILER_FLAGS
"-O2"
"-g"
"-Wall"
"-report"
"-fxscope"
"-target=XCORE-AI-EXPLORER"
"${CMAKE_CURRENT_SOURCE_DIR}/config.xscope"
"-DHID_CONTROLS=1"
"-DUNITY_SUPPORT_64"
"-DUNITY_INCLUDE_DOUBLE"
)
set_source_files_properties(
"runners/${ITEM_NAME}/${ITEM_NAME}_Runner.c"
PROPERTIES GENERATED TRUE
)
XMOS_REGISTER_APP()
set(APP_SRCS
${testsourcefile}
"runners/${ITEM_NAME}/${ITEM_NAME}_Runner.c"
"${CMAKE_CURRENT_SOURCE_DIR}/../../../Unity/src/unity.c"
)
set(APP_INCLUDES
"src"
"${CMAKE_CURRENT_SOURCE_DIR}/../../../Unity/src"
)
set(APP_DEPENDENT_MODULES
"lib_xua(>=2.0.0)"
"lib_logging(>=3.0.0)"
"lib_xassert(>=4.0.0)"
"lib_xud(>=2.0.0)"
"lib_spdif(>=4.0.0)"
"lib_mic_array(>=4.0.0)"
)
endforeach()
include("$ENV{XUA_PATH}/cmake_utils/xua.cmake")
set_target_properties(${ITEM_NAME} PROPERTIES OUTPUT_NAME ${ITEM_NAME}.xe)
target_compile_options(${ITEM_NAME} PRIVATE ${APP_COMPILER_FLAGS})
target_include_directories(${ITEM_NAME}
PRIVATE ${APP_INCLUDES}
PRIVATE ${XUA_INCLUDES_ALL}
)
target_sources(${ITEM_NAME}
PRIVATE ${APP_SRCS}
PRIVATE ${XUA_SRCS_ALL}
)
add_dependencies(${ITEM_NAME} runners)
target_link_options(${ITEM_NAME} PRIVATE ${APP_COMPILER_FLAGS})
## Set any additional flags only for C++
set(CMAKE_CXX_FLAGS "-std=c++11")
endforeach( testsourcefile ${APP_SOURCES} )
message(STATUS ${APP_SOURCES})
message(STATUS "CAME HERE 2")
## Register the application
#XMOS_REGISTER_APP()

View File

@@ -1,4 +1,4 @@
# Copyright 2021-2024 XMOS LIMITED.
# Copyright 2021-2022 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
from __future__ import print_function
from builtins import str
@@ -28,7 +28,7 @@ class UnityTestSource(pytest.File):
# unit_tests/ <- Test root directory
# |-- bin/ <- Compiled binaries of the test runners
# |-- conftest.py <- This file
# |-- src.runners <- Auto-generated buildable source of test binaries
# |-- runners/ <- Auto-generated buildable source of test binaries
# |-- src/ <- Unity test functions
# `-- wscript <- Build system file used to generate/build runners
xe_name = ((os.path.basename(self.name)).split("."))[0] + ".xe"
@@ -51,9 +51,9 @@ class UnityTestExecutable(pytest.Item):
print("run axe for executable ", self.name)
test_output = subprocess.check_output(["axe", self.name], text=True)
else:
print("run xsim for executable ", self.name)
print("run xrun for executable ", self.name)
test_output = subprocess.check_output(
["xsim", self.name],
["xrun", "--io", "--id", "0", self.name],
text=True,
stderr=subprocess.STDOUT,
)

View File

@@ -1,59 +0,0 @@
# Copyright 2024 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import glob
import os.path
import subprocess
import sys
import argparse
def parse_arguments():
parser = argparse.ArgumentParser()
parser.add_argument("--project-root", nargs='?', help="Project root directory")
parser.add_argument("--source-file", nargs='?', help="source file.")
parser.add_argument("--runner-file", nargs='?', help="runner file.")
args = parser.parse_args()
return args
def get_ruby():
"""
Check ruby is avaliable and return the command to invoke it.
"""
interpreter_name = 'ruby'
try:
dev_null = open(os.devnull, 'w')
# Call the version command to check the interpreter can be run
subprocess.check_call([interpreter_name, '--version'],
stdout=dev_null,
close_fds=True)
except OSError as e:
print("Failed to run Ruby interpreter: {}".format(e), file=sys.stderr)
exit(1) # TODO: Check this is the correct way to kill xwaf on error
return interpreter_name
def get_unity_runner_generator(project_root_path):
"""
Check the Unity generate_test_runner script is avaliable, and return the
path to it.
"""
unity_runner_generator = os.path.join(
project_root_path, 'Unity', 'auto', 'generate_test_runner.rb')
if not os.path.exists(unity_runner_generator):
print("Unity repo not found in workspace", file=sys.stderr)
exit(1) # TODO: Check this is the correct way to kill xwaf on error
return unity_runner_generator
if __name__ == "__main__":
args = parse_arguments()
print(f"in python: root {args.project_root}, source {args.source_file}, runner {args.runner_file}")
try:
subprocess.check_call([get_ruby(),
get_unity_runner_generator(args.project_root),
args.source_file,
args.runner_file])
except OSError as e:
print("Ruby generator failed for {}\n\t{}".format(unity_test_path, e),
file=sys.stderr)
exit(1) # TODO: Check this is the correct way to kill xwaf on error

View File

@@ -0,0 +1,134 @@
# Copyright 2021-2022 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import glob
import os.path
import subprocess
import sys
UNITY_TEST_DIR = "src"
UNITY_TEST_PREFIX = "test_"
UNITY_RUNNER_DIR = "runners"
UNITY_RUNNER_SUFFIX = "_Runner"
project_root = os.path.join("..", "..", "..")
def get_ruby():
"""
Check ruby is avaliable and return the command to invoke it.
"""
interpreter_name = "ruby"
try:
dev_null = open(os.devnull, "w")
# Call the version command to check the interpreter can be run
subprocess.check_call(
[interpreter_name, "--version"], stdout=dev_null, close_fds=True
)
except OSError as e:
print("Failed to run Ruby interpreter: {}".format(e), file=sys.stderr)
exit(1) # TODO: Check this is the correct way to kill xwaf on error
return interpreter_name
def get_unity_runner_generator(project_root_path):
"""
Check the Unity generate_test_runner script is avaliable, and return the
path to it.
"""
unity_runner_generator = os.path.join(
project_root_path, "Unity", "auto", "generate_test_runner.rb"
)
if not os.path.exists(unity_runner_generator):
print("Unity repo not found in workspace", file=sys.stderr)
exit(1) # TODO: Check this is the correct way to kill xwaf on error
return unity_runner_generator
def get_test_name(test_path):
"""
Return the test name by removing the extension from the filename.
"""
return os.path.splitext(os.path.basename(test_path))[0]
def get_file_type(filename):
"""
Return the extension from the filename.
"""
return filename.rsplit(".")[-1:][0]
def generate_unity_runner(
project_root_path, unity_test_path, unity_runner_dir, unity_runner_suffix
):
"""
Invoke the Unity runner generation script for the given test file, and
return the path to the generated file. The output directory will be created
if it does not already exist.
"""
runner_path = os.path.join(
os.path.join(unity_runner_dir, get_test_name(unity_test_path))
)
if not os.path.exists(runner_path):
os.makedirs(runner_path)
unity_runner_path = os.path.join(
runner_path, get_test_name(unity_test_path) + unity_runner_suffix + "." + "c"
)
try:
subprocess.check_call(
[
get_ruby(),
get_unity_runner_generator(project_root_path),
unity_test_path,
unity_runner_path,
]
)
except OSError as e:
print(
"Ruby generator failed for {}\n\t{}".format(unity_test_path, e),
file=sys.stderr,
)
exit(1) # TODO: Check this is the correct way to kill xwaf on error
def find_unity_test_paths(unity_test_dir, unity_test_prefix):
"""
Return a list of all file paths with the unity_test_prefix found in the
unity_test_dir.
"""
return glob.glob(os.path.join(unity_test_dir, unity_test_prefix + "*"))
def find_unity_tests(unity_test_dir, unity_test_prefix):
"""
Return a dictionary of all {test names, test language} pairs with the
unity_test_prefix found in the unity_test_dir.
"""
unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix)
print("unity_test_paths = ", unity_test_paths)
return {get_test_name(path): get_file_type(path) for path in unity_test_paths}
def find_unity_test_paths(unity_test_dir, unity_test_prefix):
"""
Return a list of all file paths with the unity_test_prefix found in the
unity_test_dir.
"""
return glob.glob(os.path.join(unity_test_dir, unity_test_prefix + "*"))
def generate_runners():
UNITY_TESTS = find_unity_tests(UNITY_TEST_DIR, UNITY_TEST_PREFIX)
print("UNITY_TESTS = ", UNITY_TESTS)
unity_test_paths = find_unity_test_paths(UNITY_TEST_DIR, UNITY_TEST_PREFIX)
print("unity_test_paths = ", unity_test_paths)
for unity_test_path in unity_test_paths:
generate_unity_runner(
project_root, unity_test_path, UNITY_RUNNER_DIR, UNITY_RUNNER_SUFFIX
)
if __name__ == "__main__":
generate_runners()

View File

@@ -1,222 +0,0 @@
// Copyright 2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stddef.h>
#include <stdio.h>
#include "xua_unit_tests.h"
#include "../../../lib_xua/src/midi/midiinparse.h"
#define NUM_CHANS 16
#define NOTE_OFF 128
#define NOTE_ON 144
#define PRESSURE 160
#define CONTROL 176
#define PROGRAM 192
#define PRESSURE_VAL 208
#define RANGE 224
#define SYSEX_SOM 240
#define SYSEX_EOM 247
#define DATA_RANGE 128
#define DATA_MASK (DATA_RANGE - 1)
#ifndef NUM_TESTS_PER_TEST
#define NUM_TESTS_PER_TEST 30
#endif
#ifndef CABLE_NUM
#define CABLE_NUM 0
#endif
#define RANDOM_SEED 6031769
unsigned midi_in_parse_ut(unsigned midi[3]){
// printf("Composing data: 0x%x 0x%x 0x%x\n", midi[0], midi[1], midi[2]);
struct midi_in_parse_state m_state;
void * mips = &m_state;
reset_midi_state_c_wrapper(mips);
unsigned valid = 0;
unsigned packed = 0;
midi_in_parse_c_wrapper(mips, CABLE_NUM, midi[0], &valid, &packed);
// printf("Valid: %d data: %u\n", valid, packed);
if(valid){
return packed;
}
midi_in_parse_c_wrapper(mips, CABLE_NUM, midi[1], &valid, &packed);
// printf("Valid: %d data: %u\n", valid, packed);
if(valid){
return packed;
}
midi_in_parse_c_wrapper(mips, CABLE_NUM, midi[2], &valid, &packed);
// printf("Valid: %d data: %u\n", valid, packed);
if(valid){
return packed;
}
return 0;
}
unsigned rndm = RANDOM_SEED;
void test_midi_note(void) {
for(int cmd = NOTE_OFF; cmd < NOTE_ON + NUM_CHANS; cmd++){
for(int test = 0; test < NUM_TESTS_PER_TEST; test++){
unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK};
unsigned packed = midi_in_parse_ut(midi_ref);
unsigned midi_dut[3] = {0};
unsigned size = 0;
midi_out_parse_c_wrapper(packed, midi_dut, &size);
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]);
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]);
//TEST_ASSERT_EQUAL_UINT32_ARRAY not working!?
for(int i = 0; i < size; i++){
TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]);
}
}
}
}
void test_midi_pressure(void) {
for(int cmd = PRESSURE; cmd < PRESSURE + NUM_CHANS; cmd++){
for(int test = 0; test < NUM_TESTS_PER_TEST; test++){
unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK};
unsigned packed = midi_in_parse_ut(midi_ref);
unsigned midi_dut[3] = {0};
unsigned size = 0;
midi_out_parse_c_wrapper(packed, midi_dut, &size);
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]);
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]);
//TEST_ASSERT_EQUAL_UINT32_ARRAY not working!?
for(int i = 0; i < size; i++){
TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]);
}
}
}
}
void test_midi_control(void) {
for(int cmd = CONTROL; cmd < CONTROL + NUM_CHANS; cmd++){
for(int test = 0; test < NUM_TESTS_PER_TEST; test++){
unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK};
unsigned packed = midi_in_parse_ut(midi_ref);
unsigned midi_dut[3] = {0};
unsigned size = 0;
midi_out_parse_c_wrapper(packed, midi_dut, &size);
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]);
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]);
//TEST_ASSERT_EQUAL_UINT32_ARRAY not working!?
for(int i = 0; i < size; i++){
TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]);
}
}
}
}
void test_midi_program(void) {
for(int cmd = PROGRAM; cmd < PROGRAM + NUM_CHANS; cmd++){
for(int test = 0; test < NUM_TESTS_PER_TEST; test++){
unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK};
unsigned packed = midi_in_parse_ut(midi_ref);
unsigned midi_dut[3] = {0};
unsigned size = 0;
midi_out_parse_c_wrapper(packed, midi_dut, &size);
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]);
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]);
//TEST_ASSERT_EQUAL_UINT32_ARRAY not working!?
for(int i = 0; i < size; i++){
TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]);
}
}
}
}
void test_midi_pressure_val(void) {
for(int cmd = PRESSURE_VAL; cmd < PRESSURE_VAL + NUM_CHANS; cmd++){
for(int test = 0; test < NUM_TESTS_PER_TEST; test++){
unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK};
unsigned packed = midi_in_parse_ut(midi_ref);
unsigned midi_dut[3] = {0};
unsigned size = 0;
midi_out_parse_c_wrapper(packed, midi_dut, &size);
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]);
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]);
//TEST_ASSERT_EQUAL_UINT32_ARRAY not working!?
for(int i = 0; i < size; i++){
TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]);
}
}
}
}
void test_midi_range(void) {
for(int cmd = RANGE; cmd < RANGE + NUM_CHANS; cmd++){
for(int test = 0; test < NUM_TESTS_PER_TEST; test++){
unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK};
unsigned packed = midi_in_parse_ut(midi_ref);
unsigned midi_dut[3] = {0};
unsigned size = 0;
midi_out_parse_c_wrapper(packed, midi_dut, &size);
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]);
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]);
//TEST_ASSERT_EQUAL_UINT32_ARRAY not working!?
for(int i = 0; i < size; i++){
TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]);
}
}
}
}
// https://cmtext.indiana.edu/MIDI/chapter3_system_messages.php
void test_midi_sys_ex(void) {
const unsigned MAX_SYS_EX_LENGTH = 200; // https://cycling74.com/forums/maximu-sysex-length
for(int sys_ex_length = 1; sys_ex_length < MAX_SYS_EX_LENGTH; sys_ex_length++){
unsigned midi_ref[1 + MAX_SYS_EX_LENGTH + 1] = {0}; // Add SOM + EOM.
unsigned msg_len = 1 + sys_ex_length + 1;
// Build message
midi_ref[0] = SYSEX_SOM;
midi_ref[1] = 0x00; // Extended manf ID
midi_ref[2] = random(&rndm) & DATA_MASK; // Manf ID 0
midi_ref[3] = random(&rndm) & DATA_MASK; // Manf ID 1
for(unsigned i = 4; i < msg_len - 1; i++){
midi_ref[i] = random(&rndm) & DATA_MASK; // Some data
}
midi_ref[msg_len - 1] = SYSEX_EOM; // End of sys-ex
unsigned midi_dut[1 + MAX_SYS_EX_LENGTH + 1 + 2] = {0}; // Add SOM + EOM. Add 2 because msg_size % 3 may be up to 2.
struct midi_in_parse_state mips;
reset_midi_state_c_wrapper(&mips);
unsigned valid = 0;
unsigned packed = 0;
unsigned out_idx = 0;
for(unsigned i = 0; i < msg_len; i++){
// printf("Ref byte: %d - 0x%x\n", i, midi_ref[i]);
midi_in_parse_c_wrapper((void * )&mips, CABLE_NUM, midi_ref[i], &valid, &packed);
if(valid){
unsigned size = 0;
midi_out_parse_c_wrapper(packed, &midi_dut[out_idx], &size);
// printf("SIZE: %u PACKED: 0x%8x, 0x%x 0x%x 0x%x\n", size, packed, midi_dut[out_idx + 0], midi_dut[out_idx + 1], midi_dut[out_idx + 2]);
out_idx += size;
}
}
for(unsigned i = 0; i < msg_len; i++){
// printf("%d Ref: 0x%x DUT: 0x%x\n", i, midi_ref[i], midi_dut[i]);
TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]);
}
printf("Sysex PASS length: %u\n", msg_len);
}
}

View File

@@ -1,115 +0,0 @@
// Copyright 2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "xua_unit_tests.h"
#include "../../../lib_xua/src/midi/queue.h"
#define DEBUG 0
#if DEBUG
#define dprintf(...) printf(__VA_ARGS__)
#else
#define dprintf(...)
#endif
#define RANDOM_SEED 55378008
#define USB_MIDI_DEVICE_OUT_FIFO_SIZE 1024
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
unsigned rndm = RANDOM_SEED;
void test_midi_queue_init(void) {
queue_t symbol_fifo;
unsigned symbol_fifo_storage[USB_MIDI_DEVICE_OUT_FIFO_SIZE];
memset(symbol_fifo_storage, USB_MIDI_DEVICE_OUT_FIFO_SIZE * sizeof(unsigned), 0xdb); // Non zero
queue_init_c_wrapper(&symbol_fifo, ARRAY_SIZE(symbol_fifo_storage));
int empty = queue_is_empty_c_wrapper(&symbol_fifo);
TEST_ASSERT_EQUAL_INT32(1, empty);
int full = queue_is_full_c_wrapper(&symbol_fifo);
TEST_ASSERT_EQUAL_INT32(0, full);
unsigned items = queue_items_c_wrapper(&symbol_fifo);
TEST_ASSERT_EQUAL_UINT32(0, items);
unsigned space = queue_space_c_wrapper(&symbol_fifo);
TEST_ASSERT_EQUAL_UINT32(USB_MIDI_DEVICE_OUT_FIFO_SIZE, space);
// Pop empty queue
unsigned entry = queue_pop_word_c_wrapper(&symbol_fifo, symbol_fifo_storage);
TEST_ASSERT_EQUAL_UINT32(MIDI_OUT_NULL_MESSAGE, entry);
space = queue_space_c_wrapper(&symbol_fifo);
TEST_ASSERT_EQUAL_UINT32(USB_MIDI_DEVICE_OUT_FIFO_SIZE, space);
}
void test_midi_queue_full(void) {
queue_t symbol_fifo;
unsigned symbol_fifo_storage[USB_MIDI_DEVICE_OUT_FIFO_SIZE];
queue_init_c_wrapper(&symbol_fifo, ARRAY_SIZE(symbol_fifo_storage));
for(unsigned i = 0; i < USB_MIDI_DEVICE_OUT_FIFO_SIZE; i++){
queue_push_word_c_wrapper(&symbol_fifo, symbol_fifo_storage, 1111);
}
int empty = queue_is_empty_c_wrapper(&symbol_fifo);
TEST_ASSERT_EQUAL_INT32(0, empty);
int full = queue_is_full_c_wrapper(&symbol_fifo);
TEST_ASSERT_EQUAL_INT32(1, full);
unsigned items = queue_items_c_wrapper(&symbol_fifo);
TEST_ASSERT_EQUAL_UINT32(USB_MIDI_DEVICE_OUT_FIFO_SIZE, items);
unsigned space = queue_space_c_wrapper(&symbol_fifo);
TEST_ASSERT_EQUAL_UINT32(0, space);
// We want no exception here and this to be ignored
queue_push_word_c_wrapper(&symbol_fifo, symbol_fifo_storage, 2222);
unsigned entry = queue_pop_word_c_wrapper(&symbol_fifo, symbol_fifo_storage);
TEST_ASSERT_EQUAL_UINT32(1111, entry);
}
void test_midi_queue_push_pop(void) {
queue_t symbol_fifo;
unsigned symbol_fifo_storage[USB_MIDI_DEVICE_OUT_FIFO_SIZE];
queue_init_c_wrapper(&symbol_fifo, ARRAY_SIZE(symbol_fifo_storage));
for(unsigned i = 0; i < USB_MIDI_DEVICE_OUT_FIFO_SIZE; i++){
int items = queue_items_c_wrapper(&symbol_fifo);
dprintf("Pre i: %u items: %d\n", i, items);
TEST_ASSERT_EQUAL_UINT32(i, items);
unsigned entry = i + 1000;
queue_push_word_c_wrapper(&symbol_fifo, symbol_fifo_storage, entry);
dprintf("pushed: %u\n", entry);
items = queue_items_c_wrapper(&symbol_fifo);
TEST_ASSERT_EQUAL_UINT32(i + 1, items);
dprintf("Post items: %d\n", items);
}
unsigned counter = 0;
for(int i = USB_MIDI_DEVICE_OUT_FIFO_SIZE; i > 0; i--){
int items = queue_items_c_wrapper(&symbol_fifo);
dprintf("i: %u items: %d\n", i, items);
TEST_ASSERT_EQUAL_UINT32(i, items);
unsigned entry = queue_pop_word_c_wrapper(&symbol_fifo, symbol_fifo_storage);
unsigned expected = 1000 + counter;
dprintf("expected: %u got: %d\n", expected, entry);
TEST_ASSERT_EQUAL_UINT32(expected, entry);
counter++;
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2024 XMOS LIMITED.
// Copyright 2021-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stddef.h>
#include <stdio.h>
@@ -47,26 +47,23 @@ static unsigned construct_usage_header( unsigned size )
return header;
}
void test_init( void )
void setUp( void )
{
hidReportInit();
hidResetReportDescriptor();
}
void test_validate_report( void ) {
test_init();
unsigned retVal = hidReportValidate();
TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal );
}
void test_reportid_in_use( void ) {
test_init();
unsigned reportIdInUse = hidIsReportIdInUse();
TEST_ASSERT_EQUAL_UINT( 1, reportIdInUse );
}
void test_get_next_valid_report_id( void ) {
test_init();
unsigned reportId = 0U;
reportId = hidGetNextValidReportId(reportId);
@@ -83,7 +80,6 @@ void test_get_next_valid_report_id( void ) {
}
void test_is_report_id_valid( void ) {
test_init();
unsigned isValid = 0;
unsigned reportId = 0;
@@ -110,7 +106,6 @@ void test_is_report_id_valid( void ) {
// Basic report descriptor tests
void test_unprepared_hidGetReportDescriptor( void )
{
test_init();
unsigned char* reportDescPtr = hidGetReportDescriptor();
TEST_ASSERT_NULL( reportDescPtr );
@@ -123,7 +118,6 @@ void test_unprepared_hidGetReportDescriptor( void )
void test_prepared_hidGetReportDescriptor( void )
{
test_init();
hidPrepareReportDescriptor();
unsigned char* reportDescPtr = hidGetReportDescriptor();
TEST_ASSERT_NOT_NULL( reportDescPtr );
@@ -143,7 +137,6 @@ void test_prepared_hidGetReportDescriptor( void )
void test_reset_unprepared_hidGetReportDescriptor( void )
{
test_init();
hidPrepareReportDescriptor();
hidResetReportDescriptor();
unsigned char* reportDescPtr = hidGetReportDescriptor();
@@ -152,7 +145,6 @@ void test_reset_unprepared_hidGetReportDescriptor( void )
void test_reset_prepared_hidGetReportDescriptor( void )
{
test_init();
hidPrepareReportDescriptor();
hidResetReportDescriptor();
hidPrepareReportDescriptor();
@@ -162,7 +154,6 @@ void test_reset_prepared_hidGetReportDescriptor( void )
void test_report_id_limit( void )
{
test_init();
unsigned reportIdLimit = hidGetReportIdLimit();
TEST_ASSERT_EQUAL_UINT( HID_REPORTID_LIMIT, reportIdLimit );
}
@@ -170,7 +161,6 @@ void test_report_id_limit( void )
// Basic item tests
void test_max_loc_hidGetReportItem( void )
{
test_init();
unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ];
unsigned char header;
unsigned char page;
@@ -209,7 +199,6 @@ void test_max_loc_hidGetReportItem( void )
void test_min_loc_hidGetReportItem( void )
{
test_init();
unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ];
unsigned char header;
unsigned char page;
@@ -247,7 +236,6 @@ void test_min_loc_hidGetReportItem( void )
void test_invalid_report_id( void )
{
test_init();
unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD2 };
unsigned char header = 0x33;
unsigned char page = 0x44;
@@ -265,7 +253,6 @@ void test_invalid_report_id( void )
void test_unused_report_id( void )
{
test_init();
unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD2 };
unsigned char header = 0x33;
unsigned char page = 0x44;
@@ -283,7 +270,6 @@ void test_unused_report_id( void )
void test_overflow_bit_hidGetReportItem( void )
{
test_init();
unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 };
unsigned char header = 0xAA;
unsigned char page = 0x44;
@@ -321,7 +307,6 @@ void test_overflow_bit_hidGetReportItem( void )
void test_overflow_byte_hidGetReportItem( void )
{
test_init();
unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 };
unsigned char header = 0xAA;
unsigned char page = 0x44;
@@ -359,7 +344,6 @@ void test_overflow_byte_hidGetReportItem( void )
void test_underflow_bit_hidGetReportItem( void )
{
test_init();
unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 };
unsigned char header = 0xAA;
unsigned char page = 0x44;
@@ -397,7 +381,6 @@ void test_underflow_bit_hidGetReportItem( void )
void test_underflow_byte_hidGetReportItem( void )
{
test_init();
unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 };
unsigned char header = 0xAA;
unsigned char page = 0x44;
@@ -436,7 +419,6 @@ void test_underflow_byte_hidGetReportItem( void )
// Configurable and non-configurable item tests
void test_configurable_item_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 1;
const unsigned bit = REPORT1_MIN_VALID_BIT;
const unsigned byte = REPORT1_MIN_VALID_BYTE;
@@ -458,7 +440,6 @@ void test_configurable_item_hidSetReportItem( void )
// Testing that the high byte of the report gets correctly cleared
void test_configurable_item_hidSetReportItem_multibyte_orig( void )
{
test_init();
const unsigned reportId = 2;
const unsigned bit = 1; // This byte&bit combo is originally set be 2 bytes long in the header
const unsigned byte = 0;
@@ -479,7 +460,6 @@ void test_configurable_item_hidSetReportItem_multibyte_orig( void )
void test_nonconfigurable_item_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 1;
const unsigned bit = 1; // This bit and byte combination should not appear in the
const unsigned byte = 0; // hidConfigurableElements list in hid_report_descriptors.c.
@@ -494,7 +474,6 @@ void test_nonconfigurable_item_hidSetReportItem( void )
// Bit range tests
void test_max_bit_hidSetReportItem( void )
{
test_init();
const unsigned char header = construct_usage_header( 0 );
unsigned reportId = 1;
@@ -521,7 +500,6 @@ void test_max_bit_hidSetReportItem( void )
void test_min_bit_hidSetReportItem( void )
{
test_init();
const unsigned char header = construct_usage_header( 0 );
unsigned reportId = 1;
@@ -548,7 +526,6 @@ void test_min_bit_hidSetReportItem( void )
void test_overflow_bit_hidSetReportItem( void )
{
test_init();
const unsigned char header = construct_usage_header( 0 );
unsigned reportId = 1;
@@ -575,7 +552,6 @@ void test_overflow_bit_hidSetReportItem( void )
void test_underflow_bit_hidSetReportItem( void )
{
test_init();
const unsigned char header = construct_usage_header( 0 );
unsigned reportId = 1;
@@ -602,7 +578,6 @@ void test_underflow_bit_hidSetReportItem( void )
void test_overflow_byte_hidSetReportItem( void )
{
test_init();
const unsigned char header = construct_usage_header( 0 );
unsigned reportId = 1;
@@ -629,7 +604,6 @@ void test_overflow_byte_hidSetReportItem( void )
void test_underflow_byte_hidSetReportItem( void )
{
test_init();
const unsigned char header = construct_usage_header( 0 );
unsigned reportId = 1;
@@ -657,7 +631,6 @@ void test_underflow_byte_hidSetReportItem( void )
// Size range tests
void test_max_size_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 1;
const unsigned bit = REPORT1_MIN_VALID_BIT;
const unsigned byte = REPORT1_MIN_VALID_BYTE;
@@ -671,7 +644,6 @@ void test_max_size_hidSetReportItem( void )
void test_min_size_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 1;
const unsigned bit = REPORT1_MIN_VALID_BIT;
const unsigned byte = REPORT1_MIN_VALID_BYTE;
@@ -684,7 +656,6 @@ void test_min_size_hidSetReportItem( void )
void test_unsupported_size_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = REPORT1_MIN_VALID_BIT;
const unsigned byte = REPORT1_MIN_VALID_BYTE;
@@ -698,7 +669,6 @@ void test_unsupported_size_hidSetReportItem( void )
// Combined function tests
void test_initial_modification_without_subsequent_preparation( void )
{
test_init();
const unsigned reportId = 2;
const unsigned bit = REPORT2_MIN_VALID_BIT;
const unsigned byte = REPORT2_MIN_VALID_BYTE;
@@ -715,7 +685,6 @@ void test_initial_modification_without_subsequent_preparation( void )
void test_initial_modification_with_subsequent_preparation( void )
{
test_init();
const unsigned reportId = 2;
const unsigned bit = REPORT2_MIN_VALID_BIT;
const unsigned byte = REPORT2_MIN_VALID_BYTE;
@@ -733,7 +702,6 @@ void test_initial_modification_with_subsequent_preparation( void )
void test_initial_modification_with_subsequent_verification_1( void )
{
test_init();
const unsigned reportId = 2;
const unsigned bit = REPORT2_MIN_VALID_BIT;
const unsigned byte = REPORT2_MIN_VALID_BYTE;
@@ -759,7 +727,6 @@ void test_initial_modification_with_subsequent_verification_1( void )
void test_initial_modification_with_subsequent_verification_2( void )
{
test_init();
const unsigned reportId = 3;
const unsigned bit = REPORT3_MIN_VALID_BIT;
const unsigned byte = REPORT3_MIN_VALID_BYTE;
@@ -808,7 +775,6 @@ void test_initial_modification_with_subsequent_verification_2( void )
//setIdle and associated timing functionality tests
void test_set_idle( void )
{
test_init();
unsigned reportId = 1;
unsigned reportId2 = 2;
@@ -828,7 +794,6 @@ void test_set_idle( void )
void test_set_all_idle( void )
{
test_init();
unsigned reportId = 1;
unsigned reportId2 = 2;
@@ -847,7 +812,6 @@ void test_set_all_idle( void )
void test_change_pending( void )
{
test_init();
unsigned reportId = 1;
unsigned reportId2 = 2;
@@ -867,7 +831,6 @@ void test_change_pending( void )
void test_change_pending_all( void )
{
test_init();
unsigned reportId = 1;
unsigned changePending = hidIsChangePending( reportId );
@@ -882,7 +845,6 @@ void test_change_pending_all( void )
void test_report_time( void )
{
test_init();
unsigned reportTime1 = 123;
unsigned reportTime2 = 456;
@@ -897,7 +859,6 @@ void test_report_time( void )
void test_report_time_calc( void )
{
test_init();
unsigned reportTime1 = 123;
unsigned reportTime2 = 456;
unsigned reportPeriod1 = 10;

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2024 XMOS LIMITED.
// Copyright 2021-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stddef.h>
#include <stdio.h>
@@ -34,26 +34,23 @@ static unsigned construct_usage_header( unsigned size )
return header;
}
void test_init( void )
void setUp( void )
{
hidReportInit();
hidResetReportDescriptor();
}
void test_validate_report( void ) {
test_init();
unsigned retVal = hidReportValidate();
TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal );
}
void test_reportid_in_use( void ) {
test_init();
unsigned reportIdInUse = hidIsReportIdInUse();
TEST_ASSERT_EQUAL_UINT( 0, reportIdInUse );
}
void test_get_next_valid_report_id( void ) {
test_init();
unsigned reportId = 0U;
reportId = hidGetNextValidReportId(reportId);
@@ -64,7 +61,6 @@ void test_get_next_valid_report_id( void ) {
}
void test_is_report_id_valid( void ) {
test_init();
unsigned isValid = 0;
unsigned reportId = 0;
@@ -79,7 +75,6 @@ void test_is_report_id_valid( void ) {
// Basic report descriptor tests
void test_unprepared_hidGetReportDescriptor( void )
{
test_init();
const unsigned reportId = 0;
unsigned char* reportDescPtr = hidGetReportDescriptor();
TEST_ASSERT_NULL( reportDescPtr );
@@ -90,7 +85,6 @@ void test_unprepared_hidGetReportDescriptor( void )
void test_prepared_hidGetReportDescriptor( void )
{
test_init();
const unsigned reportId = 0;
hidPrepareReportDescriptor();
@@ -103,7 +97,6 @@ void test_prepared_hidGetReportDescriptor( void )
void test_reset_unprepared_hidGetReportDescriptor( void )
{
test_init();
hidPrepareReportDescriptor();
hidResetReportDescriptor();
unsigned char* reportDescPtr = hidGetReportDescriptor();
@@ -112,7 +105,6 @@ void test_reset_unprepared_hidGetReportDescriptor( void )
void test_reset_prepared_hidGetReportDescriptor( void )
{
test_init();
hidPrepareReportDescriptor();
hidResetReportDescriptor();
hidPrepareReportDescriptor();
@@ -122,7 +114,6 @@ void test_reset_prepared_hidGetReportDescriptor( void )
void test_report_id_limit( void )
{
test_init();
unsigned reportIdLimit = hidGetReportIdLimit();
TEST_ASSERT_EQUAL_UINT( HID_REPORTID_LIMIT, reportIdLimit );
}
@@ -130,7 +121,6 @@ void test_report_id_limit( void )
// Basic item tests
void test_max_loc_hidGetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MAX_VALID_BIT;
const unsigned byte = MAX_VALID_BYTE;
@@ -148,7 +138,6 @@ void test_max_loc_hidGetReportItem( void )
void test_min_loc_hidGetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MIN_VALID_BYTE;
@@ -166,7 +155,6 @@ void test_min_loc_hidGetReportItem( void )
void test_overflow_bit_hidGetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MAX_VALID_BIT + 1;
const unsigned byte = MAX_VALID_BYTE;
@@ -184,7 +172,6 @@ void test_overflow_bit_hidGetReportItem( void )
void test_overflow_byte_hidGetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MAX_VALID_BIT;
const unsigned byte = MAX_VALID_BYTE + 1;
@@ -202,7 +189,6 @@ void test_overflow_byte_hidGetReportItem( void )
void test_underflow_bit_hidGetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const int bit = MIN_VALID_BIT - 1;
const unsigned byte = MIN_VALID_BYTE;
@@ -220,7 +206,6 @@ void test_underflow_bit_hidGetReportItem( void )
void test_underflow_byte_hidGetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const int byte = MIN_VALID_BYTE - 1;
@@ -239,7 +224,6 @@ void test_underflow_byte_hidGetReportItem( void )
// Configurable and non-configurable item tests
void test_configurable_item_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MIN_VALID_BYTE;
@@ -253,7 +237,6 @@ void test_configurable_item_hidSetReportItem( void )
void test_nonconfigurable_item_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MAX_VALID_BIT; // This bit and byte combination should not appear in the
const unsigned byte = MIN_VALID_BYTE; // hidConfigurableElements list in hid_report_descriptors.c.
@@ -268,7 +251,6 @@ void test_nonconfigurable_item_hidSetReportItem( void )
// Bit range tests
void test_max_bit_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MAX_VALID_BIT; // Only byte 1 has bit 7 not reserved, See the
const unsigned byte = MAX_VALID_BYTE; // hidConfigurableElements list in hid_report_descriptors.c.
@@ -281,7 +263,6 @@ void test_max_bit_hidSetReportItem( void )
void test_min_bit_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MIN_VALID_BYTE;
@@ -294,7 +275,6 @@ void test_min_bit_hidSetReportItem( void )
void test_overflow_bit_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MAX_VALID_BIT + 1;
const unsigned byte = MIN_VALID_BYTE;
@@ -307,7 +287,6 @@ void test_overflow_bit_hidSetReportItem( void )
void test_underflow_bit_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const int bit = MIN_VALID_BIT - 1;
const unsigned byte = MIN_VALID_BYTE;
@@ -321,7 +300,6 @@ void test_underflow_bit_hidSetReportItem( void )
// Byte range tests
void test_max_byte_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MAX_VALID_BYTE;
@@ -334,7 +312,6 @@ void test_max_byte_hidSetReportItem( void )
void test_min_byte_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MIN_VALID_BYTE;
@@ -347,7 +324,6 @@ void test_min_byte_hidSetReportItem( void )
void test_overflow_byte_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MAX_VALID_BYTE + 1;
@@ -360,7 +336,6 @@ void test_overflow_byte_hidSetReportItem( void )
void test_underflow_byte_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const int byte = MIN_VALID_BYTE - 1;
@@ -374,7 +349,6 @@ void test_underflow_byte_hidSetReportItem( void )
// Size range tests
void test_max_size_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MIN_VALID_BYTE;
@@ -388,7 +362,6 @@ void test_max_size_hidSetReportItem( void )
void test_min_size_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MIN_VALID_BYTE;
@@ -401,7 +374,6 @@ void test_min_size_hidSetReportItem( void )
void test_unsupported_size_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MIN_VALID_BYTE;
@@ -415,7 +387,6 @@ void test_unsupported_size_hidSetReportItem( void )
// Header tag and type tests
void test_bad_tag_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MIN_VALID_BYTE;
@@ -431,7 +402,6 @@ void test_bad_tag_hidSetReportItem( void )
void test_global_type_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MIN_VALID_BYTE;
@@ -445,7 +415,6 @@ void test_global_type_hidSetReportItem( void )
void test_local_type_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MIN_VALID_BYTE;
@@ -459,7 +428,6 @@ void test_local_type_hidSetReportItem( void )
void test_main_type_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MIN_VALID_BYTE;
@@ -473,7 +441,6 @@ void test_main_type_hidSetReportItem( void )
void test_reserved_type_hidSetReportItem( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MIN_VALID_BYTE;
@@ -488,7 +455,6 @@ void test_reserved_type_hidSetReportItem( void )
// Combined function tests
void test_initial_modification_without_subsequent_preparation( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MIN_VALID_BYTE;
@@ -505,7 +471,6 @@ void test_initial_modification_without_subsequent_preparation( void )
void test_initial_modification_with_subsequent_preparation( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MIN_VALID_BYTE;
@@ -523,7 +488,6 @@ void test_initial_modification_with_subsequent_preparation( void )
void test_initial_modification_with_subsequent_verification_1( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MIN_VALID_BYTE;
@@ -549,7 +513,6 @@ void test_initial_modification_with_subsequent_verification_1( void )
void test_initial_modification_with_subsequent_verification_2( void )
{
test_init();
const unsigned reportId = 0;
const unsigned bit = MIN_VALID_BIT;
const unsigned byte = MIN_VALID_BYTE;
@@ -597,7 +560,6 @@ void test_initial_modification_with_subsequent_verification_2( void )
void test_modification_without_subsequent_preparation( void )
{
test_init();
hidPrepareReportDescriptor();
unsigned char* reportDescPtr = hidGetReportDescriptor();
TEST_ASSERT_NOT_NULL( reportDescPtr );
@@ -619,7 +581,6 @@ void test_modification_without_subsequent_preparation( void )
void test_modification_with_subsequent_preparation( void )
{
test_init();
hidPrepareReportDescriptor();
unsigned char* reportDescPtr = hidGetReportDescriptor();
TEST_ASSERT_NOT_NULL( reportDescPtr );
@@ -643,7 +604,6 @@ void test_modification_with_subsequent_preparation( void )
//setIdle functionality tests
void test_set_idle( void )
{
test_init();
unsigned reportId = 0;
unsigned setIdle = hidIsIdleActive( reportId );
@@ -656,7 +616,6 @@ void test_set_idle( void )
void test_change_pending( void )
{
test_init();
unsigned reportId = 0;
unsigned changePending = hidIsChangePending( reportId );
@@ -673,7 +632,6 @@ void test_change_pending( void )
void test_report_time( void )
{
test_init();
unsigned reportTime = 123;
unsigned reportPeriod = 10;

View File

@@ -1,13 +1,10 @@
// Copyright 2021-2024 XMOS LIMITED.
// Copyright 2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifdef __XC__
#include <xs1.h>
#include <platform.h>
#include <xclib.h>
#include "../../../lib_xua/src/midi/midiinparse.h"
#include "../../../lib_xua/src/midi/midioutparse.h"
#include "../../../lib_xua/src/midi/queue.h"
#endif // __XC__
@@ -29,75 +26,3 @@ void AudioHwInit()
{
; // nothing
}
unsigned random(unsigned &x){
crc32(x, -1, 0xEB31D82E);
return x;
}
////////////////////// Wrappers for midi parse because C doesn't support return tuples
void midi_in_parse_c_wrapper(void * unsafe mips, unsigned cable_number, unsigned char b, unsigned * unsafe valid, unsigned *unsafe packed){
unsafe{
struct midi_in_parse_state * unsafe ptr = mips;
{*valid, *packed} = midi_in_parse(*ptr, cable_number, b);
}
}
void midi_out_parse_c_wrapper(unsigned tx_data, unsigned midi[3], unsigned * unsafe size){
unsafe{
{midi[0], midi[1], midi[2], *size} = midi_out_parse(tx_data);
}
}
void reset_midi_state_c_wrapper(void * unsafe mips){
unsafe{
struct midi_in_parse_state * unsafe ptr = mips;
reset_midi_state(*ptr);
}
}
/////////////////////// Wrappers for queue test
void queue_init_c_wrapper(queue_t *q, unsigned size) {
unsafe{
queue_init(*q, size);
}
}
int queue_is_empty_c_wrapper(queue_t *unsafe q) {
unsafe{
return queue_is_empty(*q);
}
}
int queue_is_full_c_wrapper(queue_t *unsafe q) {
unsafe{
return queue_is_full(*q);
}
}
void queue_push_word_c_wrapper(queue_t *q, unsigned array[], unsigned data){
unsafe{
queue_push_word(*q, array, data);
}
}
unsigned queue_pop_word_c_wrapper(queue_t *q, unsigned array[]){
unsafe{
return queue_pop_word(*q, array);
}
}
unsigned queue_items_c_wrapper(const queue_t *q){
unsafe{
return queue_items(*q);
}
}
unsigned queue_space_c_wrapper(const queue_t *q){
unsafe{
return queue_space(*q);
}
}

View File

@@ -1,28 +1,9 @@
// Copyright 2021-2024 XMOS LIMITED.
// Copyright 2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef XUA_UNIT_TESTS_H_
#define XUA_UNIT_TESTS_H_
#include "unity.h"
#include "xua_conf.h"
#include "../../../lib_xua/src/midi/queue.h"
#ifndef __XC__
void midi_in_parse_c_wrapper(void * mips, unsigned cable_number, unsigned char b, unsigned * valid, unsigned * packed);
void midi_out_parse_c_wrapper(unsigned tx_data, unsigned midi[3], unsigned * size);
void reset_midi_state_c_wrapper(void *mips);
unsigned random(unsigned *x);
void queue_init_c_wrapper(queue_t *q, unsigned size);
int queue_is_empty_c_wrapper(const queue_t *q);
int queue_is_full_c_wrapper(const queue_t *q);
void queue_push_word_c_wrapper(queue_t *q, unsigned array[], unsigned data);
unsigned queue_pop_word_c_wrapper(queue_t *q, unsigned array[]);
void queue_push_byte_c_wrapper(queue_t *q, unsigned char array[], unsigned data);
unsigned queue_pop_byte_c_wrapper(queue_t *q, unsigned char array[]);
unsigned queue_items_c_wrapper(const queue_t *q);
unsigned queue_space_c_wrapper(const queue_t *q);
#endif
#endif /* XUA_UNIT_TESTS_H_ */

View File

@@ -0,0 +1,260 @@
from __future__ import print_function
import glob
import os.path
import subprocess
import sys
from waflib import Options
from waflib.Build import BuildContext, CleanContext
TARGETS = ['xcore200', 'xcoreai']
def get_ruby():
"""
Check ruby is avaliable and return the command to invoke it.
"""
interpreter_name = 'ruby'
try:
dev_null = open(os.devnull, 'w')
# Call the version command to check the interpreter can be run
subprocess.check_call([interpreter_name, '--version'],
stdout=dev_null,
close_fds=True)
except OSError as e:
print("Failed to run Ruby interpreter: {}".format(e), file=sys.stderr)
exit(1) # TODO: Check this is the correct way to kill xwaf on error
return interpreter_name
def get_unity_runner_generator(project_root_path):
"""
Check the Unity generate_test_runner script is avaliable, and return the
path to it.
"""
unity_runner_generator = os.path.join(
project_root_path, 'Unity', 'auto', 'generate_test_runner.rb')
if not os.path.exists(unity_runner_generator):
print("Unity repo not found in workspace", file=sys.stderr)
exit(1) # TODO: Check this is the correct way to kill xwaf on error
return unity_runner_generator
def get_test_name(test_path):
"""
Return the test name by removing the extension from the filename.
"""
return os.path.splitext(os.path.basename(test_path))[0]
def get_file_type(filename):
"""
Return the extension from the filename.
"""
return filename.rsplit('.')[-1:][0]
def generate_unity_runner(project_root_path, unity_test_path, unity_runner_dir,
unity_runner_suffix):
"""
Invoke the Unity runner generation script for the given test file, and
return the path to the generated file. The output directory will be created
if it does not already exist.
"""
runner_path = os.path.join(os.path.join(unity_runner_dir, get_test_name(unity_test_path)))
if not os.path.exists(runner_path):
os.makedirs(runner_path)
unity_runner_path = os.path.join(
runner_path, get_test_name(unity_test_path) + unity_runner_suffix
+ '.' + 'c')
try:
subprocess.check_call([get_ruby(),
get_unity_runner_generator(project_root_path),
unity_test_path,
unity_runner_path])
except OSError as e:
print("Ruby generator failed for {}\n\t{}".format(unity_test_path, e),
file=sys.stderr)
exit(1) # TODO: Check this is the correct way to kill xwaf on error
def add_unity_runner_build_config(waf_conf, project_root_path, unity_test_path,
unity_runner_build_flags, target):
"""
Add a config to xwaf to build each Unity test runner into an xCORE
executable.
"""
print(f"get_test_name(unity_test_path) = {get_test_name(unity_test_path)}. target = {target}")
waf_conf.setenv(get_test_name(unity_test_path) + '_' + target)
waf_conf.load('xwaf.compiler_xcc')
waf_conf.env.XCC_FLAGS = unity_runner_build_flags
waf_conf.env.PROJECT_ROOT = project_root_path
# TODO: can the xwaf boilerplate help here?
def prepare_unity_test_for_build(waf_conf, project_root_path, unity_test_path,
unity_runner_dir, unity_runner_suffix, target):
print("unity_test_path: " + str(unity_test_path))
generate_unity_runner(project_root_path, unity_test_path,
unity_runner_dir, unity_runner_suffix)
runner_build_flags = '' # Could extract flags from the test name
add_unity_runner_build_config(waf_conf, project_root_path, unity_test_path,
runner_build_flags, target)
def find_unity_test_paths(unity_test_dir, unity_test_prefix):
"""
Return a list of all file paths with the unity_test_prefix found in the
unity_test_dir.
"""
file_list = []
for root, dirs, files in os.walk(unity_test_dir):
for f in files:
if f.startswith(unity_test_prefix):
file_list.append(os.path.join(root,f))
return file_list
def find_unity_tests(unity_test_dir, unity_test_prefix):
"""
Return a dictionary of all {test names, test language} pairs with the
unity_test_prefix found in the unity_test_dir.
"""
unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix)
return {get_test_name(path): {"language": get_file_type(path), "dir": os.path.dirname(path)}
for path in unity_test_paths}
def generate_all_unity_runners(waf_conf, project_root_path,
unity_test_dir, unity_test_prefix,
unity_runner_dir, unity_runner_suffix):
"""
Generate a runner and a build config for each test file in the
unity_test_dir.
"""
# FIXME: pass unity_tests in?
unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix)
for trgt in TARGETS:
for unity_test_path in unity_test_paths:
prepare_unity_test_for_build(waf_conf, project_root_path,
unity_test_path,
unity_runner_dir, unity_runner_suffix, trgt)
# TODO: can the xwaf boilerplate help here?
def create_waf_contexts(configs):
for trgt in TARGETS:
for test_name, params in configs.items():
print(f"test_name {test_name}, test_language {params['language']}")
for ctx in (BuildContext, CleanContext):
raw_context = ctx.__name__.replace('Context', '').lower()
class tmp(ctx):
cmd = raw_context + '_' + test_name + '_' + trgt
variant = test_name + '_' + trgt
#cmd = raw_context + '_' + test_name
#variant = test_name
language = params["language"]
target = trgt
runner = test_name
directory = params["dir"]
print(f"cmd {cmd}, variant {variant}, language {language}")
UNITY_TEST_DIR = 'src'
UNITY_TEST_PREFIX = 'test_'
UNITY_RUNNER_DIR = 'runners'
UNITY_RUNNER_SUFFIX = '_Runner'
UNITY_TESTS = find_unity_tests(UNITY_TEST_DIR, UNITY_TEST_PREFIX)
print("UNITY_TESTS: " + str(UNITY_TESTS))
create_waf_contexts(UNITY_TESTS)
def options(opt):
opt.add_option('--target', action='store', default='xcore200')
opt.load('xwaf.xcommon')
def configure(conf):
# TODO: move the call to generate_all_unity_runners() to build()
project_root = os.path.join('..', '..', '..')
generate_all_unity_runners(conf, project_root,
UNITY_TEST_DIR, UNITY_TEST_PREFIX,
UNITY_RUNNER_DIR, UNITY_RUNNER_SUFFIX)
conf.load('xwaf.xcommon')
def build(bld):
if not bld.variant:
print('Adding test runners to build queue')
trgt = [
c for c in TARGETS if c == bld.options.target
]
if len(trgt) == 0:
bld.fatal('specify a target with --target.\nAvailable targets: {}'.format(', '.join(TARGETS)))
return
for name in UNITY_TESTS:
Options.commands.insert(0, 'build_' + name + '_' + trgt[0])
#Options.commands.insert(0, 'build_' + name)
print('Build queue {}'.format(Options.commands))
else:
print('Building runner {}'.format(bld.runner))
bld.env.XSCOPE = bld.path.find_resource('config.xscope')
depends_on = ['lib_xua',
'lib_xud',
'lib_spdif',
'lib_mic_array',
'lib_logging',
'lib_xassert',
'Unity']
makefile_opts = {}
makefile_opts['SOURCE_DIRS'] = ['src', bld.directory, os.path.join('runners',bld.runner)]
if(bld.target == 'xcoreai'):
print('TARGET XCOREAI')
makefile_opts['TARGET'] = ['XCORE-AI-EXPLORER']
else:
print('TARGET XCORE200')
makefile_opts['TARGET'] = ['XCORE-200-EXPLORER']
makefile_opts['INCLUDE_DIRS'] = ['src',
bld.directory,
'../../lib_xua/api',
'../../lib_xua/src/core/pdm_mics',
'../../lib_xua/src/hid',
'../../../lib_xud/lib_xud/src/user/class']
makefile_opts['XCC_FLAGS'] = ['-O2',
'-g',
'-Wall',
'-DHID_CONTROLS=1',
'-DUNITY_SUPPORT_64',
'-DUNITY_INCLUDE_DOUBLE',
'-DXUD_CORE_CLOCK=600',
'-DXUD_SERIES_SUPPORT=4']
makefile_opts['APP_NAME'] = [bld.variant]
makefile_opts['USED_MODULES'] = depends_on
makefile_opts['XCOMMON_MAKEFILE'] = ['Makefile.common']
bld.do_xcommon(makefile_opts)
def test(bld):
# Call pytest to run Unity tests inside axe or xsim
try:
test_output = subprocess.check_output(['pytest'])
except subprocess.CalledProcessError as e:
# pytest exits non-zero if an assertion fails
test_output = e.output
print(test_output)
# TODO: ensure clean deletes the runners dir/
def dist(ctx):
ctx.load('xwaf.xcommon')
def distcheck(ctx):
ctx.load('xwaf.xcommon')