94 Commits

Author SHA1 Message Date
f9c393b5f0 新增:添加了对AK4438VN的支持
- 添加了两个宏,不使用时,不会影响编译
2024-04-19 15:27:28 +08:00
Ross Owen
4589319151 Merge pull request #376 from xross/fix/375
fix/375
2024-03-27 17:11:27 +00:00
Ross Owen
e789da24d3 Fix for device failing to enumerate when ADAT and S/PDIF transmit are enabled 2024-03-26 14:23:01 +00:00
Ross Owen
7ffeaf3dde 'Release: 4.0.0' 2024-03-22 17:53:43 +00:00
Ross Owen
9ba6425d83 xpd: Cleaned up whitespace 2024-03-22 17:52:35 +00:00
Ross Owen
8f63590956 Merge pull request #374 from xross/develop
- Update lib_spdif dependency 5.0.0 -> 6.1.0
2024-03-22 14:42:25 +00:00
Ross Owen
6235c168a1 Automated changelog updates 2024-03-22 13:47:04 +00:00
Ross Owen
f3a0dbc79f - Update lib_spdif dependency 5.0.0 -> 6.1.0
- Update version numbers to 4.0.0 in prep for release
2024-03-22 13:33:16 +00:00
Ross Owen
68a1a793cc Merge pull request #373 from xross/develop 2024-03-13 09:51:24 +00:00
Ross Owen
0fc8aec495 Update lib_xud dependency from 2.3.0 -> 2.3.1 2024-03-12 20:05:59 +00:00
Michael Banther
4e893d4565 Merge pull request #370 from danielpieczko/develop
Fix dependency version of lib_xud to v2.3.0
2024-02-29 15:53:11 +00:00
Daniel Pieczko
c11e62e27f Fix dependency version of lib_xud to v2.3.0 2024-02-29 11:19:42 +00:00
Brennan Magee
57d5ea7613 Merge pull request #369 from danielpieczko/dependency_versions
Set dependency versions to released tags
2024-02-27 14:11:16 +00:00
Daniel Pieczko
03a646f95d Set dependency versions to released tags 2024-02-27 09:27:21 +00:00
danielpieczko
05c8c44619 Merge pull request #367 from danielpieczko/mixer_guid_windows
Add Windows driver GUID command-line option on mixer host app
2024-02-05 08:32:08 +00:00
Daniel Pieczko
0d913cdce3 Add Windows driver GUID command-line option on mixer host app 2024-01-31 14:59:05 +00:00
Ross Owen
165417962f Merge pull request #363 from ed-xmos/feature/sw_pll_sync_ua
Use sw_pll for sync targets on XS3
2024-01-26 16:57:16 +00:00
Ed
cffb7a272d Re-apply typo fix 2024-01-26 15:04:52 +00:00
Ed
41566b3970 Further re-apply #341 2024-01-26 15:02:28 +00:00
Ed
7847a5ee42 Reapply volume control fix from 341 2024-01-26 14:56:53 +00:00
Ed
8ec5f8c7fc Review cleanup 2024-01-26 11:48:38 +00:00
Ed
4d3fe82113 Update docs for sync mode with sw_pll 2024-01-26 11:05:31 +00:00
Ed
5980d0edea sw_pll to develop 2024-01-26 09:22:15 +00:00
Ed
e72a386fa2 Use branch of sw_pll for now 2024-01-23 17:06:41 +00:00
Ed
0d1f81276d Make SDM loop startup safer 2024-01-23 17:06:27 +00:00
Ed
7febbfdcd0 Refactor sync mode c_sof code 2024-01-23 12:40:15 +00:00
Ed
44049ecfca Use CT_END in sw_pll comms to clear switch path 2024-01-23 11:43:36 +00:00
Ed
d49c6b4656 Fix non-integer divide result issue 2024-01-23 10:45:27 +00:00
Ed
3be17bf8cc c_mclk_change -> c_audio_rate_change 2024-01-22 14:32:03 +00:00
Ed
e8317eae36 Minor fixes 2024-01-22 09:57:40 +00:00
Ed
5669a5b021 Fix synch test 2024-01-22 09:27:18 +00:00
Ed
fc708fe4e9 Update changelog for sync / sw_pll 2024-01-19 17:13:19 +00:00
Ed
f32955a419 Add missing include in cmake 2024-01-19 17:13:01 +00:00
Ed
a9ed38252c Put PFD reset in c_mclk_change case 2024-01-19 16:14:50 +00:00
Ed
27a7d185d1 Add in plumbing between ep_buffer and audio for PLL stability synch 2024-01-19 11:06:13 +00:00
Ed
7126b91848 USE_SW_PLL -> XUA_USE_SW_PLL 2024-01-19 09:25:37 +00:00
Ed
cb84d69231 Remove unneeded define 2024-01-19 09:24:02 +00:00
Ed
8b58fe5aaa sw_pll sync fs change support 2024-01-19 09:17:29 +00:00
Ed
fe6afc93de Add reconfig of pfd on SR change 2024-01-18 17:02:35 +00:00
Ed
eb4b19ce16 Initial loop closed 2024-01-18 16:22:43 +00:00
Ed
3d9d174dcc Merge commit '4edf86b3a6405c1b3331288fabb02ffed3664c60' into feature/sw_pll_sync_ua 2024-01-18 14:39:53 +00:00
Ed
f0709d35fc WIP sync mode 2024-01-18 14:39:27 +00:00
Ross Owen
4edf86b3a6 Merge pull request #364 from shuchitak/feature/control_itf
Include control interface descriptor in cfgDesc_Audio2
2024-01-18 10:35:29 +00:00
Shuchita Khare
de0279d769 copyright check 2024-01-17 13:05:53 +00:00
Shuchita Khare
a12111ba55 Include control interface descriptor in cfgDesc_Audio2 when USB_CONTROL_DESCS is defined 2024-01-17 12:28:47 +00:00
danielpieczko
1b6a785b03 Merge pull request #360 from ed-xmos/feature/sw_pll
Use SD sw_pll for digital Rx on XS3 targets
2024-01-16 10:07:53 +00:00
Ed
2d1585b8b1 Copyright 2024-01-16 09:58:44 +00:00
Ed
eb6ed9f56e Fix guarding on i_pll for sync 2024-01-15 17:43:51 +00:00
Ed
af9a6b18b2 make use of # guards and nullable consistent 2024-01-15 17:28:38 +00:00
Ed
ca16467158 Building but not tested merge 2024-01-15 17:00:54 +00:00
Ed
aac2b4b7fb Initial conflict resolve following merge 2024-01-15 15:20:37 +00:00
Ed
c0a844c303 Manual merge from experimental/sync_app_pll (not working yet) 2024-01-15 14:08:22 +00:00
Ed
103aa8840b Merge commit 'de6210d1dd57613ced96bd5961b1562b781bb6d7' into feature/sw_pll_sync_ua 2024-01-15 11:31:57 +00:00
Ed
a4e6fd0194 More tidying 2024-01-15 10:39:09 +00:00
Ed
57debd0558 Set DCO to midpoint of SDM restart 2024-01-15 10:05:53 +00:00
danielpieczko
de6210d1dd Merge pull request #361 from danielpieczko/develop
New version of g++ on Raspbian needs different linker option order
2024-01-12 16:52:17 +00:00
Ed
ce987622d9 Fix missing ACK to audio for xcore-200 2024-01-12 16:24:44 +00:00
Ed
e04ecf5fc9 Initial audio holdoff until SD initialised 2024-01-12 16:01:16 +00:00
Ed
d81b510102 Move sw_pll init to SD task to remove backpressure on clockgen 2024-01-12 15:38:03 +00:00
Daniel Pieczko
8ef63fcdf5 New version of g++ on Raspbian needs different linker option order 2024-01-12 12:19:42 +00:00
Ed
529aea28dc Remove apppll.h and replace with calls to lib_sw_pll 2024-01-12 11:14:36 +00:00
Ed
edbadca0cd Fix xcore.ai branding 2024-01-10 16:44:45 +00:00
Ed
13d9229f52 Add junit test logging to unity stage 2024-01-10 09:57:18 +00:00
Ed
a0610fc1e0 Fix copyright dates 2024-01-10 09:53:45 +00:00
Ed
334f36e5e1 Doc typo fixed 2024-01-10 09:40:47 +00:00
Ed
c412c81fe3 Additional docs update for pll 2024-01-09 17:40:54 +00:00
Ed
9abfa167ca Initial documentation covering sw_pll 2024-01-09 17:28:55 +00:00
Ed
c6a970d7c0 Fix guarding on clkgen ACK 2024-01-09 16:02:07 +00:00
Ed
3291a05493 Remove dead code + warning 2024-01-09 14:49:48 +00:00
Ed
91d23fb1d6 Remove debug prints 2024-01-09 13:32:41 +00:00
Ed
2fcc9ca2ac Add sw_pll dep in changelog 2024-01-09 13:29:53 +00:00
Ed
6d8d66f823 Changelog entry 2024-01-09 13:23:16 +00:00
Ed
23f1a8d48e Fix race condition when changing SR when audio got misaligned 2024-01-09 13:02:57 +00:00
Ed
e6899afbb9 Move sw_pll on to 2.1.0 and develop now fixes merged 2024-01-09 10:36:14 +00:00
Ed
87a105d8f6 Tidy defines 2024-01-09 09:10:14 +00:00
Ed
3003ce7241 Refactor sw_pll code into own source file 2024-01-09 08:56:58 +00:00
Ed
ccaaf40ab3 Ensure guarding for XS2 builds and fix clockgen race condition 2024-01-08 15:45:58 +00:00
Ed
dc81964f22 Add custom branch of sw_pll to xcommon cmake 2024-01-08 10:42:02 +00:00
Ed
a3419fdba7 Update USE_SW_PLL define usage 2024-01-05 16:18:38 +00:00
Ed
4962cebc9c Comments only 2024-01-05 14:40:14 +00:00
Ed
56d728f349 Fix PLL lock time 2s -> ~150ms 2024-01-05 14:39:47 +00:00
Ed
7f8f07b4b6 Merge commit 'ace59f63a4f00196a276e7254c941462a10819e9' into feature/sw_pll
# Conflicts:
#	lib_xua/src/core/clocking/clockgen.xc
2024-01-05 11:36:23 +00:00
Ed
8e161707a5 Manually apply https://github.com/xmos/lib_xua/pull/359/files 2024-01-05 11:25:41 +00:00
Ed
b242c54574 Reduce control loop rate to 100Hz 2024-01-05 11:18:29 +00:00
Ed
702e8d14b9 Initial loop closed 2024-01-05 10:59:32 +00:00
danielpieczko
ace59f63a4 Merge pull request #359 from danielpieczko/develop
Avoid repeating old samples when entering underflow state
2024-01-05 10:12:53 +00:00
Ed
780a407519 Fix lockup in aud->clkgen notification 2024-01-05 08:34:24 +00:00
Ed
61f17f3fe9 Add mclk change logic 2024-01-04 15:32:44 +00:00
Ed
d644775e4c Add in timestamp machinery 2024-01-04 12:16:26 +00:00
Ed
2133598347 Add sw_pll dep and prepare for par in clockgen 2024-01-03 15:56:15 +00:00
Daniel Pieczko
7b843b1d56 Avoid repeating old samples when entering underflow state 2024-01-03 10:01:32 +00:00
Ross Owen
f035e1dc13 UserBufferManagementInit() now takes a sample rate param (#358)
UserBufferManagementInit() now takes a sample rate param
2023-12-07 17:30:27 +00:00
Ross Owen
c5496ea994 Fix issue creating SR list (#357)
Fix issue generating sample frequency list
2023-12-07 14:11:29 +00:00
Ross Owen
b0db22a50b Clock selection tidies and improvements (#355)
- Tidy up use of “tmp” variable when setting digital stream clock validity
- Simplify clock selection values
- Add better checks to clock selection
- Removed un-required clock selection code based on ADAT/SPDIF enabled
2023-12-06 16:08:00 +00:00
45 changed files with 1449 additions and 738 deletions

View File

@@ -1,16 +1,43 @@
lib_xua Change Log lib_xua Change Log
================== ==================
HEAD UNRELEASED
---- ----------
* FIXED: Device fails to enumerate when ADAT and S/PDIF transmit are enabled
4.0.0
-----
* ADDED: Support for XCommon CMake build system * ADDED: Support for XCommon CMake build system
* RESOLVED: Output volume control not enabled by default when MIXER disabled * FIXED: Output volume control not enabled by default when MIXER disabled
* RESOLVED: Full 32bit result of volume processing not calculated when required * FIXED: Full 32bit result of volume processing not calculated when
* RESOLVED: Input stream sending an erroneous zero-length packet when exiting underflow state required
* RESOLVED Build failures when XUA_USB_EN = 0 * FIXED: Input stream sending an erroneous zero-length packet when exiting
* RESOLVED: Clock configuration issues when ADAT and S/PDIF receive are enabled (#352) 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: 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 3.5.1
----- -----

3
Jenkinsfile vendored
View File

@@ -49,7 +49,8 @@ pipeline {
withVenv { withVenv {
runWaf('.', "configure clean build --target=xcore200") runWaf('.', "configure clean build --target=xcore200")
viewEnv() { viewEnv() {
runPython("TARGET=XCORE200 pytest -s") runPython("TARGET=XCORE200 pytest -s --junitxml=pytest_unity.xml")
junit "pytest_unity.xml"
} }
} }
} }

View File

@@ -1,10 +1,9 @@
lib_xua lib_xua
####### #######
:Version: 3.5.1 :Version: 4.0.0
:Vendor: XMOS :Vendor: XMOS
:Scope: General Use :Scope: General Use
Summary Summary
@@ -83,14 +82,15 @@ The following application notes use this library:
Required Software (dependencies) Required Software (dependencies)
================================ ================================
* lib_adat (www.github.com/xmos/lib_adat)
* lib_locks (www.github.com/xmos/lib_locks) * lib_locks (www.github.com/xmos/lib_locks)
* lib_logging (www.github.com/xmos/lib_logging) * lib_logging (www.github.com/xmos/lib_logging)
* lib_mic_array (www.github.com/xmos/lib_mic_array) * lib_mic_array (www.github.com/xmos/lib_mic_array)
* lib_xassert (www.github.com/xmos/lib_xassert) * lib_xassert (www.github.com/xmos/lib_xassert)
* lib_dsp (www.github.com/xmos/lib_dsp) * lib_dsp (www.github.com/xmos/lib_dsp)
* lib_spdif (www.github.com/xmos/lib_spdif) * 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_xud (www.github.com/xmos/lib_xud)
* lib_adat (www.github.com/xmos/lib_adat)
Documentation Documentation
============= =============
@@ -101,4 +101,3 @@ Support
======= =======
This package is supported by XMOS Ltd. Issues can be raised against the software at: http://www.xmos.com/support This package is supported by XMOS Ltd. Issues can be raised against the software at: http://www.xmos.com/support

View File

@@ -1,9 +1,11 @@
// Copyright 2017-2022 XMOS LIMITED. // Copyright 2017-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h> #include <xs1.h>
#include <platform.h> #include <platform.h>
#include "xua.h" #include "xua.h"
#include "../../shared/apppll.h" extern "C"{
#include "sw_pll.h"
}
on tile[0]: out port p_ctrl = XS1_PORT_8D; on tile[0]: out port p_ctrl = XS1_PORT_8D;
@@ -38,19 +40,26 @@ void AudioHwInit()
delay_milliseconds(100); delay_milliseconds(100);
/* Use xCORE Secondary PLL to generate *fixed* master clock */ /* Use xCORE Secondary PLL to generate *fixed* master clock */
AppPllEnable_SampleRate(DEFAULT_FREQ); if(DEFAULT_FREQ % 22050 == 0)
{
sw_pll_fixed_clock(MCLK_441);
}
else
{
sw_pll_fixed_clock(MCLK_48);
}
delay_milliseconds(100); delay_milliseconds(100);
/* DAC setup: For basic I2S input we don't need any register setup. DACs will clock auto detect etc. /* 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. * It holds DAC in reset until it gets clocks anyway.
* Note, this example doesn't use the ADC's * Note, this example doesn't use the ADCs
*/ */
} }
/* Configures the external audio hardware for the required sample frequency */ /* Configures the external audio hardware for the required sample frequency */
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC) void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC)
{ {
AppPllEnable_SampleRate(samFreq); sw_pll_fixed_clock(mClk);
} }

View File

@@ -1,9 +1,12 @@
// Copyright 2017-2022 XMOS LIMITED. // Copyright 2017-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h> #include <xs1.h>
#include <platform.h> #include <platform.h>
#include "xua.h" #include "xua.h"
#include "../../shared/apppll.h" #include "xassert.h"
extern "C"{
#include "sw_pll.h"
}
on tile[0]: out port p_ctrl = XS1_PORT_8D; on tile[0]: out port p_ctrl = XS1_PORT_8D;
@@ -38,19 +41,26 @@ void AudioHwInit()
delay_milliseconds(100); delay_milliseconds(100);
/* Use xCORE Secondary PLL to generate *fixed* master clock */ /* Use xCORE Secondary PLL to generate *fixed* master clock */
AppPllEnable_SampleRate(DEFAULT_FREQ); if(DEFAULT_FREQ % 22050 == 0)
{
sw_pll_fixed_clock(MCLK_441);
}
else
{
sw_pll_fixed_clock(MCLK_48);
}
delay_milliseconds(100); delay_milliseconds(100);
/* DAC setup: For basic I2S input we don't need any register setup. DACs will clock auto detect etc. /* 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. * It holds DAC in reset until it gets clocks anyway.
* Note, this example doesn't use the ADC's * Note, this example doesn't use the ADCs
*/ */
} }
/* Configures the external audio hardware for the required sample frequency */ /* Configures the external audio hardware for the required sample frequency */
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC) void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC)
{ {
AppPllEnable_SampleRate(samFreq); sw_pll_fixed_clock(mClk);
} }

View File

@@ -1,109 +0,0 @@
// Copyright 2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdint.h>
#include "xassert.h"
// App PLL setup
#define APP_PLL_CTL_BYPASS (0) // 0 = no bypass, 1 = bypass.
#define APP_PLL_CTL_INPUT_SEL (0) // 0 = XTAL, 1 = sysPLL
#define APP_PLL_CTL_ENABLE (1) // 0 = disabled, 1 = enabled.
// 24MHz in, 24.576MHz out, integer mode
// Found exact solution: IN 24000000.0, OUT 24576000.0, VCO 2457600000.0, RD 5, FD 512, OD 10, FOD 10
#define APP_PLL_CTL_OD_48 (4) // Output divider = (OD+1)
#define APP_PLL_CTL_F_48 (511) // FB divider = (F+1)/2
#define APP_PLL_CTL_R_48 (4) // Ref divider = (R+1)
#define APP_PLL_CTL_48 ((APP_PLL_CTL_BYPASS << 29) | (APP_PLL_CTL_INPUT_SEL << 28) | (APP_PLL_CTL_ENABLE << 27) |\
(APP_PLL_CTL_OD_48 << 23) | (APP_PLL_CTL_F_48 << 8) | APP_PLL_CTL_R_48)
// Fractional divide is M/N
#define APP_PLL_FRAC_EN_48 (0) // 0 = disabled
#define APP_PLL_FRAC_NPLUS1_CYCLES_48 (0) // M value is this reg value + 1.
#define APP_PLL_FRAC_TOTAL_CYCLES_48 (0) // N value is this reg value + 1.
#define APP_PLL_FRAC_48 ((APP_PLL_FRAC_EN_48 << 31) | (APP_PLL_FRAC_NPLUS1_CYCLES_48 << 8) | APP_PLL_FRAC_TOTAL_CYCLES_48)
// 24MHz in, 22.5792MHz out (44.1kHz * 512), frac mode
// Found exact solution: IN 24000000.0, OUT 22579200.0, VCO 2257920000.0, RD 5, FD 470.400 (m = 2, n = 5), OD 5, FOD 10
#define APP_PLL_CTL_OD_441 (4) // Output divider = (OD+1)
#define APP_PLL_CTL_F_441 (469) // FB divider = (F+1)/2
#define APP_PLL_CTL_R_441 (4) // Ref divider = (R+1)
#define APP_PLL_CTL_441 ((APP_PLL_CTL_BYPASS << 29) | (APP_PLL_CTL_INPUT_SEL << 28) | (APP_PLL_CTL_ENABLE << 27) |\
(APP_PLL_CTL_OD_441 << 23) | (APP_PLL_CTL_F_441 << 8) | APP_PLL_CTL_R_441)
#define APP_PLL_FRAC_EN_44 (1) // 1 = enabled
#define APP_PLL_FRAC_NPLUS1_CYCLES_44 (1) // M value is this reg value + 1.
#define APP_PLL_FRAC_TOTAL_CYCLES_44 (4) // N value is this reg value + 1.define APP_PLL_CTL_R_441 (4) // Ref divider = (R+1)
#define APP_PLL_FRAC_44 ((APP_PLL_FRAC_EN_44 << 31) | (APP_PLL_FRAC_NPLUS1_CYCLES_44 << 8) | APP_PLL_FRAC_TOTAL_CYCLES_44)
#define APP_PLL_DIV_INPUT_SEL (1) // 0 = sysPLL, 1 = app_PLL
#define APP_PLL_DIV_DISABLE (0) // 1 = disabled (pin connected to X1D11), 0 = enabled divider output to pin.
#define APP_PLL_DIV_VALUE (4) // Divide by N+1 - remember there's a /2 also afterwards for 50/50 duty cycle.
#define APP_PLL_DIV ((APP_PLL_DIV_INPUT_SEL << 31) | (APP_PLL_DIV_DISABLE << 16) | APP_PLL_DIV_VALUE)
/* TODO support more than two freqs..*/
void AppPllEnable(int32_t clkFreq_hz)
{
switch(clkFreq_hz)
{
case 44100*512:
// Disable the PLL
write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (APP_PLL_CTL_441 & 0xF7FFFFFF));
// Enable the PLL to invoke a reset on the appPLL.
write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_441);
// 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[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_441);
// 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[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (APP_PLL_CTL_441 & 0xF7FFFFFF));
write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_441);
// Set the fractional divider if used
write_node_config_reg(tile[0], XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, APP_PLL_FRAC_44);
break;
case 48000*512:
// Disable the PLL
write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (APP_PLL_CTL_48 & 0xF7FFFFFF));
// Enable the PLL to invoke a reset on the appPLL.
write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_48);
// 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[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_48);
// 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[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (APP_PLL_CTL_48 & 0xF7FFFFFF));
write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_48);
// Set the fractional divider if used
write_node_config_reg(tile[0], XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, APP_PLL_FRAC_48);
break;
default:
assert(0);
break;
}
// Wait for PLL output frequency to stabilise due to fractional divider enable
delay_microseconds(100);
// Turn on the clock output
write_node_config_reg(tile[0], XS1_SSWITCH_SS_APP_CLK_DIVIDER_NUM, APP_PLL_DIV);
}
void AppPllEnable_SampleRate(int32_t sampleRate_hz)
{
assert(sampleRate_hz >= 22050);
if(sampleRate_hz % 22050 == 0)
{
AppPllEnable(44100*512);
}
else
{
AppPllEnable(48000*512);
}
}

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
// Copyright 2022-2023 XMOS LIMITED. // Copyright 2022-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#define USB_MIXER_SUCCESS 0 #define USB_MIXER_SUCCESS 0
@@ -22,7 +22,14 @@ enum usb_chan_type {
#define RANGE (2) #define RANGE (2)
#define MEM (3) #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(); int usb_mixer_connect();
#endif
int usb_mixer_disconnect(); int usb_mixer_disconnect();
/* MIXER UNIT(s) INTERFACE */ /* MIXER UNIT(s) INTERFACE */

9
lib_xua/README.md Normal file
View File

@@ -0,0 +1,9 @@
# lib_xua
- 版本3.5.1
- 私有版本维护Vergil Wong
## 主要改动
- 添加了`I2S_TDM_LRCLK_EDGES`以适配AK4438VN的TDM时序参考[修改时序以配置TDM](https://docs.pawpaw.cn/docs/applications/USB-Audio-Interface/Advanced-Guide/app-note/dac-timing-config/)
- 添加了`AUDIO_UNSAFE_RESRC`,以将`unsafe`资源的赋值合并到Audio线程中以避免编译器检查额外占用核心

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2023 XMOS LIMITED. // Copyright 2011-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _XUA_AUDIOHUB_H_ #ifndef _XUA_AUDIOHUB_H_
#define _XUA_AUDIOHUB_H_ #define _XUA_AUDIOHUB_H_
@@ -12,32 +12,42 @@
#include "dfu_interface.h" #include "dfu_interface.h"
#endif #endif
#include "xua_clocking.h"
/** The audio driver thread. /** The audio driver thread.
* *
* This function drives I2S ports and handles samples to/from other digital * This function drives I2S ports and handles samples to/from other digital
* I/O threads. * I/O threads.
* *
* \param c_aud Audio sample channel connected to the mixer() thread or the * \param c_aud Audio sample channel connected to the mixer() thread or the
* decouple() thread * 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 * \param p_bclk Nullable port for I2S bit clock
* *
* \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 c_spdif_tx Channel connected to S/PDIF transmiter core from lib_spdif * \param i_SoftPll Interface to software PLL task
* *
* \param c_dig Channel connected to the clockGen() thread for * \param c_spdif_tx Channel connected to S/PDIF transmitter core from lib_spdif
* receiving/transmitting samples *
* \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
*/ */
void XUA_AudioHub(chanend ?c_aud, void XUA_AudioHub(chanend ?c_aud,
clock ?clk_audio_mclk, clock ?clk_audio_mclk,
@@ -53,10 +63,13 @@ void XUA_AudioHub(chanend ?c_aud,
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN || defined(__DOXYGEN__)) #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN || defined(__DOXYGEN__))
, chanend c_dig , chanend c_dig
#endif #endif
#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1) #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__))
, server interface i_dfu ?dfuInterface , server interface i_dfu ?dfuInterface
#endif #endif
#if (XUA_NUM_PDM_MICS > 0) #if (XUA_NUM_PDM_MICS > 0 || defined(__DOXYGEN__))
, chanend c_pdm_in , chanend c_pdm_in
#endif #endif
); );
@@ -76,8 +89,29 @@ void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode,
#endif // __XC__ #endif // __XC__
void UserBufferManagementInit(); /**
* @brief User buffer management code
*
* This function is called at the sample rate of the USB Audio stack (e.g,. 48 kHz) and between the two parameter arrays
* contain a full multi-channel audio-frame. The first array carries all the data that has been received from the USB host
* and is to be presented to the audio interfaces. The second array carries all the data received from the interfaces and
* is to be presented to the USB host. The user can chose to intercept and overwrite the samples stored in these arrays.
*
* \param sampsFromUsbToAudio Samples received from USB host and to be presented to audio interfaces
*
* \param sampsFromAudioToUsb Samples received from the audio interfaces and to be presented to the USB host
*/
void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudioToUsb[]); void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudioToUsb[]);
/**
* @brief User buffer managment init code
*
* 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);
#endif // _XUA_AUDIOHUB_H_ #endif // _XUA_AUDIOHUB_H_

View File

@@ -1,7 +1,7 @@
// Copyright 2011-2022 XMOS LIMITED. // Copyright 2011-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef __XUA_BUFFER_H__ #ifndef _XUA_BUFFER_H_
#define __XUA_BUFFER_H__ #define _XUA_BUFFER_H_
#if __XC__ #if __XC__
@@ -13,19 +13,21 @@
* Most of the chanend parameters to the function should be connected to * Most of the chanend parameters to the function should be connected to
* XUD_Manager(). The uses two cores. * XUD_Manager(). The uses two cores.
* *
* \param c_aud_out Audio OUT endpoint channel connected to the XUD * \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_in Audio IN endpoint channel connected to the XUD
* \param c_aud_fb Audio feedback 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_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_to_host MIDI IN endpoint channel connected to the XUD
* \param c_midi Channel connected to MIDI core * \param c_midi Channel connected to MIDI core
* \param c_int Audio clocking interrupt endpoint channel connected to the XUD * \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_clk_int Optional chanend connected to the clockGen() thread if present
* \param c_sof Start of frame channel connected to the XUD * \param c_sof Start of frame channel connected to the XUD
* \param c_aud_ctl Audio control channel connected to Endpoint0() * \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 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_aud Channel connected to XUA_AudioHub() core
* \param i_pll_ref Interface to task that toggles reference pin to CS2100 * \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.
*/ */
void XUA_Buffer( void XUA_Buffer(
chanend c_aud_out, chanend c_aud_out,
@@ -38,7 +40,7 @@ void XUA_Buffer(
#if defined(MIDI) || defined(__DOXYGEN__) #if defined(MIDI) || defined(__DOXYGEN__)
chanend c_midi_from_host, chanend c_midi_from_host,
chanend c_midi_to_host, chanend c_midi_to_host,
chanend c_midi, chanend c_midi,
#endif #endif
#if XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN || defined(__DOXYGEN__) #if XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN || defined(__DOXYGEN__)
chanend ?c_int, chanend ?c_int,
@@ -51,8 +53,14 @@ void XUA_Buffer(
, chanend c_hid , chanend c_hid
#endif #endif
, chanend c_aud , chanend c_aud
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || defined(__DOXYGEN__) #if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || defined(__DOYXGEN__)
, chanend c_audio_rate_change
#if (!XUA_USE_SW_PLL) || defined(__DOXYGEN__)
, client interface pll_ref_if i_pll_ref , client interface pll_ref_if i_pll_ref
#endif
#if (XUA_USE_SW_PLL) || defined(__DOXYGEN__)
, chanend c_swpll_update
#endif
#endif #endif
); );
@@ -66,7 +74,7 @@ void XUA_Buffer_Ep(chanend c_aud_out,
#ifdef MIDI #ifdef MIDI
chanend c_midi_from_host, chanend c_midi_from_host,
chanend c_midi_to_host, chanend c_midi_to_host,
chanend c_midi, chanend c_midi,
#endif #endif
#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) #if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN)
chanend ?c_int, chanend ?c_int,
@@ -81,10 +89,17 @@ void XUA_Buffer_Ep(chanend c_aud_out,
#ifdef CHAN_BUFF_CTRL #ifdef CHAN_BUFF_CTRL
, chanend c_buff_ctrl , chanend c_buff_ctrl
#endif #endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || defined(__DOXYGEN__) #if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || defined(__DOYXGEN__)
, chanend c_audio_rate_change
#if (!XUA_USE_SW_PLL) || defined(__DOXYGEN__)
, client interface pll_ref_if i_pll_ref , client interface pll_ref_if i_pll_ref
#endif
#if (XUA_USE_SW_PLL) || defined(__DOXYGEN__)
, chanend c_swpll_update
#endif
#endif #endif
); );
/** Manage the data transfer between the USB audio buffer and the /** Manage the data transfer between the USB audio buffer and the
* Audio I/O driver. * Audio I/O driver.

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2022 XMOS LIMITED. // Copyright 2011-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _CLOCKING_H_ #ifndef _CLOCKING_H_
@@ -6,6 +6,8 @@
#include <xs1.h> #include <xs1.h>
#include "sw_pll_wrapper.h"
interface pll_ref_if interface pll_ref_if
{ {
void toggle(); void toggle();
@@ -18,15 +20,31 @@ void PllRefPinTask(server interface pll_ref_if i_pll_ref, out port p_sync);
/** Clock generation and digital audio I/O handling. /** Clock generation and digital audio I/O handling.
* *
* \param c_spdif_rx channel connected to S/PDIF receive thread * \param c_spdif_rx channel connected to S/PDIF receive thread
* \param c_adat_rx channel connect to ADAT 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 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_audio channel connected to the audio() thread
* \param c_clk_ctl channel connected to Endpoint0() for configuration of the * \param c_clk_ctl channel connected to Endpoint0() for configuration of the
* clock * clock
* \param c_clk_int channel connected to the decouple() thread for clock * \param c_clk_int channel connected to the decouple() thread for clock
interrupts * 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
*
*/ */
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); 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
#endif
);
#endif #endif

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2023 XMOS LIMITED. // Copyright 2011-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // 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 * @brief Defines relating to device configuration and customisation of lib_xua
@@ -11,56 +11,55 @@
#include "xua_conf.h" #include "xua_conf.h"
#endif #endif
/* Default tile arrangement */ /*
* Tile arrangement defines
*/
/** /**
* @brief Location (tile) of audio I/O. Default: 0 * @brief Location (tile) of audio I/O. Default: 0
*/ */
#ifndef AUDIO_IO_TILE #ifndef AUDIO_IO_TILE
#define AUDIO_IO_TILE (0) #define AUDIO_IO_TILE (0)
#endif #endif
/** /**
* @brief Location (tile) of audio I/O. Default: 0 * @brief Location (tile) of audio I/O. Default: 0
*/ */
#ifndef XUD_TILE #ifndef XUD_TILE
#define XUD_TILE (0) #define XUD_TILE (0)
#endif #endif
/** /**
* @brief Location (tile) of MIDI I/O. Default: AUDIO_IO_TILE * @brief Location (tile) of MIDI I/O. Default: AUDIO_IO_TILE
*/ */
#ifndef MIDI_TILE #ifndef MIDI_TILE
#define MIDI_TILE AUDIO_IO_TILE #define MIDI_TILE AUDIO_IO_TILE
#endif #endif
/** /**
* @brief Location (tile) of SPDIF Tx. Default: AUDIO_IO_TILE * @brief Location (tile) of SPDIF Tx. Default: AUDIO_IO_TILE
*/ */
#ifndef SPDIF_TX_TILE #ifndef SPDIF_TX_TILE
#define SPDIF_TX_TILE AUDIO_IO_TILE #define SPDIF_TX_TILE AUDIO_IO_TILE
#endif #endif
/** /**
* @brief Location (tile) of PDM Rx. Default: AUDIO_IO_TILE * @brief Location (tile) of PDM Rx. Default: AUDIO_IO_TILE
*/ */
#ifndef PDM_TILE #ifndef PDM_TILE
#define PDM_TILE AUDIO_IO_TILE #define PDM_TILE AUDIO_IO_TILE
#endif #endif
/** /**
* @brief Location (tile) of reference signal to CS2100. Default: AUDIO_IO_TILE * @brief Location (tile) of reference signal to CS2100. Default: AUDIO_IO_TILE
*/ */
#ifndef PLL_REF_TILE #ifndef PLL_REF_TILE
#define PLL_REF_TILE AUDIO_IO_TILE #define PLL_REF_TILE AUDIO_IO_TILE
#endif #endif
/** /*
* @brief Disable USB functionalty just leaving AudioHub * Channel based defines
*/ */
#ifndef XUA_USB_EN
#define XUA_USB_EN (1)
#endif
/** /**
* @brief Number of input channels (device to host). Default: NONE (Must be defined by app) * @brief Number of input channels (device to host). Default: NONE (Must be defined by app)
@@ -79,18 +78,57 @@
#endif #endif
/** /**
* @brief Number of DSD output channels. Default: 0 (disabled) * @brief Number of PDM microphones in the design.
*
* Default: 0
*/
#ifndef XUA_NUM_PDM_MICS
#define XUA_NUM_PDM_MICS (0)
#endif
/**
* @brief Number of DSD output channels.
*
* Default: 0 (disabled)
*/ */
#if defined(DSD_CHANS_DAC) && (DSD_CHANS_DAC != 0) #if defined(DSD_CHANS_DAC) && (DSD_CHANS_DAC != 0)
#if defined(NATIVE_DSD) && (NATIVE_DSD == 0) #if defined(NATIVE_DSD) && (NATIVE_DSD == 0)
#undef NATIVE_DSD #undef NATIVE_DSD
#else #else
#define NATIVE_DSD (1) /* Always enable Native DSD when DSD mode is enabled */ #define NATIVE_DSD 1 /* Always enable Native DSD when DSD mode is enabled */
#endif #endif
#else #else
#define DSD_CHANS_DAC (0) #define DSD_CHANS_DAC 0
#endif #endif
/**
* @brief Number of I2S channesl to DAC/CODEC. Must be a multiple of 2.
*
* Default: NONE (Must be defined by app)
*/
#ifndef I2S_CHANS_DAC
#error I2S_CHANS_DAC not defined
#define I2S_CHANS_DAC 2 /* Define anyway for doxygen */
#else
#define I2S_WIRES_DAC (I2S_CHANS_DAC / I2S_CHANS_PER_FRAME)
#endif
/**
* @brief Number of I2S channels from ADC/CODEC. Must be a multiple of 2.
*
* Default: NONE (Must be defined by app)
*/
#ifndef I2S_CHANS_ADC
#error I2S_CHANS_ADC not defined
#define I2S_CHANS_ADC 2 /* Define anyway for doxygen */
#else
#define I2S_WIRES_ADC (I2S_CHANS_ADC / I2S_CHANS_PER_FRAME)
#endif
/*
* Defines relating to the interface to external audio hardware i.e. DAC/ADC
*/
#define XUA_PCM_FORMAT_I2S (0) #define XUA_PCM_FORMAT_I2S (0)
#define XUA_PCM_FORMAT_TDM (1) #define XUA_PCM_FORMAT_TDM (1)
/** /**
@@ -103,7 +141,7 @@
#error Bad value for XUA_PCM_FORMAT #error Bad value for XUA_PCM_FORMAT
#endif #endif
#else #else
#define XUA_PCM_FORMAT XUA_PCM_FORMAT_I2S #define XUA_PCM_FORMAT XUA_PCM_FORMAT_I2S
#endif #endif
/** /**
@@ -114,89 +152,12 @@
**/ **/
#ifndef I2S_CHANS_PER_FRAME #ifndef I2S_CHANS_PER_FRAME
#if (XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM) #if (XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM)
#define I2S_CHANS_PER_FRAME (8) #define I2S_CHANS_PER_FRAME 8
#else #else
#define I2S_CHANS_PER_FRAME (2) #define I2S_CHANS_PER_FRAME 2
#endif #endif
#endif #endif
/**
* @brief Number of IS2 channesl to DAC/CODEC. Must be a multiple of 2.
*
* Default: NONE (Must be defined by app)
*/
#ifndef I2S_CHANS_DAC
#error I2S_CHANS_DAC not defined
#define I2S_CHANS_DAC 2 /* Define anyway for doxygen */
#else
#define I2S_WIRES_DAC (I2S_CHANS_DAC / I2S_CHANS_PER_FRAME)
#endif
/**
* @brief Number of I2S channels from ADC/CODEC. Must be a multiple of 2.
*
* Default: NONE (Must be defined by app)
*/
#ifndef I2S_CHANS_ADC
#error I2S_CHANS_ADC not defined
#define I2S_CHANS_ADC 2 /* Define anyway for doxygen */
#else
#define I2S_WIRES_ADC (I2S_CHANS_ADC / I2S_CHANS_PER_FRAME)
#endif
/**
* @brief Ratio of the I2S sample rate to the USB Audio sample rate. Up and
* down-sampling will be enabled as necessary when the rates differ.
*
* Default: 1 i.e. I2S and USB Audio are running at the same sample rate.
*/
#ifndef AUD_TO_USB_RATIO
#define AUD_TO_USB_RATIO (1)
#else
#if (AUD_TO_USB_RATIO != 3) && (AUD_TO_USB_RATIO != 1)
#error Unsupported I2S to USB Audio sample rate ratio
#endif
#endif
/**
* @brief Ratio of the I2S sample rate to the PDM microphone decimator sample
* rate.
*
* Default: 1 i.e. I2S and PDM microphone decimators are running at the same sample rate.
*/
#ifndef AUD_TO_MICS_RATIO
#define AUD_TO_MICS_RATIO (1)
#else
#if (AUD_TO_MICS_RATIO != 3) && (AUD_TO_MICS_RATIO != 1)
#error Unsupported I2S to PDM microphone decimator sample rate ratio
#endif
#endif
/**
* @brief Only downsample one channel per input I2S frame.
*
* Default: 0 i.e. mono mode is disabled, all input channels will be downsampled.
*/
#ifndef I2S_DOWNSAMPLE_MONO_IN
#define I2S_DOWNSAMPLE_MONO_IN (0)
#endif
/**
* @brief Number of incoming (device to host) I2S channels to downsample.
*
* Default: The number of I2S incoming channels, or half this if mono downsampling is enabled.
*/
#if (I2S_DOWNSAMPLE_MONO_IN == 1)
#define I2S_DOWNSAMPLE_CHANS_IN (I2S_CHANS_ADC / 2)
#if ((I2S_DOWNSAMPLE_FACTOR_IN > 1) && (XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM))
#error Mono I2S input downsampling is not avaliable in TDM mode
#endif
#else
#define I2S_DOWNSAMPLE_CHANS_IN I2S_CHANS_ADC
#endif
/** /**
* @brief Number of bits per channel for I2S/TDM. Supported values: 16/32-bit. * @brief Number of bits per channel for I2S/TDM. Supported values: 16/32-bit.
* *
@@ -211,21 +172,82 @@
#endif #endif
/** /**
* @brief Max supported sample frequency for device (Hz). Default: 192000 * @brief Ratio of the I2S sample rate to the USB Audio sample rate. Up and
* down-sampling will be enabled as necessary when the rates differ.
*
* Default: 1 i.e. I2S and USB Audio are running at the same sample rate.
*/
#ifndef AUD_TO_USB_RATIO
#define AUD_TO_USB_RATIO (1)
#else
#if (AUD_TO_USB_RATIO != 3) && (AUD_TO_USB_RATIO != 1)
#error Unsupported I2S to USB Audio sample rate ratio
#endif
#endif
/**
* @brief Ratio of the I2S sample rate to the PDM microphone decimator sample
* rate.
*
* Default: 1 i.e. I2S and PDM microphone decimators are running at the same sample rate.
*/
#ifndef AUD_TO_MICS_RATIO
#define AUD_TO_MICS_RATIO (1)
#else
#if (AUD_TO_MICS_RATIO != 3) && (AUD_TO_MICS_RATIO != 1)
#error Unsupported I2S to PDM microphone decimator sample rate ratio
#endif
#endif
/**
* @brief Only downsample one channel per input I2S frame.
*
* Default: 0 i.e. mono mode is disabled, all input channels will be downsampled.
*/
#ifndef I2S_DOWNSAMPLE_MONO_IN
#define I2S_DOWNSAMPLE_MONO_IN (0)
#endif
/**
* @brief Number of incoming (device to host) I2S channels to downsample.
*
* Default: The number of I2S incoming channels, or half this if mono downsampling is enabled.
*/
#if (I2S_DOWNSAMPLE_MONO_IN == 1)
#define I2S_DOWNSAMPLE_CHANS_IN (I2S_CHANS_ADC / 2)
#if ((I2S_DOWNSAMPLE_FACTOR_IN > 1) && (XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM))
#error Mono I2S input downsampling is not avaliable in TDM mode
#endif
#else
#define I2S_DOWNSAMPLE_CHANS_IN I2S_CHANS_ADC
#endif
/*
* Clocking related defines
*/
/**
* @brief Max supported sample frequency for device (Hz).
*
* Default: 192000Hz
*/ */
#ifndef MAX_FREQ #ifndef MAX_FREQ
#define MAX_FREQ (192000) #define MAX_FREQ (192000)
#endif #endif
/** /**
* @brief Min supported sample frequency for device (Hz). Default 44100 * @brief Min supported sample frequency for device (Hz).
*
* Default: 44100Hz
*/ */
#ifndef MIN_FREQ #ifndef MIN_FREQ
#define MIN_FREQ (44100) #define MIN_FREQ (44100)
#endif #endif
/** /**
* @brief Master clock defines for 44100 rates (in Hz). Default: NONE (Must be defined by app) * @brief Master clock defines for 44100 rates (in Hz).
*
* Default: NONE (Must be defined by app)
*/ */
#ifndef MCLK_441 #ifndef MCLK_441
#error MCLK_441 not defined #error MCLK_441 not defined
@@ -233,13 +255,29 @@
#endif #endif
/** /**
* @brief Master clock defines for 48000 rates (in Hz). Default: NONE (Must be defined by app) * @brief Master clock defines for 48000 rates (in Hz).
*
* Default: NONE (Must be defined by app)
*/ */
#ifndef MCLK_48 #ifndef MCLK_48
#error MCLK_48 not defined #error MCLK_48 not defined
#define MCLK_48 (256 * 48000) /* Define anyway for doygen */ #define MCLK_48 (256 * 48000) /* Define anyway for doygen */
#endif #endif
/**
* @brief Enable/disable the use of the secondary/application PLL for generating and recovering master-clocks.
* Only available on xcore.ai devices.
*
* Default: Enabled (for xcore.ai devices)
*/
#ifndef XUA_USE_SW_PLL
#if defined(__XS3A__)
#define XUA_USE_SW_PLL (1)
#else
#define XUA_USE_SW_PLL (0)
#endif
#endif
/** /**
* @brief Default device sample frequency. A safe default should be used. * @brief Default device sample frequency. A safe default should be used.
* *
@@ -249,10 +287,25 @@
#define DEFAULT_FREQ (MIN_FREQ) #define DEFAULT_FREQ (MIN_FREQ)
#endif #endif
/* Audio Class Defines */ #define DEFAULT_MCLK (((DEFAULT_FREQ % 7350) == 0) ? MCLK_441 : MCLK_48)
/** /**
* @brief USB Audio Class Version. * @brief Defines whether XMOS device runs as master (i.e. drives LR and Bit clocks)
*
* 0: XMOS is I2S master. 1: CODEC is I2s master.
*
* Default: 0 (XMOS is master)
*/
#ifndef CODEC_MASTER
#define CODEC_MASTER (0)
#endif
/*
* Audio Class defines
*/
/**
* @brief USB Audio Class Version
* *
* Default: 2 (Audio Class version 2.0) * Default: 2 (Audio Class version 2.0)
*/ */
@@ -261,9 +314,9 @@
#endif #endif
/** /**
* @brief Whether or not to fall back to Audio Class 1.0 in USB Full-speed. * @brief Enable/disable fall back to Audio Class 1.0 in USB Full-speed.
* *
* Default: 0 (Disabled i.e. do not fall back to UAC 1.0 * Default: Disabled
*/ */
#ifndef AUDIO_CLASS_FALLBACK #ifndef AUDIO_CLASS_FALLBACK
#define AUDIO_CLASS_FALLBACK (0) #define AUDIO_CLASS_FALLBACK (0)
@@ -278,7 +331,7 @@
#if (AUDIO_CLASS == 2) #if (AUDIO_CLASS == 2)
/* Whether to run in Audio Class 2.0 mode in USB Full-speed */ /* Whether to run in Audio Class 2.0 mode in USB Full-speed */
#if !defined(FULL_SPEED_AUDIO_2) && (AUDIO_CLASS_FALLBACK == 0) #if !defined(FULL_SPEED_AUDIO_2) && (AUDIO_CLASS_FALLBACK == 0)
#define FULL_SPEED_AUDIO_2 (1) /* Default to falling back to UAC2 */ #define FULL_SPEED_AUDIO_2 1 /* Default to falling back to UAC2 */
#endif #endif
#endif #endif
@@ -295,16 +348,17 @@
#error AUDIO_CLASS set to 1 and FULL_SPEED_AUDIO_2 enabled! #error AUDIO_CLASS set to 1 and FULL_SPEED_AUDIO_2 enabled!
#endif #endif
/*
/* Feature defines */ * Feature defines
*/
/** /**
* @brief Number of PDM microphones in the design. * @brief Disable USB functionalty just leaving AudioHub
* *
* Default: None * Default: Enabled
*/ */
#ifndef XUA_NUM_PDM_MICS #ifndef XUA_USB_EN
#define XUA_NUM_PDM_MICS (0) #define XUA_USB_EN (1)
#endif #endif
/** /**
@@ -318,18 +372,14 @@
#endif #endif
/** /**
* @brief Size of a frame of microphone data samples. * @brief Size of a frame of microphone data samples. Default: 1
*
* Default: 1
*/ */
#ifndef XUA_MIC_FRAME_SIZE #ifndef XUA_MIC_FRAME_SIZE
#define XUA_MIC_FRAME_SIZE (1) #define XUA_MIC_FRAME_SIZE (1)
#endif #endif
/** /**
* @brief Enable MIDI functionality including buffering, descriptors etc. * @brief Enable MIDI functionality including buffering, descriptors etc. Default: DISABLED
*
* Default: 0 (Disabled)
*/ */
#ifndef MIDI #ifndef MIDI
#define MIDI (0) #define MIDI (0)
@@ -350,7 +400,7 @@
* @brief Enables SPDIF Tx. Default: 0 (Disabled) * @brief Enables SPDIF Tx. Default: 0 (Disabled)
*/ */
#ifndef XUA_SPDIF_TX_EN #ifndef XUA_SPDIF_TX_EN
#define XUA_SPDIF_TX_EN (0) #define XUA_SPDIF_TX_EN (0)
#endif #endif
/** /**
@@ -359,16 +409,14 @@
* Default: 0 (i.e. channels 0 & 1) * Default: 0 (i.e. channels 0 & 1)
* */ * */
#ifndef SPDIF_TX_INDEX #ifndef SPDIF_TX_INDEX
#define SPDIF_TX_INDEX (0) #define SPDIF_TX_INDEX (0)
#endif #endif
/** /**
* @brief Enables ADAT Tx. * @brief Enables ADAT Tx. Default: 0 (Disabled)
*
* Default: 0 (Disabled)
*/ */
#ifndef XUA_ADAT_TX_EN #ifndef XUA_ADAT_TX_EN
#define XUA_ADAT_TX_EN (0) #define XUA_ADAT_TX_EN (0)
#endif #endif
/** /**
@@ -377,25 +425,21 @@
* Default: 0 (i.e. channels [0:7]) * Default: 0 (i.e. channels [0:7])
* */ * */
#ifndef ADAT_TX_INDEX #ifndef ADAT_TX_INDEX
#define ADAT_TX_INDEX (0) #define ADAT_TX_INDEX (0)
#endif #endif
/** /**
* @brief Enables SPDIF Rx. * @brief Enables SPDIF Rx. Default: 0 (Disabled)
*
* Default: 0 (Disabled)
*/ */
#ifndef XUA_SPDIF_RX_EN #ifndef XUA_SPDIF_RX_EN
#define XUA_SPDIF_RX_EN (0) #define XUA_SPDIF_RX_EN (0)
#endif #endif
/** /**
* @brief Enables ADAT Rx. * @brief Enables ADAT Rx. Default: 0 (Disabled)
*
* Default: 0 (Disabled)
*/ */
#ifndef XUA_ADAT_RX_EN #ifndef XUA_ADAT_RX_EN
#define XUA_ADAT_RX_EN (0) #define XUA_ADAT_RX_EN (0)
#endif #endif
/** /**
@@ -450,20 +494,11 @@
* Default: 1 (Enabled) * Default: 1 (Enabled)
*/ */
#if !defined(XUA_DFU_EN) #if !defined(XUA_DFU_EN)
#define XUA_DFU_EN (1) #define XUA_DFU_EN (1)
#elif defined(XUA_DFU_EN) && (XUA_DFU_EN == 0) #elif defined(XUA_DFU_EN) && (XUA_DFU_EN == 0)
#undef XUA_DFU_EN #undef XUA_DFU_EN
#endif #endif
/**
* @brief Use a QSPI (rather than SPI) flash for DFU (and boot)
*
* Default: 1 (True i.e use QSPI flash)
*/
#if !defined(XUA_QUAD_SPI_FLASH)
#define XUA_QUAD_SPI_FLASH (1)
#endif
/** /**
* @brief Enable HID playback controls functionality. * @brief Enable HID playback controls functionality.
* *
@@ -472,7 +507,7 @@
* Default 0 (Disabled) * Default 0 (Disabled)
*/ */
#ifndef HID_CONTROLS #ifndef HID_CONTROLS
#define HID_CONTROLS (0) #define HID_CONTROLS (0)
#endif #endif
/** /**
@@ -488,12 +523,11 @@
* You must also supply your own function to deal with the HID endpoint(s) * You must also supply your own function to deal with the HID endpoint(s)
* in this case. * in this case.
*/ */
#if( 0 < HID_CONTROLS ) #if (HID_CONTROLS) || defined (__DOXYGEN__)
#define XUA_HID_ENABLED (1) #define XUA_HID_ENABLED (1)
#define XUA_OR_STATIC_HID_ENABLED (1) #define XUA_OR_STATIC_HID_ENABLED (1)
#endif #endif
#if defined(__static_hid_report_h_exists__) #if defined(__static_hid_report_h_exists__)
#define XUA_OR_STATIC_HID_ENABLED (1) #define XUA_OR_STATIC_HID_ENABLED (1)
#endif #endif
@@ -506,18 +540,7 @@
* Default 0 (Disabled) * Default 0 (Disabled)
*/ */
#ifndef HID_OUT_REQUIRED #ifndef HID_OUT_REQUIRED
#define HID_OUT_REQUIRED (0) #define HID_OUT_REQUIRED (0)
#endif
/**
* @brief Defines whether XMOS device runs as master (i.e. drives LR and Bit clocks)
*
* 0: XMOS is I2S master. 1: CODEC is I2s master.
*
* Default: 0 (XMOS is master)
*/
#ifndef CODEC_MASTER
#define CODEC_MASTER (0)
#endif #endif
/** /**
@@ -526,7 +549,7 @@
* Default: "" * Default: ""
*/ */
#ifndef SERIAL_STR #ifndef SERIAL_STR
#define SERIAL_STR "" #define SERIAL_STR ""
#endif #endif
/** /**
@@ -535,7 +558,7 @@
* Default: "XMOS" * Default: "XMOS"
*/ */
#ifndef VENDOR_STR #ifndef VENDOR_STR
#define VENDOR_STR "XMOS" #define VENDOR_STR "XMOS"
#endif #endif
/** /**
@@ -544,7 +567,7 @@
* Default: 0x20B1 (XMOS) * Default: 0x20B1 (XMOS)
*/ */
#ifndef VENDOR_ID #ifndef VENDOR_ID
#define VENDOR_ID (0x20B1) #define VENDOR_ID (0x20B1)
#endif #endif
/** /**
@@ -586,7 +609,7 @@
*/ */
#if (AUDIO_CLASS == 1) || (AUDIO_CLASS_FALLBACK) || defined(__DOXYGEN__) #if (AUDIO_CLASS == 1) || (AUDIO_CLASS_FALLBACK) || defined(__DOXYGEN__)
#ifndef PID_AUDIO_1 #ifndef PID_AUDIO_1
#define PID_AUDIO_1 (0x0003) #define PID_AUDIO_1 (0x0003)
#endif #endif
#endif #endif
@@ -596,28 +619,28 @@
* Default: 0x0002 * Default: 0x0002
*/ */
#ifndef PID_AUDIO_2 #ifndef PID_AUDIO_2
#define PID_AUDIO_2 (0x0002) #define PID_AUDIO_2 (0x0002)
#endif #endif
/** /**
* @brief Device firmware version number in Binary Coded Decimal format: 0xJJMN where JJ: major, M: minor, N: sub-minor version number. * @brief Device firmware version number in Binary Coded Decimal format: 0xJJMN where JJ: major, M: minor, N: sub-minor version number.
*/ */
#ifndef BCD_DEVICE_J #ifndef BCD_DEVICE_J
#define BCD_DEVICE_J (1) #define BCD_DEVICE_J (1)
#endif #endif
/** /**
* @brief Device firmware version number in Binary Coded Decimal format: 0xJJMN where JJ: major, M: minor, N: sub-minor version number. * @brief Device firmware version number in Binary Coded Decimal format: 0xJJMN where JJ: major, M: minor, N: sub-minor version number.
*/ */
#ifndef BCD_DEVICE_M #ifndef BCD_DEVICE_M
#define BCD_DEVICE_M (2) #define BCD_DEVICE_M (2)
#endif #endif
/** /**
* @brief Device firmware version number in Binary Coded Decimal format: 0xJJMN where JJ: major, M: minor, N: sub-minor version number. * @brief Device firmware version number in Binary Coded Decimal format: 0xJJMN where JJ: major, M: minor, N: sub-minor version number.
*/ */
#ifndef BCD_DEVICE_N #ifndef BCD_DEVICE_N
#define BCD_DEVICE_N (0) #define BCD_DEVICE_N (0)
#endif #endif
/** /**
@@ -1021,7 +1044,7 @@
* Default: 1 (Enabled) * Default: 1 (Enabled)
*/ */
#ifndef OUTPUT_VOLUME_CONTROL #ifndef OUTPUT_VOLUME_CONTROL
#define OUTPUT_VOLUME_CONTROL (1) #define OUTPUT_VOLUME_CONTROL (1)
#endif #endif
/** /**
@@ -1030,7 +1053,7 @@
* Default: 1 (Enabled) * Default: 1 (Enabled)
*/ */
#ifndef INPUT_VOLUME_CONTROL #ifndef INPUT_VOLUME_CONTROL
#define INPUT_VOLUME_CONTROL (1) #define INPUT_VOLUME_CONTROL (1)
#endif #endif
/* Power */ /* Power */
@@ -1072,7 +1095,7 @@
* Default: 0 (Disabled) * Default: 0 (Disabled)
*/ */
#ifndef MIXER #ifndef MIXER
#define MIXER (0) #define MIXER (0)
#endif #endif
/** /**
@@ -1082,11 +1105,11 @@
*/ */
#if (MIXER) #if (MIXER)
#ifndef MAX_MIX_COUNT #ifndef MAX_MIX_COUNT
#define MAX_MIX_COUNT (8) #define MAX_MIX_COUNT (8)
#endif #endif
#else #else
#ifndef MAX_MIX_COUNT #ifndef MAX_MIX_COUNT
#define MAX_MIX_COUNT (0) #define MAX_MIX_COUNT (0)
#endif #endif
#endif #endif
@@ -1098,7 +1121,7 @@
* Default: 18 * Default: 18
*/ */
#ifndef MIX_INPUTS #ifndef MIX_INPUTS
#define MIX_INPUTS (18) #define MIX_INPUTS (18)
#endif #endif
/* Volume processing defines */ /* Volume processing defines */
@@ -1110,7 +1133,7 @@
* Default: 0x8100 (-127db) * Default: 0x8100 (-127db)
*/ */
#ifndef MIN_VOLUME #ifndef MIN_VOLUME
#define MIN_VOLUME (0x8100) #define MIN_VOLUME (0x8100)
#endif #endif
@@ -1120,7 +1143,7 @@
* Default: 0x0000 (0db) * Default: 0x0000 (0db)
*/ */
#ifndef MAX_VOLUME #ifndef MAX_VOLUME
#define MAX_VOLUME (0x0000) #define MAX_VOLUME (0x0000)
#endif #endif
/** /**
@@ -1129,7 +1152,7 @@
* Default: 0x100 (1db) * Default: 0x100 (1db)
*/ */
#ifndef VOLUME_RES #ifndef VOLUME_RES
#define VOLUME_RES (0x100) #define VOLUME_RES (0x100)
#endif #endif
/** /**
@@ -1139,7 +1162,7 @@
* Default: 0x8100 (-127db) * Default: 0x8100 (-127db)
*/ */
#ifndef MIN_MIXER_VOLUME #ifndef MIN_MIXER_VOLUME
#define MIN_MIXER_VOLUME (0x8100) #define MIN_MIXER_VOLUME (0x8100)
#endif #endif
/** /**
@@ -1148,7 +1171,7 @@
* Default: 0x0000 (0db) * Default: 0x0000 (0db)
*/ */
#ifndef MAX_MIXER_VOLUME #ifndef MAX_MIXER_VOLUME
#define MAX_MIXER_VOLUME (0x0000) #define MAX_MIXER_VOLUME (0x0000)
#endif #endif
/** /**
@@ -1157,36 +1180,36 @@
* Default: 0x100 (1db) * Default: 0x100 (1db)
*/ */
#ifndef VOLUME_RES_MIXER #ifndef VOLUME_RES_MIXER
#define VOLUME_RES_MIXER (0x100) #define VOLUME_RES_MIXER (0x100)
#endif #endif
/* Handle out volume control in the mixer - enabled by default if mixer enabled */ /* Handle out volume control in the mixer - enabled by default */
#ifndef OUT_VOLUME_IN_MIXER #ifndef OUT_VOLUME_IN_MIXER
#if MIXER #if MIXER
#define OUT_VOLUME_IN_MIXER (1) #define OUT_VOLUME_IN_MIXER (1)
#else #else
#define OUT_VOLUME_IN_MIXER (0) #define OUT_VOLUME_IN_MIXER (0)
#endif #endif
#endif #endif
/* Apply out volume controls after the mix. Only relevant when OUT_VOLUME_IN_MIXER enabled. Enabled by default */ /* Apply out volume controls after the mix. Only relevant when OUT_VOLUME_IN_MIXER enabled. Enabled by default */
#ifndef OUT_VOLUME_AFTER_MIX #ifndef OUT_VOLUME_AFTER_MIX
#define OUT_VOLUME_AFTER_MIX (1) #define OUT_VOLUME_AFTER_MIX (1)
#endif #endif
/* Handle in volume control in the mixer - disabled by default */ /* Handle in volume control in the mixer - disabled by default */
#ifndef IN_VOLUME_IN_MIXER #ifndef IN_VOLUME_IN_MIXER
#define IN_VOLUME_IN_MIXER (0) #define IN_VOLUME_IN_MIXER (0)
#endif #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 relevant when IN_VOLUMNE_IN MIXER enabled. Enabled by default */
#ifndef IN_VOLUME_AFTER_MIX #ifndef IN_VOLUME_AFTER_MIX
#define IN_VOLUME_AFTER_MIX (1) #define IN_VOLUME_AFTER_MIX (1)
#endif #endif
/* Always enable explicit feedback EP, even when input stream is present */ /* Always enable explicit feedback EP, even when input stream is present */
#ifndef UAC_FORCE_FEEDBACK_EP #ifndef UAC_FORCE_FEEDBACK_EP
#define UAC_FORCE_FEEDBACK_EP (1) #define UAC_FORCE_FEEDBACK_EP (1)
#endif #endif
#if (defined(UAC_FORCE_FEEDBACK_EP) && UAC_FORCE_FEEDBACK_EP == 0) #if (defined(UAC_FORCE_FEEDBACK_EP) && UAC_FORCE_FEEDBACK_EP == 0)
@@ -1194,9 +1217,9 @@
#endif #endif
/* Synchronisation defines */ /* Synchronisation defines */
#define XUA_SYNCMODE_ASYNC (1) // USB_ENDPOINT_SYNCTYPE_ASYNC #define XUA_SYNCMODE_ASYNC (1) // USB_ENDPOINT_SYNCTYPE_ASYNC
#define XUA_SYNCMODE_ADAPT (2) // USB_ENDPOINT_SYNCTYPE_ADAPT #define XUA_SYNCMODE_ADAPT (2) // USB_ENDPOINT_SYNCTYPE_ADAPT
#define XUA_SYNCMODE_SYNC (3) // USB_ENDPOINT_SYNCTYPE_SYNC #define XUA_SYNCMODE_SYNC (3) // USB_ENDPOINT_SYNCTYPE_SYNC
#ifndef XUA_SYNCMODE #ifndef XUA_SYNCMODE
#define XUA_SYNCMODE XUA_SYNCMODE_ASYNC #define XUA_SYNCMODE XUA_SYNCMODE_ASYNC
@@ -1276,6 +1299,8 @@ enum USBEndpointNumber_Out
#endif /* __ASSEMBLER__ */ #endif /* __ASSEMBLER__ */
#define AUDIO_STOP_FOR_DFU (0x12345678) #define AUDIO_STOP_FOR_DFU (0x12345678)
#define AUDIO_START_FROM_DFU (0x87654321)
#define AUDIO_REBOOT_FROM_DFU (0xa5a5a5a5)
/* Result of db_to_mult(MAX_VOLUME, 8, 29) */ /* Result of db_to_mult(MAX_VOLUME, 8, 29) */
#define MAX_VOLUME_MULT (0x20000000) #define MAX_VOLUME_MULT (0x20000000)

View File

@@ -8,9 +8,9 @@ An application using the USB audio framework needs to have defines set for confi
Defaults for these defines are found in ``xua_conf_default.h``. Defaults for these defines are found in ``xua_conf_default.h``.
These defines should be over-ridden in an optional header file ``xua_conf.h`` file or in the ``Makefile`` These defines should be over-ridden in an optional header file ``xua_conf.h`` file or in the ``Makefile``
for a relevant build configuration. for a relevant build configuration.
This section fully documents all of the settable defines and their default values (where appropriate). This section fully documents all of the settable defines and their default values (where appropriate).
Code Location (tile) Code Location (tile)
-------------------- --------------------
@@ -25,12 +25,12 @@ Code Location (tile)
Channel Counts Channel Counts
-------------- --------------
.. doxygendefine:: NUM_USB_CHAN_OUT .. doxygendefine:: NUM_USB_CHAN_OUT
.. doxygendefine:: NUM_USB_CHAN_IN .. doxygendefine:: NUM_USB_CHAN_IN
.. doxygendefine:: I2S_CHANS_DAC .. doxygendefine:: I2S_CHANS_DAC
.. doxygendefine:: I2S_CHANS_ADC .. doxygendefine:: I2S_CHANS_ADC
Frequencies and Clocks Frequencies and Clocks
---------------------- ----------------------
.. doxygendefine:: MAX_FREQ .. doxygendefine:: MAX_FREQ
@@ -38,6 +38,7 @@ Frequencies and Clocks
.. doxygendefine:: DEFAULT_FREQ .. doxygendefine:: DEFAULT_FREQ
.. doxygendefine:: MCLK_441 .. doxygendefine:: MCLK_441
.. doxygendefine:: MCLK_48 .. doxygendefine:: MCLK_48
.. doxygendefine:: XUA_USE_SW_PLL
Audio Class Audio Class
----------- -----------

View File

@@ -1,74 +1,49 @@
Required User Function Definitions |newpage|
==================================
The following functions need to be defined by an application using the XMOS USB Audio framework. User Function Definitions
=========================
The following functions can be defined by an application using `lib_xua`.
.. note:: Default, empty, implementations of these functions are provided in `lib_xua`. These are marked
as weak symbols so the application can simply define its own version of them.
External Audio Hardware Configuration Functions External Audio Hardware Configuration Functions
----------------------------------------------- -----------------------------------------------
.. c:function:: void AudioHwInit(chanend ?c_codec) The following functions can be optionally used by the design to configure external audio hardware.
As a minimum, in most applications, it is expected that a implementation of `AudioHwConfig()` will need
to be provided.
This function is called when the audio core starts after the .. doxygenfunction:: AudioHwInit
device boots up and should initialize the external audio harware e.g. clocking, DAC, ADC etc .. doxygenfunction:: AudioHwConfig
.. doxygenfunction:: AudioHwConfig_Mute
.. doxygenfunction:: AudioHwConfig_UnMute
:param c_codec: An optional chanend that was original passed into
:c:func:`audio` that can be used to communicate
with other cores.
.. c:function:: void AudioHwConfig(unsigned samFreq, unsigned mclk, chanend ?c_codec, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC) Audio Stream Start/Stop Functions
---------------------------------
This function is called when the audio core starts or changes
sample rate. It should configure the extenal audio hardware to run at the specified
sample rate given the supplied master clock frequency.
:param samFreq: The sample frequency in Hz that the hardware should be configured to (in Hz).
:param mclk: The master clock frequency that is required in Hz.
:param c_codec: An optional chanend that was original passed into
:c:func:`audio` that can be used to communicate
with other cores.
:param dsdMode: Signifies if the audio hardware should be configured for DSD operation
:param sampRes_DAC: The sample resolution of the DAC stream
:param sampRes_ADC: The sample resolution of the ADC stream
Audio Streaming Functions
-------------------------
The following functions can be optionally used by the design. They can be useful for mute lines etc. The following functions can be optionally used by the design. They can be useful for mute lines etc.
.. c:function:: void AudioStreamStart(void) .. doxygenfunction:: UserAudioStreamStart
.. doxygenfunction:: UserAudioStreamStop
.. doxygenfunction:: UserAudioInputStreamStart
.. doxygenfunction:: UserAudioInputStreamStop
.. doxygenfunction:: UserAudioOutputStreamStart
.. doxygenfunction:: UserAudioOutputStreamStop
This function is called when the audio stream from device to host Host Active Functions
starts. ---------------------
.. c:function:: void AudioStreamStop(void)
This function is called when the audio stream from device to host stops.
Host Active
-----------
The following function can be used to signal that the device is connected to a valid host. The following function can be used to signal that the device is connected to a valid host.
This is called on a change in state. .. doxygenfunction:: UserHostActive
.. c:function:: void AudioStreamStart(int active)
:param active: Indicates if the host is active or not. 1 for active else 0.
HID Controls HID Controls
------------ ------------
The following function is called when the device wishes to read physical user input (buttons etc). The following function is called when the device wishes to read physical user input (buttons etc).
The function should write relevant HID bits into this array. The bit ordering and functionality is defined by the HID report descriptor used.
.. c:function:: void UserReadHIDButtons(unsigned char hidData[]) .. doxygenfunction:: UserHIDGetData
:param hidData: The function should write relevant HID bits into this array. The bit ordering and functionality is defined by the HID report descriptor used.

View File

@@ -54,7 +54,9 @@ In addition :ref:`usb_audio_optional_components` shows optional components that
* - Clockgen * - Clockgen
- Drives an external frequency generator (PLL) and manages - Drives an external frequency generator (PLL) and manages
changes between internal clocks and external clocks arising changes between internal clocks and external clocks arising
from digital input. 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.
* - MIDI * - MIDI
- Outputs and inputs MIDI over a serial UART interface. - Outputs and inputs MIDI over a serial UART interface.

View File

@@ -29,10 +29,11 @@ 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 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 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 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. streams the codebase assumes the use of an external Cirrus Logic CS2100 device or lib_sw_pll on xcore.ai designs.
The ``ClockGen()`` task from ``lib_xua`` provides the reference signal to the CS2100 device and also handles The ``ClockGen()`` task from ``lib_xua`` provides the reference signal to the CS2100 device or timing information
recording of clock validity etc. See :ref:`usb_audio_sec_clock_recovery` for full details regarding ``ClockGen()``. to lib_sw_pll 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. 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 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. * 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 audio clocks * 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.
Selection between these methods is done via writing to bits 6 and 7 of PORT 8D on tile[0]. 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-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`. 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`.
.. _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"/> <Port Location="XS1_PORT_1A" Name="PORT_SPDIF_IN"/>
When S/PDIF receive is enabled the codebase expects to drive a synchronisation signal to an external When S/PDIF receive is enabled the codebase expects to either drive a synchronisation signal to an external
Cirrus Logic CS2100 device for master-clock generation. Cirrus Logic CS2100 device or use lib_swp_pll (xcore.ai only) for master-clock generation.
The programmer should ensure the define in :ref:`opt_spdif_rx_ref_defines` is set appropriately. The programmer should ensure the define in :ref:`opt_spdif_rx_ref_defines` is set appropriately.

View File

@@ -39,8 +39,11 @@ Setting the synchronisation mode of the device is done using the define in :ref:
- USB synchronisation mode - USB synchronisation mode
- ``XUA_SYNCMODE_ASYNC`` - ``XUA_SYNCMODE_ASYNC``
When operating in synchronous mode an external Cirrus Logic CS2100 device is required for master clock When operating in synchronous mode a local master clock must be generated that is synchronised to the incoming
generation. The codebase expects to drive a synchronisation signal to this external device 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.
The programmer should ensure the define in :ref:`opt_sync_ref_defines` is set appropriately. The programmer should ensure the define in :ref:`opt_sync_ref_defines` is set appropriately.
@@ -56,8 +59,11 @@ The programmer should ensure the define in :ref:`opt_sync_ref_defines` is set ap
* - ``PLL_REF_TILE`` * - ``PLL_REF_TILE``
- Tile location of reference to CS2100 device - Tile location of reference to CS2100 device
- ``AUDIO_IO_TILE`` - ``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 this reference signal port to be defined in the application XN file as ``PORT_PLL_REF``. The codebase expects the CS2100 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:: 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"/> <Port Location="XS1_PORT_1A" Name="PORT_PLL_REF"/>

View File

@@ -15,32 +15,35 @@ the xCORE.
Using an external PLL/Clock Multiplier allows an Asynchronous mode design to lock to an external 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 clock source from a digital stream (e.g. S/PDIF or ADAT input). The codebase supports the Cirrus
Logic CS2100 device for this purpose. Other devices may be supported via code modification. Logic CS2100 device or use of lib_sw_pll (xcore.ai only) for this purpose. Other devices may be
supported via code modification.
.. note:: 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
It is expected that in a future release the secondary PLL in xCORE.ai devices, coupled with and the time of received samples. Clock Gen (via CS2100 or lib_sw_pll) generates the master clock
associated software changes, will be capable of replacing the CS2100 part for most designs. 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.
The Clock Recovery core (Clock Gen) is responsible for generating the reference frequency When using lib_sw_pll (xcore.ai only) an further core is instantiated which performs the sigma-delta
to the CS2100 device. This, in turn, generates the master clock used over the whole design. modulation of the xCORE PLL to ensure the lowest jitter over the audio band. See lib_sw_pll
This core also serves as a smaller buffer between ADAT and S/PDIF receiving cores and the Audio Hub documentation for further details.
core.
When running in *Internal Clock* mode this core simply generates this clock using a local When running in *Internal Clock* mode this core simply generates this clock using a local
timer, based on the XMOS reference clock. 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 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 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 counting samples in a given period. Either the reference clock to the CS2100 is then generated based on
the reception of these samples. 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).
If an external stream becomes invalid, the *Internal Clock* timer event will fire to ensure that 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 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 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 possibly, regardless of activity made to try and keep the jitter on the reference clock as low as possible, regardless of activity
level of the Clock Gen core. The is achieved though the use of port times to schedule pin toggling 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. 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.
The Clock Gen core gets clock selection Get/Set commands from Endpoint 0 via the ``c_clk_ctl`` 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 channel. This core also records the validity of external clocks, which is also queried

View File

@@ -1,5 +1,5 @@
XMOSNEWSTYLE = 2 XMOSNEWSTYLE = 2
DOXYGEN_DIRS=../../api DOXYGEN_DIRS=../../api ../../src/core/user/audiostream ../../src/core/user/hostactive ../../src/core/user/hid ../../src/core/user/audiohw
SOURCE_INCLUDE_DIRS=../../../lib_xua SOURCE_INCLUDE_DIRS=../../../lib_xua
SPHINX_MASTER_DOC=lib_xua SPHINX_MASTER_DOC=lib_xua

View File

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

View File

@@ -1,5 +1,5 @@
set(LIB_NAME lib_xua) set(LIB_NAME lib_xua)
set(LIB_VERSION 3.5.1) set(LIB_VERSION 4.0.0)
set(LIB_INCLUDES api set(LIB_INCLUDES api
src/core src/core
src/core/audiohub src/core/audiohub
@@ -14,18 +14,20 @@ set(LIB_INCLUDES api
src/core/support src/core/support
src/core/user src/core/user
src/core/user/audiostream src/core/user/audiostream
src/core/user/audiohw
src/core/user/hid src/core/user/hid
src/core/user/hostactive src/core/user/hostactive
src/hid src/hid
src/midi) src/midi)
set(LIB_OPTIONAL_HEADERS xua_conf.h static_hid_report.h) set(LIB_OPTIONAL_HEADERS xua_conf.h static_hid_report.h)
set(LIB_DEPENDENT_MODULES "lib_locks" set(LIB_DEPENDENT_MODULES "lib_adat(1.1.0)"
"lib_logging" "lib_locks(2.2.0)"
"lib_mic_array(feature/xcommon_cmake)" "lib_logging(3.2.0)"
"lib_spdif" "lib_mic_array(4.6.0)"
"lib_xassert" "lib_spdif(6.1.0)"
"lib_xud" "lib_sw_pll(2.1.0)"
"lib_adat") "lib_xassert(4.2.0)"
"lib_xud(2.3.1)")
set(LIB_COMPILER_FLAGS -O3 -DREF_CLK_FREQ=100 -fasm-linenum -fcomment-asm) set(LIB_COMPILER_FLAGS -O3 -DREF_CLK_FREQ=100 -fasm-linenum -fcomment-asm)

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2023 XMOS LIMITED. // Copyright 2011-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
/** /**
* @file xua_audiohub.xc * @file xua_audiohub.xc
@@ -20,6 +20,7 @@
#include "xua.h" #include "xua.h"
#include "audiohw.h"
#include "audioports.h" #include "audioports.h"
#include "mic_array_conf.h" #include "mic_array_conf.h"
#if (XUA_SPDIF_TX_EN) #if (XUA_SPDIF_TX_EN)
@@ -241,12 +242,12 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out
} }
#endif // ((DEBUG_MIC_ARRAY == 1) && (XUA_NUM_PDM_MICS > 0)) #endif // ((DEBUG_MIC_ARRAY == 1) && (XUA_NUM_PDM_MICS > 0))
UserBufferManagementInit(); UserBufferManagementInit(curSamFreq);
unsigned command = DoSampleTransfer(c_out, readBuffNo, underflowWord); unsigned command = DoSampleTransfer(c_out, readBuffNo, underflowWord);
// Reinitialise user state before entering the main loop // Reinitialise user state before entering the main loop
UserBufferManagementInit(); UserBufferManagementInit(curSamFreq);
#if (XUA_ADAT_TX_EN) #if (XUA_ADAT_TX_EN)
unsafe{ unsafe{
@@ -641,6 +642,9 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
#if (XUA_ADAT_RX_EN || XUA_SPDIF_RX_EN) #if (XUA_ADAT_RX_EN || XUA_SPDIF_RX_EN)
, chanend c_dig_rx , chanend c_dig_rx
#endif #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) #if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1)
, server interface i_dfu ?dfuInterface , server interface i_dfu ?dfuInterface
#endif #endif
@@ -666,7 +670,6 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
/* Note, marked unsafe since other cores may be using this mclk port */ /* Note, marked unsafe since other cores may be using this mclk port */
configure_clock_src(clk_audio_mclk, p_mclk_in); configure_clock_src(clk_audio_mclk, p_mclk_in);
start_clock(clk_audio_mclk);
#if (DSD_CHANS_DAC > 0) #if (DSD_CHANS_DAC > 0)
/* Make sure the DSD ports are on and buffered - just in case they are not shared with I2S */ /* Make sure the DSD ports are on and buffered - just in case they are not shared with I2S */
@@ -678,15 +681,12 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
#endif #endif
#if (XUA_ADAT_TX_EN) #if (XUA_ADAT_TX_EN)
/* Share SPDIF clk blk */ configure_out_port_no_ready(p_adat_tx, clk_audio_mclk, 0);
configure_clock_src(clk_mst_spd, p_mclk_in); set_clock_fall_delay(clk_audio_mclk, 7);
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 #endif
start_clock(clk_audio_mclk);
/* Perform required CODEC/ADC/DAC initialisation */ /* Perform required CODEC/ADC/DAC initialisation */
AudioHwInit(); AudioHwInit();
@@ -799,7 +799,23 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
} }
#endif #endif
/* Configure Clocking/CODEC/DAC/ADC for SampleFreq/MClk */ /* Configure Clocking/CODEC/DAC/ADC for SampleFreq/MClk */
/* User should mute audio hardware */
AudioHwConfig_Mute();
/* User code should configure audio harware for SampleFreq/MClk etc */
AudioHwConfig(curFreq, mClk, dsdMode, curSamRes_DAC, curSamRes_ADC); 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();
} }
if(!firstRun) if(!firstRun)

View File

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

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2023 XMOS LIMITED. // Copyright 2011-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include "xua.h" #include "xua.h"
#if XUA_USB_EN #if XUA_USB_EN
@@ -105,7 +105,12 @@ void XUA_Buffer(
#endif #endif
, chanend c_aud , chanend c_aud
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) #if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
, chanend c_audio_rate_change
#if(XUA_USE_SW_PLL)
, chanend c_sw_pll
#else
, client interface pll_ref_if i_pll_ref , client interface pll_ref_if i_pll_ref
#endif
#endif #endif
) )
{ {
@@ -141,7 +146,12 @@ void XUA_Buffer(
, c_buff_ctrl , c_buff_ctrl
#endif #endif
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) #if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
, i_pll_ref , c_audio_rate_change
#if(XUA_USE_SW_PLL)
, c_sw_pll
#else
, i_pll_ref
#endif
#endif #endif
); );
@@ -190,8 +200,13 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
#ifdef CHAN_BUFF_CTRL #ifdef CHAN_BUFF_CTRL
, chanend c_buff_ctrl , chanend c_buff_ctrl
#endif #endif
#if XUA_SYNCMODE == XUA_SYNCMODE_SYNC #if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
, chanend c_audio_rate_change
#if (XUA_USE_SW_PLL)
, chanend c_sw_pll
#else
, client interface pll_ref_if i_pll_ref , client interface pll_ref_if i_pll_ref
#endif
#endif #endif
) )
{ {
@@ -247,7 +262,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
#if (NUM_USB_CHAN_IN > 0) #if (NUM_USB_CHAN_IN > 0)
unsigned bufferIn = 1; unsigned bufferIn = 1;
#endif #endif
unsigned sofCount = 0; int sofCount = 0;
unsigned mod_from_last_time = 0; unsigned mod_from_last_time = 0;
#ifdef FB_TOLERANCE_TEST #ifdef FB_TOLERANCE_TEST
@@ -294,7 +309,6 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
unsigned iap_ea_native_interface_alt_setting = 0; unsigned iap_ea_native_interface_alt_setting = 0;
unsigned iap_ea_native_control_to_send = 0; unsigned iap_ea_native_control_to_send = 0;
unsigned iap_ea_native_incoming = 0; unsigned iap_ea_native_incoming = 0;
#endif #endif
#endif #endif
@@ -357,6 +371,24 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
#ifndef LOCAL_CLOCK_MARGIN #ifndef LOCAL_CLOCK_MARGIN
#define LOCAL_CLOCK_MARGIN (1000) #define LOCAL_CLOCK_MARGIN (1000)
#endif #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 */
timer t_sofCheck; timer t_sofCheck;
unsigned timeLastEdge; unsigned timeLastEdge;
unsigned timeNextEdge; unsigned timeNextEdge;
@@ -365,6 +397,8 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
i_pll_ref.toggle(); i_pll_ref.toggle();
#endif #endif
#endif /* (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) */
while(1) while(1)
{ {
XUD_Result_t result; XUD_Result_t result;
@@ -427,7 +461,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
/* Reset FB */ /* Reset FB */
/* Note, Endpoint 0 will hold off host for a sufficient period to allow our feedback /* Note, Endpoint 0 will hold off host for a sufficient period to allow our feedback
* to stabilise (i.e. sofCount == 128 to fire) */ * to stabilise (i.e. sofCount == 128 to fire) */
sofCount = 1; sofCount = 0;
clocks = 0; clocks = 0;
clockcounter = 0; clockcounter = 0;
mod_from_last_time = 0; mod_from_last_time = 0;
@@ -450,7 +484,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
masterClockFreq = MCLK_441; masterClockFreq = MCLK_441;
} }
} }
#endif #endif /* (MAX_FREQ != MIN_FREQ) */
/* Ideally we want to wait for handshake (and pass back up) here. But we cannot keep this /* 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 * core locked, it must stay responsive to packets (MIDI etc) and SOFs. So, set a flag and check for
* handshake elsewhere */ * handshake elsewhere */
@@ -502,13 +536,13 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
} }
#endif #endif
/* Pass on sample freq change to decouple() via global flag (saves a chanend) */ /* Pass on sample freq change to decouple() via global flag (saves a chanend) */
/* Note: freqChange flags now used to communicate other commands also */ /* Note: freqChange_flag now used to communicate other commands also */
SET_SHARED_GLOBAL0(g_freqChange, cmd); /* Set command */ SET_SHARED_GLOBAL0(g_freqChange, cmd); /* Set command */
SET_SHARED_GLOBAL(g_freqChange_flag, cmd); /* Set Flag */ SET_SHARED_GLOBAL(g_freqChange_flag, cmd); /* Set Flag */
} }
break; break;
} }
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) #if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) && (!XUA_USE_SW_PLL)
case t_sofCheck when timerafter(timeNextEdge) :> void: case t_sofCheck when timerafter(timeNextEdge) :> void:
i_pll_ref.toggle(); i_pll_ref.toggle();
timeLastEdge = timeNextEdge; timeLastEdge = timeNextEdge;
@@ -523,28 +557,61 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
/* SOF notification from XUD_Manager() */ /* SOF notification from XUD_Manager() */
case inuint_byref(c_sof, u_tmp): case inuint_byref(c_sof, u_tmp):
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) #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; unsigned usbSpeed;
int framesPerSec;
GET_SHARED_GLOBAL(usbSpeed, g_curUsbSpeed); GET_SHARED_GLOBAL(usbSpeed, g_curUsbSpeed);
static int sofCount = 0; 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) */
framesPerSec = (usbSpeed == XUD_SPEED_HS) ? 8000 : 1000; sofCount++;
if (sofCount == sofFreqDivider)
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 */
/* Port is accessed via interface to allow flexibilty with location */ /* Port is accessed via interface to allow flexibilty with location */
i_pll_ref.toggle(); i_pll_ref.toggle();
t_sofCheck :> timeLastEdge; t_sofCheck :> timeLastEdge;
sofCount = 0;
timeNextEdge = timeLastEdge + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; timeNextEdge = timeLastEdge + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN;
#endif /* (XUA_USE_SW_PLL) */
sofCount = 0;
} }
/* 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));
#elif (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) #elif (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC)
/* NOTE our feedback will be wrong for a couple of SOF's after a SF change due to /* NOTE our feedback will be wrong for a couple of SOF's after a SF change due to
@@ -646,7 +713,6 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
clockcounter = 0; clockcounter = 0;
} }
#else #else
/* Assuming 48kHz from a 24.576 master clock (0.0407uS period) /* Assuming 48kHz from a 24.576 master clock (0.0407uS period)
* MCLK ticks per SOF = 125uS / 0.0407 = 3072 MCLK ticks per SOF. * MCLK ticks per SOF = 125uS / 0.0407 = 3072 MCLK ticks per SOF.
* expected Feedback is 48000/8000 = 6 samples. so 0x60000 in 16:16 format. * expected Feedback is 48000/8000 = 6 samples. so 0x60000 in 16:16 format.
@@ -691,7 +757,6 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
clocks < (expected_fb + FB_TOLERANCE)) clocks < (expected_fb + FB_TOLERANCE))
#endif #endif
{ {
int usb_speed;
asm volatile("stw %0, dp[g_speed]"::"r"(clocks)); // g_speed = clocks asm volatile("stw %0, dp[g_speed]"::"r"(clocks)); // g_speed = clocks
GET_SHARED_GLOBAL(usb_speed, g_curUsbSpeed); GET_SHARED_GLOBAL(usb_speed, g_curUsbSpeed);
@@ -897,8 +962,8 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
#endif #endif
#endif #endif
#if XUA_HID_ENABLED #if (XUA_HID_ENABLED)
/* HID Report Data */ /* HID Report Data */
case XUD_SetData_Select(c_hid, ep_hid, result): case XUD_SetData_Select(c_hid, ep_hid, result):
hid_ready_flag = 0U; hid_ready_flag = 0U;
unsigned reportTime; unsigned reportTime;
@@ -911,7 +976,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
#endif #endif
#ifdef MIDI #ifdef MIDI
/* Received word from MIDI thread - Check for ACK or Data */ /* Received word from MIDI thread - Check for ACK or Data */
case midi_get_ack_or_data(c_midi, is_ack, datum): case midi_get_ack_or_data(c_midi, is_ack, datum):
if (is_ack) if (is_ack)
{ {
@@ -963,6 +1028,33 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
break; break;
#endif /* ifdef MIDI */ #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 #ifdef IAP
/* Received word from iap thread - Check for ACK or Data */ /* 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): case iap_get_ack_or_reset_or_data(c_iap, is_ack_iap, is_reset, datum_iap):

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2023 XMOS LIMITED. // Copyright 2011-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h> #include <xs1.h>
#include <assert.h> #include <assert.h>
@@ -25,11 +25,11 @@ unsigned g_digData[10];
typedef struct typedef struct
{ {
int receivedSamples; int receivedSamples; /* Uses by clockgen to count number of dig rx samples to ascertain clock specs */
int samples; int samples; /* Raw sample count - rolling int and never reset */
int savedSamples; int savedSamples; /* Used by validSamples() to store state of last raw sample count */
int lastDiff; int lastDiff; /* Used by validSamples() to store state of last sample count diff */
unsigned identicaldiffs; unsigned identicaldiffs; /* Used by validSamples() to store state of number of identical diffs */
int samplesPerTick; int samplesPerTick;
} Counter; } Counter;
@@ -38,6 +38,7 @@ static int clockValid[NUM_CLOCKS]; /* Store current val
static int clockInt[NUM_CLOCKS]; /* Interupt flag for clocks */ static int clockInt[NUM_CLOCKS]; /* Interupt flag for clocks */
static int clockId[NUM_CLOCKS]; static int clockId[NUM_CLOCKS];
[[distributable]] [[distributable]]
void PllRefPinTask(server interface pll_ref_if i_pll_ref, out port p_pll_ref) void PllRefPinTask(server interface pll_ref_if i_pll_ref, out port p_pll_ref)
{ {
@@ -87,27 +88,10 @@ static int abs(int x)
return 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) static void outInterrupt(chanend c_interruptControl, int value)
{ {
/* Non-blocking check for control token */ outuint(c_interruptControl, value);
//if (channelContainsControlToken(c_interruptControl)) outct(c_interruptControl, XS1_CT_END);
{
outuint(c_interruptControl, value);
outct(c_interruptControl, XS1_CT_END);
}
} }
#endif #endif
@@ -206,7 +190,7 @@ static inline int validSamples(Counter &counter, int clockIndex)
} }
} }
} }
else else /* No valid frequency found - reset state */
{ {
counter.identicaldiffs = 0; counter.identicaldiffs = 0;
counter.lastDiff = diff; counter.lastDiff = diff;
@@ -215,6 +199,13 @@ static inline int validSamples(Counter &counter, int clockIndex)
} }
#endif #endif
#if XUA_USE_SW_PLL
unsafe
{
unsigned * unsafe selected_mclk_rate_ptr = NULL;
}
#endif
#ifdef LEVEL_METER_LEDS #ifdef LEVEL_METER_LEDS
void VendorLedRefresh(unsigned levelData[]); void VendorLedRefresh(unsigned levelData[]);
unsigned g_inputLevelData[NUM_USB_CHAN_IN]; unsigned g_inputLevelData[NUM_USB_CHAN_IN];
@@ -225,14 +216,25 @@ extern int samples_to_host_inputs_buff[NUM_USB_CHAN_IN];
int VendorAudCoreReqs(unsigned cmd, chanend c); int VendorAudCoreReqs(unsigned cmd, chanend c);
#pragma unsafe arrays #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) 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
)
{ {
timer t_local; timer t_local;
unsigned timeNextEdge, timeLastEdge, timeNextClockDetection; unsigned timeNextEdge, timeLastEdge, timeNextClockDetection;
unsigned clkMode = CLOCK_INTERNAL; /* Current clocking mode in operation */ unsigned clkMode = CLOCK_INTERNAL; /* Current clocking mode in operation */
unsigned tmp; unsigned tmp;
/* start in no-SMUX (8-channel) mode */ /* Start in no-SMUX (8-channel) mode */
int smux = 0; int smux = 0;
#ifdef LEVEL_METER_LEDS #ifdef LEVEL_METER_LEDS
@@ -242,6 +244,16 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
timer t_external; 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 #endif
#if (XUA_SPDIF_RX_EN) #if (XUA_SPDIF_RX_EN)
@@ -281,21 +293,21 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
} }
/* Init clock unit state */ /* 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) #if (XUA_SPDIF_RX_EN)
clockFreq[CLOCK_SPDIF_INDEX] = 0; clockFreq[CLOCK_SPDIF] = 0;
clockValid[CLOCK_SPDIF_INDEX] = 0; clockValid[CLOCK_SPDIF] = 0;
clockInt[CLOCK_SPDIF_INDEX] = 0; clockInt[CLOCK_SPDIF] = 0;
clockId[CLOCK_SPDIF_INDEX] = ID_CLKSRC_SPDIF; clockId[CLOCK_SPDIF] = ID_CLKSRC_SPDIF;
#endif #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) #if (XUA_ADAT_RX_EN)
clockFreq[CLOCK_ADAT_INDEX] = 0; clockFreq[CLOCK_ADAT] = 0;
clockInt[CLOCK_ADAT_INDEX] = 0; clockInt[CLOCK_ADAT] = 0;
clockValid[CLOCK_ADAT_INDEX] = 0; clockValid[CLOCK_ADAT] = 0;
clockId[CLOCK_ADAT_INDEX] = ID_CLKSRC_ADAT; clockId[CLOCK_ADAT] = ID_CLKSRC_ADAT;
#endif #endif
#if (XUA_SPDIF_RX_EN) #if (XUA_SPDIF_RX_EN)
spdifCounters.receivedSamples = 0; spdifCounters.receivedSamples = 0;
@@ -333,6 +345,12 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
/* Initial ref clock output and get timestamp */ /* Initial ref clock output and get timestamp */
i_pll_ref.init(); 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) while(1)
{ {
select select
@@ -389,13 +407,9 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
case SET_SEL: case SET_SEL:
/* Update clock mode */ /* Update clock mode */
tmp = inuint(c_clk_ctl); clkMode = inuint(c_clk_ctl);
chkct(c_clk_ctl, XS1_CT_END); chkct(c_clk_ctl, XS1_CT_END);
if(tmp!=0)
{
clkMode = tmp;
}
#ifdef CLOCK_VALIDITY_CALL #ifdef CLOCK_VALIDITY_CALL
switch(clkMode) switch(clkMode)
{ {
@@ -404,12 +418,12 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
break; break;
#if (XUA_ADAT_RX_EN) #if (XUA_ADAT_RX_EN)
case CLOCK_ADAT: case CLOCK_ADAT:
VendorClockValidity(clockValid[CLOCK_ADAT_INDEX]); VendorClockValidity(clockValid[CLOCK_ADAT]);
break; break;
#endif #endif
#if (XUA_SPDIF_RX_EN) #if (XUA_SPDIF_RX_EN)
case CLOCK_SPDIF: case CLOCK_SPDIF:
VendorClockValidity(clockValid[CLOCK_SPDIF_INDEX]); VendorClockValidity(clockValid[CLOCK_SPDIF]);
break; break;
#endif #endif
} }
@@ -454,9 +468,12 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
/* Generate local clock from timer */ /* Generate local clock from timer */
case t_local when timerafter(timeNextEdge) :> void: 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 */ /* Setup next local clock edge */
i_pll_ref.toggle_timed(0); i_pll_ref.toggle_timed(0);
#endif
/* Record time of edge */ /* Record time of edge */
timeLastEdge = timeNextEdge; timeLastEdge = timeNextEdge;
@@ -483,32 +500,63 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
#endif #endif
break; break;
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
case t_external when timerafter(timeNextClockDetection) :> void: case t_external when timerafter(timeNextClockDetection) :> void:
{
timeNextClockDetection += (LOCAL_CLOCK_INCREMENT); int valid;
timeNextClockDetection += (LOCAL_CLOCK_INCREMENT);
#if (XUA_SPDIF_RX_EN) #if (XUA_SPDIF_RX_EN)
tmp = spdifCounters.samplesPerTick; /* Returns 1 if valid clock found */
valid = validSamples(spdifCounters, CLOCK_SPDIF);
/* Returns 1 if valid clock found */ setClockValidity(c_clk_int, CLOCK_SPDIF, valid, clkMode);
tmp = validSamples(spdifCounters, CLOCK_SPDIF_INDEX);
setClockValidity(c_clk_int, CLOCK_SPDIF_INDEX, tmp, clkMode);
#endif #endif
#if (XUA_ADAT_RX_EN) #if (XUA_ADAT_RX_EN)
tmp = validSamples(adatCounters, CLOCK_ADAT_INDEX); /* Returns 1 if valid clock found */
setClockValidity(c_clk_int, CLOCK_ADAT_INDEX, tmp, clkMode); valid = validSamples(adatCounters, CLOCK_ADAT);
setClockValidity(c_clk_int, CLOCK_ADAT, valid, clkMode);
#endif
}
break;
#endif #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; 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 #endif
#if (XUA_SPDIF_RX_EN) #if (XUA_SPDIF_RX_EN)
/* Receive sample from S/PDIF RX thread (streaming chan) */ /* Receive sample from S/PDIF RX thread (streaming chan) */
case c_spdif_rx :> spdifRxData: case c_spdif_rx :> spdifRxData:
#if XUA_USE_SW_PLL
/* Record time of sample */ /* 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 :> spdifRxTime;
/* Check parity and ignore if bad */ /* Check parity and ignore if bad */
@@ -530,7 +578,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
case SPDIF_FRAME_Y: case SPDIF_FRAME_Y:
/* Only store sample if not in overflow and stream is reasonably valid */ /* Only store sample if not in overflow and stream is reasonably valid */
if(!spdifOverflow && clockValid[CLOCK_SPDIF_INDEX]) if(!spdifOverflow && clockValid[CLOCK_SPDIF])
{ {
/* Store left and right sample pair to buffer */ /* Store left and right sample pair to buffer */
spdifSamples[spdifWr] = spdifLeft; spdifSamples[spdifWr] = spdifLeft;
@@ -562,7 +610,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
spdifCounters.samples += 1; spdifCounters.samples += 1;
if(clkMode == CLOCK_SPDIF && clockValid[CLOCK_SPDIF_INDEX]) if(clkMode == CLOCK_SPDIF && clockValid[CLOCK_SPDIF])
{ {
spdifCounters.receivedSamples+=1; spdifCounters.receivedSamples+=1;
@@ -578,9 +626,16 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
/* Setup for next edge */ /* Setup for next edge */
timeNextEdge = spdifRxTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; timeNextEdge = spdifRxTime + 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 */ /* Toggle edge */
i_pll_ref.toggle_timed(1); i_pll_ref.toggle_timed(1);
#endif
/* Reset counters */ /* Reset counters */
spdifCounters.receivedSamples = 0; spdifCounters.receivedSamples = 0;
} }
@@ -591,7 +646,11 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
#if (XUA_ADAT_RX_EN) #if (XUA_ADAT_RX_EN)
/* receive sample from ADAT rx thread (streaming channel with CT_END) */ /* receive sample from ADAT rx thread (streaming channel with CT_END) */
case inuint_byref(c_adat_rx, tmp): case inuint_byref(c_adat_rx, tmp):
#if XUA_USE_SW_PLL
/* record time of sample */ /* record time of sample */
asm volatile(" getts %0, res[%1]" : "=r" (mclk_time_stamp) : "r" (p_for_mclk_count_aud));
#endif
t_local :> adatReceivedTime; t_local :> adatReceivedTime;
/* Sync is: 1 | (user_byte << 4) */ /* Sync is: 1 | (user_byte << 4) */
@@ -611,7 +670,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
if (adatChannel == 8) if (adatChannel == 8)
{ {
/* only store left samples if not in overflow and stream is reasonably valid */ /* only store left samples if not in overflow and stream is reasonably valid */
if (!adatOverflow && clockValid[CLOCK_ADAT_INDEX]) if (!adatOverflow && clockValid[CLOCK_ADAT])
{ {
/* Unpick the SMUX.. */ /* Unpick the SMUX.. */
if(smux == 2) if(smux == 2)
@@ -668,7 +727,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
{ {
adatCounters.samples += 1; adatCounters.samples += 1;
if (clkMode == CLOCK_ADAT && clockValid[CLOCK_ADAT_INDEX]) if (clkMode == CLOCK_ADAT && clockValid[CLOCK_ADAT])
{ {
adatCounters.receivedSamples += 1; adatCounters.receivedSamples += 1;
@@ -684,8 +743,16 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
/* Setup for next edge */ /* Setup for next edge */
timeNextEdge = adatReceivedTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; 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 */ /* Toggle edge */
i_pll_ref.toggle_timed(1); i_pll_ref.toggle_timed(1);
#endif
/* Reset counters */ /* Reset counters */
adatCounters.receivedSamples = 0; adatCounters.receivedSamples = 0;
@@ -724,7 +791,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
spdifSamps -= 2; spdifSamps -= 2;
/* spdifSamps could go to -1 */ /* spdifSamps could go to -1 */
if(spdifSamps < 0) if(spdifSamps <= 0)
{ {
/* We're out of S/PDIF samples, mark underflow condition */ /* We're out of S/PDIF samples, mark underflow condition */
spdifUnderflow = 1; spdifUnderflow = 1;
@@ -738,7 +805,6 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
spdifOverflow = 0; spdifOverflow = 0;
} }
} }
#endif #endif
#if (XUA_ADAT_RX_EN) #if (XUA_ADAT_RX_EN)
if (adatUnderflow) if (adatUnderflow)
@@ -806,7 +872,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
} }
/* adatSamps could go to -1 */ /* adatSamps could go to -1 */
if (adatSamps < 0) if (adatSamps <= 0)
{ {
/* we're out of ADAT samples, mark underflow condition */ /* we're out of ADAT samples, mark underflow condition */
adatUnderflow = 1; adatUnderflow = 1;
@@ -823,7 +889,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interfa
outuint(c_dig_rx, 1); outuint(c_dig_rx, 1);
break; break;
#endif #endif
} } /* select */
} } /* while(1) */
} } /* clkgen task scope */

View File

@@ -0,0 +1,57 @@
// 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

@@ -0,0 +1,202 @@
// 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-2023 XMOS LIMITED. // Copyright 2011-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
/** /**
* @file xua_ep0_descriptors.h * @file xua_ep0_descriptors.h
@@ -772,6 +772,11 @@ typedef struct
unsigned char configDesc_DFU[DFU_LENGTH]; unsigned char configDesc_DFU[DFU_LENGTH];
#endif #endif
#ifdef USB_CONTROL_DESCS
/* Inferface descriptor for control */
unsigned char itfDesc_control[9];
#endif
#ifdef IAP #ifdef IAP
USB_Descriptor_Interface_t iAP_Interface; USB_Descriptor_Interface_t iAP_Interface;
USB_Descriptor_Endpoint_t iAP_Out_Endpoint; USB_Descriptor_Endpoint_t iAP_Out_Endpoint;
@@ -2104,6 +2109,21 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2=
#endif #endif
#endif /* (XUA_DFU_EN == 1) */ #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 #ifdef IAP
/* Interface descriptor */ /* Interface descriptor */
.iAP_Interface = .iAP_Interface =

View File

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

View File

@@ -1,4 +1,4 @@
// Copyright 2012-2023 XMOS LIMITED. // Copyright 2012-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include "xua.h" /* Device specific defines */ #include "xua.h" /* Device specific defines */
@@ -144,6 +144,11 @@ on tile[XUD_TILE] : in port p_spdif_rx = PORT_SPDIF_IN;
#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)
/* Reference to external clock multiplier */ /* Reference to external clock multiplier */
on tile[PLL_REF_TILE] : out port p_pll_ref = PORT_PLL_REF; 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 #endif
#ifdef MIDI #ifdef MIDI
@@ -310,6 +315,13 @@ void usb_audio_io(chanend ?c_aud_in,
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
, client interface pll_ref_if i_pll_ref , client interface pll_ref_if i_pll_ref
#endif #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
#endif
) )
{ {
#if (MIXER) #if (MIXER)
@@ -318,9 +330,14 @@ void usb_audio_io(chanend ?c_aud_in,
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
chan c_dig_rx; chan c_dig_rx;
#else chan c_audio_rate_change; /* Notification of new mclk freq to clockgen and synch */
#define c_dig_rx null #if XUA_USE_SW_PLL
#endif /* 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) */
#if (XUA_NUM_PDM_MICS > 0) && (PDM_TILE == AUDIO_IO_TILE) #if (XUA_NUM_PDM_MICS > 0) && (PDM_TILE == AUDIO_IO_TILE)
/* Configure clocks ports - sharing mclk port with I2S */ /* Configure clocks ports - sharing mclk port with I2S */
@@ -366,6 +383,9 @@ void usb_audio_io(chanend ?c_aud_in,
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
, c_dig_rx , c_dig_rx
#endif #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) #if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1)
, dfuInterface , dfuInterface
#endif #endif
@@ -385,12 +405,22 @@ 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 * However, due to the use of an interface the pll reference signal port can be on another tile
*/ */
thread_speed(); thread_speed();
clockGen(c_spdif_rx, c_adat_rx, i_pll_ref, c_dig_rx, c_clk_ctl, c_clk_int); 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
);
} }
#endif #endif
//: } // par
}
} }
#ifndef USER_MAIN_DECLARATIONS #ifndef USER_MAIN_DECLARATIONS
@@ -437,7 +467,7 @@ int main()
#define c_adat_rx null #define c_adat_rx null
#endif #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; chan c_spdif_tx;
#endif #endif
@@ -463,9 +493,16 @@ int main()
#endif #endif
#endif #endif
#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) #if (((XUA_SYNCMODE == XUA_SYNCMODE_SYNC && !XUA_USE_SW_PLL) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) )
interface pll_ref_if i_pll_ref; interface pll_ref_if i_pll_ref;
#endif #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 */
#endif
chan c_sof; chan c_sof;
chan c_xud_out[ENDPOINT_COUNT_OUT]; /* Endpoint channels for XUD */ chan c_xud_out[ENDPOINT_COUNT_OUT]; /* Endpoint channels for XUD */
chan c_xud_in[ENDPOINT_COUNT_IN]; chan c_xud_in[ENDPOINT_COUNT_IN];
@@ -487,7 +524,7 @@ int main()
{ {
USER_MAIN_CORES USER_MAIN_CORES
#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) #if (((XUA_SYNCMODE == XUA_SYNCMODE_SYNC && !XUA_USE_SW_PLL) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN))
on tile[PLL_REF_TILE]: PllRefPinTask(i_pll_ref, p_pll_ref); on tile[PLL_REF_TILE]: PllRefPinTask(i_pll_ref, p_pll_ref);
#endif #endif
on tile[XUD_TILE]: on tile[XUD_TILE]:
@@ -558,7 +595,12 @@ int main()
#endif #endif
, c_mix_out , c_mix_out
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) #if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
, c_audio_rate_change
#if (!XUA_USE_SW_PLL)
, i_pll_ref , i_pll_ref
#else
, c_sw_pll
#endif
#endif #endif
); );
//: //:
@@ -573,8 +615,16 @@ int main()
#endif /* XUA_USB_EN */ #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);
#endif
on tile[AUDIO_IO_TILE]: on tile[AUDIO_IO_TILE]:
{ {
#ifndef AUDIO_UNSAFE_RESRC
#define AUDIO_UNSAFE_RESRC
#endif
AUDIO_UNSAFE_RESRC
/* Audio I/O task, includes mixing etc */ /* Audio I/O task, includes mixing etc */
usb_audio_io(c_mix_out usb_audio_io(c_mix_out
#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) #if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE)
@@ -588,13 +638,16 @@ int main()
, dfuInterface , dfuInterface
#endif #endif
#if (XUA_NUM_PDM_MICS > 0) #if (XUA_NUM_PDM_MICS > 0)
#if (PDM_TILE == AUDIO_IO_TILE)
, c_ds_output
#endif
, c_pdm_pcm
#endif #endif
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) #if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
, i_pll_ref , 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
#endif #endif
); );
} }

View File

@@ -73,6 +73,9 @@ void ConfigAudioPorts(
if(!isnull(p_lrclk)) if(!isnull(p_lrclk))
{ {
/* Clock LR clock from bit clock-block */ /* Clock LR clock from bit clock-block */
#if((XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM)&&(I2S_TDM_LRCLK_EDGES==0))
set_port_inv(p_lrclk);//反向lrclk的输出
#endif
configure_out_port_no_ready(p_lrclk, clk_audio_bclk, 0); configure_out_port_no_ready(p_lrclk, clk_audio_bclk, 0);
} }

View File

@@ -0,0 +1,29 @@
// Copyright 2023-2024 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() */
void AudioHwInit() __attribute__ ((weak));
void AudioHwInit()
{
return;
}
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC) __attribute__ ((weak));
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC)
{
return;
}
void AudioHwConfig_Mute() __attribute__ ((weak));
void AudioHwConfig_Mute()
{
return;
}
void AudioHwConfig_UnMute() __attribute__ ((weak));
void AudioHwConfig_UnMute()
{
return;
}

View File

@@ -0,0 +1,54 @@
// Copyright 2023-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _AUDIO_HW_H_
#define _AUDIO_HW_H_
/* The functions below should be implemented for the external audio hardware arrangement of a specific design.
* Note, default (empty) implementations of these are provided in audiohub_user.c
*/
/**
* @brief User audio hardware initialisation code
*
* This function is called when the device starts up and should contain user code to perform any required audio hardware initialisation
*/
void AudioHwInit(void);
/**
* @brief User audio hardware configuration code
*
* This function is called when on sample rate change and should contain user code to configure audio hardware
* (clocking, CODECs etc) for a specific mClk/Sample frequency
*
* \param samFreq The new sample frequency (in Hz)
*
* \param mClk The new master clock frequency (in Hz)
*
* \param dsdMode DSD mode, DSD_MODE_NATIVE, DSD_MODE_DOP or DSD_MODE_OFF
*
* \param sampRes_DAC Playback sample resolution (in bits)
*
* \param sampRes_ADC Record sample resolution (in bits)
*/
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC);
/**
* @brief User code mute audio hardware
*
* This function is called before AudioHwConfig() and should contain user code to mute audio hardware before a
* sample rate change in order to reduced audible pops/clicks
*
* Note, if using the application PLL of a xcore.ai device this function will be called before the master-clock is
* changed
*/
void AudioHwConfig_Mute(void);
/**
* @brief User code to un-mute audio hardware
*
* This function is called after AudioHwConfig() and should contain user code to un-mute audio hardware after a
* sample rate change
*/
void AudioHwConfig_UnMute(void);
#endif

View File

@@ -1,30 +1,51 @@
// Copyright 2011-2021 XMOS LIMITED. // Copyright 2011-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _AUDIOSTREAM_H_ #ifndef _AUDIOSTREAM_H_
#define _AUDIOSTREAM_H_ #define _AUDIOSTREAM_H_
/* Functions that handle functions that must occur on stream start/stop e.g. DAC mute/un-mute /* Functions that handle functionality that occur on stream start/stop e.g. DAC mute/un-mute.
* * They should be implemented for the external audio hardware arrangement of a specific design.
* THESE NEED IMPLEMENTING FOR A SPECIFIC DESIGN */
*
* */
/* Any actions required for stream start e.g. DAC un-mute - run every stream start */ /**
* @brief User stream start code
*
* User code to perform any actions required at every stream start - either input or output
*/
void UserAudioStreamStart(void); void UserAudioStreamStart(void);
/* Any actions required on stream stop e.g. DAC mute - run every steam stop */ /**
* @brief User stream stop code
*
* User code to perform any actions required on every stream stop - either input or output*/
void UserAudioStreamStop(void); void UserAudioStreamStop(void);
/* Any actions required on input stream start */ /**
* @brief User input stream stop code
*
* User code to perform any actions required on input stream start i.e. device to host
*/
void UserAudioInputStreamStart(void); void UserAudioInputStreamStart(void);
/* Any actions required on input stream stop */ /**
* @brief User input stream stop code
*
* User code to perform any actions required on input stream stop i.e. device to host
*/
void UserAudioInputStreamStop(void); void UserAudioInputStreamStop(void);
/* Any actions required on output stream start */ /**
* @brief User output stream start code
*
* User code to perform any actions required on output stream start i.e. host to device
*/
void UserAudioOutputStreamStart(void); void UserAudioOutputStreamStart(void);
/* Any actions required on output stream stop */ /**
* @brief User output stream stop code
*
* User code to perfrom any actions required on output stream stop i.e. host to device
*/
void UserAudioOutputStreamStop(void); void UserAudioOutputStreamStop(void);
#endif #endif

View File

@@ -1,17 +1,17 @@
// Copyright 2013-2023 XMOS LIMITED. // Copyright 2013-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
/** /**
* @brief Human Interface Device (HID) API * @brief Human Interface Device (HID) API
* *
* This file defines the Application Programming Interface (API) used to record HID * This file defines the Application Programming Interface (API) used to record HID
* events and retrieve a HID Report for sending to a host. * events and retrieve a HID Report for sending to a host.
* The using application has the responsibility to fulfill this API. * The using application has the responsibility to fulfill this API.
* Document section numbers refer to the HID Device Class Definition, version 1.11. * Document section numbers refer to the HID Device Class Definition, version 1.11.
*/ */
#ifndef __USER_HID_H__ #ifndef _USER_HID_H_
#define __USER_HID_H__ #define _USER_HID_H_
#include <stddef.h> #include <stddef.h>
@@ -34,22 +34,16 @@ typedef struct hidEvent_t {
#define HID_MAX_DATA_BYTES ( 4 ) #define HID_MAX_DATA_BYTES ( 4 )
#define HID_EVENT_INVALID_ID ( 0x100 ) #define HID_EVENT_INVALID_ID ( 0x100 )
#if XUA_HID_ENABLED
/** /**
* \brief Get the data for the next HID Report * \brief Get the data for the next HID Report
* *
* \note This function returns the HID data as a list of unsigned char because the
* \c XUD_SetReady_In() accepts data for transmission to the USB Host using
* this type.
*
* \param[in] id The HID Report ID (see 5.6, 6.2.2.7, 8.1 and 8.2) * \param[in] id The HID Report ID (see 5.6, 6.2.2.7, 8.1 and 8.2)
* Set to zero if the application provides only one HID Report * Set to zero if the application provides only one HID Report
* which does not include a Report ID * which does not include a Report ID
* \param[out] hidData The HID data * \param[out] hidData The HID data
* If using Report IDs, this function places the Report ID in * If using Report IDs, this function places the Report ID in
* the first element; otherwise the first element holds the * the first element; otherwise the first element holds the
* first byte of HID event data. * first byte of HID event data.
* *
* \returns The length of the HID Report in the \a hidData argument * \returns The length of the HID Report in the \a hidData argument
* \retval Zero means no new HID event data has been recorded for the given \a id * \retval Zero means no new HID event data has been recorded for the given \a id
@@ -57,9 +51,8 @@ typedef struct hidEvent_t {
size_t UserHIDGetData( const unsigned id, unsigned char hidData[ HID_MAX_DATA_BYTES ]); size_t UserHIDGetData( const unsigned id, unsigned char hidData[ HID_MAX_DATA_BYTES ]);
/** /**
* \brief Initialize HID processing * \brief Initialize HID processing
*/ */
void UserHIDInit( void ); void UserHIDInit( void );
#endif /* ( 0 < HID_CONTROLS ) */ #endif /* _USER_HID_H_ */
#endif /* __USER_HID_H__ */

View File

@@ -1,4 +1,12 @@
// Copyright 2013-2021 XMOS LIMITED. // Copyright 2013-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1. // This Software is subject to the terms of the XMOS Public Licence: Version 1.
/**
* @brief User host active code
*
* This function can be used to perform user defined actions based on host present/not-present events.
* This function is called on a change in state.
*
* \param active Indicates if the host is active or not. 1 for active, else 0
*/
void UserHostActive(int active); void UserHostActive(int active);

View File

@@ -10,20 +10,19 @@
#define SET_SEL 1 /* Set value of clock selector */ #define SET_SEL 1 /* Set value of clock selector */
#define GET_FREQ 2 /* Get current freq */ #define GET_FREQ 2 /* Get current freq */
#define GET_VALID 3 /* Get current validity */ #define GET_VALID 3 /* Get current validity */
#define SET_SMUX 7 /* Set SMUX mode (ADAT) */
#define CLOCK_INTERNAL 1 enum
#define CLOCK_SPDIF 2 {
#if (XUA_SPDIF_RX_EN) CLOCK_INTERNAL = 0,
#define CLOCK_ADAT 3 #if XUA_SPDIF_RX_EN
#else CLOCK_SPDIF,
#define CLOCK_ADAT 2
#endif #endif
#if XUA_ADAT_RX_EN
#define CLOCK_INTERNAL_INDEX (CLOCK_INTERNAL - 1) CLOCK_ADAT,
#define CLOCK_ADAT_INDEX (CLOCK_ADAT - 1) #endif
#define CLOCK_SPDIF_INDEX (CLOCK_SPDIF - 1) CLOCK_COUNT
};
#define SET_SMUX 7
/* c_audioControl */ /* c_audioControl */
#define SET_SAMPLE_FREQ 4 #define SET_SAMPLE_FREQ 4

View File

@@ -3,10 +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 \ 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 \ -DBUS_SPEED=2 \
-DXUA_USE_SW_PLL=0 \
$(TEST_FLAGS) $(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 \ 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 \ -DBUS_SPEED=1 \
-DXUA_USE_SW_PLL=0 \
$(TEST_FLAGS) $(TEST_FLAGS)
TARGET = test_xs3_600.xn TARGET = test_xs3_600.xn

View File

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