forked from PAWPAW-Mirror/lib_xua
Compare commits
125 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48533bed7b | ||
|
|
6e06ed66fc | ||
|
|
c4da4c5653 | ||
|
|
3a78f44872 | ||
|
|
425eb89e07 | ||
|
|
aef5311a4b | ||
|
|
24a62d5cfd | ||
|
|
5caee234e5 | ||
|
|
e92665174d | ||
|
|
aa769dabb4 | ||
|
|
5daee760af | ||
|
|
a96a137533 | ||
|
|
a982c24303 | ||
|
|
974ede7aab | ||
|
|
c7d0449a1f | ||
|
|
f401831766 | ||
|
|
6b055755d8 | ||
|
|
cf80a9aeaf | ||
|
|
6adf19ad93 | ||
|
|
d32ff7b3f6 | ||
|
|
a9f4f51f03 | ||
|
|
0595d8c8f7 | ||
|
|
4b2cd1c5ca | ||
|
|
ec3b8a2832 | ||
|
|
99a0535198 | ||
|
|
c9f3c53228 | ||
|
|
cb690527c9 | ||
|
|
1308f3a1cb | ||
|
|
a11195b54a | ||
|
|
89541bb88f | ||
|
|
b635b0a64b | ||
|
|
70a45d219c | ||
|
|
e4517f3776 | ||
|
|
ac0e1ebe7b | ||
|
|
7ea3ead62d | ||
|
|
a942c8af3d | ||
|
|
708f65b3e9 | ||
|
|
d861d358c4 | ||
|
|
09269c61fa | ||
|
|
f7df5fca20 | ||
|
|
4a74c81a96 | ||
|
|
ad91d19f94 | ||
|
|
f97f4dbf9b | ||
|
|
823eed0725 | ||
|
|
a9762e048a | ||
|
|
67b90f6c8c | ||
|
|
62b74a0419 | ||
|
|
a73bb8217b | ||
|
|
e1352e93d8 | ||
|
|
7a52b7dd83 | ||
|
|
8c4043fd4c | ||
|
|
6023af906e | ||
|
|
3d5b9608dc | ||
|
|
82bcb360a4 | ||
|
|
653bd73143 | ||
|
|
1ec2c28ded | ||
|
|
a81bd2ba48 | ||
|
|
415bd8605d | ||
|
|
a19d968a8b | ||
|
|
6c49d5fe36 | ||
|
|
d8cc579518 | ||
|
|
3f00a9ae10 | ||
|
|
509cd4fc2e | ||
|
|
ebf4d0e387 | ||
|
|
28a530685f | ||
|
|
d429068bcf | ||
|
|
d57559764f | ||
|
|
45ad7bc425 | ||
|
|
8627ef9744 | ||
|
|
98cfaffd4b | ||
|
|
19a18c2db5 | ||
|
|
867da0d9c9 | ||
|
|
787ffee132 | ||
|
|
7acd4ffe99 | ||
|
|
9ddfff1d60 | ||
|
|
92fd8aa11b | ||
|
|
6562a35b73 | ||
|
|
c2da737961 | ||
|
|
c5b44caeff | ||
|
|
4e4a026261 | ||
|
|
d163ccc7ef | ||
|
|
32ddf24e39 | ||
|
|
a3670d6b85 | ||
|
|
9201d7a570 | ||
|
|
655612c673 | ||
|
|
873f5bedd8 | ||
|
|
e83bbf51cf | ||
|
|
8a90660f7c | ||
|
|
ba0f07d355 | ||
|
|
cd497a0c78 | ||
|
|
bee1d878af | ||
|
|
365e9bf014 | ||
|
|
d02561da19 | ||
|
|
d55ade4ecf | ||
|
|
1848209a63 | ||
|
|
7fbf400ded | ||
|
|
ca3a7eb1f9 | ||
|
|
bbd0f2693d | ||
|
|
76ab4060d5 | ||
|
|
8a991dd638 | ||
|
|
9a8dfea641 | ||
|
|
25cd5ffafc | ||
|
|
5f4aa1f7a2 | ||
|
|
e5fb428880 | ||
|
|
aecffab0c6 | ||
|
|
82f7649079 | ||
|
|
7853807790 | ||
|
|
fb6cdbb57b | ||
|
|
5822ec7037 | ||
|
|
7967275001 | ||
|
|
c960d82233 | ||
|
|
b15eb3a329 | ||
|
|
36b32a36db | ||
|
|
540fb4baa5 | ||
|
|
398d966145 | ||
|
|
a6969a8610 | ||
|
|
53bd0e4c29 | ||
|
|
098c39b659 | ||
|
|
977408d3bf | ||
|
|
4e4ae01a35 | ||
|
|
17206d4b8f | ||
|
|
2fbeb47191 | ||
|
|
255ca79718 | ||
|
|
4589319151 | ||
|
|
e789da24d3 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -48,3 +48,7 @@ host_usb_mixer_control/xmos_mixer
|
||||
**/.vscode/**
|
||||
**.egg-info
|
||||
*tests/logs/*
|
||||
midi_tx_cmds.txt
|
||||
midi_rx_cmds.txt
|
||||
trace.txt
|
||||
tests/xua_unit_tests/src.runners
|
||||
|
||||
@@ -1,6 +1,39 @@
|
||||
lib_xua Change Log
|
||||
lib_xua change log
|
||||
==================
|
||||
|
||||
4.1.0
|
||||
-----
|
||||
|
||||
* ADDED: MIDI unit and sub-system tests
|
||||
* CHANGED: Only the minimum number of ADAT input formats are enabled based
|
||||
on the supported sample rates
|
||||
* CHANGED: Enabling ADAT tx enables different channel count interface alts,
|
||||
based on sample rate
|
||||
* CHANGED: Input audio buffer size and the exit condition underflow modified
|
||||
to to fix buffer underflow in some configurations
|
||||
* CHANGED: CT_END token based handshake in MIDI channels transactions,
|
||||
reducing opportuninity for deadlock
|
||||
* FIXED: Device fails to enumerate when ADAT and S/PDIF transmit are
|
||||
enabled
|
||||
* FIXED: Update software PLL at the correct rate for ADAT S/MUX
|
||||
* FIXED: Incorrect internal input EP count for input only devices
|
||||
* FIXED: Samples transferred to ADAT tx too frequently in TDM mode
|
||||
* FIXED: S/MUX not initialised to a value based on DEFAULT_FREQ in
|
||||
clockgen
|
||||
* FIXED: Trap when moving to DSD mode on XS3A based devices
|
||||
|
||||
* Changes to dependencies:
|
||||
|
||||
- lib_adat: 1.0.1 -> 1.2.0
|
||||
|
||||
- lib_locks: 2.1.0 -> 2.2.0
|
||||
|
||||
- lib_logging: 3.1.1 -> 3.2.0
|
||||
|
||||
- lib_sw_pll: 2.1.0 -> 2.2.0
|
||||
|
||||
- lib_xassert: 4.1.0 -> 4.2.0
|
||||
|
||||
4.0.0
|
||||
-----
|
||||
|
||||
|
||||
23
Jenkinsfile
vendored
23
Jenkinsfile
vendored
@@ -1,4 +1,4 @@
|
||||
@Library('xmos_jenkins_shared_library@v0.24.0') _
|
||||
@Library('xmos_jenkins_shared_library@v0.27.0') _
|
||||
|
||||
getApproval()
|
||||
|
||||
@@ -7,6 +7,7 @@ pipeline {
|
||||
environment {
|
||||
REPO = 'lib_xua'
|
||||
VIEW = getViewName(REPO)
|
||||
TOOLS_VERSION = "15.2.1" // For unit tests
|
||||
}
|
||||
options {
|
||||
skipDefaultCheckout()
|
||||
@@ -35,7 +36,8 @@ pipeline {
|
||||
dir("${REPO}/tests"){
|
||||
viewEnv(){
|
||||
withVenv{
|
||||
runPytest('--numprocesses=4')
|
||||
sh "xmake -C test_midi -j" // Xdist does not like building so do here
|
||||
runPytest('--numprocesses=auto -vvv')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,15 +45,14 @@ pipeline {
|
||||
}
|
||||
stage('Unity tests') {
|
||||
steps {
|
||||
dir("${REPO}") {
|
||||
dir('tests') {
|
||||
dir('xua_unit_tests') {
|
||||
withVenv {
|
||||
runWaf('.', "configure clean build --target=xcore200")
|
||||
viewEnv() {
|
||||
runPython("TARGET=XCORE200 pytest -s --junitxml=pytest_unity.xml")
|
||||
junit "pytest_unity.xml"
|
||||
}
|
||||
dir("${REPO}/tests/xua_unit_tests") {
|
||||
withTools("${env.TOOLS_VERSION}") {
|
||||
withVenv {
|
||||
withEnv(["XMOS_CMAKE_PATH=${WORKSPACE}/xcommon_cmake"]) {
|
||||
sh "cmake -G 'Unix Makefiles' -B build"
|
||||
sh 'xmake -C build -j'
|
||||
runPython("pytest -s --junitxml=pytest_unity.xml")
|
||||
junit "pytest_unity.xml"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
lib_xua
|
||||
#######
|
||||
|
||||
:Version: 4.0.0
|
||||
:Version: 4.1.0
|
||||
:Vendor: XMOS
|
||||
|
||||
:Scope: General Use
|
||||
|
||||
@@ -412,6 +412,28 @@
|
||||
#define SPDIF_TX_INDEX (0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables SPDIF Rx. Default: 0 (Disabled)
|
||||
*/
|
||||
#ifndef XUA_SPDIF_RX_EN
|
||||
#define XUA_SPDIF_RX_EN (0)
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief S/PDIF Rx first channel index, defines which channels S/PDIF will be input on.
|
||||
* Note, indexed from 0.
|
||||
*
|
||||
* Default: NONE (Must be defined by app when SPDIF_RX enabled)
|
||||
*/
|
||||
#if (XUA_SPDIF_RX_EN) || defined (__DOXYGEN__)
|
||||
#ifndef SPDIF_RX_INDEX
|
||||
#error SPDIF_RX_INDEX not defined and XUA_SPDIF_RX_EN defined
|
||||
#define SPDIF_RX_INDEX 0 /* Default define for doxygen */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Enables ADAT Tx. Default: 0 (Disabled)
|
||||
*/
|
||||
@@ -419,20 +441,35 @@
|
||||
#define XUA_ADAT_TX_EN (0)
|
||||
#endif
|
||||
|
||||
/* Calculate max ADAT channels based on sample rate range. Used for Tx and Rx */
|
||||
#if (MIN_FREQ < 88200)
|
||||
#define ADAT_MAX_CHANS (8)
|
||||
#elif (MIN_FREQ < 176400)
|
||||
#define ADAT_MAX_CHANS (4)
|
||||
#else
|
||||
#define ADAT_MAX_CHANS (2)
|
||||
#endif
|
||||
|
||||
/* Set the maximum number of channels for ADAT */
|
||||
#if XUA_ADAT_TX_EN
|
||||
#define ADAT_TX_MAX_CHANS ADAT_MAX_CHANS
|
||||
#else
|
||||
#define ADAT_TX_MAX_CHANS (0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Defines which output channels (8) should be output on ADAT. Note, Output channels indexed from 0.
|
||||
*
|
||||
* Default: 0 (i.e. channels [0:7])
|
||||
* */
|
||||
#ifndef ADAT_TX_INDEX
|
||||
#define ADAT_TX_INDEX (0)
|
||||
#endif
|
||||
#if (XUA_ADAT_TX_EN) || defined(__DOXYGEN__)
|
||||
#ifndef ADAT_TX_INDEX
|
||||
#define ADAT_TX_INDEX (0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables SPDIF Rx. Default: 0 (Disabled)
|
||||
*/
|
||||
#ifndef XUA_SPDIF_RX_EN
|
||||
#define XUA_SPDIF_RX_EN (0)
|
||||
#if (ADAT_TX_INDEX + ADAT_TX_MAX_CHANS > NUM_USB_CHAN_OUT)
|
||||
#error Not enough channels for ADAT Tx
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -442,17 +479,11 @@
|
||||
#define XUA_ADAT_RX_EN (0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief S/PDIF Rx first channel index, defines which channels S/PDIF will be input on.
|
||||
* Note, indexed from 0.
|
||||
*
|
||||
* Default: NONE (Must be defined by app when SPDIF_RX enabled)
|
||||
*/
|
||||
#if (XUA_SPDIF_RX_EN) || defined (__DOXYGEN__)
|
||||
#ifndef SPDIF_RX_INDEX
|
||||
#error SPDIF_RX_INDEX not defined and XUA_SPDIF_RX_EN defined
|
||||
#define SPDIF_RX_INDEX 0 /* Default define for doxygen */
|
||||
#endif
|
||||
/* Set the maximum number of channels for ADAT */
|
||||
#if XUA_ADAT_RX_EN
|
||||
#define ADAT_RX_MAX_CHANS ADAT_MAX_CHANS
|
||||
#else
|
||||
#define ADAT_RX_MAX_CHANS (0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -462,30 +493,80 @@
|
||||
* Default: NONE (Must be defined by app when XUA_ADAT_RX_EN is true)
|
||||
*/
|
||||
#if (XUA_ADAT_RX_EN) || defined(__DOXYGEN__)
|
||||
#ifndef ADAT_RX_INDEX
|
||||
#error ADAT_RX_INDEX not defined and XUA_ADAT_RX_EN is true
|
||||
#define ADAT_RX_INDEX (0) /* Default define for doxygen */
|
||||
#ifndef ADAT_RX_INDEX
|
||||
#error ADAT_RX_INDEX not defined and XUA_ADAT_RX_EN is true
|
||||
#define ADAT_RX_INDEX (0) /* Default define for doxygen */
|
||||
#endif
|
||||
|
||||
#if (ADAT_RX_INDEX + ADAT_RX_MAX_CHANS > NUM_USB_CHAN_IN)
|
||||
#error Not enough channels for ADAT Rx
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (ADAT_RX_INDEX + 8 > NUM_USB_CHAN_IN)
|
||||
#error Not enough channels for ADAT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (XUA_ADAT_RX_EN)
|
||||
|
||||
/* Setup input stream formats for ADAT */
|
||||
#if(MAX_FREQ > 96000)
|
||||
#define INPUT_FORMAT_COUNT 3
|
||||
#elif(MAX_FREQ > 48000)
|
||||
#define INPUT_FORMAT_COUNT 2
|
||||
#else
|
||||
#define INPUT_FORMAT_COUNT 1
|
||||
#if (XUA_ADAT_RX_EN)
|
||||
#if (MAX_FREQ > 96000)
|
||||
#if (MIN_FREQ > 96000)
|
||||
#define INPUT_FORMAT_COUNT 1
|
||||
#define HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT NUM_USB_CHAN_IN
|
||||
#elif (MIN_FREQ > 48000)
|
||||
#define INPUT_FORMAT_COUNT 2
|
||||
#define HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT NUM_USB_CHAN_IN
|
||||
#define HS_STREAM_FORMAT_INPUT_2_CHAN_COUNT (NUM_USB_CHAN_IN - 2)
|
||||
#else
|
||||
#define INPUT_FORMAT_COUNT 3
|
||||
#define HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT NUM_USB_CHAN_IN
|
||||
#define HS_STREAM_FORMAT_INPUT_2_CHAN_COUNT (NUM_USB_CHAN_IN - 4)
|
||||
#define HS_STREAM_FORMAT_INPUT_3_CHAN_COUNT (NUM_USB_CHAN_IN - 6)
|
||||
#endif
|
||||
#elif (MAX_FREQ > 48000)
|
||||
#if (MIN_FREQ > 48000)
|
||||
#define INPUT_FORMAT_COUNT 1
|
||||
#define HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT NUM_USB_CHAN_IN
|
||||
#else
|
||||
#define INPUT_FORMAT_COUNT 2
|
||||
#define HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT NUM_USB_CHAN_IN
|
||||
#define HS_STREAM_FORMAT_INPUT_2_CHAN_COUNT (NUM_USB_CHAN_IN - 4)
|
||||
#endif
|
||||
#else
|
||||
#define INPUT_FORMAT_COUNT 1
|
||||
#define HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT NUM_USB_CHAN_IN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT NUM_USB_CHAN_IN
|
||||
#define HS_STREAM_FORMAT_INPUT_2_CHAN_COUNT (NUM_USB_CHAN_IN - 4)
|
||||
#define HS_STREAM_FORMAT_INPUT_3_CHAN_COUNT (NUM_USB_CHAN_IN - 6)
|
||||
/* Setup output stream formats for ADAT */
|
||||
#if (XUA_ADAT_TX_EN)
|
||||
#if (MAX_FREQ > 96000)
|
||||
#if (MIN_FREQ > 96000)
|
||||
#define OUTPUT_FORMAT_COUNT 1
|
||||
#define HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT NUM_USB_CHAN_OUT
|
||||
#elif (MIN_FREQ > 48000)
|
||||
#define OUTPUT_FORMAT_COUNT 2
|
||||
#define HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT NUM_USB_CHAN_OUT
|
||||
#define HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT (NUM_USB_CHAN_OUT - 2)
|
||||
#else
|
||||
#define OUTPUT_FORMAT_COUNT 3
|
||||
#define HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT NUM_USB_CHAN_OUT
|
||||
#define HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT (NUM_USB_CHAN_OUT - 4)
|
||||
#define HS_STREAM_FORMAT_OUTPUT_3_CHAN_COUNT (NUM_USB_CHAN_OUT - 6)
|
||||
#endif
|
||||
#elif (MAX_FREQ > 48000)
|
||||
#if (MIN_FREQ > 48000)
|
||||
#define OUTPUT_FORMAT_COUNT 1
|
||||
#define HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT NUM_USB_CHAN_OUT
|
||||
#else
|
||||
#define OUTPUT_FORMAT_COUNT 2
|
||||
#define HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT NUM_USB_CHAN_OUT
|
||||
#define HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT (NUM_USB_CHAN_OUT - 4)
|
||||
#endif
|
||||
#else
|
||||
#define OUTPUT_FORMAT_COUNT 1
|
||||
#define HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT NUM_USB_CHAN_OUT
|
||||
#endif
|
||||
#define STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS 24
|
||||
#define STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS 24
|
||||
#define STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS 24
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -962,7 +1043,18 @@
|
||||
#define HS_STREAM_FORMAT_INPUT_3_CHAN_COUNT NUM_USB_CHAN_IN
|
||||
#endif
|
||||
|
||||
/* Channel count defines for output streams */
|
||||
#ifndef HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT
|
||||
#define HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT NUM_USB_CHAN_OUT
|
||||
#endif
|
||||
|
||||
#ifndef HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT
|
||||
#define HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT NUM_USB_CHAN_OUT
|
||||
#endif
|
||||
|
||||
#ifndef HS_STREAM_FORMAT_OUTPUT_3_CHAN_COUNT
|
||||
#define HS_STREAM_FORMAT_OUTPUT_3_CHAN_COUNT NUM_USB_CHAN_OUT
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Sample sub-slot size (bytes) of input stream Alternate 1 when running in high-speed
|
||||
@@ -1226,7 +1318,7 @@
|
||||
#endif
|
||||
|
||||
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
|
||||
#if (XUA_SPDIF_RX_EN|| ADAT_RX)
|
||||
#if (XUA_SPDIF_RX_EN|| XUA_ADAT_RX_EN)
|
||||
#error "Digital input streams not supported in Sync mode"
|
||||
#endif
|
||||
#endif
|
||||
@@ -1244,7 +1336,9 @@ enum USBEndpointNumber_In
|
||||
#if (NUM_USB_CHAN_IN == 0) || defined (UAC_FORCE_FEEDBACK_EP)
|
||||
ENDPOINT_NUMBER_IN_FEEDBACK,
|
||||
#endif
|
||||
#if (NUM_USB_CHAN_IN != 0)
|
||||
ENDPOINT_NUMBER_IN_AUDIO,
|
||||
#endif
|
||||
#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN)
|
||||
ENDPOINT_NUMBER_IN_INTERRUPT, /* Audio interrupt/status EP */
|
||||
#endif
|
||||
|
||||
@@ -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.
|
||||
#ifndef _XUA_MIDI_H_
|
||||
#define _XUA_MIDI_H_
|
||||
@@ -57,24 +57,25 @@ void midi_get_ack_or_data(chanend c, int &is_ack, unsigned int &datum);
|
||||
INLINE void midi_get_ack_or_data(chanend c, int &is_ack, unsigned int &datum) {
|
||||
if (testct(c)) {
|
||||
is_ack = 1;
|
||||
(void) inct(c); // read 1-bytes control token
|
||||
(void) inuchar(c);
|
||||
(void) inuchar(c);
|
||||
(void) inuchar(c);
|
||||
chkct(c, XS1_CT_END);
|
||||
}
|
||||
else {
|
||||
is_ack = 0;
|
||||
datum = inuint(c);
|
||||
chkct(c, XS1_CT_END);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
INLINE void midi_send_ack(chanend c) {
|
||||
outct(c, MIDI_ACK);
|
||||
outuchar(c, 0);
|
||||
outuchar(c, 0);
|
||||
outuchar(c, 0);
|
||||
outct(c, XS1_CT_END);
|
||||
}
|
||||
|
||||
INLINE void midi_send_data(chanend c, unsigned int datum) {
|
||||
outuint(c, datum);
|
||||
outct(c, XS1_CT_END);
|
||||
}
|
||||
|
||||
#define MIDI_RATE (31250)
|
||||
#define MIDI_BITTIME (XS1_TIMER_MHZ * 1000000 / MIDI_RATE)
|
||||
#define MIDI_BITTIME_2 (MIDI_BITTIME>>1)
|
||||
|
||||
@@ -3,11 +3,26 @@
|
||||
MIDI
|
||||
====
|
||||
|
||||
The MIDI core implements a 31250 baud UART for both input and output. On receiving 32=bit USB MIDI events
|
||||
from the Endpoint Buffer core, it parses these and translates them to 8-bit MIDI messages which are sent
|
||||
over UART. Similarly, incoming 8-bit MIDI messages are aggregated into 32-bit USB MIDI events and
|
||||
passed on to the Endpoint Buffer core. The MIDI core is implemented in the file ``usb_midi.xc``.
|
||||
The MIDI core implements a 31250 baud UART (8-N-1) for both input and output. It uses a single dedicated thread which performs multiple functions:
|
||||
|
||||
- UART Tx peripheral.
|
||||
- UART Tx FIFO of 1024 bytes (may be configured by the user).
|
||||
- Decoding of USB MIDI message to bytes.
|
||||
- UART Rx peripheral.
|
||||
- Packing of received MIDI bytes into USB MIDI messages/events.
|
||||
|
||||
It is connected via a channel to the Endpoint Buffer core meaning that it can be placed on any XCORE tile in the system subject to resource availability. The channel uses an optimised low level protocol meaning that it always occupies a switch path.
|
||||
|
||||
The Endpoint Buffer core implements the two Bulk endpoints (one In and one Out) as well as interacting with small, shared-memory, FIFOs for each endpoint.
|
||||
|
||||
On receiving 32-bit USB MIDI events from the Endpoint Buffer core over the channel, the MIDI core parses these and translates them to 8-bit MIDI messages which are sent
|
||||
out over the UART. Up to 1024 bytes may be buffered by the MIDI task for outgoing messages in the default configuration. If the outgoing buffer is full then it will cause the USB endpoint to be NACKed which provides flow control in the case that the host application sends messages faster than the UART can transmit them. This is important because the USB bandwidth far exceeds the MIDI UART bandwidth by many orders of magnitude. The combination of buffering and flow control ensures outgoing messages are not dropped during normal operation.
|
||||
|
||||
Incoming 8-bit MIDI messages from the UART receiver are packed into 32-bit USB MIDI events and passed on to the Endpoint Buffer core. Since the rate of ingress
|
||||
to the MIDI port is tiny in comparison to the host USB bandwidth, no buffering is required in the MIDI core and the MIDI events are always forwarded on directly to USB immediately.
|
||||
|
||||
All MIDI message types are supported including `Sysex` (MIDI System Exclusive) strings allowing custom function such as bank updates and patches, backup and device firmware upgrade (DFU) where supported by the MIDI device.
|
||||
|
||||
The MIDI core is implemented in the file ``usb_midi.xc`` and the USB buffering is handled in the file ``ep_buffer.xc``.
|
||||
|
||||
The Endpoint Buffer core implements the two Bulk endpoints (one In and one Out) as well as interacting
|
||||
with small, shared-memory, FIFOs for each endpoint.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
set(LIB_NAME lib_xua)
|
||||
set(LIB_VERSION 4.0.0)
|
||||
set(LIB_VERSION 4.1.0)
|
||||
set(LIB_INCLUDES api
|
||||
src/core
|
||||
src/core/audiohub
|
||||
@@ -20,12 +20,12 @@ set(LIB_INCLUDES api
|
||||
src/hid
|
||||
src/midi)
|
||||
set(LIB_OPTIONAL_HEADERS xua_conf.h static_hid_report.h)
|
||||
set(LIB_DEPENDENT_MODULES "lib_adat(1.1.0)"
|
||||
set(LIB_DEPENDENT_MODULES "lib_adat(1.2.0)"
|
||||
"lib_locks(2.2.0)"
|
||||
"lib_logging(3.2.0)"
|
||||
"lib_mic_array(4.6.0)"
|
||||
"lib_spdif(6.1.0)"
|
||||
"lib_sw_pll(2.1.0)"
|
||||
"lib_sw_pll(2.2.0)"
|
||||
"lib_xassert(4.2.0)"
|
||||
"lib_xud(2.3.1)")
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION = 4.0.0
|
||||
VERSION = 4.1.0
|
||||
|
||||
DEBUG ?= 0
|
||||
|
||||
@@ -8,12 +8,12 @@ else
|
||||
DEBUG_FLAGS = -DXASSERT_ENABLE_ASSERTIONS=0 -DXASSERT_ENABLE_DEBUG=0 -DXASSERT_ENABLE_LINE_NUMBERS=0
|
||||
endif
|
||||
|
||||
DEPENDENT_MODULES = lib_adat(>=1.1.0) \
|
||||
DEPENDENT_MODULES = lib_adat(>=1.2.0) \
|
||||
lib_locks(>=2.2.0) \
|
||||
lib_logging(>=3.2.0) \
|
||||
lib_mic_array(>=4.6.0) \
|
||||
lib_spdif(>=6.1.0) \
|
||||
lib_sw_pll(>=2.1.0) \
|
||||
lib_sw_pll(>=2.2.0) \
|
||||
lib_xassert(>=4.2.0) \
|
||||
lib_xud(>=2.3.1)
|
||||
|
||||
|
||||
@@ -375,12 +375,11 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out
|
||||
}
|
||||
#endif // (I2S_CHANS_DAC != 0)
|
||||
|
||||
#if (XUA_ADAT_TX_EN)
|
||||
TransferAdatTxSamples(c_adat_out, samplesOut, adatSmuxMode, 1);
|
||||
#endif
|
||||
|
||||
if(frameCount == 0)
|
||||
{
|
||||
#if (XUA_ADAT_TX_EN)
|
||||
TransferAdatTxSamples(c_adat_out, samplesOut, adatSmuxMode, 1);
|
||||
#endif
|
||||
|
||||
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
|
||||
/* Sync with clockgen */
|
||||
@@ -670,7 +669,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 */
|
||||
configure_clock_src(clk_audio_mclk, p_mclk_in);
|
||||
|
||||
start_clock(clk_audio_mclk);
|
||||
|
||||
#if (DSD_CHANS_DAC > 0)
|
||||
/* Make sure the DSD ports are on and buffered - just in case they are not shared with I2S */
|
||||
@@ -682,15 +680,12 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk,
|
||||
#endif
|
||||
|
||||
#if (XUA_ADAT_TX_EN)
|
||||
/* Share SPDIF clk blk */
|
||||
configure_clock_src(clk_mst_spd, p_mclk_in);
|
||||
configure_out_port_no_ready(p_adat_tx, clk_mst_spd, 0);
|
||||
set_clock_fall_delay(clk_mst_spd, 7);
|
||||
#if (XUA_SPDIF_TX_EN == 0)
|
||||
start_clock(clk_mst_spd);
|
||||
#endif
|
||||
configure_out_port_no_ready(p_adat_tx, clk_audio_mclk, 0);
|
||||
set_clock_fall_delay(clk_audio_mclk, 7);
|
||||
#endif
|
||||
|
||||
start_clock(clk_audio_mclk);
|
||||
|
||||
/* Perform required CODEC/ADC/DAC initialisation */
|
||||
AudioHwInit();
|
||||
|
||||
|
||||
@@ -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.
|
||||
#include "xua.h"
|
||||
|
||||
@@ -42,8 +42,13 @@
|
||||
#define MAX_DEVICE_AUD_PACKET_SIZE_OUT (MAX(MAX_DEVICE_AUD_PACKET_SIZE_OUT_FS, MAX_DEVICE_AUD_PACKET_SIZE_OUT_HS))
|
||||
|
||||
/*** BUFFER SIZES ***/
|
||||
|
||||
#define BUFFER_PACKET_COUNT (4) /* How many packets too allow for in buffer - minimum is 4 */
|
||||
/* How many packets too allow for in buffer - minimum is 5.
|
||||
2 for having in the aud_to_host buffer when it comes out of underflow, space available for 2 more for to accomodate cases when
|
||||
2 pkts from audio hub get written into the aud_to_host buffer within 1 SOF period, and space for 1 extra packet to ensure that
|
||||
when the 4th packet gets written to the buffer, there's space to accomodate the next packet, otherwise handle_audio_request() will
|
||||
drop packets after writing the 4th packet in the buffer
|
||||
*/
|
||||
#define BUFFER_PACKET_COUNT (5)
|
||||
|
||||
#define BUFF_SIZE_OUT_HS MAX_DEVICE_AUD_PACKET_SIZE_OUT_HS * BUFFER_PACKET_COUNT
|
||||
#define BUFF_SIZE_OUT_FS MAX_DEVICE_AUD_PACKET_SIZE_OUT_FS * BUFFER_PACKET_COUNT
|
||||
@@ -150,68 +155,65 @@ unsigned unpackData = 0;
|
||||
unsigned packState = 0;
|
||||
unsigned packData = 0;
|
||||
|
||||
static inline void SendSamples4(chanend c_mix_out)
|
||||
static inline void _send_sample_4(chanend c_mix_out, int ch)
|
||||
{
|
||||
/* Doing this checking allows us to unroll */
|
||||
if(g_numUsbChan_Out == NUM_USB_CHAN_OUT)
|
||||
{
|
||||
/* Buffering not underflow condition send out some samples...*/
|
||||
#pragma loop unroll
|
||||
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
|
||||
{
|
||||
int sample;
|
||||
int mult;
|
||||
int h;
|
||||
unsigned l;
|
||||
|
||||
read_via_xc_ptr(sample, g_aud_from_host_rdptr);
|
||||
g_aud_from_host_rdptr+=4;
|
||||
int sample;
|
||||
read_via_xc_ptr(sample, g_aud_from_host_rdptr);
|
||||
g_aud_from_host_rdptr+=4;
|
||||
|
||||
#if (OUTPUT_VOLUME_CONTROL == 1) && (!OUT_VOLUME_IN_MIXER)
|
||||
unsafe
|
||||
{
|
||||
mult = multOutPtr[i];
|
||||
}
|
||||
{h, l} = macs(mult, sample, 0, 0);
|
||||
h <<= 3;
|
||||
int mult;
|
||||
int h;
|
||||
unsigned l;
|
||||
unsafe
|
||||
{
|
||||
mult = multOutPtr[ch];
|
||||
}
|
||||
{h, l} = macs(mult, sample, 0, 0);
|
||||
h <<= 3;
|
||||
#if (STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED == 1)
|
||||
h |= (l >>29) & 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit)
|
||||
// Note: We need all 32bits for Native DSD
|
||||
h |= (l >>29) & 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit)
|
||||
// Note: We need all 32bits for Native DSD
|
||||
#endif
|
||||
outuint(c_mix_out, h);
|
||||
outuint(c_mix_out, h);
|
||||
#else
|
||||
outuint(c_mix_out, sample);
|
||||
outuint(c_mix_out, sample);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void SendSamples4(chanend c_mix_out)
|
||||
{
|
||||
/* Doing this allows us to unroll */
|
||||
if(g_numUsbChan_Out == HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT)
|
||||
{
|
||||
#pragma loop unroll
|
||||
for(int i = 0; i < HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT; i++)
|
||||
{
|
||||
_send_sample_4(c_mix_out, i);
|
||||
}
|
||||
}
|
||||
else if(g_numUsbChan_Out == HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT)
|
||||
{
|
||||
#pragma loop unroll
|
||||
for(int i = 0; i < HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT; i++)
|
||||
{
|
||||
_send_sample_4(c_mix_out, i);
|
||||
}
|
||||
}
|
||||
else if(g_numUsbChan_Out == HS_STREAM_FORMAT_OUTPUT_3_CHAN_COUNT)
|
||||
{
|
||||
#pragma loop unroll
|
||||
for(int i = 0; i < HS_STREAM_FORMAT_OUTPUT_3_CHAN_COUNT; i++)
|
||||
{
|
||||
_send_sample_4(c_mix_out, i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#pragma loop unroll
|
||||
#pragma loop unroll
|
||||
for(int i = 0; i < NUM_USB_CHAN_OUT_FS; i++)
|
||||
{
|
||||
int sample;
|
||||
int mult;
|
||||
int h;
|
||||
unsigned l;
|
||||
|
||||
read_via_xc_ptr(sample, g_aud_from_host_rdptr);
|
||||
g_aud_from_host_rdptr+=4;
|
||||
|
||||
#if (OUTPUT_VOLUME_CONTROL == 1) && (!OUT_VOLUME_IN_MIXER)
|
||||
unsafe
|
||||
{
|
||||
mult = multOutPtr[i];
|
||||
}
|
||||
{h, l} = macs(mult, sample, 0, 0);
|
||||
h <<= 3;
|
||||
#if (STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED == 1)
|
||||
h |= (l >>29) & 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit)
|
||||
// Note: We need all 32bits for Native DSD
|
||||
#endif
|
||||
outuint(c_mix_out, h);
|
||||
#else
|
||||
outuint(c_mix_out, sample);
|
||||
#endif
|
||||
_send_sample_4(c_mix_out, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1054,7 +1056,21 @@ void XUA_Buffer_Decouple(chanend c_mix_out
|
||||
assert(fillLevel <= BUFF_SIZE_IN);
|
||||
|
||||
/* Check if we have come out of underflow */
|
||||
if (fillLevel >= IN_BUFFER_PREFILL)
|
||||
unsigned sampFreq;
|
||||
GET_SHARED_GLOBAL(sampFreq, g_freqChange_sampFreq);
|
||||
int min, mid, max;
|
||||
GetADCCounts(sampFreq, min, mid, max);
|
||||
const int min_pkt_size = ((min * g_curSubSlot_In * g_numUsbChan_In + 3) & ~0x3) + 4;
|
||||
|
||||
/*
|
||||
Come out of underflow if there are exactly 2 packets in the buffer.
|
||||
This ensures that handle_audio_request() does not drop packets when writing packets into the aud_to_host buffer
|
||||
when aud_to_host buffer is not in underflow.
|
||||
For example, coming out of underflow with 3 packets in the buffer would mean handle_audio_request()
|
||||
drops packets if 2 pkts are received from audio hub in 1 SOF period. Coming out of underflow with 4
|
||||
packets would mean handle_audio_request would drop packets after writing 1 packet to the aud_to_host buffer.
|
||||
*/
|
||||
if ((fillLevel >= (min_pkt_size*2)) && (fillLevel < (min_pkt_size*3)))
|
||||
{
|
||||
int aud_to_host_rdptr;
|
||||
GET_SHARED_GLOBAL(aud_to_host_rdptr, g_aud_to_host_rdptr);
|
||||
|
||||
@@ -839,7 +839,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
|
||||
if (midi_data_remaining_to_device)
|
||||
{
|
||||
read_via_xc_ptr(datum, midi_from_host_rdptr);
|
||||
outuint(c_midi, datum);
|
||||
midi_send_data(c_midi, datum);
|
||||
midi_from_host_rdptr += 4;
|
||||
midi_data_remaining_to_device -= 4;
|
||||
}
|
||||
@@ -992,7 +992,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
|
||||
{
|
||||
/* Read another word from the fifo and output it to MIDI thread */
|
||||
read_via_xc_ptr(datum, midi_from_host_rdptr);
|
||||
outuint(c_midi, datum);
|
||||
midi_send_data(c_midi, datum);
|
||||
midi_from_host_rdptr += 4;
|
||||
midi_data_remaining_to_device -= 4;
|
||||
}
|
||||
|
||||
@@ -235,7 +235,23 @@ void clockGen ( streaming chanend ?c_spdif_rx,
|
||||
unsigned tmp;
|
||||
|
||||
/* Start in no-SMUX (8-channel) mode */
|
||||
int smux = 0;
|
||||
int smux;
|
||||
// Initialise smux based based on the DEFAULT_FREQ
|
||||
if(DEFAULT_FREQ < 88200)
|
||||
{
|
||||
/* No SMUX */
|
||||
smux = 0;
|
||||
}
|
||||
else if(DEFAULT_FREQ < 176400)
|
||||
{
|
||||
/* SMUX */
|
||||
smux = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* SMUX II */
|
||||
smux = 2;
|
||||
}
|
||||
|
||||
#ifdef LEVEL_METER_LEDS
|
||||
timer t_level;
|
||||
@@ -723,7 +739,14 @@ void clockGen ( streaming chanend ?c_spdif_rx,
|
||||
}
|
||||
}
|
||||
}
|
||||
if(adatChannel == 4 || adatChannel == 8)
|
||||
|
||||
/* An edge needs to be recorded/toggled in the following cases:
|
||||
* smux = 0: adatChannel = 4, 8
|
||||
* smux = 1: adatChannel = 2, 4, 6, 8
|
||||
* smux = 2: adatChannel = 1, 2, 3, 4, 5, 6, 7, 8
|
||||
* This is simplified to a shift-and-mask in the if-condition below.
|
||||
*/
|
||||
if ((adatChannel != 0) && ((adatChannel << smux) & 3) == 0)
|
||||
{
|
||||
adatCounters.samples += 1;
|
||||
|
||||
@@ -764,7 +787,7 @@ void clockGen ( streaming chanend ?c_spdif_rx,
|
||||
adatChannel = 0;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#endif // XUA_ADAT_RX_EN
|
||||
|
||||
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
|
||||
/* AudioHub requests data */
|
||||
|
||||
@@ -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.
|
||||
/**
|
||||
* @brief Implements endpoint zero for an USB Audio 1.0/2.0 device
|
||||
@@ -245,7 +245,6 @@ const unsigned g_dataFormat_In[INPUT_FORMAT_COUNT] = {STREAM_FORMAT_INPUT_1
|
||||
};
|
||||
|
||||
/* Channel count */
|
||||
/* Note, currently only input changes.. */
|
||||
const unsigned g_chanCount_In_HS[INPUT_FORMAT_COUNT] = {HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT,
|
||||
#if(INPUT_FORMAT_COUNT > 1)
|
||||
HS_STREAM_FORMAT_INPUT_2_CHAN_COUNT,
|
||||
@@ -255,6 +254,15 @@ const unsigned g_chanCount_In_HS[INPUT_FORMAT_COUNT] = {HS_STREAM_FORMAT_I
|
||||
#endif
|
||||
};
|
||||
|
||||
const unsigned g_chanCount_Out_HS[OUTPUT_FORMAT_COUNT] = {HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT,
|
||||
#if(OUTPUT_FORMAT_COUNT > 1)
|
||||
HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT,
|
||||
#endif
|
||||
#if(OUTPUT_FORMAT_COUNT > 2)
|
||||
HS_STREAM_FORMAT_OUTPUT_3_CHAN_COUNT
|
||||
#endif
|
||||
};
|
||||
|
||||
XUD_ep ep0_out;
|
||||
XUD_ep ep0_in;
|
||||
|
||||
@@ -580,7 +588,7 @@ void XUA_Endpoint0_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend c_ep0
|
||||
|
||||
if(g_curUsbSpeed == XUD_SPEED_HS)
|
||||
{
|
||||
outuint(c_audioControl, NUM_USB_CHAN_OUT); /* Channel count */
|
||||
outuint(c_audioControl, g_chanCount_Out_HS[sp.wValue-1]); /* Channel count */
|
||||
outuint(c_audioControl, g_subSlot_Out_HS[sp.wValue-1]); /* Subslot */
|
||||
outuint(c_audioControl, g_sampRes_Out_HS[sp.wValue-1]); /* Resolution */
|
||||
}
|
||||
@@ -814,7 +822,14 @@ void XUA_Endpoint0_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend c_ep0
|
||||
{
|
||||
unsigned epNum = sp.wIndex & 0xff;
|
||||
|
||||
if ((epNum == ENDPOINT_ADDRESS_OUT_AUDIO) || (epNum == ENDPOINT_ADDRESS_IN_AUDIO))
|
||||
// Ensure we only check for AUDIO EPs if enabled
|
||||
#if (NUM_USB_CHAN_IN != 0 && NUM_USB_CHAN_OUT == 0)
|
||||
if (epNum == ENDPOINT_ADDRESS_IN_AUDIO)
|
||||
#elif (NUM_USB_CHAN_IN == 0 && NUM_USB_CHAN_OUT != 0)
|
||||
if (epNum == ENDPOINT_ADDRESS_OUT_AUDIO)
|
||||
#elif (NUM_USB_CHAN_IN != 0 && NUM_USB_CHAN_OUT != 0)
|
||||
if ((epNum == ENDPOINT_ADDRESS_IN_AUDIO) || (epNum == ENDPOINT_ADDRESS_OUT_AUDIO))
|
||||
#endif
|
||||
{
|
||||
#if (AUDIO_CLASS == 2) && (AUDIO_CLASS_FALLBACK)
|
||||
if(g_curUsbSpeed == XUD_SPEED_FS)
|
||||
@@ -961,20 +976,20 @@ void XUA_Endpoint0_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend c_ep0
|
||||
cfgDesc_Audio2.Audio_Out_Format.bSubslotSize = HS_STREAM_FORMAT_OUTPUT_1_SUBSLOT_BYTES;
|
||||
cfgDesc_Audio2.Audio_Out_Format.bBitResolution = HS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS;
|
||||
cfgDesc_Audio2.Audio_Out_Endpoint.wMaxPacketSize = HS_STREAM_FORMAT_OUTPUT_1_MAXPACKETSIZE;
|
||||
cfgDesc_Audio2.Audio_Out_ClassStreamInterface.bNrChannels = NUM_USB_CHAN_OUT;
|
||||
cfgDesc_Audio2.Audio_Out_ClassStreamInterface.bNrChannels = HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT;
|
||||
#endif
|
||||
#if (OUTPUT_FORMAT_COUNT > 1)
|
||||
cfgDesc_Audio2.Audio_Out_Format_2.bSubslotSize = HS_STREAM_FORMAT_OUTPUT_2_SUBSLOT_BYTES;
|
||||
cfgDesc_Audio2.Audio_Out_Format_2.bBitResolution = HS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS;
|
||||
cfgDesc_Audio2.Audio_Out_Endpoint_2.wMaxPacketSize = HS_STREAM_FORMAT_OUTPUT_2_MAXPACKETSIZE;
|
||||
cfgDesc_Audio2.Audio_Out_ClassStreamInterface_2.bNrChannels = NUM_USB_CHAN_OUT;
|
||||
cfgDesc_Audio2.Audio_Out_ClassStreamInterface_2.bNrChannels = HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT;
|
||||
#endif
|
||||
|
||||
#if (OUTPUT_FORMAT_COUNT > 2)
|
||||
cfgDesc_Audio2.Audio_Out_Format_3.bSubslotSize = HS_STREAM_FORMAT_OUTPUT_3_SUBSLOT_BYTES;
|
||||
cfgDesc_Audio2.Audio_Out_Format_3.bBitResolution = HS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS;
|
||||
cfgDesc_Audio2.Audio_Out_Endpoint_3.wMaxPacketSize = HS_STREAM_FORMAT_OUTPUT_3_MAXPACKETSIZE;
|
||||
cfgDesc_Audio2.Audio_Out_ClassStreamInterface_3.bNrChannels = NUM_USB_CHAN_OUT;
|
||||
cfgDesc_Audio2.Audio_Out_ClassStreamInterface_3.bNrChannels = HS_STREAM_FORMAT_OUTPUT_3_CHAN_COUNT;
|
||||
#endif
|
||||
#endif
|
||||
#if (NUM_USB_CHAN_IN > 0)
|
||||
|
||||
@@ -1122,9 +1122,9 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2=
|
||||
.wTerminalType = UAC_TT_OUTPUT_TERMTYPE_SPEAKER,
|
||||
0x00, /* 6 bAssocTerminal */
|
||||
#if (OUTPUT_VOLUME_CONTROL == 1)
|
||||
FU_USBOUT, /* 7 bSourceID Connect to analog input feature unit*/
|
||||
FU_USBOUT, /* 7 bSourceID Connect to analog output feature unit */
|
||||
#else
|
||||
ID_IT_USB, /* 7 bSourceID Connect to analog input feature unit*/
|
||||
ID_IT_USB, /* 7 bSourceID Connect to USB streaming input term */
|
||||
#endif
|
||||
ID_CLKSEL, /* 8 bCSourceUD */
|
||||
0x0000, /* 9 bmControls */
|
||||
@@ -1454,15 +1454,15 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2=
|
||||
/* Class Specific AS Interface Descriptor */
|
||||
.Audio_Out_ClassStreamInterface =
|
||||
{
|
||||
0x10, /* 0 bLength: 16 */
|
||||
UAC_CS_DESCTYPE_INTERFACE, /* 1 bDescriptorType: 0x24 */
|
||||
0x10, /* 0 bLength: 16 */
|
||||
UAC_CS_DESCTYPE_INTERFACE, /* 1 bDescriptorType: 0x24 */
|
||||
UAC_CS_AS_INTERFACE_SUBTYPE_AS_GENERAL, /* 2 bDescriptorSubType */
|
||||
ID_IT_USB, /* 3 bTerminalLink (Linked to USB input terminal) */
|
||||
0x00, /* 4 bmControls */
|
||||
UAC_FORMAT_TYPE_I, /* 5 bFormatType */
|
||||
STREAM_FORMAT_OUTPUT_1_DATAFORMAT,/* 6:10 bmFormats (note this is a bitmap) */
|
||||
NUM_USB_CHAN_OUT, /* 11 bNrChannels */
|
||||
0x00000000, /* 12:14: bmChannelConfig */
|
||||
ID_IT_USB, /* 3 bTerminalLink (Linked to USB input terminal) */
|
||||
0x00, /* 4 bmControls */
|
||||
UAC_FORMAT_TYPE_I, /* 5 bFormatType */
|
||||
STREAM_FORMAT_OUTPUT_1_DATAFORMAT, /* 6:10 bmFormats (note this is a bitmap) */
|
||||
HS_STREAM_FORMAT_OUTPUT_1_CHAN_COUNT, /* 11 bNrChannels */
|
||||
0x00000000, /* 12:14: bmChannelConfig */
|
||||
.iChannelNames = offsetof(StringDescTable_t, outputChanStr_1)/sizeof(char *),
|
||||
},
|
||||
|
||||
@@ -1545,15 +1545,15 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2=
|
||||
/* Class Specific AS Interface Descriptor */
|
||||
.Audio_Out_ClassStreamInterface_2 =
|
||||
{
|
||||
0x10, /* 0 bLength: 16 */
|
||||
UAC_CS_DESCTYPE_INTERFACE, /* 1 bDescriptorType: 0x24 */
|
||||
0x10, /* 0 bLength: 16 */
|
||||
UAC_CS_DESCTYPE_INTERFACE, /* 1 bDescriptorType: 0x24 */
|
||||
UAC_CS_AS_INTERFACE_SUBTYPE_AS_GENERAL, /* 2 bDescriptorSubType */
|
||||
ID_IT_USB, /* 3 bTerminalLink (Linked to USB input terminal) */
|
||||
0x00, /* 4 bmControls */
|
||||
UAC_FORMAT_TYPE_I, /* 5 bFormatType */
|
||||
STREAM_FORMAT_OUTPUT_2_DATAFORMAT,/* 6:10 bmFormats (note this is a bitmap) */
|
||||
NUM_USB_CHAN_OUT, /* 11 bNrChannels */
|
||||
0x00000000, /* 12:14: bmChannelConfig */
|
||||
ID_IT_USB, /* 3 bTerminalLink (Linked to USB input terminal) */
|
||||
0x00, /* 4 bmControls */
|
||||
UAC_FORMAT_TYPE_I, /* 5 bFormatType */
|
||||
STREAM_FORMAT_OUTPUT_2_DATAFORMAT, /* 6:10 bmFormats (note this is a bitmap) */
|
||||
HS_STREAM_FORMAT_OUTPUT_2_CHAN_COUNT, /* 11 bNrChannels */
|
||||
0x00000000, /* 12:14: bmChannelConfig */
|
||||
.iChannelNames = (offsetof(StringDescTable_t, outputChanStr_1)/sizeof(char *)),
|
||||
},
|
||||
|
||||
@@ -1636,15 +1636,15 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2=
|
||||
/* Class Specific AS Interface Descriptor */
|
||||
.Audio_Out_ClassStreamInterface_3 =
|
||||
{
|
||||
0x10, /* 0 bLength: 16 */
|
||||
UAC_CS_DESCTYPE_INTERFACE, /* 1 bDescriptorType: 0x24 */
|
||||
0x10, /* 0 bLength: 16 */
|
||||
UAC_CS_DESCTYPE_INTERFACE, /* 1 bDescriptorType: 0x24 */
|
||||
UAC_CS_AS_INTERFACE_SUBTYPE_AS_GENERAL, /* 2 bDescriptorSubType */
|
||||
ID_IT_USB, /* 3 bTerminalLink (Linked to USB input terminal) */
|
||||
0x00, /* 4 bmControls */
|
||||
UAC_FORMAT_TYPE_I, /* 5 bFormatType */
|
||||
STREAM_FORMAT_OUTPUT_3_DATAFORMAT,/* 6:10 bmFormats (note this is a bitmap) */
|
||||
NUM_USB_CHAN_OUT, /* 11 bNrChannels */
|
||||
0x00000000, /* 12:14: bmChannelConfig */
|
||||
ID_IT_USB, /* 3 bTerminalLink (Linked to USB input terminal) */
|
||||
0x00, /* 4 bmControls */
|
||||
UAC_FORMAT_TYPE_I, /* 5 bFormatType */
|
||||
STREAM_FORMAT_OUTPUT_3_DATAFORMAT, /* 6:10 bmFormats (note this is a bitmap) */
|
||||
HS_STREAM_FORMAT_OUTPUT_3_CHAN_COUNT, /* 11 bNrChannels */
|
||||
0x00000000, /* 12:14: bmChannelConfig */
|
||||
.iChannelNames = offsetof(StringDescTable_t, outputChanStr_1)/sizeof(char *),
|
||||
},
|
||||
|
||||
|
||||
@@ -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.
|
||||
#include <xs1.h>
|
||||
#include <platform.h>
|
||||
@@ -36,7 +36,10 @@ void ConfigAudioPorts(
|
||||
#ifdef __XS3A__
|
||||
/* Increase drive strength of clock ports to 8mA */
|
||||
asm volatile ("setc res[%0], %1" :: "r" (p_bclk), "r" (0x200006));
|
||||
asm volatile ("setc res[%0], %1" :: "r" (p_lrclk), "r" (0x200006));
|
||||
if(!isnull(p_lrclk))
|
||||
{
|
||||
asm volatile ("setc res[%0], %1" :: "r" (p_lrclk), "r" (0x200006));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Note this call to stop_clock() will pause forever if the port clocking the clock-block is not low.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2011-2021 XMOS LIMITED.
|
||||
// Copyright 2011-2024 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#ifndef MIDIINPARSE_XH
|
||||
#define MIDIINPARSE_XH
|
||||
@@ -19,8 +19,11 @@ struct midi_in_parse_state {
|
||||
unsigned codeIndexNumber;
|
||||
};
|
||||
|
||||
void dump_midi_in_parse_state(struct midi_in_parse_state &s);
|
||||
|
||||
#ifdef __XC__
|
||||
void reset_midi_state(struct midi_in_parse_state &mips);
|
||||
void dump_midi_in_parse_state(struct midi_in_parse_state &s);
|
||||
{unsigned int , unsigned int} midi_in_parse(struct midi_in_parse_state &mips, unsigned cable_number, unsigned char b);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
// Copyright 2011-2021 XMOS LIMITED.
|
||||
// Copyright 2011-2024 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#ifndef MIDIOUTPARSE_XH
|
||||
#define MIDIOUTPARSE_XH
|
||||
|
||||
// If for any reason we pop a message when not needed (should never happen) this will cause midiparse out to send a size of 0 (drops packet)
|
||||
#define MIDI_OUT_NULL_MESSAGE 0x00000000
|
||||
|
||||
#ifdef __XC__
|
||||
// Takes a MIDI packet and decomoses it into up to 3 data bytes followed by a byte count.
|
||||
{unsigned, unsigned, unsigned, unsigned} midi_out_parse(unsigned event);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// Copyright 2013-2021 XMOS LIMITED.
|
||||
// Copyright 2013-2024 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#ifndef QUEUE_H_
|
||||
#define QUEUE_H_
|
||||
|
||||
#define assert(x) asm("ecallf %0"::"r"(x));
|
||||
#include "midioutparse.h"
|
||||
#include "xassert.h"
|
||||
|
||||
|
||||
typedef struct queue_t {
|
||||
/// Read index.
|
||||
@@ -14,12 +16,14 @@ typedef struct queue_t {
|
||||
unsigned mask;
|
||||
} queue_t;
|
||||
|
||||
#ifdef __XC__
|
||||
|
||||
inline int is_power_of_2(unsigned x) {
|
||||
return x != 0 && (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
inline void queue_init(queue_t &q, unsigned size) {
|
||||
assert(is_power_of_2(size));
|
||||
xassert(is_power_of_2(size) && "MIDI FIFO size must be a power of 2"); // Keep this enabled as will be discovered duirng dev time
|
||||
q.rdptr = 0;
|
||||
q.wrptr = 0;
|
||||
q.size = size;
|
||||
@@ -36,25 +40,26 @@ inline int queue_is_full(const queue_t &q) {
|
||||
|
||||
inline void queue_push_word(queue_t &q, unsigned array[], unsigned data)
|
||||
{
|
||||
assert(!queue_is_full(q));
|
||||
|
||||
if(queue_is_full(q)) {
|
||||
xassert(0 && "Unexpected push to MIDI queue when full");
|
||||
// Silently drop message if asserts not enabled
|
||||
return;
|
||||
}
|
||||
|
||||
array[q.wrptr++ & q.mask] = data;
|
||||
}
|
||||
|
||||
inline unsigned queue_pop_word(queue_t &q, unsigned array[]) {
|
||||
assert(!queue_is_empty(q));
|
||||
if(queue_is_empty(q)){
|
||||
xassert(0 && "Unexpected pop from MIDI queue when empty");
|
||||
// Return NULL messaqe if asserts not enabled
|
||||
return MIDI_OUT_NULL_MESSAGE;
|
||||
}
|
||||
|
||||
return array[q.rdptr++ & q.mask];
|
||||
}
|
||||
|
||||
inline void queue_push_byte(queue_t &q, unsigned char array[], unsigned data)
|
||||
{
|
||||
assert(!queue_is_full(q));
|
||||
array[q.wrptr++ & q.mask] = data;
|
||||
}
|
||||
|
||||
inline unsigned queue_pop_byte(queue_t &q, unsigned char array[]) {
|
||||
assert(!queue_is_empty(q));
|
||||
return array[q.rdptr++ & q.mask];
|
||||
}
|
||||
|
||||
inline unsigned queue_items(const queue_t &q) {
|
||||
return q.wrptr - q.rdptr;
|
||||
@@ -64,4 +69,6 @@ inline unsigned queue_space(const queue_t &q) {
|
||||
return q.size - queue_items(q);
|
||||
}
|
||||
|
||||
#endif // __XC__
|
||||
|
||||
#endif /* QUEUE_H_ */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2013-2021 XMOS LIMITED.
|
||||
// Copyright 2013-2024 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#include "queue.h"
|
||||
|
||||
@@ -9,7 +9,5 @@ extern inline int queue_is_empty(const queue_t &q);
|
||||
extern inline int queue_is_full(const queue_t &q);
|
||||
extern inline void queue_push_word(queue_t &q, unsigned array[], unsigned data);
|
||||
extern inline unsigned queue_pop_word(queue_t &q, unsigned array[]);
|
||||
extern inline void queue_push_byte(queue_t &q, unsigned char array[], unsigned data);
|
||||
extern inline unsigned queue_pop_byte(queue_t &q, unsigned char array[]);
|
||||
extern inline unsigned queue_space(const queue_t &q);
|
||||
extern inline unsigned queue_items(const queue_t &q);
|
||||
|
||||
@@ -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.
|
||||
#include <xs1.h>
|
||||
#include <xclib.h>
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "iap_user.h"
|
||||
#include "coprocessor_user.h"
|
||||
#endif
|
||||
//#define MIDI_LOOPBACK 1
|
||||
|
||||
int icount = 0;
|
||||
static unsigned makeSymbol(unsigned data)
|
||||
{
|
||||
@@ -89,9 +89,9 @@ void usb_midi(
|
||||
|
||||
struct midi_in_parse_state mips;
|
||||
|
||||
// the symbol fifo (to go out of uart)
|
||||
// the symbol fifo (to go out of uart).
|
||||
queue_t symbol_fifo;
|
||||
unsigned symbol_fifo_arr[USB_MIDI_DEVICE_OUT_FIFO_SIZE]; // Used for 32bit USB MIDI events
|
||||
unsigned symbol_fifo_arr[USB_MIDI_DEVICE_OUT_FIFO_SIZE]; // Used for outgoing UART symbols (which include the start and stop bit)
|
||||
|
||||
unsigned rxPT, txPT;
|
||||
int midi_from_host_overflow = 0;
|
||||
@@ -197,7 +197,7 @@ void usb_midi(
|
||||
{
|
||||
// send data
|
||||
// printstr("uart->decouple: ");
|
||||
outuint(c_midi, event);
|
||||
midi_send_data(c_midi, event);
|
||||
waiting_for_ack = 1;
|
||||
th_count++;
|
||||
}
|
||||
@@ -264,7 +264,7 @@ void usb_midi(
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
// Received as packet from USB
|
||||
case !authenticating => midi_get_ack_or_data(c_midi, is_ack, datum):
|
||||
|
||||
if (is_ack)
|
||||
@@ -272,7 +272,7 @@ void usb_midi(
|
||||
// have we got more data to send
|
||||
if (!queue_is_empty(midi_to_host_fifo))
|
||||
{
|
||||
outuint(c_midi, queue_pop_word(midi_to_host_fifo, midi_to_host_fifo_arr));
|
||||
midi_send_data(c_midi, queue_pop_word(midi_to_host_fifo, midi_to_host_fifo_arr));
|
||||
th_count++;
|
||||
}
|
||||
else
|
||||
@@ -281,6 +281,7 @@ void usb_midi(
|
||||
}
|
||||
}
|
||||
else
|
||||
// A midi packet from the host
|
||||
{
|
||||
unsigned midi[3];
|
||||
unsigned size;
|
||||
@@ -295,7 +296,7 @@ void usb_midi(
|
||||
{
|
||||
// send data
|
||||
event = byterev(event);
|
||||
outuint(c_midi, event);
|
||||
midi_send_data(c_midi, event);
|
||||
th_count++;
|
||||
waiting_for_ack = 1;
|
||||
}
|
||||
@@ -327,7 +328,7 @@ void usb_midi(
|
||||
midi_from_host_overflow = 1;
|
||||
}
|
||||
// Drop through to the isTX guarded case
|
||||
if (!isTX)
|
||||
if (!isTX && size > 0) // do not start tx'ing if this packet has no size
|
||||
{
|
||||
t :> txT; // Should be enough to trigger the other case
|
||||
isTX = 1;
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
# Copyright 2022-2023 XMOS LIMITED.
|
||||
# Copyright 2022-2024 XMOS LIMITED.
|
||||
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
import pytest
|
||||
import time
|
||||
|
||||
import Pyxsim
|
||||
from pathlib import Path
|
||||
from midi_test_helpers import MIDI_TEST_CONFIGS
|
||||
import subprocess
|
||||
|
||||
@pytest.fixture()
|
||||
def test_file(request):
|
||||
@@ -39,3 +42,16 @@ def pytest_addoption(parser):
|
||||
@pytest.fixture
|
||||
def options(request):
|
||||
yield request.config.option
|
||||
|
||||
# We use the same binary multiple times so just build once for all MIDI tests
|
||||
@pytest.fixture(scope="session")
|
||||
def build_midi():
|
||||
cmd = "xmake -C test_midi -j"
|
||||
# result = subprocess.run(cmd, capture_output=True, text=True, shell=True)
|
||||
# return_code = result.returncode
|
||||
return_code = 0
|
||||
|
||||
assert return_code == 0, f"{result.stderr}\n{result.stdout}"
|
||||
|
||||
return str(Path(__file__).parent / f"test_midi/bin/")
|
||||
|
||||
|
||||
81
tests/midi_test_helpers.py
Normal file
81
tests/midi_test_helpers.py
Normal file
@@ -0,0 +1,81 @@
|
||||
# Copyright 2024 XMOS LIMITED.
|
||||
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
MIDI_TEST_CONFIGS = ["xs2", "xs3"]
|
||||
MIDI_RATE = 31250
|
||||
|
||||
@contextlib.contextmanager
|
||||
def cd(newdir, cleanup=lambda: True):
|
||||
prevdir = os.getcwd()
|
||||
os.chdir(os.path.expanduser(newdir))
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
os.chdir(prevdir)
|
||||
cleanup()
|
||||
|
||||
@contextlib.contextmanager
|
||||
def tempdir():
|
||||
dirpath = tempfile.mkdtemp(dir=os.getcwd())
|
||||
def cleanup():
|
||||
shutil.rmtree(dirpath)
|
||||
with cd(dirpath, cleanup):
|
||||
yield dirpath
|
||||
|
||||
class midi_expect_tx:
|
||||
def __init(self):
|
||||
pass
|
||||
|
||||
def expect(self, commands):
|
||||
expected = ""
|
||||
for command in commands:
|
||||
while len(command) < 3:
|
||||
command.append(0)
|
||||
expected += "uart_tx_checker: " + " ".join([f"0x{byte:02x}" for byte in command]) + "\n"
|
||||
|
||||
return expected + "\n"
|
||||
|
||||
class midi_expect_rx:
|
||||
def __init(self):
|
||||
pass
|
||||
|
||||
def expect(self, commands):
|
||||
expected = ""
|
||||
for command in commands:
|
||||
while len(command) < 3:
|
||||
command.append(0)
|
||||
expected += "dut_midi_rx: " + " ".join([f"{byte}" for byte in command]) + "\n"
|
||||
|
||||
return expected + "\n"
|
||||
|
||||
midi_tx_file = "midi_tx_cmds.txt"
|
||||
midi_rx_file = "midi_rx_cmds.txt"
|
||||
|
||||
def create_midi_tx_file(commands=None):
|
||||
with open(midi_tx_file, "wt") as mt:
|
||||
if commands is None:
|
||||
return
|
||||
for command in commands:
|
||||
while len(command) < 3:
|
||||
command.append(0)
|
||||
text = " ".join([str(byte) for byte in command]) + "\n"
|
||||
mt.write(text)
|
||||
|
||||
def create_midi_rx_file(num_commands=0):
|
||||
with open(midi_rx_file, "wt") as mr:
|
||||
text = f"{num_commands}\n"
|
||||
mr.write(text)
|
||||
|
||||
|
||||
|
||||
# Test/dev only
|
||||
if __name__ == "__main__":
|
||||
with tempdir() as td:
|
||||
print(td)
|
||||
create_midi_tx_file()
|
||||
input("PRESS ENTER TO CONTINUE")
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016-2022 XMOS LIMITED.
|
||||
// Copyright 2016-2024 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#ifdef HARDWARE
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
/* CS2100 lists typical lock time as 100 * input period */
|
||||
#define AUDIO_PLL_LOCK_DELAY (40000000)
|
||||
|
||||
#if defined(SPDIF_RX) || defined(ADAT_RX)
|
||||
#if defined(XUA_SPDIF_RX_EN) || defined(XUA_ADAT_RX_EN)
|
||||
#define USE_FRACTIONAL_N 1
|
||||
#endif
|
||||
|
||||
@@ -32,7 +32,7 @@ port p_i2c = on tile[0]:PORT_I2C;
|
||||
|
||||
#ifdef USE_FRACTIONAL_N
|
||||
|
||||
#if !(defined(SPDIF_RX) || defined(ADAT_RX))
|
||||
#if !(defined(XUA_SPDIF_RX_EN) || defined(XUA_ADAT_RX_EN))
|
||||
/* Choose a frequency the xcore can easily generate internally */
|
||||
#define PLL_SYNC_FREQ 1000000
|
||||
#else
|
||||
@@ -95,7 +95,7 @@ void PllMult(unsigned output, unsigned ref, client interface i2c_master_if i2c)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !(defined(SPDIF_RX) || defined(ADAT_RX)) && defined(USE_FRACTIONAL_N)
|
||||
#if !(defined(XUA_SPDIF_RX_EN) || defined(XUA_ADAT_RX_EN)) && defined(USE_FRACTIONAL_N)
|
||||
on tile[AUDIO_IO_TILE] : out port p_pll_clk = PORT_PLL_REF;
|
||||
on tile[AUDIO_IO_TILE] : clock clk_pll_sync = XS1_CLKBLK_5;
|
||||
#endif
|
||||
@@ -111,7 +111,7 @@ void wait_us(int microseconds)
|
||||
|
||||
void AudioHwInit(chanend ?c_codec)
|
||||
{
|
||||
#if !(defined(SPDIF_RX) || defined(ADAT_RX)) && defined(USE_FRACTIONAL_N)
|
||||
#if !(defined(XUA_SPDIF_RX_EN) || defined(XUA_ADAT_RX_EN)) && defined(USE_FRACTIONAL_N)
|
||||
/* Output a fixed sync clock to the pll */
|
||||
configure_clock_rate(clk_pll_sync, 100, 100/(PLL_SYNC_FREQ/1000000));
|
||||
configure_port_clock_output(p_pll_clk, clk_pll_sync);
|
||||
|
||||
37
tests/test_midi/Makefile
Normal file
37
tests/test_midi/Makefile
Normal file
@@ -0,0 +1,37 @@
|
||||
# The TARGET variable determines what target system the application is
|
||||
# compiled for. It either refers to an XN file in the source directories
|
||||
# or a valid argument for the --target option when compiling.
|
||||
|
||||
ifeq ($(CONFIG), xs2)
|
||||
TARGET = XCORE-200-EXPLORER
|
||||
else
|
||||
TARGET = XCORE-AI-EXPLORER #for xs3 and also loopback test
|
||||
endif
|
||||
|
||||
# The APP_NAME variable determines the name of the final .xe file. It should
|
||||
# not include the .xe postfix. If left blank the name will default to
|
||||
# the project name
|
||||
|
||||
APP_NAME =
|
||||
|
||||
# The flags passed to xcc when building the application
|
||||
# You can also set the following to override flags for a particular language:
|
||||
#
|
||||
# XCC_XC_FLAGS, XCC_C_FLAGS, XCC_ASM_FLAGS, XCC_CPP_FLAGS
|
||||
#
|
||||
# If the variable XCC_MAP_FLAGS is set it overrides the flags passed to
|
||||
# xcc for the final link (mapping) stage.
|
||||
XCC_FLAGS_xs2 = $(EXTRA_BUILD_FLAGS) -O2 -g
|
||||
XCC_FLAGS_xs3 = $(EXTRA_BUILD_FLAGS) -O2 -g
|
||||
XCC_FLAGS_LOOPBACK = $(EXTRA_BUILD_FLAGS) -O2 -g -DMIDI_LOOPBACK=1
|
||||
|
||||
# The USED_MODULES variable lists other module used by the application.
|
||||
USED_MODULES = lib_xua
|
||||
|
||||
|
||||
#=============================================================================
|
||||
# The following part of the Makefile includes the common build infrastructure
|
||||
# for compiling XMOS applications. You should not need to edit below here.
|
||||
|
||||
XMOS_MAKE_PATH ?= ../..
|
||||
include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common
|
||||
182
tests/test_midi/src/app_midi_simple.xc
Normal file
182
tests/test_midi/src/app_midi_simple.xc
Normal file
@@ -0,0 +1,182 @@
|
||||
// Copyright 2024 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
|
||||
#include <xs1.h>
|
||||
#include <platform.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <xclib.h>
|
||||
|
||||
#include "xua.h"
|
||||
#include "xud_device.h"
|
||||
#include "midiinparse.h"
|
||||
#include "midioutparse.h"
|
||||
|
||||
on tile[MIDI_TILE] : port p_midi_tx = XS1_PORT_4C;
|
||||
#if(MIDI_RX_PORT_WIDTH == 4)
|
||||
on tile[MIDI_TILE] : buffered in port:4 p_midi_rx = XS1_PORT_1F;
|
||||
#elif(MIDI_RX_PORT_WIDTH == 1)
|
||||
on tile[MIDI_TILE] : buffered in port:1 p_midi_rx = XS1_PORT_1F;
|
||||
#endif
|
||||
#define CLKBLK_MIDI XS1_CLKBLK_2
|
||||
on tile[MIDI_TILE] : clock clk_midi = CLKBLK_MIDI;
|
||||
|
||||
#define MAX_TEST_COMMANDS 100
|
||||
#define TEST_COMMAND_FILE_TX "midi_tx_cmds.txt"
|
||||
#define TEST_COMMAND_FILE_RX "midi_rx_cmds.txt"
|
||||
|
||||
#define DEBUG 0 // Prints for debugging. Turn off for actual test
|
||||
|
||||
#if DEBUG
|
||||
#define dprintf(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define dprintf(...)
|
||||
#endif
|
||||
|
||||
/* See hwsupport.xc */
|
||||
void board_setup();
|
||||
|
||||
#ifndef CABLE_NUM
|
||||
#define CABLE_NUM 0
|
||||
#endif
|
||||
|
||||
unsigned midi_in_parse_helper(unsigned midi[3], struct midi_in_parse_state &m_state){
|
||||
|
||||
unsigned valid = 0;
|
||||
unsigned packed = 0;
|
||||
|
||||
for(int i = 0; i < 3; i++){
|
||||
dprintf("Packing byte %d 0x%x\n", i, midi[i]);
|
||||
{valid, packed} = midi_in_parse(m_state, CABLE_NUM, midi[i]);
|
||||
if(valid){
|
||||
return packed;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
{unsigned, unsigned} read_config_file(uint8_t commands[MAX_TEST_COMMANDS][3])
|
||||
{
|
||||
unsigned tx_line_count = 0;
|
||||
|
||||
FILE * movable fptr_tx = fopen(TEST_COMMAND_FILE_TX,"rt");
|
||||
if (fptr_tx == NULL) {
|
||||
dprintf("WARNING: TX command file %s not found or unable to open.\n", TEST_COMMAND_FILE_TX);
|
||||
} else {
|
||||
unsigned a,b,c;
|
||||
while (fscanf(fptr_tx, "%u %u %u\n", &a, &b, &c) == 3) {
|
||||
commands[tx_line_count][0] = a;
|
||||
commands[tx_line_count][1] = b;
|
||||
commands[tx_line_count][2] = c;
|
||||
//printf("Line %u params: 0x%x 0x%x 0x%x\n", tx_line_count, commands[tx_line_count][0], commands[tx_line_count][1], commands[tx_line_count][2]);
|
||||
tx_line_count++;
|
||||
if(tx_line_count > MAX_TEST_COMMANDS){
|
||||
printf("ERROR: Too many lines in TX command file\n");
|
||||
tx_line_count = MAX_TEST_COMMANDS;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(move(fptr_tx));
|
||||
|
||||
|
||||
unsigned rx_cmd_count = 0;
|
||||
|
||||
FILE * movable fptr_rx = fopen(TEST_COMMAND_FILE_RX,"rt");
|
||||
if (fptr_rx == NULL) {
|
||||
dprintf("WARNING: RX command file %s not found or unable to open.\n", TEST_COMMAND_FILE_RX);
|
||||
} else {
|
||||
if(fscanf(fptr_rx, "%u\n", &rx_cmd_count) != 1){
|
||||
printf("ERROR: Not enough or too many items in RX command file line\n");
|
||||
}
|
||||
}
|
||||
fclose(move(fptr_rx));
|
||||
|
||||
return {tx_line_count, rx_cmd_count};
|
||||
}
|
||||
|
||||
void test(chanend c_midi){
|
||||
uint8_t commands[MAX_TEST_COMMANDS][3] = {{0}};
|
||||
unsigned num_to_tx = 0;
|
||||
unsigned num_to_rx = 0;
|
||||
{num_to_tx, num_to_rx} = read_config_file(commands);
|
||||
dprintf("Sending %u MIDI command line(s) and receiving %u MIDI command(s)\n", num_to_tx, num_to_rx);
|
||||
|
||||
// For MIDI Rx
|
||||
int is_ack;
|
||||
unsigned rx_packet;
|
||||
|
||||
// Midi in parse state
|
||||
struct midi_in_parse_state m_state;
|
||||
reset_midi_state(m_state);
|
||||
|
||||
// Counters for Rx and Tx
|
||||
unsigned tx_cmd_count = 0;
|
||||
unsigned rx_cmd_count = 0;
|
||||
|
||||
timer tmr;
|
||||
|
||||
int t_tx; // Used for delay between Txs
|
||||
int tx_end; // Used to wait for packet to have fully left
|
||||
tmr :> t_tx;
|
||||
tmr :> tx_end;
|
||||
|
||||
const int max_tx_time = XS1_TIMER_HZ / 31250 * 3 * (8 + 1 + 1); // 30 bits at 31.25 kbps is 0.96ms
|
||||
const int tx_interval = XS1_TIMER_HZ / 8000; // SoF rate on HS
|
||||
tx_end += max_tx_time; // One whole packet
|
||||
|
||||
while(tx_cmd_count < num_to_tx || rx_cmd_count < num_to_rx ){
|
||||
select{
|
||||
case midi_get_ack_or_data(c_midi, is_ack, rx_packet):
|
||||
if(is_ack){
|
||||
dprintf("ACK from Tx\n");
|
||||
tx_cmd_count++;
|
||||
} else {
|
||||
unsigned midi_data[3] = {0};
|
||||
unsigned byte_count = 0;
|
||||
// Even though this is an Rx from MIDI we use midi_out_parse so we can decode the packet
|
||||
{midi_data[0], midi_data[1], midi_data[2], byte_count} = midi_out_parse(byterev(rx_packet));
|
||||
dprintf("Got packet from MIDI: 0x%8x\n", rx_packet);
|
||||
// Note this needs to always print for capfd in pytest to pick it up
|
||||
printf("dut_midi_rx: %u %u %u\n", midi_data[0], midi_data[1], midi_data[2]);
|
||||
rx_cmd_count++;
|
||||
midi_send_ack(c_midi);
|
||||
}
|
||||
break;
|
||||
|
||||
case tx_cmd_count < num_to_tx => tmr when timerafter(t_tx) :> int _:
|
||||
unsigned midi[] = {commands[tx_cmd_count][0], commands[tx_cmd_count][1], commands[tx_cmd_count][2]};
|
||||
// Even though this is a Tx to MIDI we use midi_in_parse_helper to form the packet from bytes
|
||||
unsigned tx_packet = midi_in_parse_helper(midi, m_state);
|
||||
midi_send_data(c_midi, byterev(tx_packet));
|
||||
dprintf("Sent packet to midi: %u %u %u (0x%8x)\n", commands[tx_cmd_count][0], commands[tx_cmd_count][1], commands[tx_cmd_count][2], tx_packet);
|
||||
t_tx += tx_interval;
|
||||
tx_end += max_tx_time;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dprintf("Tx and Rx count met - exiting after last tx complete.\n");
|
||||
tmr when timerafter(tx_end) :> int _; // wait until packet definitely departed
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
chan c_midi;
|
||||
|
||||
par
|
||||
{
|
||||
on tile[0]: test(c_midi);
|
||||
on tile[1]: usb_midi(p_midi_rx, p_midi_tx, clk_midi, c_midi, 0);
|
||||
|
||||
// Setup HW so we can run this on the MC board
|
||||
on tile[0]: board_setup();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
47
tests/test_midi/src/hwsupport.xc
Normal file
47
tests/test_midi/src/hwsupport.xc
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2017-2024 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#include <xs1.h>
|
||||
#include <platform.h>
|
||||
#include "xua.h"
|
||||
|
||||
|
||||
on tile[0]: out port p_ctrl = XS1_PORT_8D; /* p_ctrl:
|
||||
* [0:3] - Unused
|
||||
* [4] - EN_3v3_N (1v0 hardware only)
|
||||
* [5] - EN_3v3A
|
||||
* [6] - EXT_PLL_SEL (CS2100:0, SI: 1)
|
||||
* [7] - MCLK_DIR (Out:0, In: 1)
|
||||
*/
|
||||
|
||||
#define USE_FRACTIONAL_N (0)
|
||||
|
||||
#if (USE_FRACTIONAL_N)
|
||||
#define EXT_PLL_SEL__MCLK_DIR (0x00)
|
||||
#else
|
||||
#define EXT_PLL_SEL__MCLK_DIR (0x80)
|
||||
#endif
|
||||
|
||||
/* Board setup for XU316 MC Audio (1v1) */
|
||||
void board_setup()
|
||||
{
|
||||
/* "Drive high mode" - drive high for 1, non-driving for 0 */
|
||||
set_port_drive_high(p_ctrl);
|
||||
|
||||
/* Drive control port to turn on 3V3 and mclk direction appropriately.
|
||||
* Bits set to low will be high-z, pulled down */
|
||||
p_ctrl <: EXT_PLL_SEL__MCLK_DIR | 0x20;
|
||||
|
||||
/* Wait for power supplies to be up and stable */
|
||||
delay_milliseconds(10);
|
||||
}
|
||||
|
||||
/* Configures the external audio hardware at startup. Note this runs on Tile[1] */
|
||||
void AudioHwInit()
|
||||
{
|
||||
}
|
||||
|
||||
/* Configures the external audio hardware for the required sample frequency */
|
||||
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC)
|
||||
{
|
||||
}
|
||||
|
||||
28
tests/test_midi/src/xua_conf.h
Normal file
28
tests/test_midi/src/xua_conf.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2017-2024 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
|
||||
#ifndef _XUA_CONF_H_
|
||||
#define _XUA_CONF_H_
|
||||
|
||||
#define NUM_USB_CHAN_OUT 2 /* Number of channels from host to device */
|
||||
#define NUM_USB_CHAN_IN 0 /* Number of channels from device to host */
|
||||
#define I2S_CHANS_DAC 2 /* Number of I2S channels out of xCORE */
|
||||
#define I2S_CHANS_ADC 0 /* Number of I2S channels in to xCORE */
|
||||
#define MCLK_441 (512 * 44100) /* 44.1kHz family master clock frequency */
|
||||
#define MCLK_48 (512 * 48000) /* 48kHz family master clock frequency */
|
||||
#define MIN_FREQ 48000 /* Minimum sample rate */
|
||||
#define MAX_FREQ 48000 /* Maximum sample rate */
|
||||
|
||||
#define EXCLUDE_USB_AUDIO_MAIN
|
||||
|
||||
#define MIDI 1
|
||||
#define MIDI_TILE 1
|
||||
#define VENDOR_STR "XMOS"
|
||||
#define VENDOR_ID 0x20B1
|
||||
#define PRODUCT_STR_A2 "XUA Example"
|
||||
#define PRODUCT_STR_A1 "XUA Example"
|
||||
#define PID_AUDIO_1 1
|
||||
#define PID_AUDIO_2 2
|
||||
#define XUA_DFU_EN 0 /* Disable DFU (for simplicity of example */
|
||||
|
||||
#endif
|
||||
8
tests/test_midi/src/xud_conf.h
Normal file
8
tests/test_midi/src/xud_conf.h
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2017-2024 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
|
||||
#include "xua_conf.h"
|
||||
|
||||
/* TODO */
|
||||
#define XUD_UAC_NUM_USB_CHAN_OUT NUM_USB_CHAN_OUT
|
||||
#define XUD_UAC_NUM_USB_CHAN_IN NUM_USB_CHAN_IN
|
||||
62
tests/test_midi_loopback.py
Normal file
62
tests/test_midi_loopback.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# Copyright 2014-2024 XMOS LIMITED.
|
||||
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
|
||||
import pytest
|
||||
import Pyxsim
|
||||
from Pyxsim import testers
|
||||
from pathlib import Path
|
||||
from uart_rx_checker import UARTRxChecker
|
||||
from midi_test_helpers import midi_expect_rx, create_midi_rx_file, create_midi_tx_file, tempdir, MIDI_RATE
|
||||
from distutils.dir_util import copy_tree # we're using python 3.7 and dirs_exist_ok=True isn't available until 3.8 :(
|
||||
|
||||
MAX_CYCLES = 15000000
|
||||
|
||||
|
||||
#####
|
||||
# This test takes the built binary, copies it to a tmp dir and runs the midi loopback test which sends some commands
|
||||
# the firmware receives them, prints and compares with the expected output
|
||||
#####
|
||||
def test_midi_loopback(capfd, build_midi):
|
||||
# Need tempdir as we use the same config files and this causes issues when using xdist
|
||||
with tempdir() as tmpdirname:
|
||||
config = "LOOPBACK"
|
||||
copy_tree(build_midi, tmpdirname)
|
||||
xe = str(Path(tmpdirname) / f"{config}/test_midi_{config}.xe")
|
||||
|
||||
midi_commands = [
|
||||
[0x90, 60, 81], #note on
|
||||
[0xc0, 15], #instr select
|
||||
[0xe0, 0, 96], #pitch bend
|
||||
[0xff], #MIDI reset
|
||||
[0x80, 60, 81], #note off
|
||||
[0xf0, 0x00, 0x21], [0x1D, 0x0b, 0x33], [0x3f, 0x1e, 0xf7], # Sysex, Ableton, 0b33f1e, terminator
|
||||
[0xc0, 17], #instr select
|
||||
]
|
||||
|
||||
create_midi_rx_file(len(midi_commands))
|
||||
create_midi_tx_file(midi_commands)
|
||||
|
||||
expected = midi_expect_rx().expect(midi_commands)
|
||||
tester = testers.ComparisonTester(expected, ordered = True)
|
||||
|
||||
simthreads = []
|
||||
|
||||
simargs = ["--max-cycles", str(MAX_CYCLES)]
|
||||
#This is just for local debug so we can capture the traces if needed. It slows xsim down a lot
|
||||
# simargs.extend(["--trace-to", "trace.txt", "--vcd-tracing", "-tile tile[1] -ports -o trace.vcd"])
|
||||
|
||||
Pyxsim.run_with_pyxsim(
|
||||
xe,
|
||||
simthreads=simthreads,
|
||||
timeout=180,
|
||||
simargs=simargs,
|
||||
)
|
||||
capture = capfd.readouterr().out
|
||||
result = tester.run(capture.split("\n"))
|
||||
|
||||
# Print to console
|
||||
with capfd.disabled():
|
||||
print("CAPTURE:", capture)
|
||||
print("EXPECTED:", expected)
|
||||
|
||||
assert result
|
||||
71
tests/test_midi_rx.py
Normal file
71
tests/test_midi_rx.py
Normal file
@@ -0,0 +1,71 @@
|
||||
# Copyright 2014-2024 XMOS LIMITED.
|
||||
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
|
||||
import pytest
|
||||
import Pyxsim
|
||||
from Pyxsim import testers
|
||||
from pathlib import Path
|
||||
from uart_rx_checker import UARTRxChecker
|
||||
from midi_test_helpers import midi_expect_rx, create_midi_rx_file, create_midi_tx_file, tempdir, MIDI_TEST_CONFIGS, MIDI_RATE
|
||||
from distutils.dir_util import copy_tree # we're using python 3.7 and dirs_exist_ok=True isn't available until 3.8 :(
|
||||
|
||||
MAX_CYCLES = 15000000
|
||||
|
||||
|
||||
#####
|
||||
# This test takes the built binary, copies it to a tmp dir and runs the midi Rx test which sends some commands
|
||||
# to using the UARTRX checker and the firmware receives them
|
||||
#####
|
||||
@pytest.mark.parametrize("config", MIDI_TEST_CONFIGS)
|
||||
def test_rx(capfd, config, build_midi):
|
||||
# Need tempdir as we use the same config files and this causes issues when using xdist
|
||||
with tempdir() as tmpdirname:
|
||||
copy_tree(build_midi, tmpdirname)
|
||||
xe = str(Path(tmpdirname) / f"{config}/test_midi_{config}.xe")
|
||||
|
||||
midi_commands = [[0x90, 0x91, 0x90],# Invalid and should be discarded
|
||||
[0x90, 60, 81], # Note on
|
||||
[0x80, 60, 81]] # Note off
|
||||
|
||||
midi_command_expected = midi_commands[1:] # should skip invalid first message
|
||||
|
||||
create_midi_rx_file(len(midi_command_expected))
|
||||
create_midi_tx_file()
|
||||
|
||||
expected = midi_expect_rx().expect(midi_command_expected)
|
||||
tester = testers.ComparisonTester(expected, ordered = True)
|
||||
|
||||
rx_port = "tile[1]:XS1_PORT_1F"
|
||||
tx_port = "tile[1]:XS1_PORT_4C" # Needed so that UARTRxChecker (a transmitter) knows when to start
|
||||
baud = MIDI_RATE
|
||||
bpb = 8
|
||||
parity = 0
|
||||
stop = 1
|
||||
|
||||
midi_commands_flattened = [item for row in midi_commands for item in row]
|
||||
|
||||
simthreads = [
|
||||
UARTRxChecker(tx_port, rx_port, parity, baud, stop, bpb, midi_commands_flattened, debug=False)
|
||||
]
|
||||
|
||||
simargs = ["--max-cycles", str(MAX_CYCLES)]
|
||||
#This is just for local debug so we can capture the traces if needed. It slows xsim down so not good for Jenkins
|
||||
# simargs.extend(["--trace-to", "trace.txt", "--vcd-tracing", "-tile tile[1] -ports -o trace.vcd"])
|
||||
|
||||
# with capfd.disabled(): # use to see xsim and tester output
|
||||
Pyxsim.run_with_pyxsim(
|
||||
xe,
|
||||
simthreads=simthreads,
|
||||
timeout=120,
|
||||
simargs=simargs,
|
||||
)
|
||||
capture = capfd.readouterr().out
|
||||
result = tester.run(capture.split("\n"))
|
||||
|
||||
# Print to console
|
||||
with capfd.disabled():
|
||||
print("CAPTURE:", capture)
|
||||
print("EXPECTED:", expected)
|
||||
|
||||
|
||||
assert result, f"expected: {expected}\n capture: {capture}"
|
||||
80
tests/test_midi_tx.py
Normal file
80
tests/test_midi_tx.py
Normal file
@@ -0,0 +1,80 @@
|
||||
# Copyright 2014-2024 XMOS LIMITED.
|
||||
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
|
||||
import pytest
|
||||
import Pyxsim
|
||||
from Pyxsim import testers
|
||||
from pathlib import Path
|
||||
from uart_tx_checker import UARTTxChecker
|
||||
from midi_test_helpers import midi_expect_tx, create_midi_tx_file, create_midi_rx_file, tempdir, MIDI_TEST_CONFIGS, MIDI_RATE
|
||||
from distutils.dir_util import copy_tree # we're using python 3.7 and dirs_exist_ok=True isn't available until 3.8 :(
|
||||
|
||||
MAX_CYCLES = 15000000
|
||||
|
||||
#####
|
||||
# This test takes the built binary, copies it to a tmp dir and runs the midi Tx test which sends some commands
|
||||
# to the firmware and then receives them using the UARTTX checker
|
||||
#####
|
||||
@pytest.mark.parametrize("config", MIDI_TEST_CONFIGS)
|
||||
def test_tx(capfd, config, build_midi):
|
||||
|
||||
# Need tempdir as we use the same config files and this causes issues when using xdist
|
||||
with tempdir() as tmpdirname:
|
||||
copy_tree(build_midi, tmpdirname)
|
||||
xe = str(Path(tmpdirname) / f"{config}/test_midi_{config}.xe")
|
||||
|
||||
midi_commands = [[0x90, 0x91, 0x90],# Invalid and should be discarded
|
||||
[0x90, 60, 81], # Note on
|
||||
[0x80, 60, 81]] # Note off
|
||||
|
||||
# Make a 1D list from the 2D list [1:] because first is invalid and we expect to skip it
|
||||
midi_command_expected = [[item for row in midi_commands[1:] for item in row]]
|
||||
|
||||
create_midi_tx_file(midi_commands)
|
||||
create_midi_rx_file()
|
||||
|
||||
expected = midi_expect_tx().expect(midi_command_expected)
|
||||
tester = testers.ComparisonTester(expected, ordered = True)
|
||||
|
||||
tx_port = "tile[1]:XS1_PORT_4C"
|
||||
baud = MIDI_RATE
|
||||
bpb = 8
|
||||
parity = 0
|
||||
stop = 1
|
||||
length_of_test = sum(len(cmd) for cmd in midi_command_expected)
|
||||
|
||||
simthreads = [
|
||||
UARTTxChecker(tx_port, parity, baud, length_of_test, stop, bpb, debug=False)
|
||||
]
|
||||
|
||||
|
||||
simargs = ["--max-cycles", str(MAX_CYCLES), "--trace-to", "trace.txt"]
|
||||
#This is just for local debug so we can capture the traces if needed. It slows xsim down so not needed
|
||||
# simargs.extend(["--vcd-tracing", "-tile tile[1] -ports -o trace.vcd"])
|
||||
|
||||
# with capfd.disabled(): # use to see xsim and tester output
|
||||
Pyxsim.run_with_pyxsim(
|
||||
xe,
|
||||
simthreads=simthreads,
|
||||
timeout=120,
|
||||
simargs=simargs,
|
||||
)
|
||||
|
||||
capture = capfd.readouterr().out
|
||||
result = tester.run(capture.split("\n"))
|
||||
|
||||
# Print to console
|
||||
with capfd.disabled():
|
||||
print("CAPTURE:", capture)
|
||||
print("EXPECTED:", expected)
|
||||
|
||||
# Show tail of trace if there is an error
|
||||
if not result:
|
||||
with capfd.disabled():
|
||||
print("Simulator trace tail:")
|
||||
with open("trace.txt") as trace:
|
||||
output = trace.readlines()
|
||||
print("".join(output[-25:]))
|
||||
|
||||
|
||||
assert result, f"expected: {expected}\n capture: {capture}"
|
||||
178
tests/uart_rx_checker.py
Normal file
178
tests/uart_rx_checker.py
Normal file
@@ -0,0 +1,178 @@
|
||||
# Copyright 2022-2024 XMOS LIMITED.
|
||||
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
import Pyxsim as px
|
||||
from typing import Sequence
|
||||
from functools import partial
|
||||
|
||||
# We need to disable output buffering for this test to work on MacOS; this has
|
||||
# no effect on Linux systems. Let's redefine print once to avoid putting the
|
||||
# same argument everywhere.
|
||||
print = partial(print, flush=True)
|
||||
|
||||
Parity = dict(
|
||||
UART_PARITY_EVEN=0,
|
||||
UART_PARITY_ODD=1,
|
||||
UART_PARITY_NONE=2,
|
||||
UART_PARITY_BAD=3
|
||||
)
|
||||
|
||||
# From tools 15.2.1 we need to add an extra factor to go from ps to fs
|
||||
time_scaling_factor = 1000
|
||||
|
||||
class DriveHigh(px.SimThread):
|
||||
def __init__(self, p):
|
||||
self._p = p
|
||||
|
||||
def run(self):
|
||||
xsi = self.xsi
|
||||
|
||||
xsi.drive_port_pins(self._p, 1);
|
||||
|
||||
|
||||
class UARTRxChecker(px.SimThread):
|
||||
def __init__(self, tx_port, rx_port, parity, baud, stop_bits, bpb, data=[0x7f, 0x00, 0x2f, 0xff],
|
||||
intermittent=False, debug=False):
|
||||
"""
|
||||
Create a UARTRxChecker instance.
|
||||
|
||||
:param rx_port: Receive port of the UART device under test.
|
||||
:param parity: Parity of the UART connection.
|
||||
:param baud: BAUD rate of the UART connection.
|
||||
:param stop_bits: Number of stop_bits for each UART byte.
|
||||
:param bpb: Number of data bits per "byte" of UART data.
|
||||
:param data: A list of bytes to send (default: [0x7f, 0x00, 0x2f, 0xff])
|
||||
:param intermittent: Add a random delay between sent bytes.
|
||||
"""
|
||||
self._tx_port = tx_port
|
||||
self._rx_port = rx_port
|
||||
self._parity = parity
|
||||
self._baud = baud
|
||||
self._stop_bits = stop_bits
|
||||
self._bits_per_byte = bpb
|
||||
self._data = data
|
||||
self._intermittent = intermittent
|
||||
# Hex value of stop bits, as MSB 1st char, e.g. 0b11 : 0xC0
|
||||
|
||||
def send_byte(self, xsi, byte):
|
||||
"""
|
||||
Send a byte to the rx_port
|
||||
|
||||
:param xsi: XMOS Simulator Instance.
|
||||
:param byte: Byte to send
|
||||
"""
|
||||
# Send start bit
|
||||
self.send_start(xsi)
|
||||
|
||||
# Send data
|
||||
self.send_data(xsi, byte)
|
||||
|
||||
# Send parity
|
||||
self.send_parity(xsi, byte)
|
||||
|
||||
# Send stop bit(s)
|
||||
self.send_stop(xsi)
|
||||
|
||||
|
||||
def send_start(self, xsi):
|
||||
"""
|
||||
Send a start bit.
|
||||
|
||||
:param xsi: XMOS Simulator Instance.
|
||||
"""
|
||||
xsi.drive_port_pins(self._rx_port, 0)
|
||||
self.wait_baud_time(xsi)
|
||||
|
||||
def send_data(self, xsi, byte):
|
||||
"""
|
||||
Write the data bits to the rx_port
|
||||
|
||||
:param xsi: XMOS Simulator Instance.
|
||||
:param byte: Data to send.
|
||||
"""
|
||||
# print(f"Checker sent 0x{byte:02x}")
|
||||
for x in range(self._bits_per_byte):
|
||||
# print(f" Sending bit {x}")
|
||||
xsi.drive_port_pins(self._rx_port, (byte & (0x01 << x)) >= 1)
|
||||
# print(f" (x): {((byte & (0x01 << x))>=1)}")
|
||||
self.wait_baud_time(xsi)
|
||||
|
||||
def send_parity(self, xsi, byte):
|
||||
"""
|
||||
Send the parity bit to the rx_port
|
||||
|
||||
:param xsi: XMOS Simulator Instance.
|
||||
:param byte: Data to send parity of.
|
||||
"""
|
||||
parity = (self._parity - 1) % 3 #parity enum in lib_uart (old XC) different from SDK
|
||||
if parity < 2:
|
||||
crc_sum = 0
|
||||
for x in range(self._bits_per_byte):
|
||||
crc_sum += ((byte & (0x01 << x)) >= 1)
|
||||
crc_sum += parity
|
||||
# print "Parity for 0x%02x: %d" % (byte, crc_sum%2)
|
||||
xsi.drive_port_pins(self._rx_port, crc_sum % 2)
|
||||
self.wait_baud_time(xsi)
|
||||
elif parity == Parity['UART_PARITY_BAD']:
|
||||
# print "Sending bad parity bit"
|
||||
self.send_bad_parity(xsi)
|
||||
|
||||
def send_stop(self, xsi):
|
||||
"""
|
||||
Send the stop bit(s) to the rx_port
|
||||
|
||||
:param xsi: XMOS Simulator Instance.
|
||||
"""
|
||||
for x in range(self._stop_bits):
|
||||
xsi.drive_port_pins(self._rx_port, 1)
|
||||
self.wait_baud_time(xsi)
|
||||
|
||||
def send_bad_parity(self, xsi):
|
||||
"""
|
||||
Send a parity bit of 1 to simulate an incorrect parity state.
|
||||
|
||||
:param xsi: XMOS Simulator Instance.
|
||||
"""
|
||||
# Always send a parity bit of 1
|
||||
xsi.drive_port_pins(self._rx_port, 0)
|
||||
self.wait_baud_time(xsi)
|
||||
|
||||
def get_bit_time(self):
|
||||
"""
|
||||
Returns the expected time between bits for the currently set BAUD rate.
|
||||
|
||||
Returns float value in nanoseconds.
|
||||
"""
|
||||
# Return float value in ps
|
||||
return (1.0 / self._baud) * 1e12 * time_scaling_factor
|
||||
|
||||
def wait_baud_time(self, xsi):
|
||||
"""
|
||||
Wait for 1 bit time, as determined by the baud rate.
|
||||
"""
|
||||
self.wait_until(xsi.get_time() + self.get_bit_time())
|
||||
|
||||
def wait_half_baud_time(self, xsi):
|
||||
"""
|
||||
Wait for half a bit time, as determined by the baud rate.
|
||||
"""
|
||||
self.wait_until(xsi.get_time() + (self.get_bit_time() / 2))
|
||||
|
||||
def run(self):
|
||||
xsi = self.xsi
|
||||
# Drive the uart line high.
|
||||
xsi.drive_port_pins(self._rx_port, 1)
|
||||
|
||||
# Wait for the device to bring up it's tx port, indicating it is ready
|
||||
self.wait((lambda _x: self.xsi.is_port_driving(self._tx_port)))
|
||||
|
||||
# If we're doing an intermittent send, add a delay between each byte
|
||||
# sent. Delay is in ns. 20,000ns = 20ms, 100,000ns = 100ms. Delays could
|
||||
# be more variable, but it hurts test time substantially.
|
||||
if self._intermittent:
|
||||
for x in self._data:
|
||||
k = randint(20000, 100000)
|
||||
self.wait_until(xsi.get_time() + k)
|
||||
self.send_byte(xsi, x)
|
||||
else:
|
||||
for x in self._data:
|
||||
self.send_byte(xsi, x)
|
||||
248
tests/uart_tx_checker.py
Normal file
248
tests/uart_tx_checker.py
Normal file
@@ -0,0 +1,248 @@
|
||||
# Copyright 2022-2024 XMOS LIMITED.
|
||||
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
import Pyxsim as px
|
||||
from typing import Sequence
|
||||
from functools import partial
|
||||
|
||||
# We need to disable output buffering for this test to work on MacOS; this has
|
||||
# no effect on Linux systems. Let's redefine print once to avoid putting the
|
||||
# same argument everywhere.
|
||||
print = partial(print, flush=True)
|
||||
|
||||
# From tools 15.2.1 we need to add an extra factor to go from ps to fs
|
||||
time_scaling_factor = 1000
|
||||
|
||||
class UARTTxChecker(px.SimThread):
|
||||
"""
|
||||
This simulator thread will act as a UART device, and will check sent and
|
||||
transations caused by the device, by looking at the tx pins.
|
||||
"""
|
||||
|
||||
def __init__(self, tx_port, parity, baud, length, stop_bits, bpb, debug=False):
|
||||
"""
|
||||
Create a UARTTxChecker instance.
|
||||
|
||||
:param tx_port: Transmit port of the UART device under test.
|
||||
:param parity: Parity of the UART connection.
|
||||
:param baud: BAUD rate of the UART connection.
|
||||
:param length: Length of transmission to check.
|
||||
:param stop_bits: Number of stop_bits for each UART byte.
|
||||
:param bpb: Number of data bits per "byte" of UART data.
|
||||
"""
|
||||
self._tx_port = tx_port
|
||||
self._parity = parity
|
||||
self._baud = baud
|
||||
self._length = length
|
||||
self._stop_bits = stop_bits
|
||||
self._bits_per_byte = bpb
|
||||
# Hex value of stop bits, as MSB 1st char, e.g. 0b11 : 0xC0
|
||||
self.debug = debug
|
||||
|
||||
def get_port_val(self, xsi, port):
|
||||
"""
|
||||
Sample the state of a port
|
||||
|
||||
:rtype: int
|
||||
:param xsi: XMOS Simulator Instance.
|
||||
:param port: Port to sample.
|
||||
"""
|
||||
is_driving = xsi.is_port_driving(port)
|
||||
if not is_driving:
|
||||
return 1
|
||||
else:
|
||||
return xsi.sample_port_pins(port)
|
||||
|
||||
def get_bit_time(self):
|
||||
"""
|
||||
Returns the expected time between bits for the currently set BAUD rate.
|
||||
|
||||
Returns float value in nanoseconds.
|
||||
:rtype: float
|
||||
"""
|
||||
# Return float value in ps
|
||||
return (1.0/self._baud) * 1e12 * time_scaling_factor
|
||||
|
||||
def wait_baud_time(self, xsi):
|
||||
"""
|
||||
Wait for 1 bit time, as determined by the baud rate.
|
||||
"""
|
||||
self.wait_until(xsi.get_time() + self.get_bit_time())
|
||||
return True
|
||||
|
||||
def wait_half_baud_time(self, xsi):
|
||||
"""
|
||||
Wait for half a bit time, as determined by the baud rate.
|
||||
"""
|
||||
self.wait_until(xsi.get_time() + (self.get_bit_time() / 2))
|
||||
|
||||
def read_packet(self, xsi, parity, length=4):
|
||||
"""
|
||||
Read a given number of bytes of UART traffic sent by the device.
|
||||
|
||||
Returns a list of bytes sent by the device.
|
||||
|
||||
:rtype: list
|
||||
:param xsi: XMOS Simulator Instance.
|
||||
:param parity: The UART partiy setting. See Parity.
|
||||
:param length: The number of bytes to read. Defaults to 4.
|
||||
"""
|
||||
packet = []
|
||||
start_time = 0
|
||||
got_start_bit = False
|
||||
|
||||
initial_port_val = self.get_port_val(xsi, self._tx_port)
|
||||
if self.debug: print("tx starts high: %s" % ("True" if initial_port_val else "False"))
|
||||
|
||||
for x in range(length):
|
||||
byte = self.read_byte(xsi, parity)
|
||||
if self.debug: print(f"Checker got byte: {byte}")
|
||||
packet.append(chr(byte))
|
||||
return packet
|
||||
|
||||
def read_byte(self, xsi, parity):
|
||||
"""
|
||||
Read 1 byte of UART traffic sent by the device
|
||||
|
||||
Returns an int, representing a byte read from the uart. Should be in the range 0 <= x < 2^bits_per_byte
|
||||
|
||||
:rtype: int
|
||||
:param xsi: XMOS Simulator Instance.
|
||||
:param parity: The UART partiy setting. See Parity.
|
||||
"""
|
||||
byte = 0
|
||||
val = 0
|
||||
|
||||
# Recv start bit
|
||||
initial_port_val = self.get_port_val(xsi, self._tx_port)
|
||||
|
||||
if initial_port_val == 1:
|
||||
self.wait_for_port_pins_change([self._tx_port])
|
||||
#else go for it as assume tx has just fallen with no interframe gap
|
||||
|
||||
# The tx line should go low for 1 bit time
|
||||
if self.get_val_timeout(xsi, self._tx_port) == 0:
|
||||
if self.debug: print("Start bit recv'd")
|
||||
else:
|
||||
print("Start bit issue")
|
||||
return False
|
||||
|
||||
# recv the byte
|
||||
crc_sum = 0
|
||||
for j in range(self._bits_per_byte):
|
||||
val = self.get_val_timeout(xsi, self._tx_port)
|
||||
byte += (val << j)
|
||||
crc_sum += val
|
||||
|
||||
if self.debug: print(f"Sampled {self._bits_per_byte} data bits: 0x{hex(byte)}")
|
||||
|
||||
# Check the parity if needs be
|
||||
self.check_parity(xsi, crc_sum, parity)
|
||||
|
||||
# Get the stop bit
|
||||
self.check_stopbit(xsi)
|
||||
|
||||
# Print a new line to split bytes in output
|
||||
if self.debug: print()
|
||||
|
||||
return byte
|
||||
|
||||
def check_parity(self, xsi, crc_sum, parity):
|
||||
"""
|
||||
Read the parity bit and check it against a crc sum. Print correctness.
|
||||
|
||||
:param xsi: XMOS Simulator Instance.
|
||||
:param crc_sum: The checksum to test parity against.
|
||||
:param parity: The UART partiy setting. See Parity.
|
||||
"""
|
||||
if parity > 0:
|
||||
parity_val = 0 if parity == 1 else 1
|
||||
read = self.get_val_timeout(xsi, self._tx_port)
|
||||
if read == (crc_sum + parity_val) % 2:
|
||||
print("Parity bit correct")
|
||||
else:
|
||||
print("Parity bit incorrect. Got %d, expected %d" % (read, (crc_sum + parity_val) % 2))
|
||||
else:
|
||||
if self.debug: print("Parity bit correct")
|
||||
|
||||
def check_stopbit(self, xsi):
|
||||
"""
|
||||
Read the stop bit(s) of a UART transmission and print correctness.
|
||||
|
||||
:param xsi: XMOS Simulator Instance.
|
||||
"""
|
||||
stop_bits_correct = True
|
||||
for i in range(self._stop_bits):
|
||||
# The stop bits should stay high for this time
|
||||
if self.get_val_timeout(xsi, self._tx_port) == 0:
|
||||
stop_bits_correct = False
|
||||
if self.debug: print("Stop bit correct: %s" % ("True" if stop_bits_correct else "False"))
|
||||
|
||||
def get_val_timeout(self, xsi, port):
|
||||
"""
|
||||
Get a value from a given port of the device, with a timeout determined
|
||||
by the BAUD rate.
|
||||
|
||||
Returns whether the pin is high (True) or low (False)
|
||||
|
||||
:rtype: bool
|
||||
:param xsi: XMOS Simulator Instance.
|
||||
:param port: The port to sample.
|
||||
"""
|
||||
# This intentionally has a 0.3% slop. It is per-byte and gives some
|
||||
# wiggle-room if the clock doesn't divide into ns nicely.
|
||||
timeout = self.get_bit_time() * 0.5
|
||||
short_timeout = self.get_bit_time() * 0.2485
|
||||
|
||||
# Allow for "rise" time
|
||||
self.wait_until(xsi.get_time() + short_timeout)
|
||||
|
||||
# Get val
|
||||
K = self.wait_time_or_pin_change(xsi, timeout, port)
|
||||
|
||||
# Allow for "fall" time
|
||||
self.wait_until(xsi.get_time() + short_timeout)
|
||||
return K
|
||||
|
||||
def wait_time_or_pin_change(self, xsi, timeout, port):
|
||||
"""
|
||||
Waits for a given timeout, or until a port changes state. Which ever
|
||||
occurs 1st. Prints an error if the former causes the function to break.
|
||||
|
||||
Returns whether the pin is high (True) or low (False)
|
||||
|
||||
:rtype: bool
|
||||
:param xsi: XMOS Simulator Instance.
|
||||
:param timeout: Time to wait.
|
||||
:param port: Port to sample.
|
||||
"""
|
||||
start_time = xsi.get_time()
|
||||
start_val = self.get_port_val(xsi, port)
|
||||
transitioned_during_wait = False
|
||||
|
||||
def _continue(_timeout, _start_time, _start_val):
|
||||
if xsi.get_time() >= _start_time + _timeout:
|
||||
return True
|
||||
if self.get_port_val(xsi, port) != _start_val:
|
||||
transitioned_during_wait = True
|
||||
return True
|
||||
return False
|
||||
wait_fun = (lambda x: _continue(timeout, start_time, start_val))
|
||||
self.wait(wait_fun)
|
||||
|
||||
# Start value should *not* have changed during timeout
|
||||
if transitioned_during_wait:
|
||||
print("FAIL :: Unexpected Transition.")
|
||||
|
||||
return start_val
|
||||
|
||||
def run(self):
|
||||
# Wait for the xcore to bring the uart tx port up
|
||||
self.wait((lambda x: self.xsi.is_port_driving(self._tx_port)))
|
||||
self.wait((lambda x: self.get_port_val(self.xsi, self._tx_port) == 1))
|
||||
|
||||
K = self.read_packet(self.xsi, self._parity, self._length)
|
||||
|
||||
# Print each member of K as a hex byte
|
||||
# inline lambda function mapped over a list? awh yiss.
|
||||
print("uart_tx_checker:", " ".join(map((lambda x: "0x%02x" % ord(x)), K)))
|
||||
|
||||
@@ -1,103 +1,97 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake)
|
||||
|
||||
set(XMOS_TOOLS_PATH $ENV{XMOS_TOOL_PATH}/bin)
|
||||
|
||||
#**********************
|
||||
# Setup XMOS toolchain
|
||||
#**********************
|
||||
if(NOT DEFINED ENV{XUA_PATH})
|
||||
message(FATAL_ERROR "XUA_PATH environment variable not defined")
|
||||
# some more commands
|
||||
# Auto-generate schedule and top level config files
|
||||
if( NOT ${Python3_FOUND} )
|
||||
message(FATAL_ERROR "Python3 not found for running . ")
|
||||
endif()
|
||||
include("$ENV{XUA_PATH}/cmake_utils/xmos_toolchain.cmake")
|
||||
|
||||
#**********************
|
||||
# Project
|
||||
#**********************
|
||||
# Disable in-source build.
|
||||
#if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
|
||||
# message(FATAL_ERROR "In-source build is not allowed! Please specify a build folder.\n\tex:cmake -B build")
|
||||
#endif()
|
||||
#copy conftest.py in the build directory since pytest_collect_file only collects tests from the directory tree where conftest.py is present
|
||||
configure_file( conftest.py conftest.py COPYONLY )
|
||||
|
||||
## executable output directory
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
|
||||
|
||||
# Set unity runner generate script
|
||||
set(GEN_RUNNER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/generate_unity_runner.py)
|
||||
|
||||
# Create directory for runner files
|
||||
set(RUNNERS_DIR ${CMAKE_CURRENT_LIST_DIR}/src.runners )
|
||||
file(MAKE_DIRECTORY ${RUNNERS_DIR} )
|
||||
|
||||
# Find unit test files
|
||||
file(GLOB_RECURSE TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/test_*/*.c)
|
||||
|
||||
# For every source file in xua_unit_tests/
|
||||
foreach(TESTFILE ${TEST_SOURCES})
|
||||
set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..)
|
||||
|
||||
# Get test name from C file stem
|
||||
cmake_path(GET TESTFILE STEM TESTNAME)
|
||||
project(${TESTNAME})
|
||||
message(STATUS "Processing unit test: ${TESTNAME}")
|
||||
|
||||
# Create runner file directory
|
||||
file(MAKE_DIRECTORY ${RUNNERS_DIR}/${TESTNAME})
|
||||
|
||||
#####################
|
||||
## Create runner file
|
||||
#####################
|
||||
set( RUNNER_FILE ${RUNNERS_DIR}/${TESTNAME}/${TESTNAME}_Runner.c )
|
||||
set( GEN_RUNNER_SCRIPT_BYPRODUCTS ${RUNNER_FILE})
|
||||
|
||||
unset(GEN_RUNNER_SCRIPT_ARGS)
|
||||
list(APPEND GEN_RUNNER_SCRIPT_ARGS --project-root ${XMOS_SANDBOX_DIR})
|
||||
list(APPEND GEN_RUNNER_SCRIPT_ARGS --source-file ${TESTFILE})
|
||||
list(APPEND GEN_RUNNER_SCRIPT_ARGS --runner-file ${RUNNER_FILE})
|
||||
|
||||
## Add command to generate runner file
|
||||
add_custom_command(
|
||||
OUTPUT ${RUNNER_FILE}
|
||||
COMMAND python ${GEN_RUNNER_SCRIPT} ${GEN_RUNNER_SCRIPT_ARGS}
|
||||
COMMENT "Generate XUA Unit Test Runner" )
|
||||
|
||||
##########################
|
||||
## Do xcommon cmake build
|
||||
##########################
|
||||
set(APP_HW_TARGET XK-EVK-XU316)
|
||||
set(APP_DEPENDENT_MODULES "lib_xua"
|
||||
"lib_unity(2.5.2)")
|
||||
# set(APP_PCA_ENABLE ON)
|
||||
set(APP_COMPILER_FLAGS ${EXTRA_BUILD_FLAGS} -fcomment-asm
|
||||
-Wall
|
||||
-O2
|
||||
-report
|
||||
-g
|
||||
-fxscope
|
||||
-DUSB_TILE=tile[0]
|
||||
-DUNITY_SUPPORT_64
|
||||
-DUNITY_INCLUDE_DOUBLE
|
||||
-DXUD_CORE_CLOCK=600
|
||||
-DXUD_SERIES_SUPPORT=4
|
||||
-DXASSERT_ENABLE_ASSERTIONS=0
|
||||
)
|
||||
|
||||
# For HID tests only enable HID
|
||||
if(${TESTFILE} MATCHES ".+hid.*")
|
||||
list(APPEND APP_COMPILER_FLAGS "-DHID_CONTROLS=1")
|
||||
endif()
|
||||
|
||||
|
||||
## Define project
|
||||
project(xua_unit_tests VERSION 0.1.0)
|
||||
# Workaround for xcommon cmake pre-pending CMAKE_CURRENT_LIST_DIR
|
||||
string(REPLACE ${CMAKE_CURRENT_LIST_DIR} "" UNIT_TEST_SOURCE_RELATIVE ${TESTFILE})
|
||||
string(REPLACE ${CMAKE_CURRENT_LIST_DIR} "" RUNNER_FILE_RELATIVE ${RUNNER_FILE})
|
||||
|
||||
## Enable languages for project
|
||||
enable_language(CXX XC C ASM)
|
||||
set(APP_C_SRCS ${RUNNER_FILE_RELATIVE}
|
||||
${UNIT_TEST_SOURCE_RELATIVE}
|
||||
)
|
||||
|
||||
message(STATUS "CAME HERE")
|
||||
add_custom_target("runners" ALL)
|
||||
add_custom_command(
|
||||
TARGET runners
|
||||
COMMAND python generate_unity_runners.py
|
||||
COMMENT "generate unity runners"
|
||||
)
|
||||
|
||||
message(STATUS "CAME HERE 1")
|
||||
file( GLOB APP_SOURCES src/test*.xc )
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
|
||||
foreach( testsourcefile ${APP_SOURCES} )
|
||||
get_filename_component(ITEM_NAME ${testsourcefile} NAME_WE)
|
||||
message(STATUS "item_name " ${ITEM_NAME})
|
||||
add_executable(${ITEM_NAME})
|
||||
set(APP_COMPILER_FLAGS
|
||||
"-O2"
|
||||
"-g"
|
||||
"-Wall"
|
||||
"-report"
|
||||
"-fxscope"
|
||||
"-target=XCORE-AI-EXPLORER"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/config.xscope"
|
||||
"-DHID_CONTROLS=1"
|
||||
"-DUNITY_SUPPORT_64"
|
||||
"-DUNITY_INCLUDE_DOUBLE"
|
||||
)
|
||||
set_source_files_properties(
|
||||
"runners/${ITEM_NAME}/${ITEM_NAME}_Runner.c"
|
||||
PROPERTIES GENERATED TRUE
|
||||
)
|
||||
get_filename_component(TEST_FILE_DIR ${TESTFILE} DIRECTORY)
|
||||
set(APP_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/src
|
||||
${TEST_FILE_DIR}
|
||||
${XMOS_SANDBOX_DIR}/lib_xud/lib_xud/src/user/class)
|
||||
|
||||
set(APP_SRCS
|
||||
${testsourcefile}
|
||||
"runners/${ITEM_NAME}/${ITEM_NAME}_Runner.c"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../../../Unity/src/unity.c"
|
||||
)
|
||||
set(APP_INCLUDES
|
||||
"src"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../../../Unity/src"
|
||||
)
|
||||
set(APP_DEPENDENT_MODULES
|
||||
"lib_xua(>=2.0.0)"
|
||||
"lib_logging(>=3.0.0)"
|
||||
"lib_xassert(>=4.0.0)"
|
||||
"lib_xud(>=2.0.0)"
|
||||
"lib_spdif(>=4.0.0)"
|
||||
"lib_mic_array(>=4.0.0)"
|
||||
)
|
||||
XMOS_REGISTER_APP()
|
||||
|
||||
include("$ENV{XUA_PATH}/cmake_utils/xua.cmake")
|
||||
set_target_properties(${ITEM_NAME} PROPERTIES OUTPUT_NAME ${ITEM_NAME}.xe)
|
||||
target_compile_options(${ITEM_NAME} PRIVATE ${APP_COMPILER_FLAGS})
|
||||
|
||||
target_include_directories(${ITEM_NAME}
|
||||
PRIVATE ${APP_INCLUDES}
|
||||
PRIVATE ${XUA_INCLUDES_ALL}
|
||||
)
|
||||
|
||||
target_sources(${ITEM_NAME}
|
||||
PRIVATE ${APP_SRCS}
|
||||
PRIVATE ${XUA_SRCS_ALL}
|
||||
)
|
||||
add_dependencies(${ITEM_NAME} runners)
|
||||
target_link_options(${ITEM_NAME} PRIVATE ${APP_COMPILER_FLAGS})
|
||||
## Set any additional flags only for C++
|
||||
set(CMAKE_CXX_FLAGS "-std=c++11")
|
||||
|
||||
endforeach( testsourcefile ${APP_SOURCES} )
|
||||
|
||||
message(STATUS ${APP_SOURCES})
|
||||
|
||||
message(STATUS "CAME HERE 2")
|
||||
## Register the application
|
||||
#XMOS_REGISTER_APP()
|
||||
endforeach()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2021-2022 XMOS LIMITED.
|
||||
# Copyright 2021-2024 XMOS LIMITED.
|
||||
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
from __future__ import print_function
|
||||
from builtins import str
|
||||
@@ -28,7 +28,7 @@ class UnityTestSource(pytest.File):
|
||||
# unit_tests/ <- Test root directory
|
||||
# |-- bin/ <- Compiled binaries of the test runners
|
||||
# |-- conftest.py <- This file
|
||||
# |-- runners/ <- Auto-generated buildable source of test binaries
|
||||
# |-- src.runners <- Auto-generated buildable source of test binaries
|
||||
# |-- src/ <- Unity test functions
|
||||
# `-- wscript <- Build system file used to generate/build runners
|
||||
xe_name = ((os.path.basename(self.name)).split("."))[0] + ".xe"
|
||||
@@ -51,9 +51,9 @@ class UnityTestExecutable(pytest.Item):
|
||||
print("run axe for executable ", self.name)
|
||||
test_output = subprocess.check_output(["axe", self.name], text=True)
|
||||
else:
|
||||
print("run xrun for executable ", self.name)
|
||||
print("run xsim for executable ", self.name)
|
||||
test_output = subprocess.check_output(
|
||||
["xrun", "--io", "--id", "0", self.name],
|
||||
["xsim", self.name],
|
||||
text=True,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
|
||||
59
tests/xua_unit_tests/generate_unity_runner.py
Executable file
59
tests/xua_unit_tests/generate_unity_runner.py
Executable file
@@ -0,0 +1,59 @@
|
||||
# Copyright 2024 XMOS LIMITED.
|
||||
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
|
||||
import glob
|
||||
import os.path
|
||||
import subprocess
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
def parse_arguments():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--project-root", nargs='?', help="Project root directory")
|
||||
parser.add_argument("--source-file", nargs='?', help="source file.")
|
||||
parser.add_argument("--runner-file", nargs='?', help="runner file.")
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
def get_ruby():
|
||||
"""
|
||||
Check ruby is avaliable and return the command to invoke it.
|
||||
"""
|
||||
interpreter_name = 'ruby'
|
||||
try:
|
||||
dev_null = open(os.devnull, 'w')
|
||||
# Call the version command to check the interpreter can be run
|
||||
subprocess.check_call([interpreter_name, '--version'],
|
||||
stdout=dev_null,
|
||||
close_fds=True)
|
||||
except OSError as e:
|
||||
print("Failed to run Ruby interpreter: {}".format(e), file=sys.stderr)
|
||||
exit(1) # TODO: Check this is the correct way to kill xwaf on error
|
||||
|
||||
return interpreter_name
|
||||
|
||||
def get_unity_runner_generator(project_root_path):
|
||||
"""
|
||||
Check the Unity generate_test_runner script is avaliable, and return the
|
||||
path to it.
|
||||
"""
|
||||
unity_runner_generator = os.path.join(
|
||||
project_root_path, 'Unity', 'auto', 'generate_test_runner.rb')
|
||||
if not os.path.exists(unity_runner_generator):
|
||||
print("Unity repo not found in workspace", file=sys.stderr)
|
||||
exit(1) # TODO: Check this is the correct way to kill xwaf on error
|
||||
return unity_runner_generator
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_arguments()
|
||||
print(f"in python: root {args.project_root}, source {args.source_file}, runner {args.runner_file}")
|
||||
|
||||
try:
|
||||
subprocess.check_call([get_ruby(),
|
||||
get_unity_runner_generator(args.project_root),
|
||||
args.source_file,
|
||||
args.runner_file])
|
||||
except OSError as e:
|
||||
print("Ruby generator failed for {}\n\t{}".format(unity_test_path, e),
|
||||
file=sys.stderr)
|
||||
exit(1) # TODO: Check this is the correct way to kill xwaf on error
|
||||
@@ -1,134 +0,0 @@
|
||||
# Copyright 2021-2022 XMOS LIMITED.
|
||||
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
import glob
|
||||
import os.path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
UNITY_TEST_DIR = "src"
|
||||
UNITY_TEST_PREFIX = "test_"
|
||||
UNITY_RUNNER_DIR = "runners"
|
||||
UNITY_RUNNER_SUFFIX = "_Runner"
|
||||
project_root = os.path.join("..", "..", "..")
|
||||
|
||||
|
||||
def get_ruby():
|
||||
"""
|
||||
Check ruby is avaliable and return the command to invoke it.
|
||||
"""
|
||||
interpreter_name = "ruby"
|
||||
try:
|
||||
dev_null = open(os.devnull, "w")
|
||||
# Call the version command to check the interpreter can be run
|
||||
subprocess.check_call(
|
||||
[interpreter_name, "--version"], stdout=dev_null, close_fds=True
|
||||
)
|
||||
except OSError as e:
|
||||
print("Failed to run Ruby interpreter: {}".format(e), file=sys.stderr)
|
||||
exit(1) # TODO: Check this is the correct way to kill xwaf on error
|
||||
|
||||
return interpreter_name
|
||||
|
||||
|
||||
def get_unity_runner_generator(project_root_path):
|
||||
"""
|
||||
Check the Unity generate_test_runner script is avaliable, and return the
|
||||
path to it.
|
||||
"""
|
||||
unity_runner_generator = os.path.join(
|
||||
project_root_path, "Unity", "auto", "generate_test_runner.rb"
|
||||
)
|
||||
if not os.path.exists(unity_runner_generator):
|
||||
print("Unity repo not found in workspace", file=sys.stderr)
|
||||
exit(1) # TODO: Check this is the correct way to kill xwaf on error
|
||||
return unity_runner_generator
|
||||
|
||||
|
||||
def get_test_name(test_path):
|
||||
"""
|
||||
Return the test name by removing the extension from the filename.
|
||||
"""
|
||||
return os.path.splitext(os.path.basename(test_path))[0]
|
||||
|
||||
|
||||
def get_file_type(filename):
|
||||
"""
|
||||
Return the extension from the filename.
|
||||
"""
|
||||
return filename.rsplit(".")[-1:][0]
|
||||
|
||||
|
||||
def generate_unity_runner(
|
||||
project_root_path, unity_test_path, unity_runner_dir, unity_runner_suffix
|
||||
):
|
||||
"""
|
||||
Invoke the Unity runner generation script for the given test file, and
|
||||
return the path to the generated file. The output directory will be created
|
||||
if it does not already exist.
|
||||
"""
|
||||
runner_path = os.path.join(
|
||||
os.path.join(unity_runner_dir, get_test_name(unity_test_path))
|
||||
)
|
||||
if not os.path.exists(runner_path):
|
||||
os.makedirs(runner_path)
|
||||
|
||||
unity_runner_path = os.path.join(
|
||||
runner_path, get_test_name(unity_test_path) + unity_runner_suffix + "." + "c"
|
||||
)
|
||||
|
||||
try:
|
||||
subprocess.check_call(
|
||||
[
|
||||
get_ruby(),
|
||||
get_unity_runner_generator(project_root_path),
|
||||
unity_test_path,
|
||||
unity_runner_path,
|
||||
]
|
||||
)
|
||||
except OSError as e:
|
||||
print(
|
||||
"Ruby generator failed for {}\n\t{}".format(unity_test_path, e),
|
||||
file=sys.stderr,
|
||||
)
|
||||
exit(1) # TODO: Check this is the correct way to kill xwaf on error
|
||||
|
||||
|
||||
def find_unity_test_paths(unity_test_dir, unity_test_prefix):
|
||||
"""
|
||||
Return a list of all file paths with the unity_test_prefix found in the
|
||||
unity_test_dir.
|
||||
"""
|
||||
return glob.glob(os.path.join(unity_test_dir, unity_test_prefix + "*"))
|
||||
|
||||
|
||||
def find_unity_tests(unity_test_dir, unity_test_prefix):
|
||||
"""
|
||||
Return a dictionary of all {test names, test language} pairs with the
|
||||
unity_test_prefix found in the unity_test_dir.
|
||||
"""
|
||||
unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix)
|
||||
print("unity_test_paths = ", unity_test_paths)
|
||||
return {get_test_name(path): get_file_type(path) for path in unity_test_paths}
|
||||
|
||||
|
||||
def find_unity_test_paths(unity_test_dir, unity_test_prefix):
|
||||
"""
|
||||
Return a list of all file paths with the unity_test_prefix found in the
|
||||
unity_test_dir.
|
||||
"""
|
||||
return glob.glob(os.path.join(unity_test_dir, unity_test_prefix + "*"))
|
||||
|
||||
|
||||
def generate_runners():
|
||||
UNITY_TESTS = find_unity_tests(UNITY_TEST_DIR, UNITY_TEST_PREFIX)
|
||||
print("UNITY_TESTS = ", UNITY_TESTS)
|
||||
unity_test_paths = find_unity_test_paths(UNITY_TEST_DIR, UNITY_TEST_PREFIX)
|
||||
print("unity_test_paths = ", unity_test_paths)
|
||||
for unity_test_path in unity_test_paths:
|
||||
generate_unity_runner(
|
||||
project_root, unity_test_path, UNITY_RUNNER_DIR, UNITY_RUNNER_SUFFIX
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate_runners()
|
||||
222
tests/xua_unit_tests/src/test_midi_parse/test_midi_parse.c
Normal file
222
tests/xua_unit_tests/src/test_midi_parse/test_midi_parse.c
Normal file
@@ -0,0 +1,222 @@
|
||||
// Copyright 2024 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "xua_unit_tests.h"
|
||||
#include "../../../lib_xua/src/midi/midiinparse.h"
|
||||
|
||||
#define NUM_CHANS 16
|
||||
#define NOTE_OFF 128
|
||||
#define NOTE_ON 144
|
||||
#define PRESSURE 160
|
||||
#define CONTROL 176
|
||||
#define PROGRAM 192
|
||||
#define PRESSURE_VAL 208
|
||||
#define RANGE 224
|
||||
#define SYSEX_SOM 240
|
||||
#define SYSEX_EOM 247
|
||||
|
||||
#define DATA_RANGE 128
|
||||
#define DATA_MASK (DATA_RANGE - 1)
|
||||
|
||||
#ifndef NUM_TESTS_PER_TEST
|
||||
#define NUM_TESTS_PER_TEST 30
|
||||
#endif
|
||||
|
||||
#ifndef CABLE_NUM
|
||||
#define CABLE_NUM 0
|
||||
#endif
|
||||
|
||||
#define RANDOM_SEED 6031769
|
||||
|
||||
unsigned midi_in_parse_ut(unsigned midi[3]){
|
||||
// printf("Composing data: 0x%x 0x%x 0x%x\n", midi[0], midi[1], midi[2]);
|
||||
|
||||
struct midi_in_parse_state m_state;
|
||||
void * mips = &m_state;
|
||||
reset_midi_state_c_wrapper(mips);
|
||||
|
||||
unsigned valid = 0;
|
||||
unsigned packed = 0;
|
||||
|
||||
midi_in_parse_c_wrapper(mips, CABLE_NUM, midi[0], &valid, &packed);
|
||||
// printf("Valid: %d data: %u\n", valid, packed);
|
||||
if(valid){
|
||||
return packed;
|
||||
}
|
||||
midi_in_parse_c_wrapper(mips, CABLE_NUM, midi[1], &valid, &packed);
|
||||
// printf("Valid: %d data: %u\n", valid, packed);
|
||||
if(valid){
|
||||
return packed;
|
||||
}
|
||||
midi_in_parse_c_wrapper(mips, CABLE_NUM, midi[2], &valid, &packed);
|
||||
// printf("Valid: %d data: %u\n", valid, packed);
|
||||
if(valid){
|
||||
return packed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned rndm = RANDOM_SEED;
|
||||
|
||||
|
||||
void test_midi_note(void) {
|
||||
for(int cmd = NOTE_OFF; cmd < NOTE_ON + NUM_CHANS; cmd++){
|
||||
for(int test = 0; test < NUM_TESTS_PER_TEST; test++){
|
||||
unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK};
|
||||
unsigned packed = midi_in_parse_ut(midi_ref);
|
||||
|
||||
unsigned midi_dut[3] = {0};
|
||||
unsigned size = 0;
|
||||
midi_out_parse_c_wrapper(packed, midi_dut, &size);
|
||||
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]);
|
||||
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]);
|
||||
//TEST_ASSERT_EQUAL_UINT32_ARRAY not working!?
|
||||
for(int i = 0; i < size; i++){
|
||||
TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_midi_pressure(void) {
|
||||
for(int cmd = PRESSURE; cmd < PRESSURE + NUM_CHANS; cmd++){
|
||||
for(int test = 0; test < NUM_TESTS_PER_TEST; test++){
|
||||
unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK};
|
||||
unsigned packed = midi_in_parse_ut(midi_ref);
|
||||
|
||||
unsigned midi_dut[3] = {0};
|
||||
unsigned size = 0;
|
||||
midi_out_parse_c_wrapper(packed, midi_dut, &size);
|
||||
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]);
|
||||
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]);
|
||||
//TEST_ASSERT_EQUAL_UINT32_ARRAY not working!?
|
||||
for(int i = 0; i < size; i++){
|
||||
TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_midi_control(void) {
|
||||
for(int cmd = CONTROL; cmd < CONTROL + NUM_CHANS; cmd++){
|
||||
for(int test = 0; test < NUM_TESTS_PER_TEST; test++){
|
||||
unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK};
|
||||
unsigned packed = midi_in_parse_ut(midi_ref);
|
||||
|
||||
unsigned midi_dut[3] = {0};
|
||||
unsigned size = 0;
|
||||
midi_out_parse_c_wrapper(packed, midi_dut, &size);
|
||||
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]);
|
||||
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]);
|
||||
//TEST_ASSERT_EQUAL_UINT32_ARRAY not working!?
|
||||
for(int i = 0; i < size; i++){
|
||||
TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_midi_program(void) {
|
||||
for(int cmd = PROGRAM; cmd < PROGRAM + NUM_CHANS; cmd++){
|
||||
for(int test = 0; test < NUM_TESTS_PER_TEST; test++){
|
||||
unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK};
|
||||
unsigned packed = midi_in_parse_ut(midi_ref);
|
||||
|
||||
unsigned midi_dut[3] = {0};
|
||||
unsigned size = 0;
|
||||
midi_out_parse_c_wrapper(packed, midi_dut, &size);
|
||||
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]);
|
||||
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]);
|
||||
//TEST_ASSERT_EQUAL_UINT32_ARRAY not working!?
|
||||
for(int i = 0; i < size; i++){
|
||||
TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_midi_pressure_val(void) {
|
||||
for(int cmd = PRESSURE_VAL; cmd < PRESSURE_VAL + NUM_CHANS; cmd++){
|
||||
for(int test = 0; test < NUM_TESTS_PER_TEST; test++){
|
||||
unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK};
|
||||
unsigned packed = midi_in_parse_ut(midi_ref);
|
||||
|
||||
unsigned midi_dut[3] = {0};
|
||||
unsigned size = 0;
|
||||
midi_out_parse_c_wrapper(packed, midi_dut, &size);
|
||||
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]);
|
||||
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]);
|
||||
//TEST_ASSERT_EQUAL_UINT32_ARRAY not working!?
|
||||
for(int i = 0; i < size; i++){
|
||||
TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_midi_range(void) {
|
||||
for(int cmd = RANGE; cmd < RANGE + NUM_CHANS; cmd++){
|
||||
for(int test = 0; test < NUM_TESTS_PER_TEST; test++){
|
||||
unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK};
|
||||
unsigned packed = midi_in_parse_ut(midi_ref);
|
||||
|
||||
unsigned midi_dut[3] = {0};
|
||||
unsigned size = 0;
|
||||
midi_out_parse_c_wrapper(packed, midi_dut, &size);
|
||||
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]);
|
||||
// printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]);
|
||||
//TEST_ASSERT_EQUAL_UINT32_ARRAY not working!?
|
||||
for(int i = 0; i < size; i++){
|
||||
TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://cmtext.indiana.edu/MIDI/chapter3_system_messages.php
|
||||
void test_midi_sys_ex(void) {
|
||||
const unsigned MAX_SYS_EX_LENGTH = 200; // https://cycling74.com/forums/maximu-sysex-length
|
||||
|
||||
for(int sys_ex_length = 1; sys_ex_length < MAX_SYS_EX_LENGTH; sys_ex_length++){
|
||||
unsigned midi_ref[1 + MAX_SYS_EX_LENGTH + 1] = {0}; // Add SOM + EOM.
|
||||
unsigned msg_len = 1 + sys_ex_length + 1;
|
||||
|
||||
// Build message
|
||||
midi_ref[0] = SYSEX_SOM;
|
||||
midi_ref[1] = 0x00; // Extended manf ID
|
||||
midi_ref[2] = random(&rndm) & DATA_MASK; // Manf ID 0
|
||||
midi_ref[3] = random(&rndm) & DATA_MASK; // Manf ID 1
|
||||
for(unsigned i = 4; i < msg_len - 1; i++){
|
||||
midi_ref[i] = random(&rndm) & DATA_MASK; // Some data
|
||||
}
|
||||
midi_ref[msg_len - 1] = SYSEX_EOM; // End of sys-ex
|
||||
|
||||
unsigned midi_dut[1 + MAX_SYS_EX_LENGTH + 1 + 2] = {0}; // Add SOM + EOM. Add 2 because msg_size % 3 may be up to 2.
|
||||
|
||||
struct midi_in_parse_state mips;
|
||||
reset_midi_state_c_wrapper(&mips);
|
||||
unsigned valid = 0;
|
||||
unsigned packed = 0;
|
||||
|
||||
unsigned out_idx = 0;
|
||||
for(unsigned i = 0; i < msg_len; i++){
|
||||
// printf("Ref byte: %d - 0x%x\n", i, midi_ref[i]);
|
||||
midi_in_parse_c_wrapper((void * )&mips, CABLE_NUM, midi_ref[i], &valid, &packed);
|
||||
if(valid){
|
||||
unsigned size = 0;
|
||||
midi_out_parse_c_wrapper(packed, &midi_dut[out_idx], &size);
|
||||
// printf("SIZE: %u PACKED: 0x%8x, 0x%x 0x%x 0x%x\n", size, packed, midi_dut[out_idx + 0], midi_dut[out_idx + 1], midi_dut[out_idx + 2]);
|
||||
out_idx += size;
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < msg_len; i++){
|
||||
// printf("%d Ref: 0x%x DUT: 0x%x\n", i, midi_ref[i], midi_dut[i]);
|
||||
TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]);
|
||||
}
|
||||
printf("Sysex PASS length: %u\n", msg_len);
|
||||
}
|
||||
}
|
||||
115
tests/xua_unit_tests/src/test_midi_queue/test_midi_queue.c
Normal file
115
tests/xua_unit_tests/src/test_midi_queue/test_midi_queue.c
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright 2024 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xua_unit_tests.h"
|
||||
#include "../../../lib_xua/src/midi/queue.h"
|
||||
|
||||
#define DEBUG 0
|
||||
|
||||
#if DEBUG
|
||||
#define dprintf(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define dprintf(...)
|
||||
#endif
|
||||
|
||||
|
||||
#define RANDOM_SEED 55378008
|
||||
#define USB_MIDI_DEVICE_OUT_FIFO_SIZE 1024
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
unsigned rndm = RANDOM_SEED;
|
||||
|
||||
|
||||
void test_midi_queue_init(void) {
|
||||
queue_t symbol_fifo;
|
||||
unsigned symbol_fifo_storage[USB_MIDI_DEVICE_OUT_FIFO_SIZE];
|
||||
memset(symbol_fifo_storage, USB_MIDI_DEVICE_OUT_FIFO_SIZE * sizeof(unsigned), 0xdb); // Non zero
|
||||
|
||||
queue_init_c_wrapper(&symbol_fifo, ARRAY_SIZE(symbol_fifo_storage));
|
||||
|
||||
int empty = queue_is_empty_c_wrapper(&symbol_fifo);
|
||||
TEST_ASSERT_EQUAL_INT32(1, empty);
|
||||
|
||||
int full = queue_is_full_c_wrapper(&symbol_fifo);
|
||||
TEST_ASSERT_EQUAL_INT32(0, full);
|
||||
|
||||
unsigned items = queue_items_c_wrapper(&symbol_fifo);
|
||||
TEST_ASSERT_EQUAL_UINT32(0, items);
|
||||
|
||||
unsigned space = queue_space_c_wrapper(&symbol_fifo);
|
||||
TEST_ASSERT_EQUAL_UINT32(USB_MIDI_DEVICE_OUT_FIFO_SIZE, space);
|
||||
|
||||
// Pop empty queue
|
||||
unsigned entry = queue_pop_word_c_wrapper(&symbol_fifo, symbol_fifo_storage);
|
||||
TEST_ASSERT_EQUAL_UINT32(MIDI_OUT_NULL_MESSAGE, entry);
|
||||
|
||||
space = queue_space_c_wrapper(&symbol_fifo);
|
||||
TEST_ASSERT_EQUAL_UINT32(USB_MIDI_DEVICE_OUT_FIFO_SIZE, space);
|
||||
}
|
||||
|
||||
void test_midi_queue_full(void) {
|
||||
queue_t symbol_fifo;
|
||||
unsigned symbol_fifo_storage[USB_MIDI_DEVICE_OUT_FIFO_SIZE];
|
||||
queue_init_c_wrapper(&symbol_fifo, ARRAY_SIZE(symbol_fifo_storage));
|
||||
|
||||
for(unsigned i = 0; i < USB_MIDI_DEVICE_OUT_FIFO_SIZE; i++){
|
||||
queue_push_word_c_wrapper(&symbol_fifo, symbol_fifo_storage, 1111);
|
||||
}
|
||||
|
||||
int empty = queue_is_empty_c_wrapper(&symbol_fifo);
|
||||
TEST_ASSERT_EQUAL_INT32(0, empty);
|
||||
|
||||
int full = queue_is_full_c_wrapper(&symbol_fifo);
|
||||
TEST_ASSERT_EQUAL_INT32(1, full);
|
||||
|
||||
unsigned items = queue_items_c_wrapper(&symbol_fifo);
|
||||
TEST_ASSERT_EQUAL_UINT32(USB_MIDI_DEVICE_OUT_FIFO_SIZE, items);
|
||||
|
||||
unsigned space = queue_space_c_wrapper(&symbol_fifo);
|
||||
TEST_ASSERT_EQUAL_UINT32(0, space);
|
||||
|
||||
// We want no exception here and this to be ignored
|
||||
queue_push_word_c_wrapper(&symbol_fifo, symbol_fifo_storage, 2222);
|
||||
|
||||
unsigned entry = queue_pop_word_c_wrapper(&symbol_fifo, symbol_fifo_storage);
|
||||
TEST_ASSERT_EQUAL_UINT32(1111, entry);
|
||||
}
|
||||
|
||||
void test_midi_queue_push_pop(void) {
|
||||
queue_t symbol_fifo;
|
||||
unsigned symbol_fifo_storage[USB_MIDI_DEVICE_OUT_FIFO_SIZE];
|
||||
queue_init_c_wrapper(&symbol_fifo, ARRAY_SIZE(symbol_fifo_storage));
|
||||
|
||||
for(unsigned i = 0; i < USB_MIDI_DEVICE_OUT_FIFO_SIZE; i++){
|
||||
int items = queue_items_c_wrapper(&symbol_fifo);
|
||||
dprintf("Pre i: %u items: %d\n", i, items);
|
||||
TEST_ASSERT_EQUAL_UINT32(i, items);
|
||||
|
||||
unsigned entry = i + 1000;
|
||||
queue_push_word_c_wrapper(&symbol_fifo, symbol_fifo_storage, entry);
|
||||
dprintf("pushed: %u\n", entry);
|
||||
|
||||
items = queue_items_c_wrapper(&symbol_fifo);
|
||||
TEST_ASSERT_EQUAL_UINT32(i + 1, items);
|
||||
|
||||
dprintf("Post items: %d\n", items);
|
||||
}
|
||||
|
||||
unsigned counter = 0;
|
||||
for(int i = USB_MIDI_DEVICE_OUT_FIFO_SIZE; i > 0; i--){
|
||||
int items = queue_items_c_wrapper(&symbol_fifo);
|
||||
dprintf("i: %u items: %d\n", i, items);
|
||||
TEST_ASSERT_EQUAL_UINT32(i, items);
|
||||
|
||||
unsigned entry = queue_pop_word_c_wrapper(&symbol_fifo, symbol_fifo_storage);
|
||||
unsigned expected = 1000 + counter;
|
||||
|
||||
dprintf("expected: %u got: %d\n", expected, entry);
|
||||
TEST_ASSERT_EQUAL_UINT32(expected, entry);
|
||||
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 XMOS LIMITED.
|
||||
// Copyright 2021-2024 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
@@ -47,23 +47,26 @@ static unsigned construct_usage_header( unsigned size )
|
||||
return header;
|
||||
}
|
||||
|
||||
void setUp( void )
|
||||
void test_init( void )
|
||||
{
|
||||
hidReportInit();
|
||||
hidResetReportDescriptor();
|
||||
}
|
||||
|
||||
void test_validate_report( void ) {
|
||||
test_init();
|
||||
unsigned retVal = hidReportValidate();
|
||||
TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal );
|
||||
}
|
||||
|
||||
void test_reportid_in_use( void ) {
|
||||
test_init();
|
||||
unsigned reportIdInUse = hidIsReportIdInUse();
|
||||
TEST_ASSERT_EQUAL_UINT( 1, reportIdInUse );
|
||||
}
|
||||
|
||||
void test_get_next_valid_report_id( void ) {
|
||||
test_init();
|
||||
unsigned reportId = 0U;
|
||||
|
||||
reportId = hidGetNextValidReportId(reportId);
|
||||
@@ -80,6 +83,7 @@ void test_get_next_valid_report_id( void ) {
|
||||
}
|
||||
|
||||
void test_is_report_id_valid( void ) {
|
||||
test_init();
|
||||
unsigned isValid = 0;
|
||||
|
||||
unsigned reportId = 0;
|
||||
@@ -106,6 +110,7 @@ void test_is_report_id_valid( void ) {
|
||||
// Basic report descriptor tests
|
||||
void test_unprepared_hidGetReportDescriptor( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned char* reportDescPtr = hidGetReportDescriptor();
|
||||
TEST_ASSERT_NULL( reportDescPtr );
|
||||
|
||||
@@ -118,6 +123,7 @@ void test_unprepared_hidGetReportDescriptor( void )
|
||||
|
||||
void test_prepared_hidGetReportDescriptor( void )
|
||||
{
|
||||
test_init();
|
||||
hidPrepareReportDescriptor();
|
||||
unsigned char* reportDescPtr = hidGetReportDescriptor();
|
||||
TEST_ASSERT_NOT_NULL( reportDescPtr );
|
||||
@@ -137,6 +143,7 @@ void test_prepared_hidGetReportDescriptor( void )
|
||||
|
||||
void test_reset_unprepared_hidGetReportDescriptor( void )
|
||||
{
|
||||
test_init();
|
||||
hidPrepareReportDescriptor();
|
||||
hidResetReportDescriptor();
|
||||
unsigned char* reportDescPtr = hidGetReportDescriptor();
|
||||
@@ -145,6 +152,7 @@ void test_reset_unprepared_hidGetReportDescriptor( void )
|
||||
|
||||
void test_reset_prepared_hidGetReportDescriptor( void )
|
||||
{
|
||||
test_init();
|
||||
hidPrepareReportDescriptor();
|
||||
hidResetReportDescriptor();
|
||||
hidPrepareReportDescriptor();
|
||||
@@ -154,6 +162,7 @@ void test_reset_prepared_hidGetReportDescriptor( void )
|
||||
|
||||
void test_report_id_limit( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned reportIdLimit = hidGetReportIdLimit();
|
||||
TEST_ASSERT_EQUAL_UINT( HID_REPORTID_LIMIT, reportIdLimit );
|
||||
}
|
||||
@@ -161,6 +170,7 @@ void test_report_id_limit( void )
|
||||
// Basic item tests
|
||||
void test_max_loc_hidGetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ];
|
||||
unsigned char header;
|
||||
unsigned char page;
|
||||
@@ -199,6 +209,7 @@ void test_max_loc_hidGetReportItem( void )
|
||||
|
||||
void test_min_loc_hidGetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ];
|
||||
unsigned char header;
|
||||
unsigned char page;
|
||||
@@ -236,6 +247,7 @@ void test_min_loc_hidGetReportItem( void )
|
||||
|
||||
void test_invalid_report_id( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD2 };
|
||||
unsigned char header = 0x33;
|
||||
unsigned char page = 0x44;
|
||||
@@ -253,6 +265,7 @@ void test_invalid_report_id( void )
|
||||
|
||||
void test_unused_report_id( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD2 };
|
||||
unsigned char header = 0x33;
|
||||
unsigned char page = 0x44;
|
||||
@@ -270,6 +283,7 @@ void test_unused_report_id( void )
|
||||
|
||||
void test_overflow_bit_hidGetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 };
|
||||
unsigned char header = 0xAA;
|
||||
unsigned char page = 0x44;
|
||||
@@ -307,6 +321,7 @@ void test_overflow_bit_hidGetReportItem( void )
|
||||
|
||||
void test_overflow_byte_hidGetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 };
|
||||
unsigned char header = 0xAA;
|
||||
unsigned char page = 0x44;
|
||||
@@ -344,6 +359,7 @@ void test_overflow_byte_hidGetReportItem( void )
|
||||
|
||||
void test_underflow_bit_hidGetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 };
|
||||
unsigned char header = 0xAA;
|
||||
unsigned char page = 0x44;
|
||||
@@ -381,6 +397,7 @@ void test_underflow_bit_hidGetReportItem( void )
|
||||
|
||||
void test_underflow_byte_hidGetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 };
|
||||
unsigned char header = 0xAA;
|
||||
unsigned char page = 0x44;
|
||||
@@ -419,6 +436,7 @@ void test_underflow_byte_hidGetReportItem( void )
|
||||
// Configurable and non-configurable item tests
|
||||
void test_configurable_item_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 1;
|
||||
const unsigned bit = REPORT1_MIN_VALID_BIT;
|
||||
const unsigned byte = REPORT1_MIN_VALID_BYTE;
|
||||
@@ -440,6 +458,7 @@ void test_configurable_item_hidSetReportItem( void )
|
||||
// Testing that the high byte of the report gets correctly cleared
|
||||
void test_configurable_item_hidSetReportItem_multibyte_orig( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 2;
|
||||
const unsigned bit = 1; // This byte&bit combo is originally set be 2 bytes long in the header
|
||||
const unsigned byte = 0;
|
||||
@@ -460,6 +479,7 @@ void test_configurable_item_hidSetReportItem_multibyte_orig( void )
|
||||
|
||||
void test_nonconfigurable_item_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 1;
|
||||
const unsigned bit = 1; // This bit and byte combination should not appear in the
|
||||
const unsigned byte = 0; // hidConfigurableElements list in hid_report_descriptors.c.
|
||||
@@ -474,6 +494,7 @@ void test_nonconfigurable_item_hidSetReportItem( void )
|
||||
// Bit range tests
|
||||
void test_max_bit_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned char header = construct_usage_header( 0 );
|
||||
|
||||
unsigned reportId = 1;
|
||||
@@ -500,6 +521,7 @@ void test_max_bit_hidSetReportItem( void )
|
||||
|
||||
void test_min_bit_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned char header = construct_usage_header( 0 );
|
||||
|
||||
unsigned reportId = 1;
|
||||
@@ -526,6 +548,7 @@ void test_min_bit_hidSetReportItem( void )
|
||||
|
||||
void test_overflow_bit_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned char header = construct_usage_header( 0 );
|
||||
|
||||
unsigned reportId = 1;
|
||||
@@ -552,6 +575,7 @@ void test_overflow_bit_hidSetReportItem( void )
|
||||
|
||||
void test_underflow_bit_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned char header = construct_usage_header( 0 );
|
||||
|
||||
unsigned reportId = 1;
|
||||
@@ -578,6 +602,7 @@ void test_underflow_bit_hidSetReportItem( void )
|
||||
|
||||
void test_overflow_byte_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned char header = construct_usage_header( 0 );
|
||||
|
||||
unsigned reportId = 1;
|
||||
@@ -604,6 +629,7 @@ void test_overflow_byte_hidSetReportItem( void )
|
||||
|
||||
void test_underflow_byte_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned char header = construct_usage_header( 0 );
|
||||
|
||||
unsigned reportId = 1;
|
||||
@@ -631,6 +657,7 @@ void test_underflow_byte_hidSetReportItem( void )
|
||||
// Size range tests
|
||||
void test_max_size_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 1;
|
||||
const unsigned bit = REPORT1_MIN_VALID_BIT;
|
||||
const unsigned byte = REPORT1_MIN_VALID_BYTE;
|
||||
@@ -644,6 +671,7 @@ void test_max_size_hidSetReportItem( void )
|
||||
|
||||
void test_min_size_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 1;
|
||||
const unsigned bit = REPORT1_MIN_VALID_BIT;
|
||||
const unsigned byte = REPORT1_MIN_VALID_BYTE;
|
||||
@@ -656,6 +684,7 @@ void test_min_size_hidSetReportItem( void )
|
||||
|
||||
void test_unsupported_size_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = REPORT1_MIN_VALID_BIT;
|
||||
const unsigned byte = REPORT1_MIN_VALID_BYTE;
|
||||
@@ -669,6 +698,7 @@ void test_unsupported_size_hidSetReportItem( void )
|
||||
// Combined function tests
|
||||
void test_initial_modification_without_subsequent_preparation( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 2;
|
||||
const unsigned bit = REPORT2_MIN_VALID_BIT;
|
||||
const unsigned byte = REPORT2_MIN_VALID_BYTE;
|
||||
@@ -685,6 +715,7 @@ void test_initial_modification_without_subsequent_preparation( void )
|
||||
|
||||
void test_initial_modification_with_subsequent_preparation( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 2;
|
||||
const unsigned bit = REPORT2_MIN_VALID_BIT;
|
||||
const unsigned byte = REPORT2_MIN_VALID_BYTE;
|
||||
@@ -702,6 +733,7 @@ void test_initial_modification_with_subsequent_preparation( void )
|
||||
|
||||
void test_initial_modification_with_subsequent_verification_1( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 2;
|
||||
const unsigned bit = REPORT2_MIN_VALID_BIT;
|
||||
const unsigned byte = REPORT2_MIN_VALID_BYTE;
|
||||
@@ -727,6 +759,7 @@ void test_initial_modification_with_subsequent_verification_1( void )
|
||||
|
||||
void test_initial_modification_with_subsequent_verification_2( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 3;
|
||||
const unsigned bit = REPORT3_MIN_VALID_BIT;
|
||||
const unsigned byte = REPORT3_MIN_VALID_BYTE;
|
||||
@@ -775,6 +808,7 @@ void test_initial_modification_with_subsequent_verification_2( void )
|
||||
//setIdle and associated timing functionality tests
|
||||
void test_set_idle( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned reportId = 1;
|
||||
unsigned reportId2 = 2;
|
||||
|
||||
@@ -794,6 +828,7 @@ void test_set_idle( void )
|
||||
|
||||
void test_set_all_idle( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned reportId = 1;
|
||||
unsigned reportId2 = 2;
|
||||
|
||||
@@ -812,6 +847,7 @@ void test_set_all_idle( void )
|
||||
|
||||
void test_change_pending( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned reportId = 1;
|
||||
unsigned reportId2 = 2;
|
||||
|
||||
@@ -831,6 +867,7 @@ void test_change_pending( void )
|
||||
|
||||
void test_change_pending_all( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned reportId = 1;
|
||||
|
||||
unsigned changePending = hidIsChangePending( reportId );
|
||||
@@ -845,6 +882,7 @@ void test_change_pending_all( void )
|
||||
|
||||
void test_report_time( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned reportTime1 = 123;
|
||||
unsigned reportTime2 = 456;
|
||||
|
||||
@@ -859,6 +897,7 @@ void test_report_time( void )
|
||||
|
||||
void test_report_time_calc( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned reportTime1 = 123;
|
||||
unsigned reportTime2 = 456;
|
||||
unsigned reportPeriod1 = 10;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 XMOS LIMITED.
|
||||
// Copyright 2021-2024 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
@@ -34,23 +34,26 @@ static unsigned construct_usage_header( unsigned size )
|
||||
return header;
|
||||
}
|
||||
|
||||
void setUp( void )
|
||||
void test_init( void )
|
||||
{
|
||||
hidReportInit();
|
||||
hidResetReportDescriptor();
|
||||
}
|
||||
|
||||
void test_validate_report( void ) {
|
||||
test_init();
|
||||
unsigned retVal = hidReportValidate();
|
||||
TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal );
|
||||
}
|
||||
|
||||
void test_reportid_in_use( void ) {
|
||||
test_init();
|
||||
unsigned reportIdInUse = hidIsReportIdInUse();
|
||||
TEST_ASSERT_EQUAL_UINT( 0, reportIdInUse );
|
||||
}
|
||||
|
||||
void test_get_next_valid_report_id( void ) {
|
||||
test_init();
|
||||
unsigned reportId = 0U;
|
||||
|
||||
reportId = hidGetNextValidReportId(reportId);
|
||||
@@ -61,6 +64,7 @@ void test_get_next_valid_report_id( void ) {
|
||||
}
|
||||
|
||||
void test_is_report_id_valid( void ) {
|
||||
test_init();
|
||||
unsigned isValid = 0;
|
||||
|
||||
unsigned reportId = 0;
|
||||
@@ -75,6 +79,7 @@ void test_is_report_id_valid( void ) {
|
||||
// Basic report descriptor tests
|
||||
void test_unprepared_hidGetReportDescriptor( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
unsigned char* reportDescPtr = hidGetReportDescriptor();
|
||||
TEST_ASSERT_NULL( reportDescPtr );
|
||||
@@ -85,6 +90,7 @@ void test_unprepared_hidGetReportDescriptor( void )
|
||||
|
||||
void test_prepared_hidGetReportDescriptor( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
|
||||
hidPrepareReportDescriptor();
|
||||
@@ -97,6 +103,7 @@ void test_prepared_hidGetReportDescriptor( void )
|
||||
|
||||
void test_reset_unprepared_hidGetReportDescriptor( void )
|
||||
{
|
||||
test_init();
|
||||
hidPrepareReportDescriptor();
|
||||
hidResetReportDescriptor();
|
||||
unsigned char* reportDescPtr = hidGetReportDescriptor();
|
||||
@@ -105,6 +112,7 @@ void test_reset_unprepared_hidGetReportDescriptor( void )
|
||||
|
||||
void test_reset_prepared_hidGetReportDescriptor( void )
|
||||
{
|
||||
test_init();
|
||||
hidPrepareReportDescriptor();
|
||||
hidResetReportDescriptor();
|
||||
hidPrepareReportDescriptor();
|
||||
@@ -114,6 +122,7 @@ void test_reset_prepared_hidGetReportDescriptor( void )
|
||||
|
||||
void test_report_id_limit( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned reportIdLimit = hidGetReportIdLimit();
|
||||
TEST_ASSERT_EQUAL_UINT( HID_REPORTID_LIMIT, reportIdLimit );
|
||||
}
|
||||
@@ -121,6 +130,7 @@ void test_report_id_limit( void )
|
||||
// Basic item tests
|
||||
void test_max_loc_hidGetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MAX_VALID_BIT;
|
||||
const unsigned byte = MAX_VALID_BYTE;
|
||||
@@ -138,6 +148,7 @@ void test_max_loc_hidGetReportItem( void )
|
||||
|
||||
void test_min_loc_hidGetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -155,6 +166,7 @@ void test_min_loc_hidGetReportItem( void )
|
||||
|
||||
void test_overflow_bit_hidGetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MAX_VALID_BIT + 1;
|
||||
const unsigned byte = MAX_VALID_BYTE;
|
||||
@@ -172,6 +184,7 @@ void test_overflow_bit_hidGetReportItem( void )
|
||||
|
||||
void test_overflow_byte_hidGetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MAX_VALID_BIT;
|
||||
const unsigned byte = MAX_VALID_BYTE + 1;
|
||||
@@ -189,6 +202,7 @@ void test_overflow_byte_hidGetReportItem( void )
|
||||
|
||||
void test_underflow_bit_hidGetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const int bit = MIN_VALID_BIT - 1;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -206,6 +220,7 @@ void test_underflow_bit_hidGetReportItem( void )
|
||||
|
||||
void test_underflow_byte_hidGetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const int byte = MIN_VALID_BYTE - 1;
|
||||
@@ -224,6 +239,7 @@ void test_underflow_byte_hidGetReportItem( void )
|
||||
// Configurable and non-configurable item tests
|
||||
void test_configurable_item_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -237,6 +253,7 @@ void test_configurable_item_hidSetReportItem( void )
|
||||
|
||||
void test_nonconfigurable_item_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MAX_VALID_BIT; // This bit and byte combination should not appear in the
|
||||
const unsigned byte = MIN_VALID_BYTE; // hidConfigurableElements list in hid_report_descriptors.c.
|
||||
@@ -251,6 +268,7 @@ void test_nonconfigurable_item_hidSetReportItem( void )
|
||||
// Bit range tests
|
||||
void test_max_bit_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MAX_VALID_BIT; // Only byte 1 has bit 7 not reserved, See the
|
||||
const unsigned byte = MAX_VALID_BYTE; // hidConfigurableElements list in hid_report_descriptors.c.
|
||||
@@ -263,6 +281,7 @@ void test_max_bit_hidSetReportItem( void )
|
||||
|
||||
void test_min_bit_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -275,6 +294,7 @@ void test_min_bit_hidSetReportItem( void )
|
||||
|
||||
void test_overflow_bit_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MAX_VALID_BIT + 1;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -287,6 +307,7 @@ void test_overflow_bit_hidSetReportItem( void )
|
||||
|
||||
void test_underflow_bit_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const int bit = MIN_VALID_BIT - 1;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -300,6 +321,7 @@ void test_underflow_bit_hidSetReportItem( void )
|
||||
// Byte range tests
|
||||
void test_max_byte_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MAX_VALID_BYTE;
|
||||
@@ -312,6 +334,7 @@ void test_max_byte_hidSetReportItem( void )
|
||||
|
||||
void test_min_byte_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -324,6 +347,7 @@ void test_min_byte_hidSetReportItem( void )
|
||||
|
||||
void test_overflow_byte_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MAX_VALID_BYTE + 1;
|
||||
@@ -336,6 +360,7 @@ void test_overflow_byte_hidSetReportItem( void )
|
||||
|
||||
void test_underflow_byte_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const int byte = MIN_VALID_BYTE - 1;
|
||||
@@ -349,6 +374,7 @@ void test_underflow_byte_hidSetReportItem( void )
|
||||
// Size range tests
|
||||
void test_max_size_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -362,6 +388,7 @@ void test_max_size_hidSetReportItem( void )
|
||||
|
||||
void test_min_size_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -374,6 +401,7 @@ void test_min_size_hidSetReportItem( void )
|
||||
|
||||
void test_unsupported_size_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -387,6 +415,7 @@ void test_unsupported_size_hidSetReportItem( void )
|
||||
// Header tag and type tests
|
||||
void test_bad_tag_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -402,6 +431,7 @@ void test_bad_tag_hidSetReportItem( void )
|
||||
|
||||
void test_global_type_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -415,6 +445,7 @@ void test_global_type_hidSetReportItem( void )
|
||||
|
||||
void test_local_type_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -428,6 +459,7 @@ void test_local_type_hidSetReportItem( void )
|
||||
|
||||
void test_main_type_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -441,6 +473,7 @@ void test_main_type_hidSetReportItem( void )
|
||||
|
||||
void test_reserved_type_hidSetReportItem( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -455,6 +488,7 @@ void test_reserved_type_hidSetReportItem( void )
|
||||
// Combined function tests
|
||||
void test_initial_modification_without_subsequent_preparation( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -471,6 +505,7 @@ void test_initial_modification_without_subsequent_preparation( void )
|
||||
|
||||
void test_initial_modification_with_subsequent_preparation( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -488,6 +523,7 @@ void test_initial_modification_with_subsequent_preparation( void )
|
||||
|
||||
void test_initial_modification_with_subsequent_verification_1( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -513,6 +549,7 @@ void test_initial_modification_with_subsequent_verification_1( void )
|
||||
|
||||
void test_initial_modification_with_subsequent_verification_2( void )
|
||||
{
|
||||
test_init();
|
||||
const unsigned reportId = 0;
|
||||
const unsigned bit = MIN_VALID_BIT;
|
||||
const unsigned byte = MIN_VALID_BYTE;
|
||||
@@ -560,6 +597,7 @@ void test_initial_modification_with_subsequent_verification_2( void )
|
||||
|
||||
void test_modification_without_subsequent_preparation( void )
|
||||
{
|
||||
test_init();
|
||||
hidPrepareReportDescriptor();
|
||||
unsigned char* reportDescPtr = hidGetReportDescriptor();
|
||||
TEST_ASSERT_NOT_NULL( reportDescPtr );
|
||||
@@ -581,6 +619,7 @@ void test_modification_without_subsequent_preparation( void )
|
||||
|
||||
void test_modification_with_subsequent_preparation( void )
|
||||
{
|
||||
test_init();
|
||||
hidPrepareReportDescriptor();
|
||||
unsigned char* reportDescPtr = hidGetReportDescriptor();
|
||||
TEST_ASSERT_NOT_NULL( reportDescPtr );
|
||||
@@ -604,6 +643,7 @@ void test_modification_with_subsequent_preparation( void )
|
||||
//setIdle functionality tests
|
||||
void test_set_idle( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned reportId = 0;
|
||||
|
||||
unsigned setIdle = hidIsIdleActive( reportId );
|
||||
@@ -616,6 +656,7 @@ void test_set_idle( void )
|
||||
|
||||
void test_change_pending( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned reportId = 0;
|
||||
|
||||
unsigned changePending = hidIsChangePending( reportId );
|
||||
@@ -632,6 +673,7 @@ void test_change_pending( void )
|
||||
|
||||
void test_report_time( void )
|
||||
{
|
||||
test_init();
|
||||
unsigned reportTime = 123;
|
||||
unsigned reportPeriod = 10;
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
// Copyright 2021 XMOS LIMITED.
|
||||
// Copyright 2021-2024 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#ifdef __XC__
|
||||
|
||||
#include <xs1.h>
|
||||
#include <platform.h>
|
||||
#include <xclib.h>
|
||||
#include "../../../lib_xua/src/midi/midiinparse.h"
|
||||
#include "../../../lib_xua/src/midi/midioutparse.h"
|
||||
#include "../../../lib_xua/src/midi/queue.h"
|
||||
|
||||
#endif // __XC__
|
||||
|
||||
@@ -26,3 +29,75 @@ void AudioHwInit()
|
||||
{
|
||||
; // nothing
|
||||
}
|
||||
|
||||
unsigned random(unsigned &x){
|
||||
crc32(x, -1, 0xEB31D82E);
|
||||
return x;
|
||||
}
|
||||
|
||||
////////////////////// Wrappers for midi parse because C doesn't support return tuples
|
||||
void midi_in_parse_c_wrapper(void * unsafe mips, unsigned cable_number, unsigned char b, unsigned * unsafe valid, unsigned *unsafe packed){
|
||||
unsafe{
|
||||
struct midi_in_parse_state * unsafe ptr = mips;
|
||||
{*valid, *packed} = midi_in_parse(*ptr, cable_number, b);
|
||||
}
|
||||
}
|
||||
|
||||
void midi_out_parse_c_wrapper(unsigned tx_data, unsigned midi[3], unsigned * unsafe size){
|
||||
unsafe{
|
||||
{midi[0], midi[1], midi[2], *size} = midi_out_parse(tx_data);
|
||||
}
|
||||
}
|
||||
|
||||
void reset_midi_state_c_wrapper(void * unsafe mips){
|
||||
unsafe{
|
||||
struct midi_in_parse_state * unsafe ptr = mips;
|
||||
reset_midi_state(*ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////// Wrappers for queue test
|
||||
|
||||
|
||||
void queue_init_c_wrapper(queue_t *q, unsigned size) {
|
||||
unsafe{
|
||||
queue_init(*q, size);
|
||||
}
|
||||
}
|
||||
|
||||
int queue_is_empty_c_wrapper(queue_t *unsafe q) {
|
||||
unsafe{
|
||||
return queue_is_empty(*q);
|
||||
}
|
||||
}
|
||||
|
||||
int queue_is_full_c_wrapper(queue_t *unsafe q) {
|
||||
unsafe{
|
||||
return queue_is_full(*q);
|
||||
}
|
||||
}
|
||||
|
||||
void queue_push_word_c_wrapper(queue_t *q, unsigned array[], unsigned data){
|
||||
unsafe{
|
||||
queue_push_word(*q, array, data);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned queue_pop_word_c_wrapper(queue_t *q, unsigned array[]){
|
||||
unsafe{
|
||||
return queue_pop_word(*q, array);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned queue_items_c_wrapper(const queue_t *q){
|
||||
unsafe{
|
||||
return queue_items(*q);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned queue_space_c_wrapper(const queue_t *q){
|
||||
unsafe{
|
||||
return queue_space(*q);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,28 @@
|
||||
// Copyright 2021 XMOS LIMITED.
|
||||
// Copyright 2021-2024 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#ifndef XUA_UNIT_TESTS_H_
|
||||
#define XUA_UNIT_TESTS_H_
|
||||
|
||||
#include "unity.h"
|
||||
#include "xua_conf.h"
|
||||
#include "../../../lib_xua/src/midi/queue.h"
|
||||
|
||||
#ifndef __XC__
|
||||
void midi_in_parse_c_wrapper(void * mips, unsigned cable_number, unsigned char b, unsigned * valid, unsigned * packed);
|
||||
void midi_out_parse_c_wrapper(unsigned tx_data, unsigned midi[3], unsigned * size);
|
||||
void reset_midi_state_c_wrapper(void *mips);
|
||||
unsigned random(unsigned *x);
|
||||
|
||||
void queue_init_c_wrapper(queue_t *q, unsigned size);
|
||||
int queue_is_empty_c_wrapper(const queue_t *q);
|
||||
int queue_is_full_c_wrapper(const queue_t *q);
|
||||
void queue_push_word_c_wrapper(queue_t *q, unsigned array[], unsigned data);
|
||||
unsigned queue_pop_word_c_wrapper(queue_t *q, unsigned array[]);
|
||||
void queue_push_byte_c_wrapper(queue_t *q, unsigned char array[], unsigned data);
|
||||
unsigned queue_pop_byte_c_wrapper(queue_t *q, unsigned char array[]);
|
||||
unsigned queue_items_c_wrapper(const queue_t *q);
|
||||
unsigned queue_space_c_wrapper(const queue_t *q);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* XUA_UNIT_TESTS_H_ */
|
||||
|
||||
@@ -1,260 +0,0 @@
|
||||
from __future__ import print_function
|
||||
import glob
|
||||
import os.path
|
||||
import subprocess
|
||||
import sys
|
||||
from waflib import Options
|
||||
from waflib.Build import BuildContext, CleanContext
|
||||
|
||||
TARGETS = ['xcore200', 'xcoreai']
|
||||
|
||||
def get_ruby():
|
||||
"""
|
||||
Check ruby is avaliable and return the command to invoke it.
|
||||
"""
|
||||
interpreter_name = 'ruby'
|
||||
try:
|
||||
dev_null = open(os.devnull, 'w')
|
||||
# Call the version command to check the interpreter can be run
|
||||
subprocess.check_call([interpreter_name, '--version'],
|
||||
stdout=dev_null,
|
||||
close_fds=True)
|
||||
except OSError as e:
|
||||
print("Failed to run Ruby interpreter: {}".format(e), file=sys.stderr)
|
||||
exit(1) # TODO: Check this is the correct way to kill xwaf on error
|
||||
|
||||
return interpreter_name
|
||||
|
||||
|
||||
def get_unity_runner_generator(project_root_path):
|
||||
"""
|
||||
Check the Unity generate_test_runner script is avaliable, and return the
|
||||
path to it.
|
||||
"""
|
||||
unity_runner_generator = os.path.join(
|
||||
project_root_path, 'Unity', 'auto', 'generate_test_runner.rb')
|
||||
if not os.path.exists(unity_runner_generator):
|
||||
print("Unity repo not found in workspace", file=sys.stderr)
|
||||
exit(1) # TODO: Check this is the correct way to kill xwaf on error
|
||||
return unity_runner_generator
|
||||
|
||||
|
||||
def get_test_name(test_path):
|
||||
"""
|
||||
Return the test name by removing the extension from the filename.
|
||||
"""
|
||||
return os.path.splitext(os.path.basename(test_path))[0]
|
||||
|
||||
|
||||
def get_file_type(filename):
|
||||
"""
|
||||
Return the extension from the filename.
|
||||
"""
|
||||
return filename.rsplit('.')[-1:][0]
|
||||
|
||||
|
||||
def generate_unity_runner(project_root_path, unity_test_path, unity_runner_dir,
|
||||
unity_runner_suffix):
|
||||
"""
|
||||
Invoke the Unity runner generation script for the given test file, and
|
||||
return the path to the generated file. The output directory will be created
|
||||
if it does not already exist.
|
||||
"""
|
||||
runner_path = os.path.join(os.path.join(unity_runner_dir, get_test_name(unity_test_path)))
|
||||
if not os.path.exists(runner_path):
|
||||
os.makedirs(runner_path)
|
||||
|
||||
unity_runner_path = os.path.join(
|
||||
runner_path, get_test_name(unity_test_path) + unity_runner_suffix
|
||||
+ '.' + 'c')
|
||||
|
||||
try:
|
||||
subprocess.check_call([get_ruby(),
|
||||
get_unity_runner_generator(project_root_path),
|
||||
unity_test_path,
|
||||
unity_runner_path])
|
||||
except OSError as e:
|
||||
print("Ruby generator failed for {}\n\t{}".format(unity_test_path, e),
|
||||
file=sys.stderr)
|
||||
exit(1) # TODO: Check this is the correct way to kill xwaf on error
|
||||
|
||||
|
||||
def add_unity_runner_build_config(waf_conf, project_root_path, unity_test_path,
|
||||
unity_runner_build_flags, target):
|
||||
"""
|
||||
Add a config to xwaf to build each Unity test runner into an xCORE
|
||||
executable.
|
||||
"""
|
||||
print(f"get_test_name(unity_test_path) = {get_test_name(unity_test_path)}. target = {target}")
|
||||
waf_conf.setenv(get_test_name(unity_test_path) + '_' + target)
|
||||
waf_conf.load('xwaf.compiler_xcc')
|
||||
waf_conf.env.XCC_FLAGS = unity_runner_build_flags
|
||||
waf_conf.env.PROJECT_ROOT = project_root_path
|
||||
# TODO: can the xwaf boilerplate help here?
|
||||
|
||||
|
||||
def prepare_unity_test_for_build(waf_conf, project_root_path, unity_test_path,
|
||||
unity_runner_dir, unity_runner_suffix, target):
|
||||
print("unity_test_path: " + str(unity_test_path))
|
||||
generate_unity_runner(project_root_path, unity_test_path,
|
||||
unity_runner_dir, unity_runner_suffix)
|
||||
runner_build_flags = '' # Could extract flags from the test name
|
||||
add_unity_runner_build_config(waf_conf, project_root_path, unity_test_path,
|
||||
runner_build_flags, target)
|
||||
|
||||
|
||||
def find_unity_test_paths(unity_test_dir, unity_test_prefix):
|
||||
"""
|
||||
Return a list of all file paths with the unity_test_prefix found in the
|
||||
unity_test_dir.
|
||||
"""
|
||||
file_list = []
|
||||
for root, dirs, files in os.walk(unity_test_dir):
|
||||
for f in files:
|
||||
if f.startswith(unity_test_prefix):
|
||||
file_list.append(os.path.join(root,f))
|
||||
return file_list
|
||||
|
||||
|
||||
def find_unity_tests(unity_test_dir, unity_test_prefix):
|
||||
"""
|
||||
Return a dictionary of all {test names, test language} pairs with the
|
||||
unity_test_prefix found in the unity_test_dir.
|
||||
"""
|
||||
unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix)
|
||||
return {get_test_name(path): {"language": get_file_type(path), "dir": os.path.dirname(path)}
|
||||
for path in unity_test_paths}
|
||||
|
||||
|
||||
def generate_all_unity_runners(waf_conf, project_root_path,
|
||||
unity_test_dir, unity_test_prefix,
|
||||
unity_runner_dir, unity_runner_suffix):
|
||||
"""
|
||||
Generate a runner and a build config for each test file in the
|
||||
unity_test_dir.
|
||||
"""
|
||||
# FIXME: pass unity_tests in?
|
||||
unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix)
|
||||
for trgt in TARGETS:
|
||||
for unity_test_path in unity_test_paths:
|
||||
prepare_unity_test_for_build(waf_conf, project_root_path,
|
||||
unity_test_path,
|
||||
unity_runner_dir, unity_runner_suffix, trgt)
|
||||
|
||||
|
||||
# TODO: can the xwaf boilerplate help here?
|
||||
def create_waf_contexts(configs):
|
||||
for trgt in TARGETS:
|
||||
for test_name, params in configs.items():
|
||||
print(f"test_name {test_name}, test_language {params['language']}")
|
||||
for ctx in (BuildContext, CleanContext):
|
||||
raw_context = ctx.__name__.replace('Context', '').lower()
|
||||
|
||||
class tmp(ctx):
|
||||
cmd = raw_context + '_' + test_name + '_' + trgt
|
||||
variant = test_name + '_' + trgt
|
||||
#cmd = raw_context + '_' + test_name
|
||||
#variant = test_name
|
||||
language = params["language"]
|
||||
target = trgt
|
||||
runner = test_name
|
||||
directory = params["dir"]
|
||||
print(f"cmd {cmd}, variant {variant}, language {language}")
|
||||
|
||||
|
||||
UNITY_TEST_DIR = 'src'
|
||||
UNITY_TEST_PREFIX = 'test_'
|
||||
UNITY_RUNNER_DIR = 'runners'
|
||||
UNITY_RUNNER_SUFFIX = '_Runner'
|
||||
UNITY_TESTS = find_unity_tests(UNITY_TEST_DIR, UNITY_TEST_PREFIX)
|
||||
|
||||
print("UNITY_TESTS: " + str(UNITY_TESTS))
|
||||
|
||||
create_waf_contexts(UNITY_TESTS)
|
||||
|
||||
def options(opt):
|
||||
opt.add_option('--target', action='store', default='xcore200')
|
||||
opt.load('xwaf.xcommon')
|
||||
|
||||
def configure(conf):
|
||||
# TODO: move the call to generate_all_unity_runners() to build()
|
||||
project_root = os.path.join('..', '..', '..')
|
||||
generate_all_unity_runners(conf, project_root,
|
||||
UNITY_TEST_DIR, UNITY_TEST_PREFIX,
|
||||
UNITY_RUNNER_DIR, UNITY_RUNNER_SUFFIX)
|
||||
conf.load('xwaf.xcommon')
|
||||
|
||||
def build(bld):
|
||||
if not bld.variant:
|
||||
print('Adding test runners to build queue')
|
||||
trgt = [
|
||||
c for c in TARGETS if c == bld.options.target
|
||||
]
|
||||
|
||||
if len(trgt) == 0:
|
||||
bld.fatal('specify a target with --target.\nAvailable targets: {}'.format(', '.join(TARGETS)))
|
||||
return
|
||||
|
||||
for name in UNITY_TESTS:
|
||||
Options.commands.insert(0, 'build_' + name + '_' + trgt[0])
|
||||
#Options.commands.insert(0, 'build_' + name)
|
||||
print('Build queue {}'.format(Options.commands))
|
||||
else:
|
||||
print('Building runner {}'.format(bld.runner))
|
||||
bld.env.XSCOPE = bld.path.find_resource('config.xscope')
|
||||
|
||||
depends_on = ['lib_xua',
|
||||
'lib_xud',
|
||||
'lib_spdif',
|
||||
'lib_mic_array',
|
||||
'lib_logging',
|
||||
'lib_xassert',
|
||||
'Unity']
|
||||
|
||||
makefile_opts = {}
|
||||
makefile_opts['SOURCE_DIRS'] = ['src', bld.directory, os.path.join('runners',bld.runner)]
|
||||
if(bld.target == 'xcoreai'):
|
||||
print('TARGET XCOREAI')
|
||||
makefile_opts['TARGET'] = ['XCORE-AI-EXPLORER']
|
||||
else:
|
||||
print('TARGET XCORE200')
|
||||
makefile_opts['TARGET'] = ['XCORE-200-EXPLORER']
|
||||
|
||||
makefile_opts['INCLUDE_DIRS'] = ['src',
|
||||
bld.directory,
|
||||
'../../lib_xua/api',
|
||||
'../../lib_xua/src/core/pdm_mics',
|
||||
'../../lib_xua/src/hid',
|
||||
'../../../lib_xud/lib_xud/src/user/class']
|
||||
|
||||
makefile_opts['XCC_FLAGS'] = ['-O2',
|
||||
'-g',
|
||||
'-Wall',
|
||||
'-DHID_CONTROLS=1',
|
||||
'-DUNITY_SUPPORT_64',
|
||||
'-DUNITY_INCLUDE_DOUBLE',
|
||||
'-DXUD_CORE_CLOCK=600',
|
||||
'-DXUD_SERIES_SUPPORT=4']
|
||||
|
||||
makefile_opts['APP_NAME'] = [bld.variant]
|
||||
makefile_opts['USED_MODULES'] = depends_on
|
||||
makefile_opts['XCOMMON_MAKEFILE'] = ['Makefile.common']
|
||||
bld.do_xcommon(makefile_opts)
|
||||
|
||||
|
||||
def test(bld):
|
||||
# Call pytest to run Unity tests inside axe or xsim
|
||||
try:
|
||||
test_output = subprocess.check_output(['pytest'])
|
||||
except subprocess.CalledProcessError as e:
|
||||
# pytest exits non-zero if an assertion fails
|
||||
test_output = e.output
|
||||
print(test_output)
|
||||
|
||||
|
||||
# TODO: ensure clean deletes the runners dir/
|
||||
def dist(ctx):
|
||||
ctx.load('xwaf.xcommon')
|
||||
|
||||
def distcheck(ctx):
|
||||
ctx.load('xwaf.xcommon')
|
||||
Reference in New Issue
Block a user