From b24c1163275ec6638ae0f2d8370aec5408f2059c Mon Sep 17 00:00:00 2001 From: uvvpavel Date: Fri, 12 Sep 2025 14:55:05 +0100 Subject: [PATCH 01/13] adding vsetc to both threads --- lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S | 3 ++- lib_sw_dac/src/standard_fidelity/software_dac_sf.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S b/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S index 56afe74..c93d5cd 100644 --- a/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S +++ b/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S @@ -27,7 +27,8 @@ .linkset FUNCTION_NAME.nstackwords, 0 .align 16 FUNCTION_NAME: - { in r11, res[r0] ; nop } + { ldc r11, 0 ; nop } + { in r11, res[r0] ; vsetc r11 } .Louter_loop: { set dp, r11 ; nop } { ldw r9, dp[SDAC_BUF_N] ; nop } diff --git a/lib_sw_dac/src/standard_fidelity/software_dac_sf.c b/lib_sw_dac/src/standard_fidelity/software_dac_sf.c index d5c15bd..c09d17c 100644 --- a/lib_sw_dac/src/standard_fidelity/software_dac_sf.c +++ b/lib_sw_dac/src/standard_fidelity/software_dac_sf.c @@ -298,6 +298,8 @@ int filter_x125_16(sw_dac_sf_t *sd, int32_t *output, int ch, int32_t sample) { } void filter_task(sw_dac_sf_t *sd, chanend_t c_in, chanend_t c_out) { + // setup the vpu for the filters and predistortion + asm("ldc r11, 0\n vsetc r11"); int sample_rate = 48000; int32_t data[4][SDAC_BUF_TOTAL]; memset(data, 0, sizeof(data)); From 2b9003b8a0052891bc89d96dfc0d435fa07da72b Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 17 Sep 2025 12:18:16 +0100 Subject: [PATCH 02/13] Initial select in SD - working for first --- lib_sw_dac/src/standard_fidelity/sdac_sf.h | 12 + .../src/standard_fidelity/software_dac_sf.h | 5 + tests/test_stuff/CMakeLists.txt | 24 ++ tests/test_stuff/run_sim.sh | 3 + tests/test_stuff/src/config.xscope | 22 ++ tests/test_stuff/src/main.c | 235 +++++++++++++++++ tests/test_stuff/src/run_sf.S | 72 ++++++ tests/test_stuff/src/sigma_delta_1_5_test.S | 243 ++++++++++++++++++ tests/test_stuff/src/sw_dac_conf.h | 2 + 9 files changed, 618 insertions(+) create mode 100644 tests/test_stuff/CMakeLists.txt create mode 100644 tests/test_stuff/run_sim.sh create mode 100644 tests/test_stuff/src/config.xscope create mode 100644 tests/test_stuff/src/main.c create mode 100644 tests/test_stuff/src/run_sf.S create mode 100644 tests/test_stuff/src/sigma_delta_1_5_test.S create mode 100644 tests/test_stuff/src/sw_dac_conf.h diff --git a/lib_sw_dac/src/standard_fidelity/sdac_sf.h b/lib_sw_dac/src/standard_fidelity/sdac_sf.h index 2ad2dc2..dd685d1 100644 --- a/lib_sw_dac/src/standard_fidelity/sdac_sf.h +++ b/lib_sw_dac/src/standard_fidelity/sdac_sf.h @@ -16,6 +16,11 @@ #define SDAC_OUT_PORTS_L 5 // Two ports #define SDAC_OUT_PORTS_R 6 +#define SDAC_TIMEOUT_TRIG 7 // Used for safe timeout in SD/PWM +#define SDAC_TIMEOUT_PERIOD 8 // Used for safe timeout in SD/PWM +#define SDAC_TIMEOUT_WORD 9 // Used for safe timeout in SD/PWM +#define SDAC_TIMEOUT_RESID 10 // Used for safe timeout in SD/PWM +#define SDAC_TIMEOUT_OCCURED 11 // Used for safe timeout in SD/PWM // These are offsets to the array exchanged between // the filter thread the sigma-delta thread @@ -27,3 +32,10 @@ #define SDAC_BUF_R (SDAC_BUF_L + SDAC_FILTER_MAX) #define SDAC_BUF_STEP 1 #define SDAC_BUF_TOTAL (SDAC_BUF_R + SDAC_FILTER_MAX) + +// These are stack offsets for storing information for the select in SD +// DO NOT MODIFY! +#define SDAC_STACK_SD_STRUCT_BASE 8 +#define SDAC_STACK_TIMEOUT_PERIOD 9 +#define SDAC_STACK_TIMEOUT_TRIG 10 +#define SDAC_STACK_TIMEOUT_RESID 11 \ No newline at end of file diff --git a/lib_sw_dac/src/standard_fidelity/software_dac_sf.h b/lib_sw_dac/src/standard_fidelity/software_dac_sf.h index d3847be..4148870 100644 --- a/lib_sw_dac/src/standard_fidelity/software_dac_sf.h +++ b/lib_sw_dac/src/standard_fidelity/software_dac_sf.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #ifndef __software_dac_sf_h__ @@ -31,6 +32,10 @@ typedef struct { xclock_t clock_block; int *sd_coeffs; port_t out_ports[CHANNELS]; + int timeout_period; + int timeout_word; + hwtimer_t timeout_resid; + int timeout_occurred; // Upsampling filters int32_t *filter0[CHANNELS]; diff --git a/tests/test_stuff/CMakeLists.txt b/tests/test_stuff/CMakeLists.txt new file mode 100644 index 0000000..a1c7830 --- /dev/null +++ b/tests/test_stuff/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.21) +include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake) + +project(test_sigma_delta) + +set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..) + +set(APP_HW_TARGET XK-EVK-XU316) + +set(APP_DEPENDENT_MODULES "lib_sw_dac" + ) + +set(APP_PCA_ENABLE OFF) # TODO why does PCA cause build error + +set(APP_COMPILER_FLAGS -O3 + -mno-dual-issue + -g + -report + -fcmdline-buffer-bytes=1024) + +set(APP_INCLUDES src) + + +XMOS_REGISTER_APP() \ No newline at end of file diff --git a/tests/test_stuff/run_sim.sh b/tests/test_stuff/run_sim.sh new file mode 100644 index 0000000..1d01b9a --- /dev/null +++ b/tests/test_stuff/run_sim.sh @@ -0,0 +1,3 @@ +xsim --xscope "-offline xscope.xmt" --vcd-tracing "-o trace.vcd -tile tile[0] -ports -clock-blocks -instructions -cores" --trace-to trace.txt --args bin/test_sigma_delta.xe 0 100 +xobjdump -S bin/test_sigma_delta.xe > disass.txt + diff --git a/tests/test_stuff/src/config.xscope b/tests/test_stuff/src/config.xscope new file mode 100644 index 0000000..b7f63d6 --- /dev/null +++ b/tests/test_stuff/src/config.xscope @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/test_stuff/src/main.c b/tests/test_stuff/src/main.c new file mode 100644 index 0000000..5bb00e9 --- /dev/null +++ b/tests/test_stuff/src/main.c @@ -0,0 +1,235 @@ +// Copyright 2025 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sw_dac.h" +#include "sdac_sf.h" + +#include "sigma_delta_modulators.h" + +#include "../../common/sin1500.h" +#include + +const int n_sd_loops = 5; + + +DECLARE_JOB(test_app, (sw_dac_sf_t *, chanend_t, int, int)); +void test_app(sw_dac_sf_t *sd, chanend_t c_sd_in, int burn, int n_loops) { + xscope_mode_lossless(); + + if(burn){ + local_thread_mode_set_bits(thread_mode_fast); // Always issue + } + + const int num_buffs_in_sd = 4; + + int32_t data[num_buffs_in_sd][SDAC_BUF_TOTAL] = {{0}}; + int sample_idx = 0; + + for(int loop_count = 0; loop_count < n_loops; loop_count++){ + // printf("loop: %d\n", loop_count); + // xscope_int(0, loop_count); + + int idx = loop_count % num_buffs_in_sd; + + // Send to SD modulator/PWM + // First setup array to transfer to SD + data[idx][SDAC_BUF_N] = n_sd_loops; + for(int i = 0; i < n_sd_loops; i++){ + int32_t sample = sin1500[sample_idx % 1500] >> 2; // More amplitude than this and it clips/ overflows + data[idx][SDAC_BUF_L + i] = sample; + data[idx][SDAC_BUF_R + i] = -sample; + sample_idx++; + } + + chanend_out_word(c_sd_in, (int) &data[idx][0]); + + } + + printf("Completed test app, timed out: %d\n", sd->timeout_occurred); + _Exit(0); +} + +DECLARE_JOB(burn, (void)); +void burn(void){ + while(1); +} + +const int nominal_delay = 100; + +DECLARE_JOB(select_test, (chanend_t, hwtimer_t)); +void select_test(chanend_t c, hwtimer_t tmr) +{ + int time_trig = hwtimer_get_time(tmr); + hwtimer_set_trigger_time(tmr, time_trig + nominal_delay); + int last_chan = 0; + + port_t p_out = XS1_PORT_16A; + port_enable(p_out); + int port_val = 0x55555555; + + // Normal loop - receive word over chan but timeout if takes too long + SELECT_RES( + CASE_THEN(c, event_c), + CASE_THEN(tmr, event_tmr)) + { + // Normal loop + event_c: + { + last_chan = chanend_in_word(c); + time_trig = hwtimer_get_time(tmr); + time_trig += nominal_delay; + hwtimer_change_trigger_time(tmr, time_trig); + port_out(p_out, last_chan); + SELECT_CONTINUE_NO_RESET; + } + + // Timeout + event_tmr: + { +#if 1 // Single select - simpler + // Send idle word on port immediately + port_out(p_out, port_val); + port_val = ~port_val; // There is a bit of a gap if always idle so ensure we have no DC on output + time_trig = hwtimer_get_time(tmr); + time_trig += nominal_delay; + hwtimer_change_trigger_time(tmr, time_trig); + SELECT_CONTINUE_NO_RESET; +#else + // New select - send idle word until we get new value over chan + // Send idle word on port immediately + port_out(p_out, 0x55555555); + // New select - send idle word until we get new value over chan + SELECT_RES( + CASE_THEN(c, event_restart_c), + DEFAULT_THEN(default_idle)) + { + // Send idle on port + default_idle: + port_out(p_out, 0x55555555); + continue; + } + + { + // We have a new word over chan + event_restart_c: + // Do not input value, save for outer select when we break to that + printintln(last_chan); + time_trig = hwtimer_get_time(tmr); + time_trig += nominal_delay; + hwtimer_change_trigger_time(tmr, time_trig); + SELECT_CONTINUE_RESET; + break; + } +#endif + } // event_tmr + } +} + +DECLARE_JOB(test_select, (sw_dac_sf_t *, chanend_t)); +void test_select(sw_dac_sf_t *sd, chanend_t c){ + hwtimer_t tmr = hwtimer_alloc(); + int time_trig = hwtimer_get_time(tmr); + // Increase delay until we hit threshold + for(int i = nominal_delay - 50; i < nominal_delay + 100; i++){ + chanend_out_word(c, i); + time_trig += i; + hwtimer_wait_until(tmr, time_trig); + } + // Big delay + time_trig += nominal_delay * 20; + hwtimer_wait_until(tmr, time_trig); + // Run normally + for(int i = 0; i < 50; i++){ + chanend_out_word(c, i); + time_trig += i; + hwtimer_wait_until(tmr, time_trig); + } + _Exit(0); +} + +extern void sigma_delta_1_5_test(sw_dac_sf_t *sd, chanend_t ce); // does not return + +DECLARE_JOB(run_sd_pwm, (sw_dac_sf_t *, chanend_t)); +void run_sd_pwm(sw_dac_sf_t *sd, chanend_t c_in) { + hwtimer_t tmr = hwtimer_alloc(); + sd->timeout_period = 80 * n_sd_loops; // at 1.5MHz this should be 66.666 so set above + sd->timeout_word = 0x0ff0; + sd->timeout_resid = tmr; + sd->timeout_occurred = 0; + hwtimer_set_trigger_time(sd->timeout_resid, hwtimer_get_time(tmr) + sd->timeout_period); + + sigma_delta_1_5_test(sd, c_in); +} + + +#if 1 +int main(int argc, char *argv[]) { + if(argc != 3){ + printf("Error - need to pass burn and loops as args\n"); + _Exit(-1); + } + int burn = atoi(argv[1]); + int n_loops = atoi(argv[2]); + + printf("Started test app, loops: %d, burn: %d\n", n_loops, burn); + + + channel_t c_sd_ip = chan_alloc(); + port_t p_left = XS1_PORT_1A; + port_t p_right = XS1_PORT_1B; + + xclock_t clk = XS1_CLKBLK_1; + port_t ports[2] = {p_left, p_right}; // L and R outputs + + // Setup clock block to run from MCLK in which is set to 24MHz by the App PLL + clock_enable(clk); + clock_set_source_clk_ref(clk); + clock_set_divide(clk, 4 / 2); // 25MHz + + sw_dac_sf_t sd; + sw_dac_sf_init(&sd, ports, clk, 8, sd_coeffs_o6_f1_5_n8, + 2.8544, 2.8684735298, // scale, limit + 1.0/120000, -1.0/250000, // flat_comp_x2, x3 + 3.0/157, 0.63/157); // pwm comp x2, x3 + + if(burn){ + PAR_JOBS( + PJOB(burn, ()), + PJOB(burn, ()), + PJOB(burn, ()), + PJOB(burn, ()), + PJOB(burn, ()), + PJOB(test_app, (&sd, c_sd_ip.end_a, burn, n_loops)), + PJOB(run_sd_pwm, (&sd, c_sd_ip.end_b)) + ); + } else { + PAR_JOBS( + PJOB(test_app, (&sd, c_sd_ip.end_a, burn, n_loops)), + PJOB(run_sd_pwm, (&sd, c_sd_ip.end_b)) + ); + + } +} +#else +int main(void){ + hwtimer_t tmr = hwtimer_alloc(); + channel_t c = chan_alloc(); + + printf("chan resID: 0x%x, tmr resID: 0x%x\n", c.end_b, tmr); + + PAR_JOBS( + PJOB(select_test, (c.end_b, tmr)), + PJOB(test_select, (c.end_a)) + ); + +} +#endif \ No newline at end of file diff --git a/tests/test_stuff/src/run_sf.S b/tests/test_stuff/src/run_sf.S new file mode 100644 index 0000000..86c6b16 --- /dev/null +++ b/tests/test_stuff/src/run_sf.S @@ -0,0 +1,72 @@ +// Copyright 2024-2025 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include + + .issue_mode dual + +#include "sdac_sf.h" + +#define NSTACKWORDS 18 +#define FUNCTION_NAME sigma_delta_1_5_test + + .cc_top FUNCTION_NAME.function + .type FUNCTION_NAME,@function + + + // sigma_delta_1_5_test(sw_dac_sf_t *sdac, chanend_t *chan) + + // Scratch registers: r2 r3 r11 + + // TODO: use DP rather than r10; frees up r10 + + .globl FUNCTION_NAME + .globl FUNCTION_NAME.nstackwords + .linkset FUNCTION_NAME.nstackwords, NSTACKWORDS + .align 16 +FUNCTION_NAME: + { dualentsp NSTACKWORDS ; nop} + std r4, r5, sp[0] + std r6, r7, sp[1] + std r8, r9, sp[2] + stw r10, sp[6] + stw dp, sp[7] + // Now store select info for the timeout and copy to stack + stw r0, sp[SDAC_STACK_SD_STRUCT_BASE] // Store sd struct addr + ldw r4, r0[SDAC_TIMEOUT_PERIOD]; + stw r4, sp[SDAC_STACK_TIMEOUT_PERIOD] + ldw r4, r0[SDAC_TIMEOUT_TRIG]; + stw r4, sp[SDAC_STACK_TIMEOUT_TRIG] + ldw r4, r0[SDAC_TIMEOUT_RESID]; + stw r4, sp[SDAC_STACK_TIMEOUT_RESID] + + // Initialise registers for sigma_delta_1_5_test + ldw r10, r0[SDAC_PWM_LOOKUP] + ldw r4, r0[SDAC_SD_COEFFS] + ldw r5, r0[SDAC_SD_STATE_L] + ldw r2, r0[SDAC_SD_STATE_R] + ldc r3, 32 + ldw r7, r0[SDAC_OUT_PORTS_L] + ldw r8, r0[SDAC_OUT_PORTS_R] + + // Fill the ports with data + out res[r7], r7 + out res[r8], r7 + + // Now start the clock block, it is on already and linked to the ports + ldw r11, r0[SDAC_CLOCK_BLOCK] + setc res[r11], XS1_SETC_RUN_STARTR + + // Finally initialise channel register for sigma_delta + add r0, r1, 0 + + bl sigma_delta_1_5_bespoke_abi_test + +.exit2: + ldd r4, r5, sp[0] + ldd r6, r7, sp[1] + ldd r8, r9, sp[2] + ldw r10, sp[6] + ldw dp, sp[7] + retsp NSTACKWORDS + + .cc_bottom FUNCTION_NAME.function diff --git a/tests/test_stuff/src/sigma_delta_1_5_test.S b/tests/test_stuff/src/sigma_delta_1_5_test.S new file mode 100644 index 0000000..cdc5a26 --- /dev/null +++ b/tests/test_stuff/src/sigma_delta_1_5_test.S @@ -0,0 +1,243 @@ +// Copyright 2024-2025 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include "sdac_sf.h" + +#define FUNCTION_NAME sigma_delta_1_5_bespoke_abi_test + +#define FRACTIONAL_BITS 28 // TODO: modified software_dac_sf.c scale2 in sync + + .cc_top FUNCTION_NAME.function + .type FUNCTION_NAME,@function + + .issue_mode dual + // Used in loop r0, dp, r9 + // Not in use r2 + // Scratch registers: r11 r6 + // Channel independent + // PWM table r10 + // 32 r3 + // Set coeffs r4 + // Channel dependent + // L R + // Output port r7 r8 + // State r5 r2 + // Input data r1 r1 + .globl FUNCTION_NAME + .globl FUNCTION_NAME.nstackwords + .linkset FUNCTION_NAME.nstackwords, 0 + .align 16 +FUNCTION_NAME: +// setup select + ldap r11, .sd_timeout; //r11 <- &sd_timeout handler + ldw r6, sp[8]; // Get sd struct base addr + ldw r6, r6[SDAC_TIMEOUT_RESID]; + setv res[r6], r11; //set event vector + eeu res[r6]; // enable + + ldap r11, .input_handler; //r11 <- input_handler + setv res[r0], r11; + eeu res[r0]; // enable + + setsr 0x01; // event enable + +// Main loop + // { in r11, res[r0] ; nop } +.Louter_loop: + waiteu; +.input_handler: + { in r11, res[r0] ; nop } + + { set dp, r11 ; nop } + { ldw r9, dp[SDAC_BUF_N] ; nop } + nop +.Linner_loop: + { ldw r1, dp[SDAC_BUF_L] ; sub r9, r9, 1 } + +// Left channel: + { stw r1, r5[0] ; ldc r11, 31 } // Store input value into state vector + { ld8u r1, r5[r11] ; ldap r11, min_max_table } // Load top byte of output value + { vclrdr ; nop } + { ldw r1, r11[r1] ; ldc r11, FRACTIONAL_BITS} // clip value to [-5..5] and quantise + ashr r6, r1, r11 // Make into a fixed point value and sign extend for PWM + { stw r1, r5[1] ; ldc r1, 8 } // Store feedback + { vldc r5[0] ; add r1, r5, r1 } + { vlmaccr r4[0] ; add r4, r4, r3 } + { vlmaccr r4[0] ; add r4, r4, r3 } + { vlmaccr r4[0] ; add r4, r4, r3 } + { vlmaccr r4[0] ; add r4, r4, r3 } + { vlmaccr r4[0] ; add r4, r4, r3 } + { vlmaccr r4[0] ; ldap r11, zeroes } + { vlsat r11[0] ; shl r11, r3, 2 } + { vstr r1[0] ; sub r4, r4, r11 } + { ldw r1, r10[r6] ; sub r4, r4, r3 } +#if defined(SW_DAC_SD_TEST_MODE) + out res[r7], r1 +#else + outpw res[r7], r1, 16 +#endif + + { ldw r1, dp[SDAC_BUF_R] ; nop } +// Right channel: + { stw r1, r2[0] ; ldc r11, 31 } // Store input value into state vector + { ld8u r1, r2[r11] ; ldap r11, min_max_table } // Load top byte of output value + { vclrdr ; nop } + { ldw r1, r11[r1] ; ldc r11, FRACTIONAL_BITS} // clip value to [-5..5] and quantise + ashr r6, r1, r11 // Make into a fixed point value and sign extend for PWM + { stw r1, r2[1] ; ldc r1, 8 } // Store feedback + { vldc r2[0] ; add r1, r2, r1 } + { vlmaccr r4[0] ; add r4, r4, r3 } + { vlmaccr r4[0] ; add r4, r4, r3 } + { vlmaccr r4[0] ; add r4, r4, r3 } + { vlmaccr r4[0] ; add r4, r4, r3 } + { vlmaccr r4[0] ; add r4, r4, r3 } + { vlmaccr r4[0] ; ldap r11, zeroes } + { vlsat r11[0] ; shl r11, r3, 2 } + { vstr r1[0] ; sub r4, r4, r11 } + { ldw r1, r10[r6] ; sub r4, r4, r3 } +#if defined(SW_DAC_SD_TEST_MODE) + out res[r8], r1 +#else + outpw res[r8], r1, 16 +#endif + + { bt r9, .Linner_loop ; ldaw dp, dp[SDAC_BUF_STEP] } + // { bu .Louter_loop ; in r11, res[r0] } + + + // Move the timeout timer on + ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; + getd r11, res[r6]; + ldw r6, sp[SDAC_STACK_TIMEOUT_PERIOD]; + add r11, r11, r6; + stw r11, sp[SDAC_STACK_TIMEOUT_TRIG]; // write back to struct + ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; + setd res[r6], r11; // Move time trigger forwards + bu .Louter_loop; + + // Select handler + .align 16 +.sd_timeout: + // Get the out dummy word, output it and then invert it to avoid DC output content + ldw r6, sp[8]; // Get sd struct base addr + ldw r11, r6[SDAC_TIMEOUT_WORD]; + outpw res[r7], r11, 16; + outpw res[r8], r11, 16; + not r11, r11; + stw r11, r6[SDAC_TIMEOUT_WORD]; + // Move the timeout timer on + ldw r6, sp[8]; // Get sd struct base addr + ldw r11, r6[SDAC_TIMEOUT_PERIOD]; + ldw r6, r6[SDAC_TIMEOUT_TRIG] + add r11, r11, r6; + ldw r6, sp[8]; // Get sd struct base addr + stw r11, r6[SDAC_TIMEOUT_TRIG]; // write back to struct + ldw r6, sp[8]; // Get sd struct base addr + ldw r6, r6[SDAC_TIMEOUT_RESID]; + setd res[r6], r11; // Move time trigger forwards + // Set timeout occurred flag + ldw r6, sp[8]; // Get sd struct base addr + ldc r11, 0x1; + stw r11, r6[SDAC_TIMEOUT_OCCURED]; + + bu .Louter_loop; + + + .align 4 + +#define F(x) ((x)<<(FRACTIONAL_BITS - 1)) +zeroes: // Must be before min_max_table + .word 0,0,0,0,0,0,0,0 +min_max_table: +#define ADD_HALF +#if FRACTIONAL_BITS == 27 +#if !defined(ADD_HALF) // AH !AH + .word F(-1), F(-1), F(-1), F(-1) // 00 +#endif + .word F( 1), F( 1), F( 1), F( 1) // 0x00 04 + .word F( 1), F( 1), F( 1), F( 1), F( 3), F( 3), F( 3), F( 3) // 0x04 08 + .word F( 3), F( 3), F( 3), F( 3), F( 5), F( 5), F( 5), F( 5) // 0x0c 10 + .word F( 5), F( 5), F( 5), F( 5), F( 7), F( 7), F( 7), F( 7) // 0x14 18 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x1c 20 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x24 28 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x2c 30 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x34 38 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x3c 40 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x44 48 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x4c 50 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x54 58 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x5c 60 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x64 68 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x6c 70 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x74 78 +#if defined(ADD_HALF) + .word F( 7), F( 7), F( 7), F( 7) // 0x7c +#else + .word F(-7), F(-7), F(-7), F(-7) // 80 +#endif + .word F(-7), F(-7), F(-7), F(-7) // 0x80 84 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0x84 88 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0x8c 90 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0x94 98 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0x9c a0 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xa4 a8 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xac b0 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xb4 b8 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xbc c0 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xc4 c8 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xcc d0 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xd4 d8 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xdc e0 + .word F(-7), F(-7), F(-7), F(-7), F(-5), F(-5), F(-5), F(-5) // 0xe4 e8 + .word F(-5), F(-5), F(-5), F(-5), F(-3), F(-3), F(-3), F(-3) // 0xec f0 + .word F(-3), F(-3), F(-3), F(-3), F(-1), F(-1), F(-1), F(-1) // 0xf4 f8 +#if defined(ADD_HALF) + .word F(-1), F(-1), F(-1), F(-1) // 0xfc +#endif + +#elif FRACTIONAL_BITS == 28 + +#if !defined(ADD_HALF) // AH !AH + .word F(-1), F(-1), F(-1), F(-1), F(-1), F(-1), F(-1), F(-1) // 00 +#endif + .word F( 1), F( 1), F( 1), F( 1), F( 1), F( 1), F( 1), F( 1) // 0x00 08 + .word F( 1), F( 1), F( 1), F( 1), F( 1), F( 1), F( 1), F( 1) // 0x08 10 + .word F( 3), F( 3), F( 3), F( 3), F( 3), F( 3), F( 3), F( 3) // 0x10 18 + .word F( 3), F( 3), F( 3), F( 3), F( 3), F( 3), F( 3), F( 3) // 0x18 20 + .word F( 5), F( 5), F( 5), F( 5), F( 5), F( 5), F( 5), F( 5) // 0x20 28 + .word F( 5), F( 5), F( 5), F( 5), F( 5), F( 5), F( 5), F( 5) // 0x28 30 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x30 38 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x38 40 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x40 48 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x48 50 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x50 58 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x58 60 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x60 68 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x68 70 + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x70 78 +#if defined(ADD_HALF) + .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x78 +#else + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 80 +#endif + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0x80 88 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0x88 90 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0x90 98 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0x98 a0 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xa0 a8 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xa8 b0 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xb0 b8 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xb8 c0 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xc0 c8 + .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xc8 d0 + .word F(-5), F(-5), F(-5), F(-5), F(-5), F(-5), F(-5), F(-5) // 0xd0 d8 + .word F(-5), F(-5), F(-5), F(-5), F(-5), F(-5), F(-5), F(-5) // 0xd8 e0 + .word F(-3), F(-3), F(-3), F(-3), F(-3), F(-3), F(-3), F(-3) // 0xe0 e8 + .word F(-3), F(-3), F(-3), F(-3), F(-3), F(-3), F(-3), F(-3) // 0xe8 f0 + .word F(-1), F(-1), F(-1), F(-1), F(-1), F(-1), F(-1), F(-1) // 0xf0 f8 +#if defined(ADD_HALF) + .word F(-1), F(-1), F(-1), F(-1), F(-1), F(-1), F(-1), F(-1) // 0xf8 +#endif +#else +#error "FRACTIONAL_BITS not defined correctly" +#endif + .cc_bottom FUNCTION_NAME.function diff --git a/tests/test_stuff/src/sw_dac_conf.h b/tests/test_stuff/src/sw_dac_conf.h new file mode 100644 index 0000000..04c8f12 --- /dev/null +++ b/tests/test_stuff/src/sw_dac_conf.h @@ -0,0 +1,2 @@ +// Copyright 2025 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. From 376f1b8969f9d9885e3f3fafe47ef47e5382dc2e Mon Sep 17 00:00:00 2001 From: Ed Date: Wed, 17 Sep 2025 17:00:02 +0100 Subject: [PATCH 03/13] Use all sp[] addressing Ensure idle word is output --- lib_sw_dac/src/standard_fidelity/sdac_sf.h | 14 +++--- tests/test_stuff/run_sim.sh | 2 +- tests/test_stuff/sim.gtkw | 43 +++++++++++++++++ tests/test_stuff/src/main.c | 11 +++-- tests/test_stuff/src/run_sf.S | 4 +- tests/test_stuff/src/sigma_delta_1_5_test.S | 51 ++++++++++----------- 6 files changed, 84 insertions(+), 41 deletions(-) create mode 100644 tests/test_stuff/sim.gtkw diff --git a/lib_sw_dac/src/standard_fidelity/sdac_sf.h b/lib_sw_dac/src/standard_fidelity/sdac_sf.h index dd685d1..55cf96a 100644 --- a/lib_sw_dac/src/standard_fidelity/sdac_sf.h +++ b/lib_sw_dac/src/standard_fidelity/sdac_sf.h @@ -16,16 +16,14 @@ #define SDAC_OUT_PORTS_L 5 // Two ports #define SDAC_OUT_PORTS_R 6 -#define SDAC_TIMEOUT_TRIG 7 // Used for safe timeout in SD/PWM -#define SDAC_TIMEOUT_PERIOD 8 // Used for safe timeout in SD/PWM -#define SDAC_TIMEOUT_WORD 9 // Used for safe timeout in SD/PWM -#define SDAC_TIMEOUT_RESID 10 // Used for safe timeout in SD/PWM -#define SDAC_TIMEOUT_OCCURED 11 // Used for safe timeout in SD/PWM +#define SDAC_TIMEOUT_PERIOD 7 // Used for safe timeout in SD/PWM +#define SDAC_TIMEOUT_WORD 8 // Used for safe timeout in SD/PWM +#define SDAC_TIMEOUT_RESID 9 // Used for safe timeout in SD/PWM +#define SDAC_TIMEOUT_OCCURED 10 // Used for safe timeout in SD/PWM // These are offsets to the array exchanged between // the filter thread the sigma-delta thread // Do not modify! - #define SDAC_FILTER_MAX 42 #define SDAC_BUF_N 0 #define SDAC_BUF_L 1 @@ -37,5 +35,5 @@ // DO NOT MODIFY! #define SDAC_STACK_SD_STRUCT_BASE 8 #define SDAC_STACK_TIMEOUT_PERIOD 9 -#define SDAC_STACK_TIMEOUT_TRIG 10 -#define SDAC_STACK_TIMEOUT_RESID 11 \ No newline at end of file +#define SDAC_STACK_TIMEOUT_WORD 10 +#define SDAC_STACK_TIMEOUT_RESID 11 diff --git a/tests/test_stuff/run_sim.sh b/tests/test_stuff/run_sim.sh index 1d01b9a..5d1a9e6 100644 --- a/tests/test_stuff/run_sim.sh +++ b/tests/test_stuff/run_sim.sh @@ -1,3 +1,3 @@ -xsim --xscope "-offline xscope.xmt" --vcd-tracing "-o trace.vcd -tile tile[0] -ports -clock-blocks -instructions -cores" --trace-to trace.txt --args bin/test_sigma_delta.xe 0 100 +xsim --xscope "-offline xscope.xmt" --vcd-tracing "-o trace.vcd -tile tile[0] -ports -ports-detailed -clock-blocks -instructions -cores -timers -cycles" --trace-to trace.txt --args bin/test_sigma_delta.xe 0 100 xobjdump -S bin/test_sigma_delta.xe > disass.txt diff --git a/tests/test_stuff/sim.gtkw b/tests/test_stuff/sim.gtkw new file mode 100644 index 0000000..79ce02c --- /dev/null +++ b/tests/test_stuff/sim.gtkw @@ -0,0 +1,43 @@ +[*] +[*] GTKWave Analyzer v3.3.116 (w)1999-2023 BSI +[*] Wed Sep 17 16:53:25 2025 +[*] +[dumpfile] "/home/ed/sandboxes/sw_dac/lib_sw_dac/tests/test_stuff/trace.vcd" +[dumpfile_mtime] "Wed Sep 17 16:51:52 2025" +[dumpfile_size] 16117009 +[savefile] "/home/ed/sandboxes/sw_dac/lib_sw_dac/tests/test_stuff/sim.gtkw" +[timestart] 80990000 +[size] 1580 1162 +[pos] -1 -1 +*-23.490168 109320000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[sst_width] 463 +[signals_width] 342 +[sst_expanded] 1 +[sst_vpaned_height] 353 +@28 +tile[0]_XS1_PORT_1A +tile[0]_XS1_PORT_1B +@22 +tile[0]_ClkBlk1_fallEdge +tile[0]_Core0_waitingResourceId[31:0] +@820 +tile[0]_Core0_waitingResourceName[63:0] +@22 +tile[0]_Core1_waiting +tile[0]_Core1_waitingResourceId[31:0] +@820 +tile[0]_Core1_waitingResourceName[63:0] +@22 +tile[0]_INSTRUCTION_PC[31:0] +tile[0]_INSTRUCTION_THREAD_ID[7:0] +@820 +tile[0]_INSTRUCTION_MNEMONIC[63:0] +@22 +tile[0]_Core0_waiting +tile[0]_Timer0_time[63:0] +@24 +tile[0]_CYCLES[31:0] +@23 +tile[0]_XS1_PORT_1A_transferReg[31:0] +[pattern_trace] 1 +[pattern_trace] 0 diff --git a/tests/test_stuff/src/main.c b/tests/test_stuff/src/main.c index 5bb00e9..acbba54 100644 --- a/tests/test_stuff/src/main.c +++ b/tests/test_stuff/src/main.c @@ -18,7 +18,7 @@ #include "../../common/sin1500.h" #include -const int n_sd_loops = 5; +const int n_sd_loops = 2; DECLARE_JOB(test_app, (sw_dac_sf_t *, chanend_t, int, int)); @@ -34,6 +34,8 @@ void test_app(sw_dac_sf_t *sd, chanend_t c_sd_in, int burn, int n_loops) { int32_t data[num_buffs_in_sd][SDAC_BUF_TOTAL] = {{0}}; int sample_idx = 0; + hwtimer_t tmr = hwtimer_alloc(); + for(int loop_count = 0; loop_count < n_loops; loop_count++){ // printf("loop: %d\n", loop_count); // xscope_int(0, loop_count); @@ -48,6 +50,9 @@ void test_app(sw_dac_sf_t *sd, chanend_t c_sd_in, int burn, int n_loops) { data[idx][SDAC_BUF_L + i] = sample; data[idx][SDAC_BUF_R + i] = -sample; sample_idx++; + if(sample_idx == 50){ + hwtimer_delay(tmr, 3000); + } } chanend_out_word(c_sd_in, (int) &data[idx][0]); @@ -161,11 +166,11 @@ extern void sigma_delta_1_5_test(sw_dac_sf_t *sd, chanend_t ce); // does not ret DECLARE_JOB(run_sd_pwm, (sw_dac_sf_t *, chanend_t)); void run_sd_pwm(sw_dac_sf_t *sd, chanend_t c_in) { hwtimer_t tmr = hwtimer_alloc(); - sd->timeout_period = 80 * n_sd_loops; // at 1.5MHz this should be 66.666 so set above + sd->timeout_period = 70; // at 1.5MHz this should be 66.666 so set above sd->timeout_word = 0x0ff0; sd->timeout_resid = tmr; sd->timeout_occurred = 0; - hwtimer_set_trigger_time(sd->timeout_resid, hwtimer_get_time(tmr) + sd->timeout_period); + hwtimer_set_trigger_time(sd->timeout_resid, hwtimer_get_time(tmr) + sd->timeout_period + 500); // Allow some extra cycles for entry sigma_delta_1_5_test(sd, c_in); } diff --git a/tests/test_stuff/src/run_sf.S b/tests/test_stuff/src/run_sf.S index 86c6b16..768c22f 100644 --- a/tests/test_stuff/src/run_sf.S +++ b/tests/test_stuff/src/run_sf.S @@ -34,10 +34,10 @@ FUNCTION_NAME: stw r0, sp[SDAC_STACK_SD_STRUCT_BASE] // Store sd struct addr ldw r4, r0[SDAC_TIMEOUT_PERIOD]; stw r4, sp[SDAC_STACK_TIMEOUT_PERIOD] - ldw r4, r0[SDAC_TIMEOUT_TRIG]; - stw r4, sp[SDAC_STACK_TIMEOUT_TRIG] ldw r4, r0[SDAC_TIMEOUT_RESID]; stw r4, sp[SDAC_STACK_TIMEOUT_RESID] + ldw r4, r0[SDAC_TIMEOUT_WORD]; + stw r4, sp[SDAC_STACK_TIMEOUT_WORD] // Initialise registers for sigma_delta_1_5_test ldw r10, r0[SDAC_PWM_LOOKUP] diff --git a/tests/test_stuff/src/sigma_delta_1_5_test.S b/tests/test_stuff/src/sigma_delta_1_5_test.S index cdc5a26..5591d30 100644 --- a/tests/test_stuff/src/sigma_delta_1_5_test.S +++ b/tests/test_stuff/src/sigma_delta_1_5_test.S @@ -29,8 +29,7 @@ FUNCTION_NAME: // setup select ldap r11, .sd_timeout; //r11 <- &sd_timeout handler - ldw r6, sp[8]; // Get sd struct base addr - ldw r6, r6[SDAC_TIMEOUT_RESID]; + ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; setv res[r6], r11; //set event vector eeu res[r6]; // enable @@ -38,7 +37,7 @@ FUNCTION_NAME: setv res[r0], r11; eeu res[r0]; // enable - setsr 0x01; // event enable + setsr 0x01; // event enable in thread // Main loop // { in r11, res[r0] ; nop } @@ -49,7 +48,17 @@ FUNCTION_NAME: { set dp, r11 ; nop } { ldw r9, dp[SDAC_BUF_N] ; nop } - nop + + // Move the timeout timer on + ldw r11, sp[SDAC_STACK_TIMEOUT_PERIOD]; + mul r11, r11, r9; + ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; + getd r6, res[r6]; + add r11, r11, r6; + ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; + setd res[r6], r11; // Move time trigger forwards + + // nop .Linner_loop: { ldw r1, dp[SDAC_BUF_L] ; sub r9, r9, 1 } @@ -102,41 +111,29 @@ FUNCTION_NAME: { bt r9, .Linner_loop ; ldaw dp, dp[SDAC_BUF_STEP] } // { bu .Louter_loop ; in r11, res[r0] } - - - // Move the timeout timer on - ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; - getd r11, res[r6]; - ldw r6, sp[SDAC_STACK_TIMEOUT_PERIOD]; - add r11, r11, r6; - stw r11, sp[SDAC_STACK_TIMEOUT_TRIG]; // write back to struct - ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; - setd res[r6], r11; // Move time trigger forwards bu .Louter_loop; // Select handler - .align 16 + .align 8 .sd_timeout: // Get the out dummy word, output it and then invert it to avoid DC output content - ldw r6, sp[8]; // Get sd struct base addr - ldw r11, r6[SDAC_TIMEOUT_WORD]; + ldw r11, sp[SDAC_STACK_TIMEOUT_WORD]; outpw res[r7], r11, 16; outpw res[r8], r11, 16; not r11, r11; - stw r11, r6[SDAC_TIMEOUT_WORD]; + stw r11, sp[SDAC_STACK_TIMEOUT_WORD]; + // Move the timeout timer on - ldw r6, sp[8]; // Get sd struct base addr - ldw r11, r6[SDAC_TIMEOUT_PERIOD]; - ldw r6, r6[SDAC_TIMEOUT_TRIG] + ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; + getd r11, res[r6]; + ldw r6, sp[SDAC_STACK_TIMEOUT_PERIOD]; add r11, r11, r6; - ldw r6, sp[8]; // Get sd struct base addr - stw r11, r6[SDAC_TIMEOUT_TRIG]; // write back to struct - ldw r6, sp[8]; // Get sd struct base addr - ldw r6, r6[SDAC_TIMEOUT_RESID]; + ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; setd res[r6], r11; // Move time trigger forwards - // Set timeout occurred flag - ldw r6, sp[8]; // Get sd struct base addr + + // Set timeout occurred flag in struct ldc r11, 0x1; + ldw r6, sp[SDAC_STACK_SD_STRUCT_BASE]; stw r11, r6[SDAC_TIMEOUT_OCCURED]; bu .Louter_loop; From 46f0ef4186a16c16ce500861ff0492205bb81093 Mon Sep 17 00:00:00 2001 From: Ed Date: Thu, 18 Sep 2025 09:27:15 +0100 Subject: [PATCH 04/13] Move select timeout into main SD code, Add timeout to test_sd --- lib_sw_dac/src/standard_fidelity/run_sf.S | 12 +- .../src/standard_fidelity/sigma_delta_1_5.S | 69 ++++- .../src/standard_fidelity/software_dac_sf.c | 19 +- .../src/standard_fidelity/software_dac_sf.h | 5 +- tests/requirements.txt | 1 + .../CMakeLists.txt | 0 .../{test_stuff => test_pwm_idle}/run_sim.sh | 0 tests/{test_stuff => test_pwm_idle}/sim.gtkw | 22 +- .../src/config.xscope | 0 tests/test_pwm_idle/src/main.c | 121 +++++++++ .../src/sw_dac_conf.h | 0 tests/test_sigma_delta.py | 44 ++-- tests/test_sigma_delta/src/main.c | 24 +- tests/test_stuff/src/main.c | 240 ------------------ tests/test_stuff/src/run_sf.S | 72 ------ tests/test_stuff/src/sigma_delta_1_5_test.S | 240 ------------------ 16 files changed, 260 insertions(+), 609 deletions(-) rename tests/{test_stuff => test_pwm_idle}/CMakeLists.txt (100%) rename tests/{test_stuff => test_pwm_idle}/run_sim.sh (100%) rename tests/{test_stuff => test_pwm_idle}/sim.gtkw (82%) rename tests/{test_stuff => test_pwm_idle}/src/config.xscope (100%) create mode 100644 tests/test_pwm_idle/src/main.c rename tests/{test_stuff => test_pwm_idle}/src/sw_dac_conf.h (100%) delete mode 100644 tests/test_stuff/src/main.c delete mode 100644 tests/test_stuff/src/run_sf.S delete mode 100644 tests/test_stuff/src/sigma_delta_1_5_test.S diff --git a/lib_sw_dac/src/standard_fidelity/run_sf.S b/lib_sw_dac/src/standard_fidelity/run_sf.S index d9290f1..441952e 100644 --- a/lib_sw_dac/src/standard_fidelity/run_sf.S +++ b/lib_sw_dac/src/standard_fidelity/run_sf.S @@ -31,8 +31,16 @@ FUNCTION_NAME: stw r10, sp[6] stw dp, sp[7] + // Now store select info for the timeout and copy to stack for easier access + stw r0, sp[SDAC_STACK_SD_STRUCT_BASE] // Store sd struct addr + ldw r4, r0[SDAC_TIMEOUT_PERIOD] + stw r4, sp[SDAC_STACK_TIMEOUT_PERIOD] + ldw r4, r0[SDAC_TIMEOUT_RESID] + stw r4, sp[SDAC_STACK_TIMEOUT_RESID] + ldw r4, r0[SDAC_TIMEOUT_WORD] + stw r4, sp[SDAC_STACK_TIMEOUT_WORD] + // Initialise registers for sigma_delta_1_5 - ldw r10, r0[SDAC_PWM_LOOKUP] ldw r4, r0[SDAC_SD_COEFFS] ldw r5, r0[SDAC_SD_STATE_L] @@ -52,7 +60,7 @@ FUNCTION_NAME: // Finally initialise channel register for sigma_delta add r0, r1, 0 - bl sigma_delta_1_5_bespoke_abi + bl sigma_delta_1_5_bespoke_abi .exit2: ldd r4, r5, sp[0] diff --git a/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S b/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S index c93d5cd..49dceb9 100644 --- a/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S +++ b/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S @@ -11,7 +11,6 @@ .issue_mode dual // Used in loop r0, dp, r9 - // Not in use r2 // Scratch registers: r11 r6 // Channel independent // PWM table r10 @@ -27,12 +26,37 @@ .linkset FUNCTION_NAME.nstackwords, 0 .align 16 FUNCTION_NAME: - { ldc r11, 0 ; nop } - { in r11, res[r0] ; vsetc r11 } +// setup select + ldap r11, .sd_timeout; //r11 <- &sd_timeout handler + ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; + setv res[r6], r11; //set event vector + eeu res[r6]; // enable + + ldap r11, .input_handler; //r11 <- input_handler + setv res[r0], r11; + eeu res[r0]; // enable + + setsr 0x01; // event enable in thread + +// Main loop .Louter_loop: + waiteu; // This does the IN or timeout +.input_handler: + { in r11, res[r0] ; nop } + { set dp, r11 ; nop } - { ldw r9, dp[SDAC_BUF_N] ; nop } - nop + { ldw r9, dp[SDAC_BUF_N] ; nop } // Get number of samples + + // Move the timeout timer on + ldw r11, sp[SDAC_STACK_TIMEOUT_PERIOD]; + mul r11, r11, r9 + ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; + getd r6, res[r6]; + add r11, r11, r6; + ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; + setd res[r6], r11; // Move time trigger forwards + + // nop .Linner_loop: { ldw r1, dp[SDAC_BUF_L] ; sub r9, r9, 1 } @@ -84,7 +108,40 @@ FUNCTION_NAME: #endif { bt r9, .Linner_loop ; ldaw dp, dp[SDAC_BUF_STEP] } - { bu .Louter_loop ; in r11, res[r0] } + // { bu .Louter_loop ; in r11, res[r0] } + bu .Louter_loop; + + // Select handler for timeout. This does not need to speed optimised + .align 8 +.sd_timeout: + // Get the out dummy word, output it and then invert it to avoid DC output content + ldw r11, sp[SDAC_STACK_TIMEOUT_WORD]; +#if defined(SW_DAC_SD_TEST_MODE) + out res[r7], r11 + out res[r8], r11 +#else + outpw res[r7], r11, 16; + outpw res[r8], r11, 16; +#endif + not r11, r11; + stw r11, sp[SDAC_STACK_TIMEOUT_WORD]; + + // Move the timeout timer on + ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; + getd r11, res[r6]; + ldw r6, sp[SDAC_STACK_TIMEOUT_PERIOD]; + add r11, r11, r6; + ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; + setd res[r6], r11; // Move time trigger forwards + + // Set timeout occurred flag in struct + ldc r11, 0x1; + ldw r6, sp[SDAC_STACK_SD_STRUCT_BASE]; + stw r11, r6[SDAC_TIMEOUT_OCCURED]; + + bu .Louter_loop; + + .align 4 #define F(x) ((x)<<(FRACTIONAL_BITS - 1)) diff --git a/lib_sw_dac/src/standard_fidelity/software_dac_sf.c b/lib_sw_dac/src/standard_fidelity/software_dac_sf.c index c09d17c..4afbf90 100644 --- a/lib_sw_dac/src/standard_fidelity/software_dac_sf.c +++ b/lib_sw_dac/src/standard_fidelity/software_dac_sf.c @@ -14,6 +14,8 @@ #include "sw_dac_conf_default.h" #include "pre_distort.h" +// Assembly kernel that runs the sigma-delta. +void sigma_delta_1_5(sw_dac_sf_t *sd, chanend_t ce); // does not return static void init_ports(port_t dac_ports[2], xclock_t clk) { for(int i = 0; i < 2; i++) { @@ -118,8 +120,8 @@ void sw_dac_sf_init(sw_dac_sf_t *sd, } } -DECLARE_JOB(filter_task, (sw_dac_sf_t *, chanend_t, chanend_t)); -DECLARE_JOB(sigma_delta_task, (sw_dac_sf_t *, chanend_t)); +DECLARE_JOB(filter_task, (sw_dac_sf_t *, chanend_t, chanend_t)); +DECLARE_JOB(sigma_delta_task_sf, (sw_dac_sf_t *, chanend_t)); static inline int filter_x125_64_i16_o32_n16_phased(sw_dac_sf_t *sd, int32_t *output, int32_t samples[16]) { switch(sd->bank) { @@ -364,7 +366,14 @@ void filter_task(sw_dac_sf_t *sd, chanend_t c_in, chanend_t c_out) { chanend_out_control_token(c_out, 1); } -void sigma_delta_task(sw_dac_sf_t *sd, chanend_t c_in) { +void sigma_delta_task_sf(sw_dac_sf_t *sd, chanend_t c_in) { + hwtimer_t tmr = hwtimer_alloc(); + sd->timeout_period = 70; // at 1.5MHz this will be 66.666 so set slightly above + sd->timeout_word = 0x0ff0; // 50% duty cycle + sd->timeout_resid = tmr; + sd->timeout_occurred = 0; + hwtimer_set_trigger_time(sd->timeout_resid, hwtimer_get_time(tmr) + sd->timeout_period + 500); // Allow some extra cycles for entry + sigma_delta_1_5(sd, c_in); } @@ -372,7 +381,7 @@ void sw_dac_sf(sw_dac_sf_t *sd, chanend_t c_in) { channel_t c = chan_alloc(); PAR_JOBS( - PJOB(filter_task, (sd, c_in, c.end_a)), - PJOB(sigma_delta_task, (sd, c.end_b)) + PJOB(filter_task, (sd, c_in, c.end_a)), + PJOB(sigma_delta_task_sf, (sd, c.end_b)) ); } diff --git a/lib_sw_dac/src/standard_fidelity/software_dac_sf.h b/lib_sw_dac/src/standard_fidelity/software_dac_sf.h index 4148870..58fba4f 100644 --- a/lib_sw_dac/src/standard_fidelity/software_dac_sf.h +++ b/lib_sw_dac/src/standard_fidelity/software_dac_sf.h @@ -171,9 +171,6 @@ void sw_dac_sf(sw_dac_sf_t *sd, chanend_t ce); #else DECLARE_JOB(sw_dac_sf, (sw_dac_sf_t *, chanend_t)); // Allow calling from PAR_JOB #endif -/** - * Assembly kernel that runs the sigma-delta. - */ -void sigma_delta_1_5(sw_dac_sf_t *sd, chanend_t ce); // does not return + #endif diff --git a/tests/requirements.txt b/tests/requirements.txt index 3171d69..af68553 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -19,6 +19,7 @@ # same modules should appear in the setup.py list as given below. pytest==8.3.3 pytest-xdist==3.6.1 +pytest-timeout==2.4.0 filelock==3.19.1 numpy==2.3.2 scipy==1.16.1 diff --git a/tests/test_stuff/CMakeLists.txt b/tests/test_pwm_idle/CMakeLists.txt similarity index 100% rename from tests/test_stuff/CMakeLists.txt rename to tests/test_pwm_idle/CMakeLists.txt diff --git a/tests/test_stuff/run_sim.sh b/tests/test_pwm_idle/run_sim.sh similarity index 100% rename from tests/test_stuff/run_sim.sh rename to tests/test_pwm_idle/run_sim.sh diff --git a/tests/test_stuff/sim.gtkw b/tests/test_pwm_idle/sim.gtkw similarity index 82% rename from tests/test_stuff/sim.gtkw rename to tests/test_pwm_idle/sim.gtkw index 79ce02c..6efda2c 100644 --- a/tests/test_stuff/sim.gtkw +++ b/tests/test_pwm_idle/sim.gtkw @@ -1,15 +1,15 @@ [*] [*] GTKWave Analyzer v3.3.116 (w)1999-2023 BSI -[*] Wed Sep 17 16:53:25 2025 +[*] Wed Sep 17 16:56:41 2025 [*] [dumpfile] "/home/ed/sandboxes/sw_dac/lib_sw_dac/tests/test_stuff/trace.vcd" -[dumpfile_mtime] "Wed Sep 17 16:51:52 2025" +[dumpfile_mtime] "Wed Sep 17 16:54:52 2025" [dumpfile_size] 16117009 [savefile] "/home/ed/sandboxes/sw_dac/lib_sw_dac/tests/test_stuff/sim.gtkw" -[timestart] 80990000 +[timestart] 64250000 [size] 1580 1162 -[pos] -1 -1 -*-23.490168 109320000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[pos] -292 -36 +*-24.090168 109320000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 [sst_width] 463 [signals_width] 342 [sst_expanded] 1 @@ -19,13 +19,9 @@ tile[0]_XS1_PORT_1A tile[0]_XS1_PORT_1B @22 tile[0]_ClkBlk1_fallEdge -tile[0]_Core0_waitingResourceId[31:0] -@820 -tile[0]_Core0_waitingResourceName[63:0] -@22 tile[0]_Core1_waiting tile[0]_Core1_waitingResourceId[31:0] -@820 +@821 tile[0]_Core1_waitingResourceName[63:0] @22 tile[0]_INSTRUCTION_PC[31:0] @@ -34,10 +30,14 @@ tile[0]_INSTRUCTION_THREAD_ID[7:0] tile[0]_INSTRUCTION_MNEMONIC[63:0] @22 tile[0]_Core0_waiting +@820 +tile[0]_Core0_waitingResourceName[63:0] +@22 +tile[0]_Core0_waitingResourceId[31:0] tile[0]_Timer0_time[63:0] @24 tile[0]_CYCLES[31:0] -@23 +@22 tile[0]_XS1_PORT_1A_transferReg[31:0] [pattern_trace] 1 [pattern_trace] 0 diff --git a/tests/test_stuff/src/config.xscope b/tests/test_pwm_idle/src/config.xscope similarity index 100% rename from tests/test_stuff/src/config.xscope rename to tests/test_pwm_idle/src/config.xscope diff --git a/tests/test_pwm_idle/src/main.c b/tests/test_pwm_idle/src/main.c new file mode 100644 index 0000000..57876ce --- /dev/null +++ b/tests/test_pwm_idle/src/main.c @@ -0,0 +1,121 @@ +// Copyright 2025 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sw_dac.h" +#include "sdac_sf.h" +#include "sigma_delta_modulators.h" + +#include "../../common/sin1500.h" +#include + +DECLARE_JOB(sigma_delta_task_sf, (sw_dac_sf_t *, chanend_t)); + + +const int n_sd_loops = 2; + +DECLARE_JOB(test_app, (sw_dac_sf_t *, chanend_t, int, int)); +void test_app(sw_dac_sf_t *sd, chanend_t c_sd_in, int burn, int n_loops) { + xscope_mode_lossless(); + + if(burn){ + local_thread_mode_set_bits(thread_mode_fast); // Always issue if enabled + } + + const int num_buffs_in_sd = 4; + + int32_t data[num_buffs_in_sd][SDAC_BUF_TOTAL] = {{0}}; + int sample_idx = 0; + + hwtimer_t tmr = hwtimer_alloc(); + + for(int loop_count = 0; loop_count < n_loops; loop_count++){ + // printf("loop: %d\n", loop_count); + // xscope_int(0, loop_count); + + int idx = loop_count % num_buffs_in_sd; + + // Send to SD modulator/PWM + // First setup array to transfer to SD + data[idx][SDAC_BUF_N] = n_sd_loops; + for(int i = 0; i < n_sd_loops; i++){ + int32_t sample = sin1500[sample_idx % 1500] >> 2; // More amplitude than this and it clips/ overflows + data[idx][SDAC_BUF_L + i] = sample; + data[idx][SDAC_BUF_R + i] = -sample; + sample_idx++; + if(sample_idx == 50){ + hwtimer_delay(tmr, 3000); + } + } + + chanend_out_word(c_sd_in, (int) &data[idx][0]); + + } + + printf("Completed test app, timed out: %d\n", sd->timeout_occurred); + _Exit(0); +} + +DECLARE_JOB(burn, (void)); +void burn(void){ + while(1); +} + + + + +int main(int argc, char *argv[]) { + if(argc != 3){ + printf("Error - need to pass burn and loops as args\n"); + _Exit(-1); + } + int burn = atoi(argv[1]); + int n_loops = atoi(argv[2]); + + printf("Started test app, loops: %d, burn: %d\n", n_loops, burn); + + + channel_t c_sd_ip = chan_alloc(); + port_t p_left = XS1_PORT_1A; + port_t p_right = XS1_PORT_1B; + + xclock_t clk = XS1_CLKBLK_1; + port_t ports[2] = {p_left, p_right}; // L and R outputs + + // Setup clock block to run from MCLK in which is set to 24MHz by the App PLL + clock_enable(clk); + clock_set_source_clk_ref(clk); + clock_set_divide(clk, 4 / 2); // 25MHz + + sw_dac_sf_t sd; + sw_dac_sf_init(&sd, ports, clk, 8, sd_coeffs_o6_f1_5_n8, + 2.8544, 2.8684735298, // scale, limit + 1.0/120000, -1.0/250000, // flat_comp_x2, x3 + 3.0/157, 0.63/157); // pwm comp x2, x3 + + if(burn){ + PAR_JOBS( + PJOB(burn, ()), + PJOB(burn, ()), + PJOB(burn, ()), + PJOB(burn, ()), + PJOB(burn, ()), + PJOB(test_app, (&sd, c_sd_ip.end_a, burn, n_loops)), + PJOB(sigma_delta_task_sf, (&sd, c_sd_ip.end_b)) + ); + } else { + PAR_JOBS( + PJOB(test_app, (&sd, c_sd_ip.end_a, burn, n_loops)), + PJOB(sigma_delta_task_sf, (&sd, c_sd_ip.end_b)) + ); + + } +} diff --git a/tests/test_stuff/src/sw_dac_conf.h b/tests/test_pwm_idle/src/sw_dac_conf.h similarity index 100% rename from tests/test_stuff/src/sw_dac_conf.h rename to tests/test_pwm_idle/src/sw_dac_conf.h diff --git a/tests/test_sigma_delta.py b/tests/test_sigma_delta.py index 5297388..24b4bc9 100644 --- a/tests/test_sigma_delta.py +++ b/tests/test_sigma_delta.py @@ -75,6 +75,29 @@ def parse_xscope(filepath, pwm_lookup): return np.array(pairs, dtype=np.int32) +def check_hw_presence(): + run_cmd = f'xrun -l' + stdout = subprocess.check_output(run_cmd, shell = True) + run_output = stdout.decode("utf-8") + + return True if "No Available Devices Found" in run_output else False + +def run_on_sim(binary, xscope_file, burn, num_loops): + run_cmd = f'xsim --xscope "-offline {xscope_file}" --args {binary} {burn} {num_loops}' + print("Running cmd: ", run_cmd) + stdout = subprocess.check_output(run_cmd, shell = True) + + return stdout.decode("utf-8").splitlines() + +def run_on_hw(binary, xscope_file, burn, num_loops): + # Ensure we don't spin up two HW instances at the same time + with FileLock("xrun.lock"): + run_cmd = f'xrun --id 0 --xscope-file {xscope_file} --args {binary} {burn} {num_loops}' + print("Running cmd: ", run_cmd) + stdout = subprocess.check_output(run_cmd, shell = True) + + return stdout.decode("utf-8").splitlines() + """ This test runs just the sigma delta (and PWM) thread. It feeds in a 1.5MHz sampled sinewave of 1kHz and then captures the outputs to the ports over a channel. @@ -87,6 +110,7 @@ def parse_xscope(filepath, pwm_lookup): The test automatically detects if there is an available target or not and uses xsim or xrun. """ @pytest.mark.parametrize("burn", [1, 0]) +@pytest.mark.timeout(60 * 4) def test_sigma_delta(request, burn): test_name = "test_sigma_delta" @@ -99,29 +123,17 @@ def test_sigma_delta(request, burn): shutil.copy2(binary, tmp_binary) # Check to see if we have HW - run_cmd = f'xrun -l' - stdout = subprocess.check_output(run_cmd, shell = True) - run_output = stdout.decode("utf-8") - using_simuator = True if "No Available Devices Found" in run_output else False - + using_simuator = check_hw_presence() print(f"Using simulator: {using_simuator}") + # About 2 mins on xsim and about 30s on xrun num_loops = 10000 if using_simuator else 2000000 xscope_file = Path(f"logs/{test_name}_trace_{burn}_{num_loops}.vcd") if using_simuator: - run_cmd = f'xsim --xscope "-offline {xscope_file}" --args {tmp_binary} {burn} {num_loops}' - print("Running cmd: ", run_cmd) - stdout = subprocess.check_output(run_cmd, shell = True) - run_output = stdout.decode("utf-8").splitlines() + run_output = run_on_sim(tmp_binary, xscope_file, burn, num_loops) else: - # Ensure we don't spin up two HW instances at the same time - with FileLock("xrun.lock"): - run_cmd = f'xrun --id 0 --xscope-file {xscope_file} --args {tmp_binary} {burn} {num_loops}' - print("Running cmd: ", run_cmd) - stdout = subprocess.check_output(run_cmd, shell = True) - run_output = stdout.decode("utf-8").splitlines() - + run_output = run_on_hw(tmp_binary, xscope_file, burn, num_loops) tmp_binary.unlink() # delete diff --git a/tests/test_sigma_delta/src/main.c b/tests/test_sigma_delta/src/main.c index e004157..26db8c9 100644 --- a/tests/test_sigma_delta/src/main.c +++ b/tests/test_sigma_delta/src/main.c @@ -13,6 +13,8 @@ #include "../../common/sin1500.h" #include +DECLARE_JOB(sigma_delta_task_sf, (sw_dac_sf_t *, chanend_t)); + DECLARE_JOB(test_app, (chanend_t, chanend_t, chanend_t, int, int)); void test_app(chanend_t c_sd_in, chanend_t port_l, chanend_t port_r, int burn, int n_loops) { @@ -64,10 +66,6 @@ void burn(void){ while(1); } -DECLARE_JOB(run_sd_pwm, (sw_dac_sf_t *, chanend_t)); -void run_sd_pwm(sw_dac_sf_t *sd, chanend_t c_in) { - sigma_delta_1_5(sd, c_in); -} int main(int argc, char *argv[]) { if(argc != 3){ @@ -99,18 +97,18 @@ int main(int argc, char *argv[]) { if(burn){ PAR_JOBS( - PJOB(burn, ()), - PJOB(burn, ()), - PJOB(burn, ()), - PJOB(burn, ()), - PJOB(burn, ()), - PJOB(test_app, (c_sd_ip.end_a, c_sd_op_0.end_b, c_sd_op_1.end_b, burn, n_loops)), - PJOB(run_sd_pwm, (&sd, c_sd_ip.end_b)) + PJOB(burn, ()), + PJOB(burn, ()), + PJOB(burn, ()), + PJOB(burn, ()), + PJOB(burn, ()), + PJOB(test_app, (c_sd_ip.end_a, c_sd_op_0.end_b, c_sd_op_1.end_b, burn, n_loops)), + PJOB(sigma_delta_task_sf, (&sd, c_sd_ip.end_b)) ); } else { PAR_JOBS( - PJOB(test_app, (c_sd_ip.end_a, c_sd_op_0.end_b, c_sd_op_1.end_b, burn, n_loops)), - PJOB(run_sd_pwm, (&sd, c_sd_ip.end_b)) + PJOB(test_app, (c_sd_ip.end_a, c_sd_op_0.end_b, c_sd_op_1.end_b, burn, n_loops)), + PJOB(sigma_delta_task_sf, (&sd, c_sd_ip.end_b)) ); } diff --git a/tests/test_stuff/src/main.c b/tests/test_stuff/src/main.c deleted file mode 100644 index acbba54..0000000 --- a/tests/test_stuff/src/main.c +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2025 XMOS LIMITED. -// This Software is subject to the terms of the XMOS Public Licence: Version 1. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sw_dac.h" -#include "sdac_sf.h" - -#include "sigma_delta_modulators.h" - -#include "../../common/sin1500.h" -#include - -const int n_sd_loops = 2; - - -DECLARE_JOB(test_app, (sw_dac_sf_t *, chanend_t, int, int)); -void test_app(sw_dac_sf_t *sd, chanend_t c_sd_in, int burn, int n_loops) { - xscope_mode_lossless(); - - if(burn){ - local_thread_mode_set_bits(thread_mode_fast); // Always issue - } - - const int num_buffs_in_sd = 4; - - int32_t data[num_buffs_in_sd][SDAC_BUF_TOTAL] = {{0}}; - int sample_idx = 0; - - hwtimer_t tmr = hwtimer_alloc(); - - for(int loop_count = 0; loop_count < n_loops; loop_count++){ - // printf("loop: %d\n", loop_count); - // xscope_int(0, loop_count); - - int idx = loop_count % num_buffs_in_sd; - - // Send to SD modulator/PWM - // First setup array to transfer to SD - data[idx][SDAC_BUF_N] = n_sd_loops; - for(int i = 0; i < n_sd_loops; i++){ - int32_t sample = sin1500[sample_idx % 1500] >> 2; // More amplitude than this and it clips/ overflows - data[idx][SDAC_BUF_L + i] = sample; - data[idx][SDAC_BUF_R + i] = -sample; - sample_idx++; - if(sample_idx == 50){ - hwtimer_delay(tmr, 3000); - } - } - - chanend_out_word(c_sd_in, (int) &data[idx][0]); - - } - - printf("Completed test app, timed out: %d\n", sd->timeout_occurred); - _Exit(0); -} - -DECLARE_JOB(burn, (void)); -void burn(void){ - while(1); -} - -const int nominal_delay = 100; - -DECLARE_JOB(select_test, (chanend_t, hwtimer_t)); -void select_test(chanend_t c, hwtimer_t tmr) -{ - int time_trig = hwtimer_get_time(tmr); - hwtimer_set_trigger_time(tmr, time_trig + nominal_delay); - int last_chan = 0; - - port_t p_out = XS1_PORT_16A; - port_enable(p_out); - int port_val = 0x55555555; - - // Normal loop - receive word over chan but timeout if takes too long - SELECT_RES( - CASE_THEN(c, event_c), - CASE_THEN(tmr, event_tmr)) - { - // Normal loop - event_c: - { - last_chan = chanend_in_word(c); - time_trig = hwtimer_get_time(tmr); - time_trig += nominal_delay; - hwtimer_change_trigger_time(tmr, time_trig); - port_out(p_out, last_chan); - SELECT_CONTINUE_NO_RESET; - } - - // Timeout - event_tmr: - { -#if 1 // Single select - simpler - // Send idle word on port immediately - port_out(p_out, port_val); - port_val = ~port_val; // There is a bit of a gap if always idle so ensure we have no DC on output - time_trig = hwtimer_get_time(tmr); - time_trig += nominal_delay; - hwtimer_change_trigger_time(tmr, time_trig); - SELECT_CONTINUE_NO_RESET; -#else - // New select - send idle word until we get new value over chan - // Send idle word on port immediately - port_out(p_out, 0x55555555); - // New select - send idle word until we get new value over chan - SELECT_RES( - CASE_THEN(c, event_restart_c), - DEFAULT_THEN(default_idle)) - { - // Send idle on port - default_idle: - port_out(p_out, 0x55555555); - continue; - } - - { - // We have a new word over chan - event_restart_c: - // Do not input value, save for outer select when we break to that - printintln(last_chan); - time_trig = hwtimer_get_time(tmr); - time_trig += nominal_delay; - hwtimer_change_trigger_time(tmr, time_trig); - SELECT_CONTINUE_RESET; - break; - } -#endif - } // event_tmr - } -} - -DECLARE_JOB(test_select, (sw_dac_sf_t *, chanend_t)); -void test_select(sw_dac_sf_t *sd, chanend_t c){ - hwtimer_t tmr = hwtimer_alloc(); - int time_trig = hwtimer_get_time(tmr); - // Increase delay until we hit threshold - for(int i = nominal_delay - 50; i < nominal_delay + 100; i++){ - chanend_out_word(c, i); - time_trig += i; - hwtimer_wait_until(tmr, time_trig); - } - // Big delay - time_trig += nominal_delay * 20; - hwtimer_wait_until(tmr, time_trig); - // Run normally - for(int i = 0; i < 50; i++){ - chanend_out_word(c, i); - time_trig += i; - hwtimer_wait_until(tmr, time_trig); - } - _Exit(0); -} - -extern void sigma_delta_1_5_test(sw_dac_sf_t *sd, chanend_t ce); // does not return - -DECLARE_JOB(run_sd_pwm, (sw_dac_sf_t *, chanend_t)); -void run_sd_pwm(sw_dac_sf_t *sd, chanend_t c_in) { - hwtimer_t tmr = hwtimer_alloc(); - sd->timeout_period = 70; // at 1.5MHz this should be 66.666 so set above - sd->timeout_word = 0x0ff0; - sd->timeout_resid = tmr; - sd->timeout_occurred = 0; - hwtimer_set_trigger_time(sd->timeout_resid, hwtimer_get_time(tmr) + sd->timeout_period + 500); // Allow some extra cycles for entry - - sigma_delta_1_5_test(sd, c_in); -} - - -#if 1 -int main(int argc, char *argv[]) { - if(argc != 3){ - printf("Error - need to pass burn and loops as args\n"); - _Exit(-1); - } - int burn = atoi(argv[1]); - int n_loops = atoi(argv[2]); - - printf("Started test app, loops: %d, burn: %d\n", n_loops, burn); - - - channel_t c_sd_ip = chan_alloc(); - port_t p_left = XS1_PORT_1A; - port_t p_right = XS1_PORT_1B; - - xclock_t clk = XS1_CLKBLK_1; - port_t ports[2] = {p_left, p_right}; // L and R outputs - - // Setup clock block to run from MCLK in which is set to 24MHz by the App PLL - clock_enable(clk); - clock_set_source_clk_ref(clk); - clock_set_divide(clk, 4 / 2); // 25MHz - - sw_dac_sf_t sd; - sw_dac_sf_init(&sd, ports, clk, 8, sd_coeffs_o6_f1_5_n8, - 2.8544, 2.8684735298, // scale, limit - 1.0/120000, -1.0/250000, // flat_comp_x2, x3 - 3.0/157, 0.63/157); // pwm comp x2, x3 - - if(burn){ - PAR_JOBS( - PJOB(burn, ()), - PJOB(burn, ()), - PJOB(burn, ()), - PJOB(burn, ()), - PJOB(burn, ()), - PJOB(test_app, (&sd, c_sd_ip.end_a, burn, n_loops)), - PJOB(run_sd_pwm, (&sd, c_sd_ip.end_b)) - ); - } else { - PAR_JOBS( - PJOB(test_app, (&sd, c_sd_ip.end_a, burn, n_loops)), - PJOB(run_sd_pwm, (&sd, c_sd_ip.end_b)) - ); - - } -} -#else -int main(void){ - hwtimer_t tmr = hwtimer_alloc(); - channel_t c = chan_alloc(); - - printf("chan resID: 0x%x, tmr resID: 0x%x\n", c.end_b, tmr); - - PAR_JOBS( - PJOB(select_test, (c.end_b, tmr)), - PJOB(test_select, (c.end_a)) - ); - -} -#endif \ No newline at end of file diff --git a/tests/test_stuff/src/run_sf.S b/tests/test_stuff/src/run_sf.S deleted file mode 100644 index 768c22f..0000000 --- a/tests/test_stuff/src/run_sf.S +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2024-2025 XMOS LIMITED. -// This Software is subject to the terms of the XMOS Public Licence: Version 1. -#include - - .issue_mode dual - -#include "sdac_sf.h" - -#define NSTACKWORDS 18 -#define FUNCTION_NAME sigma_delta_1_5_test - - .cc_top FUNCTION_NAME.function - .type FUNCTION_NAME,@function - - - // sigma_delta_1_5_test(sw_dac_sf_t *sdac, chanend_t *chan) - - // Scratch registers: r2 r3 r11 - - // TODO: use DP rather than r10; frees up r10 - - .globl FUNCTION_NAME - .globl FUNCTION_NAME.nstackwords - .linkset FUNCTION_NAME.nstackwords, NSTACKWORDS - .align 16 -FUNCTION_NAME: - { dualentsp NSTACKWORDS ; nop} - std r4, r5, sp[0] - std r6, r7, sp[1] - std r8, r9, sp[2] - stw r10, sp[6] - stw dp, sp[7] - // Now store select info for the timeout and copy to stack - stw r0, sp[SDAC_STACK_SD_STRUCT_BASE] // Store sd struct addr - ldw r4, r0[SDAC_TIMEOUT_PERIOD]; - stw r4, sp[SDAC_STACK_TIMEOUT_PERIOD] - ldw r4, r0[SDAC_TIMEOUT_RESID]; - stw r4, sp[SDAC_STACK_TIMEOUT_RESID] - ldw r4, r0[SDAC_TIMEOUT_WORD]; - stw r4, sp[SDAC_STACK_TIMEOUT_WORD] - - // Initialise registers for sigma_delta_1_5_test - ldw r10, r0[SDAC_PWM_LOOKUP] - ldw r4, r0[SDAC_SD_COEFFS] - ldw r5, r0[SDAC_SD_STATE_L] - ldw r2, r0[SDAC_SD_STATE_R] - ldc r3, 32 - ldw r7, r0[SDAC_OUT_PORTS_L] - ldw r8, r0[SDAC_OUT_PORTS_R] - - // Fill the ports with data - out res[r7], r7 - out res[r8], r7 - - // Now start the clock block, it is on already and linked to the ports - ldw r11, r0[SDAC_CLOCK_BLOCK] - setc res[r11], XS1_SETC_RUN_STARTR - - // Finally initialise channel register for sigma_delta - add r0, r1, 0 - - bl sigma_delta_1_5_bespoke_abi_test - -.exit2: - ldd r4, r5, sp[0] - ldd r6, r7, sp[1] - ldd r8, r9, sp[2] - ldw r10, sp[6] - ldw dp, sp[7] - retsp NSTACKWORDS - - .cc_bottom FUNCTION_NAME.function diff --git a/tests/test_stuff/src/sigma_delta_1_5_test.S b/tests/test_stuff/src/sigma_delta_1_5_test.S deleted file mode 100644 index 5591d30..0000000 --- a/tests/test_stuff/src/sigma_delta_1_5_test.S +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2024-2025 XMOS LIMITED. -// This Software is subject to the terms of the XMOS Public Licence: Version 1. -#include "sdac_sf.h" - -#define FUNCTION_NAME sigma_delta_1_5_bespoke_abi_test - -#define FRACTIONAL_BITS 28 // TODO: modified software_dac_sf.c scale2 in sync - - .cc_top FUNCTION_NAME.function - .type FUNCTION_NAME,@function - - .issue_mode dual - // Used in loop r0, dp, r9 - // Not in use r2 - // Scratch registers: r11 r6 - // Channel independent - // PWM table r10 - // 32 r3 - // Set coeffs r4 - // Channel dependent - // L R - // Output port r7 r8 - // State r5 r2 - // Input data r1 r1 - .globl FUNCTION_NAME - .globl FUNCTION_NAME.nstackwords - .linkset FUNCTION_NAME.nstackwords, 0 - .align 16 -FUNCTION_NAME: -// setup select - ldap r11, .sd_timeout; //r11 <- &sd_timeout handler - ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; - setv res[r6], r11; //set event vector - eeu res[r6]; // enable - - ldap r11, .input_handler; //r11 <- input_handler - setv res[r0], r11; - eeu res[r0]; // enable - - setsr 0x01; // event enable in thread - -// Main loop - // { in r11, res[r0] ; nop } -.Louter_loop: - waiteu; -.input_handler: - { in r11, res[r0] ; nop } - - { set dp, r11 ; nop } - { ldw r9, dp[SDAC_BUF_N] ; nop } - - // Move the timeout timer on - ldw r11, sp[SDAC_STACK_TIMEOUT_PERIOD]; - mul r11, r11, r9; - ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; - getd r6, res[r6]; - add r11, r11, r6; - ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; - setd res[r6], r11; // Move time trigger forwards - - // nop -.Linner_loop: - { ldw r1, dp[SDAC_BUF_L] ; sub r9, r9, 1 } - -// Left channel: - { stw r1, r5[0] ; ldc r11, 31 } // Store input value into state vector - { ld8u r1, r5[r11] ; ldap r11, min_max_table } // Load top byte of output value - { vclrdr ; nop } - { ldw r1, r11[r1] ; ldc r11, FRACTIONAL_BITS} // clip value to [-5..5] and quantise - ashr r6, r1, r11 // Make into a fixed point value and sign extend for PWM - { stw r1, r5[1] ; ldc r1, 8 } // Store feedback - { vldc r5[0] ; add r1, r5, r1 } - { vlmaccr r4[0] ; add r4, r4, r3 } - { vlmaccr r4[0] ; add r4, r4, r3 } - { vlmaccr r4[0] ; add r4, r4, r3 } - { vlmaccr r4[0] ; add r4, r4, r3 } - { vlmaccr r4[0] ; add r4, r4, r3 } - { vlmaccr r4[0] ; ldap r11, zeroes } - { vlsat r11[0] ; shl r11, r3, 2 } - { vstr r1[0] ; sub r4, r4, r11 } - { ldw r1, r10[r6] ; sub r4, r4, r3 } -#if defined(SW_DAC_SD_TEST_MODE) - out res[r7], r1 -#else - outpw res[r7], r1, 16 -#endif - - { ldw r1, dp[SDAC_BUF_R] ; nop } -// Right channel: - { stw r1, r2[0] ; ldc r11, 31 } // Store input value into state vector - { ld8u r1, r2[r11] ; ldap r11, min_max_table } // Load top byte of output value - { vclrdr ; nop } - { ldw r1, r11[r1] ; ldc r11, FRACTIONAL_BITS} // clip value to [-5..5] and quantise - ashr r6, r1, r11 // Make into a fixed point value and sign extend for PWM - { stw r1, r2[1] ; ldc r1, 8 } // Store feedback - { vldc r2[0] ; add r1, r2, r1 } - { vlmaccr r4[0] ; add r4, r4, r3 } - { vlmaccr r4[0] ; add r4, r4, r3 } - { vlmaccr r4[0] ; add r4, r4, r3 } - { vlmaccr r4[0] ; add r4, r4, r3 } - { vlmaccr r4[0] ; add r4, r4, r3 } - { vlmaccr r4[0] ; ldap r11, zeroes } - { vlsat r11[0] ; shl r11, r3, 2 } - { vstr r1[0] ; sub r4, r4, r11 } - { ldw r1, r10[r6] ; sub r4, r4, r3 } -#if defined(SW_DAC_SD_TEST_MODE) - out res[r8], r1 -#else - outpw res[r8], r1, 16 -#endif - - { bt r9, .Linner_loop ; ldaw dp, dp[SDAC_BUF_STEP] } - // { bu .Louter_loop ; in r11, res[r0] } - bu .Louter_loop; - - // Select handler - .align 8 -.sd_timeout: - // Get the out dummy word, output it and then invert it to avoid DC output content - ldw r11, sp[SDAC_STACK_TIMEOUT_WORD]; - outpw res[r7], r11, 16; - outpw res[r8], r11, 16; - not r11, r11; - stw r11, sp[SDAC_STACK_TIMEOUT_WORD]; - - // Move the timeout timer on - ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; - getd r11, res[r6]; - ldw r6, sp[SDAC_STACK_TIMEOUT_PERIOD]; - add r11, r11, r6; - ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; - setd res[r6], r11; // Move time trigger forwards - - // Set timeout occurred flag in struct - ldc r11, 0x1; - ldw r6, sp[SDAC_STACK_SD_STRUCT_BASE]; - stw r11, r6[SDAC_TIMEOUT_OCCURED]; - - bu .Louter_loop; - - - .align 4 - -#define F(x) ((x)<<(FRACTIONAL_BITS - 1)) -zeroes: // Must be before min_max_table - .word 0,0,0,0,0,0,0,0 -min_max_table: -#define ADD_HALF -#if FRACTIONAL_BITS == 27 -#if !defined(ADD_HALF) // AH !AH - .word F(-1), F(-1), F(-1), F(-1) // 00 -#endif - .word F( 1), F( 1), F( 1), F( 1) // 0x00 04 - .word F( 1), F( 1), F( 1), F( 1), F( 3), F( 3), F( 3), F( 3) // 0x04 08 - .word F( 3), F( 3), F( 3), F( 3), F( 5), F( 5), F( 5), F( 5) // 0x0c 10 - .word F( 5), F( 5), F( 5), F( 5), F( 7), F( 7), F( 7), F( 7) // 0x14 18 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x1c 20 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x24 28 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x2c 30 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x34 38 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x3c 40 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x44 48 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x4c 50 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x54 58 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x5c 60 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x64 68 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x6c 70 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x74 78 -#if defined(ADD_HALF) - .word F( 7), F( 7), F( 7), F( 7) // 0x7c -#else - .word F(-7), F(-7), F(-7), F(-7) // 80 -#endif - .word F(-7), F(-7), F(-7), F(-7) // 0x80 84 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0x84 88 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0x8c 90 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0x94 98 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0x9c a0 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xa4 a8 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xac b0 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xb4 b8 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xbc c0 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xc4 c8 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xcc d0 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xd4 d8 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xdc e0 - .word F(-7), F(-7), F(-7), F(-7), F(-5), F(-5), F(-5), F(-5) // 0xe4 e8 - .word F(-5), F(-5), F(-5), F(-5), F(-3), F(-3), F(-3), F(-3) // 0xec f0 - .word F(-3), F(-3), F(-3), F(-3), F(-1), F(-1), F(-1), F(-1) // 0xf4 f8 -#if defined(ADD_HALF) - .word F(-1), F(-1), F(-1), F(-1) // 0xfc -#endif - -#elif FRACTIONAL_BITS == 28 - -#if !defined(ADD_HALF) // AH !AH - .word F(-1), F(-1), F(-1), F(-1), F(-1), F(-1), F(-1), F(-1) // 00 -#endif - .word F( 1), F( 1), F( 1), F( 1), F( 1), F( 1), F( 1), F( 1) // 0x00 08 - .word F( 1), F( 1), F( 1), F( 1), F( 1), F( 1), F( 1), F( 1) // 0x08 10 - .word F( 3), F( 3), F( 3), F( 3), F( 3), F( 3), F( 3), F( 3) // 0x10 18 - .word F( 3), F( 3), F( 3), F( 3), F( 3), F( 3), F( 3), F( 3) // 0x18 20 - .word F( 5), F( 5), F( 5), F( 5), F( 5), F( 5), F( 5), F( 5) // 0x20 28 - .word F( 5), F( 5), F( 5), F( 5), F( 5), F( 5), F( 5), F( 5) // 0x28 30 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x30 38 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x38 40 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x40 48 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x48 50 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x50 58 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x58 60 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x60 68 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x68 70 - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x70 78 -#if defined(ADD_HALF) - .word F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7), F( 7) // 0x78 -#else - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 80 -#endif - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0x80 88 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0x88 90 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0x90 98 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0x98 a0 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xa0 a8 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xa8 b0 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xb0 b8 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xb8 c0 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xc0 c8 - .word F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7), F(-7) // 0xc8 d0 - .word F(-5), F(-5), F(-5), F(-5), F(-5), F(-5), F(-5), F(-5) // 0xd0 d8 - .word F(-5), F(-5), F(-5), F(-5), F(-5), F(-5), F(-5), F(-5) // 0xd8 e0 - .word F(-3), F(-3), F(-3), F(-3), F(-3), F(-3), F(-3), F(-3) // 0xe0 e8 - .word F(-3), F(-3), F(-3), F(-3), F(-3), F(-3), F(-3), F(-3) // 0xe8 f0 - .word F(-1), F(-1), F(-1), F(-1), F(-1), F(-1), F(-1), F(-1) // 0xf0 f8 -#if defined(ADD_HALF) - .word F(-1), F(-1), F(-1), F(-1), F(-1), F(-1), F(-1), F(-1) // 0xf8 -#endif -#else -#error "FRACTIONAL_BITS not defined correctly" -#endif - .cc_bottom FUNCTION_NAME.function From 5dfdaaba79e3d9a614c8343c002bf8cdda89927c Mon Sep 17 00:00:00 2001 From: Ed Date: Mon, 22 Sep 2025 11:44:14 +0100 Subject: [PATCH 05/13] Dual issue outer loop setup and timeout timer increment --- .../src/standard_fidelity/sigma_delta_1_5.S | 54 ++++++++----------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S b/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S index 49dceb9..7b67cca 100644 --- a/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S +++ b/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S @@ -1,7 +1,8 @@ // Copyright 2024-2025 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include #include "sdac_sf.h" - + #define FUNCTION_NAME sigma_delta_1_5_bespoke_abi #define FRACTIONAL_BITS 28 // TODO: modified software_dac_sf.c scale2 in sync @@ -26,6 +27,9 @@ .linkset FUNCTION_NAME.nstackwords, 0 .align 16 FUNCTION_NAME: + // Init VPU + ldc r11, 0 + vsetc r11 // setup select ldap r11, .sd_timeout; //r11 <- &sd_timeout handler ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; @@ -38,27 +42,20 @@ FUNCTION_NAME: setsr 0x01; // event enable in thread + waiteu; // This does the initial IN or timeout // Main loop .Louter_loop: - waiteu; // This does the IN or timeout .input_handler: - { in r11, res[r0] ; nop } - - { set dp, r11 ; nop } - { ldw r9, dp[SDAC_BUF_N] ; nop } // Get number of samples + // This section inits the DP, loads nsamp and moves on the timeout timer according to nsamp + // We have r11, r6 and also r1 is available in this stage until the inner loop + { in r11, res[r0] ; ldw r1, sp[SDAC_STACK_TIMEOUT_PERIOD]} // Grab input data pointer from filter + load timeout time period + { ldw r9, r11[SDAC_BUF_N] ; gettime r6} // Get number of samples, Read timer value (we know all timers are same val) + mul r1, r1, r9 // mul timeout time by num samples to receive (inner loop count) + { ldw r6, sp[SDAC_STACK_TIMEOUT_RESID] ; add r1, r1, r6;} // Copy input token (sf ptr) to DP for later use & move on timer time + { set dp, r11 ; setd res[r6], r1} // Move time trigger forwards, and init the DP for later use - // Move the timeout timer on - ldw r11, sp[SDAC_STACK_TIMEOUT_PERIOD]; - mul r11, r11, r9 - ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; - getd r6, res[r6]; - add r11, r11, r6; - ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; - setd res[r6], r11; // Move time trigger forwards - - // nop .Linner_loop: - { ldw r1, dp[SDAC_BUF_L] ; sub r9, r9, 1 } + { ldw r1, dp[SDAC_BUF_L] ; sub r9, r9, 1 } // Left channel: { stw r1, r5[0] ; ldc r11, 31 } // Store input value into state vector @@ -66,7 +63,7 @@ FUNCTION_NAME: { vclrdr ; nop } { ldw r1, r11[r1] ; ldc r11, FRACTIONAL_BITS} // clip value to [-5..5] and quantise ashr r6, r1, r11 // Make into a fixed point value and sign extend for PWM - { stw r1, r5[1] ; ldc r1, 8 } // Store feedback + { stw r1, r5[1] ; ldc r1, 8 } // Store feedback { vldc r5[0] ; add r1, r5, r1 } { vlmaccr r4[0] ; add r4, r4, r3 } { vlmaccr r4[0] ; add r4, r4, r3 } @@ -108,8 +105,7 @@ FUNCTION_NAME: #endif { bt r9, .Linner_loop ; ldaw dp, dp[SDAC_BUF_STEP] } - // { bu .Louter_loop ; in r11, res[r0] } - bu .Louter_loop; + waiteu // Select handler for timeout. This does not need to speed optimised .align 8 @@ -126,20 +122,14 @@ FUNCTION_NAME: not r11, r11; stw r11, sp[SDAC_STACK_TIMEOUT_WORD]; - // Move the timeout timer on - ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; - getd r11, res[r6]; - ldw r6, sp[SDAC_STACK_TIMEOUT_PERIOD]; - add r11, r11, r6; - ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; - setd res[r6], r11; // Move time trigger forwards + // Move the timeout timer on by at least one PWM period. We may come back here if no IN from filter + { gettime r11 ; ldw r6, sp[SDAC_STACK_TIMEOUT_PERIOD]} + { add r11, r11, r6 ; ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]} + { setd res[r6], r11 ; ldc r11, 0x1;} // Move time trigger forwards and load timeout flag // Set timeout occurred flag in struct - ldc r11, 0x1; - ldw r6, sp[SDAC_STACK_SD_STRUCT_BASE]; - stw r11, r6[SDAC_TIMEOUT_OCCURED]; - - bu .Louter_loop; + ldw r6, sp[SDAC_STACK_SD_STRUCT_BASE] ; + {stw r11, r6[SDAC_TIMEOUT_OCCURED] ; waiteu} .align 4 From a287549fa0f4ebc1b362a2160a907c65cc5db9f8 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 23 Sep 2025 09:00:46 +0100 Subject: [PATCH 06/13] Add test for SD idle --- CHANGELOG.rst | 5 + README.rst | 8 +- .../src/standard_fidelity/software_dac_sf.c | 12 +- tests/test_sigma_delta.py | 32 ++-- tests/test_sigma_delta/src/main.c | 75 +++++++--- tests/test_sigma_delta_idle.py | 138 ++++++++++++++++++ 6 files changed, 227 insertions(+), 43 deletions(-) create mode 100644 tests/test_sigma_delta_idle.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b5355b8..4d76e40 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,11 @@ lib_sw_dac change log ===================== +UNRELEASED +---------- + + * FIXED: Now sends PWM zero when not fed with samples + 0.1.0 ----- diff --git a/README.rst b/README.rst index a4ca82c..949c6a0 100644 --- a/README.rst +++ b/README.rst @@ -23,18 +23,14 @@ Features ******** * Supports 48000, 96000 and 192000 Hz sample rate, stereo output -* Uses two hardware threads, two one-bit ports, one clock-block and 32 kB of RAM +* Uses two hardware threads, two one-bit ports, one timer, one clock-block and 32 kB of RAM ************ Known issues ************ * Fixed configuration only currently (SF = Standard Fidelity) -* 192kHz SD requires 85 MHz thread speed (max 7 threads at 600 MHz core clock) -* The API requires that the software DAC is continually fed samples at the input - sample rate. Failure to do so will result in full scale outputs glitches. Please - ensure your application disables the hardware output stage before stopping - feeding samples. +* 192 kHz input requires 85 MHz thread speed (max 7 threads at 600 MHz core clock). 96 kHz and 48 kHz can support 8 used threads. **************** Development repo diff --git a/lib_sw_dac/src/standard_fidelity/software_dac_sf.c b/lib_sw_dac/src/standard_fidelity/software_dac_sf.c index 4afbf90..3d33768 100644 --- a/lib_sw_dac/src/standard_fidelity/software_dac_sf.c +++ b/lib_sw_dac/src/standard_fidelity/software_dac_sf.c @@ -43,6 +43,11 @@ void sw_dac_sf_init(sw_dac_sf_t *sd, #if !defined(SW_DAC_SD_TEST_MODE) init_ports(dac_ports, clk); #endif + sd->timeout_period = 70; // at 1.5MHz this will be 66.666 so set slightly above + sd->timeout_word = 0x0ff0; // 50% duty cycle 16b word + sd->timeout_resid = hwtimer_alloc(); + sd->timeout_occurred = 0; + sd->clock_block = clk; memcpy(&sd->out_ports[0], dac_ports, 2 * sizeof(port_t)); sd->sd_coeffs = &sd_coeffs[0][0]; @@ -367,12 +372,7 @@ void filter_task(sw_dac_sf_t *sd, chanend_t c_in, chanend_t c_out) { } void sigma_delta_task_sf(sw_dac_sf_t *sd, chanend_t c_in) { - hwtimer_t tmr = hwtimer_alloc(); - sd->timeout_period = 70; // at 1.5MHz this will be 66.666 so set slightly above - sd->timeout_word = 0x0ff0; // 50% duty cycle - sd->timeout_resid = tmr; - sd->timeout_occurred = 0; - hwtimer_set_trigger_time(sd->timeout_resid, hwtimer_get_time(tmr) + sd->timeout_period + 500); // Allow some extra cycles for entry + hwtimer_set_trigger_time(sd->timeout_resid, hwtimer_get_time(sd->timeout_resid) + sd->timeout_period + 500); // Allow some extra cycles for entry sigma_delta_1_5(sd, c_in); } diff --git a/tests/test_sigma_delta.py b/tests/test_sigma_delta.py index 24b4bc9..5921b8f 100644 --- a/tests/test_sigma_delta.py +++ b/tests/test_sigma_delta.py @@ -17,6 +17,7 @@ max_pcm = 32767 pwm_rate = 1500000 filter_cutoff = 48000 +pwm_idle_words = [0x0ff0, 0xfffffffffffff00f] def parse_output(stdout): pwm_lookup = {} @@ -27,11 +28,14 @@ def parse_output(stdout): current_loop = None loop_count = 0 max_pwm_magnitude = 0 + timedout = 0 for line in stdout: line = line.strip() if not line or "Started" in line or "Completed" in line: continue + if "Timeout" in line: + timedout = int(line.split(":")[1]) # Parse PWM table m_pwm = pwm_pattern.match(line) @@ -41,8 +45,11 @@ def parse_output(stdout): pwm_lookup[hexval] = idx max_pwm_magnitude = abs(idx) if abs(idx) > max_pwm_magnitude else max_pwm_magnitude + # Add idle words + for pwd_idle_word in pwm_idle_words: + pwm_lookup[pwd_idle_word] = 0 - return pwm_lookup, max_pwm_magnitude + return pwm_lookup, max_pwm_magnitude, timedout def parse_xscope(filepath, pwm_lookup): text = open(filepath, "r").read() @@ -67,6 +74,8 @@ def parse_xscope(filepath, pwm_lookup): if var == left_pwm_idx: left = pwm_lookup[pwm] + # if pwm in pwm_idle_words: + # print(f"Idle at {sample_num}") if var == right_pwm_idx: right = pwm_lookup[pwm] pairs.append((left, right)) @@ -82,17 +91,17 @@ def check_hw_presence(): return True if "No Available Devices Found" in run_output else False -def run_on_sim(binary, xscope_file, burn, num_loops): - run_cmd = f'xsim --xscope "-offline {xscope_file}" --args {binary} {burn} {num_loops}' +def run_on_sim(binary, xscope_file, burn, num_loops, pause_at): + run_cmd = f'xsim --xscope "-offline {xscope_file}" --args {binary} {burn} {num_loops} {pause_at}' print("Running cmd: ", run_cmd) stdout = subprocess.check_output(run_cmd, shell = True) return stdout.decode("utf-8").splitlines() -def run_on_hw(binary, xscope_file, burn, num_loops): +def run_on_hw(binary, xscope_file, burn, num_loops, pause_at): # Ensure we don't spin up two HW instances at the same time with FileLock("xrun.lock"): - run_cmd = f'xrun --id 0 --xscope-file {xscope_file} --args {binary} {burn} {num_loops}' + run_cmd = f'xrun --id 0 --xscope-file {xscope_file} --args {binary} {burn} {num_loops} {pause_at}' print("Running cmd: ", run_cmd) stdout = subprocess.check_output(run_cmd, shell = True) @@ -109,8 +118,8 @@ def run_on_hw(binary, xscope_file, burn, num_loops): the expected THDN values of -90 or so unless we run for a long time on HW and downsample to 48k. The test automatically detects if there is an available target or not and uses xsim or xrun. """ -@pytest.mark.parametrize("burn", [1, 0]) -@pytest.mark.timeout(60 * 4) +@pytest.mark.parametrize("burn", [0, 1]) +@pytest.mark.timeout(60 * 6) def test_sigma_delta(request, burn): test_name = "test_sigma_delta" @@ -131,18 +140,20 @@ def test_sigma_delta(request, burn): xscope_file = Path(f"logs/{test_name}_trace_{burn}_{num_loops}.vcd") if using_simuator: - run_output = run_on_sim(tmp_binary, xscope_file, burn, num_loops) + run_output = run_on_sim(tmp_binary, xscope_file, burn, num_loops, num_loops) #pause_at == num_loops so no pause else: - run_output = run_on_hw(tmp_binary, xscope_file, burn, num_loops) + run_output = run_on_hw(tmp_binary, xscope_file, burn, num_loops, num_loops) #pause_at == num_loops so no pause tmp_binary.unlink() # delete print("Parsing terminal output...") - pwm_lookup, max_pwm_magnitude = parse_output(run_output) + pwm_lookup, max_pwm_magnitude, timedout = parse_output(run_output) print("PWM Table (hex→index):") for k, v in pwm_lookup.items(): print(f" {hex(k)} → {v}") + print(f"SD thread timed out: {timedout}") + print("Parsing XSCOPE output...") pwm_vals = parse_xscope(xscope_file, pwm_lookup) @@ -151,6 +162,7 @@ def test_sigma_delta(request, burn): print(f"\nPWM output array shape: {pwm_array.shape}") + # Filter like the one on demo board sos = butter(N=2, Wn=filter_cutoff, fs=pwm_rate, output='sos') # Brickwall filter diff --git a/tests/test_sigma_delta/src/main.c b/tests/test_sigma_delta/src/main.c index 26db8c9..a1c6a2c 100644 --- a/tests/test_sigma_delta/src/main.c +++ b/tests/test_sigma_delta/src/main.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -15,15 +16,42 @@ DECLARE_JOB(sigma_delta_task_sf, (sw_dac_sf_t *, chanend_t)); +int running = 1; -DECLARE_JOB(test_app, (chanend_t, chanend_t, chanend_t, int, int)); -void test_app(chanend_t c_sd_in, chanend_t port_l, chanend_t port_r, int burn, int n_loops) { + +DECLARE_JOB(test_consumer, (chanend_t, chanend_t, int)); +void test_consumer(chanend_t port_l, chanend_t port_r, int burn) { xscope_mode_lossless(); + + if(burn){ + local_thread_mode_set_bits(thread_mode_fast); // Always issue + } + + hwtimer_t tmr = hwtimer_alloc(); + + uint32_t time_trig = hwtimer_get_trigger_time(tmr); + while(running){ + time_trig += (XS1_TIMER_HZ / 1500000); // Throttle consumption of outs because we are working over a chanend + hwtimer_wait_until(tmr, time_trig); + unsigned pwm_l = chanend_in_word(port_l); + unsigned pwm_r = chanend_in_word(port_r); + // printf("port: 0x%x 0x%x\n", pwm_l, pwm_r); + + xscope_int(0, pwm_l); + xscope_int(1, pwm_r); + } +} + + +DECLARE_JOB(test_producer, (sw_dac_sf_t*, chanend_t, int, int, int)); +void test_producer(sw_dac_sf_t *sd, chanend_t c_sd_in, int burn, int n_loops, int pause_at) { + hwtimer_t tmr = hwtimer_alloc(); if(burn){ local_thread_mode_set_bits(thread_mode_fast); // Always issue } - int n_sd_loops = 5; + + int n_sd_loops = 5; // How many samples to pass in each loop const int num_buffs_in_sd = 4; @@ -31,9 +59,6 @@ void test_app(chanend_t c_sd_in, chanend_t port_l, chanend_t port_r, int burn, i int sample_idx = 0; for(int loop_count = 0; loop_count < n_loops; loop_count++){ - // printf("loop: %d\n", loop_count); - // xscope_int(0, loop_count); - int idx = loop_count % num_buffs_in_sd; // Send to SD modulator/PWM @@ -46,18 +71,20 @@ void test_app(chanend_t c_sd_in, chanend_t port_l, chanend_t port_r, int burn, i sample_idx++; } + if(loop_count == pause_at){ + const int num_milliseconds_pause = 20; + hwtimer_delay(tmr, XS1_TIMER_KHZ * num_milliseconds_pause); + } + chanend_out_word(c_sd_in, (int) &data[idx][0]); - for(int n_outs = 0; n_outs < n_sd_loops; n_outs++){ - unsigned pwm_l = chanend_in_word(port_l); - unsigned pwm_r = chanend_in_word(port_r); - // printf("port: 0x%x 0x%x\n", pwm_l, pwm_r); - xscope_int(0, pwm_l); - xscope_int(1, pwm_r); - } } + running = 0; // Stop consuming samples in consumer app + printf("Timeout: %d\n", sd->timeout_occurred); printf("Completed test app\n"); + hwtimer_delay(tmr, XS1_TIMER_MHZ); // FLush print + _Exit(0); } @@ -68,15 +95,15 @@ void burn(void){ int main(int argc, char *argv[]) { - if(argc != 3){ - printf("Error - need to pass burn and loops as args\n"); + if(argc != 4){ + printf("Error - need to pass burn and loops and pause_at as args\n"); _Exit(-1); } int burn = atoi(argv[1]); int n_loops = atoi(argv[2]); + int pause_at = atoi(argv[3]); - printf("Started test app, loops: %d, burn: %d\n", burn, n_loops); - + printf("Started test app, burn: %d, n_loops: %d, pause_at: %d\n", burn, n_loops, pause_at); channel_t c_sd_ip = chan_alloc(); channel_t c_sd_op_0 = chan_alloc(); @@ -95,19 +122,25 @@ int main(int argc, char *argv[]) { 1.0/120000, -1.0/250000, // flat_comp_x2, x3 3.0/157, 0.63/157); // pwm comp x2, x3 + // For xscope runs, we cannot sustain 2 x 1.5MHz 32b streams so make timeout large + if(n_loops == pause_at){ + sd.timeout_period = 100000; + } + + if(burn){ PAR_JOBS( PJOB(burn, ()), PJOB(burn, ()), PJOB(burn, ()), - PJOB(burn, ()), - PJOB(burn, ()), - PJOB(test_app, (c_sd_ip.end_a, c_sd_op_0.end_b, c_sd_op_1.end_b, burn, n_loops)), + PJOB(test_producer, (&sd, c_sd_ip.end_a, burn, n_loops, pause_at)), + PJOB(test_consumer, (c_sd_op_0.end_b, c_sd_op_1.end_b, burn)), PJOB(sigma_delta_task_sf, (&sd, c_sd_ip.end_b)) ); } else { PAR_JOBS( - PJOB(test_app, (c_sd_ip.end_a, c_sd_op_0.end_b, c_sd_op_1.end_b, burn, n_loops)), + PJOB(test_producer, (&sd, c_sd_ip.end_a, burn, n_loops, pause_at)), + PJOB(test_consumer, (c_sd_op_0.end_b, c_sd_op_1.end_b, burn)), PJOB(sigma_delta_task_sf, (&sd, c_sd_ip.end_b)) ); diff --git a/tests/test_sigma_delta_idle.py b/tests/test_sigma_delta_idle.py new file mode 100644 index 0000000..8c74ee6 --- /dev/null +++ b/tests/test_sigma_delta_idle.py @@ -0,0 +1,138 @@ +# Copyright 2025 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +import re +import pytest +from pathlib import Path +from filelock import FileLock +import subprocess +import shutil +import numpy as np +from scipy.signal import resample_poly +from scipy.io import wavfile +from helpers import create_if_needed +from scipy.signal import butter, sosfilt, sosfiltfilt # Simulate the filter on xms0021 +from scipy.signal import firwin, freqz, lfilter # Brickwall filter +from test_sigma_delta import parse_output, parse_xscope, run_on_sim + +max_pcm = 32767 +pwm_rate = 1500000 +filter_cutoff = 48000 + +def analyze_signal(signal, fs): + # --- RMS --- + rms = np.sqrt(np.mean(signal**2)) + + # --- Mean --- + mean_val = np.mean(signal) + + # --- Approximate frequency (FFT method) --- + fft_vals = np.fft.fft(signal) + fft_freqs = np.fft.fftfreq(len(signal), 1/fs) + + pos_mask = fft_freqs > 0 + fft_vals = np.abs(fft_vals[pos_mask]) + fft_freqs = fft_freqs[pos_mask] + fft_freq = fft_freqs[np.argmax(fft_vals)] + + return rms, mean_val, fft_freq + +""" +Test to check whether we can recover from not feeding the SDM/PWM for a while. +Only works on sim because on HW, xscope_lossless sometimes backs up and breaks timing upstream. +This test runs just the sigma delta (and PWM) thread. It feeds in a 1.5MHz sampled sinewave +of 1kHz and then captures the outputs to the ports over a channel. +These are then converted to a time series of DC voltage levels and filtered and optionally +resampled to something a bit more manageable than 1.5MHz. +We check the output against RMS, freq and mean (DC level) +""" +@pytest.mark.parametrize("burn", [0, 1]) +@pytest.mark.timeout(60 * 6) +def test_sigma_delta_idle(request, burn): + dut_test_name = "test_sigma_delta" # Re-use same DUT app + test_name = "test_sigma_delta_idle" # But rename it so we don't clash with other tests using xdist + + cwd = Path(request.fspath).parent + binary = Path(f'{cwd}/{dut_test_name}/bin/{dut_test_name}.xe') + assert Path(binary).exists(), f"Cannot find {binary}" + tmp_binary = Path(f'{cwd}/{dut_test_name}/bin/{test_name}_{burn}.xe') # Needed for xdist + create_if_needed("logs") + with FileLock("file_copy.lock"): + shutil.copy2(binary, tmp_binary) + + + # About 2min on xsim + num_loops = 10000 + pause_at = num_loops // 2 + xscope_file = Path(f"logs/{test_name}_trace_{burn}_{num_loops}.vcd") + run_output = run_on_sim(tmp_binary, xscope_file, burn, num_loops, pause_at) + + tmp_binary.unlink() # delete + + print("Parsing terminal output...") + pwm_lookup, max_pwm_magnitude, timedout = parse_output(run_output) + print("PWM Table (hex→index):") + for k, v in pwm_lookup.items(): + print(f" {hex(k)} → {v}") + print(f"Timed out: {timedout}") + + + print("Parsing XSCOPE output...") + pwm_vals = parse_xscope(xscope_file, pwm_lookup) + + pwm_array = np.array(pwm_vals, dtype=float) + pwm_array /= max_pwm_magnitude # scale to +-1 + print(f"\nPWM output array shape: {pwm_array.shape}") + + + # Filter like the one on demo board + sos = butter(N=2, Wn=filter_cutoff, fs=pwm_rate, output='sos') + # Brickwall filter + brickwall = firwin(2001, filter_cutoff, fs=pwm_rate, window="hamming", pass_zero="lowpass") + + # Doesn't matter which we choose, as it's idle + recovery we are checking for + output_sample_rate = 48000 + + # Resample ratios + down = 125 + up = output_sample_rate / (pwm_rate / down) + for channel in [0, 1]: + print() + one_channel = pwm_array[:, channel] + # filtered = sosfilt(sos, one_channel) # like xms0021 HW - second order linear-phase-ish + filtered = lfilter(brickwall, 1.0, one_channel) # brickwall + + skip = 40 # There is a delay on the filter so skip the first few + downsampled = resample_poly(filtered, up=up, down=down, axis=0) + len_samples = downsampled.shape[0] + print(f"Resampling to: {output_sample_rate}, len_samples: {len_samples}") + + wave_name = Path(f"logs/{test_name}_{output_sample_rate}_{burn}_{num_loops}.wav") + wavfile.write(wave_name, output_sample_rate, np.int16(downsampled * max_pcm)) + + section_num = 1 + # These are tuned by looking at the output wav and measuring the signal, silence, signal sections + sections = [[0, int(len_samples*0.333)], + [int(len_samples*0.4), int(len_samples*0.6)], + [int(len_samples*0.666), len_samples]] + expected = [[0.34, -0.12, 1000], # RMS, Mean, freq + [0.0, 0.0, None], + [0.34, -0.12, 1000]] + + for section, expected in zip(sections, expected): + start, end = section + rms, mean, freq = analyze_signal(downsampled[start:end], output_sample_rate) + exp_rms, exp_mean, exp_freq = expected + print(f"section {section_num} {section}, rms: {rms:.2f} ({exp_rms:.2f}), mean: {mean:.2f} ({exp_mean:.2f}), freq: {freq:.2f} ({exp_freq})") + + # Note because of short run, we have to be quite lax on RTOL + assert np.isclose(exp_rms, rms, rtol = 0.1), f"Expected RMS: {exp_rms} Actual RMS: {rms} - see {wave_name}" + assert np.isclose(exp_mean, mean, rtol = 0.1), f"Expected RMS: {exp_mean} Actual RMS: {mean} - see {wave_name}" + if exp_freq is not None: + assert np.isclose(exp_freq, freq, rtol = 0.05), f"Expected RMS: {exp_freq} Actual RMS: {freq} - see {wave_name}" + + section_num += 1 + + + + + From 31259a4f165e418f28111d8d90dacc887ba18413 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 23 Dec 2025 11:59:19 +0000 Subject: [PATCH 07/13] Change SW_DAC_SD_TEST_MODE to ifdef and init ports with underflow word --- lib_sw_dac/src/standard_fidelity/run_sf.S | 7 ++++--- lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S | 6 +++--- lib_sw_dac/src/standard_fidelity/software_dac_sf.c | 8 ++++---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib_sw_dac/src/standard_fidelity/run_sf.S b/lib_sw_dac/src/standard_fidelity/run_sf.S index 441952e..154825a 100644 --- a/lib_sw_dac/src/standard_fidelity/run_sf.S +++ b/lib_sw_dac/src/standard_fidelity/run_sf.S @@ -49,9 +49,10 @@ FUNCTION_NAME: ldw r7, r0[SDAC_OUT_PORTS_L] ldw r8, r0[SDAC_OUT_PORTS_R] - // Fill the ports with data - out res[r7], r7 - out res[r8], r7 + // Fill the ports with data, but clock not started yet + ldw r11, r0[SDAC_TIMEOUT_WORD] + out res[r7], r11 + out res[r8], r11 // Now start the clock block, it is on already and linked to the ports ldw r11, r0[SDAC_CLOCK_BLOCK] diff --git a/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S b/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S index 7b67cca..5d624f3 100644 --- a/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S +++ b/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S @@ -74,7 +74,7 @@ FUNCTION_NAME: { vlsat r11[0] ; shl r11, r3, 2 } { vstr r1[0] ; sub r4, r4, r11 } { ldw r1, r10[r6] ; sub r4, r4, r3 } -#if defined(SW_DAC_SD_TEST_MODE) +#if SW_DAC_SD_TEST_MODE out res[r7], r1 #else outpw res[r7], r1, 16 @@ -98,7 +98,7 @@ FUNCTION_NAME: { vlsat r11[0] ; shl r11, r3, 2 } { vstr r1[0] ; sub r4, r4, r11 } { ldw r1, r10[r6] ; sub r4, r4, r3 } -#if defined(SW_DAC_SD_TEST_MODE) +#if SW_DAC_SD_TEST_MODE out res[r8], r1 #else outpw res[r8], r1, 16 @@ -112,7 +112,7 @@ FUNCTION_NAME: .sd_timeout: // Get the out dummy word, output it and then invert it to avoid DC output content ldw r11, sp[SDAC_STACK_TIMEOUT_WORD]; -#if defined(SW_DAC_SD_TEST_MODE) +#if SW_DAC_SD_TEST_MODE out res[r7], r11 out res[r8], r11 #else diff --git a/lib_sw_dac/src/standard_fidelity/software_dac_sf.c b/lib_sw_dac/src/standard_fidelity/software_dac_sf.c index e22986d..1891027 100644 --- a/lib_sw_dac/src/standard_fidelity/software_dac_sf.c +++ b/lib_sw_dac/src/standard_fidelity/software_dac_sf.c @@ -40,11 +40,11 @@ void sw_dac_sf_init(sw_dac_sf_t *sd, float p_x2, float p_x3) { memset(sd, 0, sizeof(*sd)); -#if !defined(SW_DAC_SD_TEST_MODE) +#if !SW_DAC_SD_TEST_MODE init_ports(dac_ports, clk); #endif sd->timeout_period = 70; // at 1.5MHz this will be 66.666 so set slightly above - sd->timeout_word = 0x0ff0; // 50% duty cycle 16b word + sd->timeout_word = 0x0ff00ff0; // 50% duty cycle 16b word normally but full 32b version here for startup sd->timeout_resid = hwtimer_alloc(); sd->timeout_occurred = 0; @@ -106,7 +106,7 @@ void sw_dac_sf_init(sw_dac_sf_t *sd, uint64_t mask_reversed = mask << (2*pwm_max-(i+pwm_max)); first_64 = mask_reversed | (1 << (2*pwm_max)) | (mask << (1 + 2*pwm_max)); sd->pwm_lookup[i] = first_64; -#if defined(SW_DAC_SD_TEST_MODE) +#if SW_DAC_SD_TEST_MODE printf("PWM %d:0x%08x\n", i, (int) sd->pwm_lookup[i]); #endif } @@ -118,7 +118,7 @@ void sw_dac_sf_init(sw_dac_sf_t *sd, uint64_t mask_reversed = mask << (2*pwm_max-(i+pwm_max)); first_64 = mask_reversed | (3 << (2*pwm_max)) | (mask << (2 + 2*pwm_max)); sd->pwm_lookup[i] = first_64; -#if defined(SW_DAC_SD_TEST_MODE) +#if SW_DAC_SD_TEST_MODE printf("PWM %d:0x%08x\n", i, (int) sd->pwm_lookup[i]); #endif } From 501730e068b418eeb5f18b6c06841774aacb4917 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 23 Dec 2025 13:35:17 +0000 Subject: [PATCH 08/13] Add sync to stereo ports on recovery from timeout --- lib_sw_dac/src/standard_fidelity/run_sf.S | 5 ++++- lib_sw_dac/src/standard_fidelity/sdac_sf.h | 3 ++- .../src/standard_fidelity/sigma_delta_1_5.S | 18 ++++++++++++++---- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/lib_sw_dac/src/standard_fidelity/run_sf.S b/lib_sw_dac/src/standard_fidelity/run_sf.S index 154825a..f71cbae 100644 --- a/lib_sw_dac/src/standard_fidelity/run_sf.S +++ b/lib_sw_dac/src/standard_fidelity/run_sf.S @@ -6,7 +6,7 @@ #include "sdac_sf.h" -#define NSTACKWORDS 18 +#define NSTACKWORDS 20 #define FUNCTION_NAME sigma_delta_1_5 .cc_top FUNCTION_NAME.function @@ -39,6 +39,9 @@ FUNCTION_NAME: stw r4, sp[SDAC_STACK_TIMEOUT_RESID] ldw r4, r0[SDAC_TIMEOUT_WORD] stw r4, sp[SDAC_STACK_TIMEOUT_WORD] + ldw r4, r0[SDAC_CLOCK_BLOCK] + stw r4, sp[SDAC_STACK_TIMEOUT_CLKBLK] + // Initialise registers for sigma_delta_1_5 ldw r10, r0[SDAC_PWM_LOOKUP] diff --git a/lib_sw_dac/src/standard_fidelity/sdac_sf.h b/lib_sw_dac/src/standard_fidelity/sdac_sf.h index 50a9476..323b9ef 100644 --- a/lib_sw_dac/src/standard_fidelity/sdac_sf.h +++ b/lib_sw_dac/src/standard_fidelity/sdac_sf.h @@ -32,9 +32,10 @@ #define SDAC_BUF_STEP 1 #define SDAC_BUF_TOTAL (SDAC_BUF_R + SDAC_FILTER_MAX) -// These are stack offsets for storing information for the select in SD +// These are stack offsets for storing information for the select and timeout in SD // DO NOT MODIFY! #define SDAC_STACK_SD_STRUCT_BASE 8 #define SDAC_STACK_TIMEOUT_PERIOD 9 #define SDAC_STACK_TIMEOUT_WORD 10 #define SDAC_STACK_TIMEOUT_RESID 11 +#define SDAC_STACK_TIMEOUT_CLKBLK 12 diff --git a/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S b/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S index 5d624f3..92883d4 100644 --- a/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S +++ b/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S @@ -38,7 +38,7 @@ FUNCTION_NAME: ldap r11, .input_handler; //r11 <- input_handler setv res[r0], r11; - eeu res[r0]; // enable + eeu res[r0]; // enable EEBLE setsr 0x01; // event enable in thread @@ -111,17 +111,27 @@ FUNCTION_NAME: .align 8 .sd_timeout: // Get the out dummy word, output it and then invert it to avoid DC output content - ldw r11, sp[SDAC_STACK_TIMEOUT_WORD]; + ldw r11, sp[SDAC_STACK_TIMEOUT_WORD] + ldw r6, sp[SDAC_STACK_TIMEOUT_CLKBLK] #if SW_DAC_SD_TEST_MODE out res[r7], r11 out res[r8], r11 #else - outpw res[r7], r11, 16; - outpw res[r8], r11, 16; + // Note this isn't perfect - the port buffer will empty before next timeout + // However since this is a bad situation anyway (output should have been muted if expected) + // and the timeout word is inverted so we will have continuous activity on the port and near 50% duty + // so it will avert issues where the SD is not fed which result in toasting the amplified + syncr res[r7] // Ensure we have space in port buffers + syncr res[r8] + setc res[r6], XS1_SETC_RUN_STOPR // Stop clock so we can pre-load both ports + out res[r7], r11 + out res[r8], r11 + setc res[r6], XS1_SETC_RUN_STARTR // Start the clock outputs will be sync'ed until empty #endif not r11, r11; stw r11, sp[SDAC_STACK_TIMEOUT_WORD]; + // Move the timeout timer on by at least one PWM period. We may come back here if no IN from filter { gettime r11 ; ldw r6, sp[SDAC_STACK_TIMEOUT_PERIOD]} { add r11, r11, r6 ; ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]} From f7cad20e181158d39d34a56748f516b8ad41c8c9 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 23 Dec 2025 15:28:48 +0000 Subject: [PATCH 09/13] Update test_sigma delta to holdoff SD until producer ready + remove unused app --- tests/test_pwm_idle/CMakeLists.txt | 24 ----- tests/test_pwm_idle/run_sim.sh | 3 - tests/test_pwm_idle/sim.gtkw | 43 --------- tests/test_pwm_idle/src/config.xscope | 22 ----- tests/test_pwm_idle/src/main.c | 121 -------------------------- tests/test_pwm_idle/src/sw_dac_conf.h | 2 - tests/test_sigma_delta/CMakeLists.txt | 14 +-- tests/test_sigma_delta/src/main.c | 39 ++++++--- 8 files changed, 36 insertions(+), 232 deletions(-) delete mode 100644 tests/test_pwm_idle/CMakeLists.txt delete mode 100644 tests/test_pwm_idle/run_sim.sh delete mode 100644 tests/test_pwm_idle/sim.gtkw delete mode 100644 tests/test_pwm_idle/src/config.xscope delete mode 100644 tests/test_pwm_idle/src/main.c delete mode 100644 tests/test_pwm_idle/src/sw_dac_conf.h diff --git a/tests/test_pwm_idle/CMakeLists.txt b/tests/test_pwm_idle/CMakeLists.txt deleted file mode 100644 index a1c7830..0000000 --- a/tests/test_pwm_idle/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -cmake_minimum_required(VERSION 3.21) -include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake) - -project(test_sigma_delta) - -set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..) - -set(APP_HW_TARGET XK-EVK-XU316) - -set(APP_DEPENDENT_MODULES "lib_sw_dac" - ) - -set(APP_PCA_ENABLE OFF) # TODO why does PCA cause build error - -set(APP_COMPILER_FLAGS -O3 - -mno-dual-issue - -g - -report - -fcmdline-buffer-bytes=1024) - -set(APP_INCLUDES src) - - -XMOS_REGISTER_APP() \ No newline at end of file diff --git a/tests/test_pwm_idle/run_sim.sh b/tests/test_pwm_idle/run_sim.sh deleted file mode 100644 index 5d1a9e6..0000000 --- a/tests/test_pwm_idle/run_sim.sh +++ /dev/null @@ -1,3 +0,0 @@ -xsim --xscope "-offline xscope.xmt" --vcd-tracing "-o trace.vcd -tile tile[0] -ports -ports-detailed -clock-blocks -instructions -cores -timers -cycles" --trace-to trace.txt --args bin/test_sigma_delta.xe 0 100 -xobjdump -S bin/test_sigma_delta.xe > disass.txt - diff --git a/tests/test_pwm_idle/sim.gtkw b/tests/test_pwm_idle/sim.gtkw deleted file mode 100644 index 6efda2c..0000000 --- a/tests/test_pwm_idle/sim.gtkw +++ /dev/null @@ -1,43 +0,0 @@ -[*] -[*] GTKWave Analyzer v3.3.116 (w)1999-2023 BSI -[*] Wed Sep 17 16:56:41 2025 -[*] -[dumpfile] "/home/ed/sandboxes/sw_dac/lib_sw_dac/tests/test_stuff/trace.vcd" -[dumpfile_mtime] "Wed Sep 17 16:54:52 2025" -[dumpfile_size] 16117009 -[savefile] "/home/ed/sandboxes/sw_dac/lib_sw_dac/tests/test_stuff/sim.gtkw" -[timestart] 64250000 -[size] 1580 1162 -[pos] -292 -36 -*-24.090168 109320000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -[sst_width] 463 -[signals_width] 342 -[sst_expanded] 1 -[sst_vpaned_height] 353 -@28 -tile[0]_XS1_PORT_1A -tile[0]_XS1_PORT_1B -@22 -tile[0]_ClkBlk1_fallEdge -tile[0]_Core1_waiting -tile[0]_Core1_waitingResourceId[31:0] -@821 -tile[0]_Core1_waitingResourceName[63:0] -@22 -tile[0]_INSTRUCTION_PC[31:0] -tile[0]_INSTRUCTION_THREAD_ID[7:0] -@820 -tile[0]_INSTRUCTION_MNEMONIC[63:0] -@22 -tile[0]_Core0_waiting -@820 -tile[0]_Core0_waitingResourceName[63:0] -@22 -tile[0]_Core0_waitingResourceId[31:0] -tile[0]_Timer0_time[63:0] -@24 -tile[0]_CYCLES[31:0] -@22 -tile[0]_XS1_PORT_1A_transferReg[31:0] -[pattern_trace] 1 -[pattern_trace] 0 diff --git a/tests/test_pwm_idle/src/config.xscope b/tests/test_pwm_idle/src/config.xscope deleted file mode 100644 index b7f63d6..0000000 --- a/tests/test_pwm_idle/src/config.xscope +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/tests/test_pwm_idle/src/main.c b/tests/test_pwm_idle/src/main.c deleted file mode 100644 index 57876ce..0000000 --- a/tests/test_pwm_idle/src/main.c +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2025 XMOS LIMITED. -// This Software is subject to the terms of the XMOS Public Licence: Version 1. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sw_dac.h" -#include "sdac_sf.h" -#include "sigma_delta_modulators.h" - -#include "../../common/sin1500.h" -#include - -DECLARE_JOB(sigma_delta_task_sf, (sw_dac_sf_t *, chanend_t)); - - -const int n_sd_loops = 2; - -DECLARE_JOB(test_app, (sw_dac_sf_t *, chanend_t, int, int)); -void test_app(sw_dac_sf_t *sd, chanend_t c_sd_in, int burn, int n_loops) { - xscope_mode_lossless(); - - if(burn){ - local_thread_mode_set_bits(thread_mode_fast); // Always issue if enabled - } - - const int num_buffs_in_sd = 4; - - int32_t data[num_buffs_in_sd][SDAC_BUF_TOTAL] = {{0}}; - int sample_idx = 0; - - hwtimer_t tmr = hwtimer_alloc(); - - for(int loop_count = 0; loop_count < n_loops; loop_count++){ - // printf("loop: %d\n", loop_count); - // xscope_int(0, loop_count); - - int idx = loop_count % num_buffs_in_sd; - - // Send to SD modulator/PWM - // First setup array to transfer to SD - data[idx][SDAC_BUF_N] = n_sd_loops; - for(int i = 0; i < n_sd_loops; i++){ - int32_t sample = sin1500[sample_idx % 1500] >> 2; // More amplitude than this and it clips/ overflows - data[idx][SDAC_BUF_L + i] = sample; - data[idx][SDAC_BUF_R + i] = -sample; - sample_idx++; - if(sample_idx == 50){ - hwtimer_delay(tmr, 3000); - } - } - - chanend_out_word(c_sd_in, (int) &data[idx][0]); - - } - - printf("Completed test app, timed out: %d\n", sd->timeout_occurred); - _Exit(0); -} - -DECLARE_JOB(burn, (void)); -void burn(void){ - while(1); -} - - - - -int main(int argc, char *argv[]) { - if(argc != 3){ - printf("Error - need to pass burn and loops as args\n"); - _Exit(-1); - } - int burn = atoi(argv[1]); - int n_loops = atoi(argv[2]); - - printf("Started test app, loops: %d, burn: %d\n", n_loops, burn); - - - channel_t c_sd_ip = chan_alloc(); - port_t p_left = XS1_PORT_1A; - port_t p_right = XS1_PORT_1B; - - xclock_t clk = XS1_CLKBLK_1; - port_t ports[2] = {p_left, p_right}; // L and R outputs - - // Setup clock block to run from MCLK in which is set to 24MHz by the App PLL - clock_enable(clk); - clock_set_source_clk_ref(clk); - clock_set_divide(clk, 4 / 2); // 25MHz - - sw_dac_sf_t sd; - sw_dac_sf_init(&sd, ports, clk, 8, sd_coeffs_o6_f1_5_n8, - 2.8544, 2.8684735298, // scale, limit - 1.0/120000, -1.0/250000, // flat_comp_x2, x3 - 3.0/157, 0.63/157); // pwm comp x2, x3 - - if(burn){ - PAR_JOBS( - PJOB(burn, ()), - PJOB(burn, ()), - PJOB(burn, ()), - PJOB(burn, ()), - PJOB(burn, ()), - PJOB(test_app, (&sd, c_sd_ip.end_a, burn, n_loops)), - PJOB(sigma_delta_task_sf, (&sd, c_sd_ip.end_b)) - ); - } else { - PAR_JOBS( - PJOB(test_app, (&sd, c_sd_ip.end_a, burn, n_loops)), - PJOB(sigma_delta_task_sf, (&sd, c_sd_ip.end_b)) - ); - - } -} diff --git a/tests/test_pwm_idle/src/sw_dac_conf.h b/tests/test_pwm_idle/src/sw_dac_conf.h deleted file mode 100644 index 04c8f12..0000000 --- a/tests/test_pwm_idle/src/sw_dac_conf.h +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright 2025 XMOS LIMITED. -// This Software is subject to the terms of the XMOS Public Licence: Version 1. diff --git a/tests/test_sigma_delta/CMakeLists.txt b/tests/test_sigma_delta/CMakeLists.txt index 26fac0f..78e2edc 100644 --- a/tests/test_sigma_delta/CMakeLists.txt +++ b/tests/test_sigma_delta/CMakeLists.txt @@ -12,14 +12,16 @@ set(APP_DEPENDENT_MODULES "lib_sw_dac" set(APP_PCA_ENABLE OFF) # TODO why does PCA cause build error -set(APP_COMPILER_FLAGS -O3 - -mcmodel=large - -g - -report - -DSW_DAC_SD_TEST_MODE=1 - -fcmdline-buffer-bytes=1024) +set(COMPILER_FLAGS_COMMON -O3 + -mcmodel=large + -g + -report + -fcmdline-buffer-bytes=1024) set(APP_INCLUDES src) +set(APP_COMPILER_FLAGS_CHAN ${COMPILER_FLAGS_COMMON} -DSW_DAC_SD_TEST_MODE=1) +set(APP_COMPILER_FLAGS_PORT ${COMPILER_FLAGS_COMMON} -DSW_DAC_SD_TEST_MODE=0) + XMOS_REGISTER_APP() diff --git a/tests/test_sigma_delta/src/main.c b/tests/test_sigma_delta/src/main.c index a1c6a2c..aaec8f2 100644 --- a/tests/test_sigma_delta/src/main.c +++ b/tests/test_sigma_delta/src/main.c @@ -13,8 +13,9 @@ #include "sdac_sf.h" #include "../../common/sin1500.h" #include +#include -DECLARE_JOB(sigma_delta_task_sf, (sw_dac_sf_t *, chanend_t)); +extern void sigma_delta_task_sf(sw_dac_sf_t * sd, chanend_t c_sd); int running = 1; @@ -59,6 +60,7 @@ void test_producer(sw_dac_sf_t *sd, chanend_t c_sd_in, int burn, int n_loops, in int sample_idx = 0; for(int loop_count = 0; loop_count < n_loops; loop_count++){ + // printintln(loop_count); int idx = loop_count % num_buffs_in_sd; // Send to SD modulator/PWM @@ -73,6 +75,7 @@ void test_producer(sw_dac_sf_t *sd, chanend_t c_sd_in, int burn, int n_loops, in if(loop_count == pause_at){ const int num_milliseconds_pause = 20; + printstr("pause\n"); hwtimer_delay(tmr, XS1_TIMER_KHZ * num_milliseconds_pause); } @@ -93,6 +96,15 @@ void burn(void){ while(1); } +// This ensures we don't start the SD until we are ready to produce +// Ensures we don't hit the timeout case in SD at startup +DECLARE_JOB(sigma_delta_task_sf_delay_start, (sw_dac_sf_t *, chanend_t)); +void sigma_delta_task_sf_delay_start(sw_dac_sf_t * sd, chanend_t c_sd){ + hwtimer_t tmr = hwtimer_alloc(); + hwtimer_delay(tmr, 20 * 100); // 20us + hwtimer_free(tmr); + sigma_delta_task_sf(sd, c_sd); +} int main(int argc, char *argv[]) { if(argc != 4){ @@ -109,8 +121,13 @@ int main(int argc, char *argv[]) { channel_t c_sd_op_0 = chan_alloc(); channel_t c_sd_op_1 = chan_alloc(); xclock_t clk = XS1_CLKBLK_1; +#if SW_DAC_SD_TEST_MODE port_t ports[2] = {c_sd_op_0.end_a, c_sd_op_1.end_a}; // L and R outputs - +#else + port_t ports[2] = {XS1_PORT_1A, XS1_PORT_1B}; // L and R outputs + port_enable(ports[0]); + port_enable(ports[1]); +#endif // Setup clock block to run from MCLK in which is set to 24MHz by the App PLL clock_enable(clk); clock_set_source_clk_ref(clk); @@ -130,18 +147,18 @@ int main(int argc, char *argv[]) { if(burn){ PAR_JOBS( - PJOB(burn, ()), - PJOB(burn, ()), - PJOB(burn, ()), - PJOB(test_producer, (&sd, c_sd_ip.end_a, burn, n_loops, pause_at)), - PJOB(test_consumer, (c_sd_op_0.end_b, c_sd_op_1.end_b, burn)), - PJOB(sigma_delta_task_sf, (&sd, c_sd_ip.end_b)) + PJOB(burn, ()), + PJOB(burn, ()), + PJOB(burn, ()), + PJOB(test_producer, (&sd, c_sd_ip.end_a, burn, n_loops, pause_at)), + PJOB(test_consumer, (c_sd_op_0.end_b, c_sd_op_1.end_b, burn)), + PJOB(sigma_delta_task_sf_delay_start, (&sd, c_sd_ip.end_b)) ); } else { PAR_JOBS( - PJOB(test_producer, (&sd, c_sd_ip.end_a, burn, n_loops, pause_at)), - PJOB(test_consumer, (c_sd_op_0.end_b, c_sd_op_1.end_b, burn)), - PJOB(sigma_delta_task_sf, (&sd, c_sd_ip.end_b)) + PJOB(test_producer, (&sd, c_sd_ip.end_a, burn, n_loops, pause_at)), + PJOB(test_consumer, (c_sd_op_0.end_b, c_sd_op_1.end_b, burn)), + PJOB(sigma_delta_task_sf_delay_start, (&sd, c_sd_ip.end_b)) ); } From 8facfef5b42711f9eb3d2a8193eb9e6fc6756845 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 23 Dec 2025 15:49:12 +0000 Subject: [PATCH 10/13] DI setup of select in SD --- .../src/standard_fidelity/sigma_delta_1_5.S | 18 +++++++----------- tests/requirements.txt | 5 +++-- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S b/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S index 92883d4..f788270 100644 --- a/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S +++ b/lib_sw_dac/src/standard_fidelity/sigma_delta_1_5.S @@ -27,22 +27,18 @@ .linkset FUNCTION_NAME.nstackwords, 0 .align 16 FUNCTION_NAME: - // Init VPU - ldc r11, 0 - vsetc r11 -// setup select - ldap r11, .sd_timeout; //r11 <- &sd_timeout handler - ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; - setv res[r6], r11; //set event vector +// Init VPU and the select statement + { ldw r6, sp[SDAC_STACK_TIMEOUT_RESID]; ldc r11, 0 } + { vsetc r11 ; ldap r11, .sd_timeout } + + { setv res[r6], r11 ; ldap r11, .input_handler} eeu res[r6]; // enable - ldap r11, .input_handler; //r11 <- input_handler setv res[r0], r11; - eeu res[r0]; // enable EEBLE - - setsr 0x01; // event enable in thread + eeu res[r0]; // enable waiteu; // This does the initial IN or timeout + // Main loop .Louter_loop: .input_handler: diff --git a/tests/requirements.txt b/tests/requirements.txt index af68553..9afcf8e 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -21,7 +21,6 @@ pytest==8.3.3 pytest-xdist==3.6.1 pytest-timeout==2.4.0 filelock==3.19.1 -numpy==2.3.2 scipy==1.16.1 # Development dependencies @@ -37,4 +36,6 @@ scipy==1.16.1 # of its own setup.py file, then this list must include an entry for that # setup.py file, e.g., '-e .' or '-e ./python' (without the quotes). --e git+ssh://git@github.com/xmos/test_support.git@v2.0.0#egg=test_support \ No newline at end of file +-e git+ssh://git@github.com/xmos/test_support.git@v2.0.0#egg=test_support +# Head of develop as of 14th nov with THDN script just merged. This can be moved on when released +-e git+ssh://git@github.com/xmos/audio_test_tools.git@ab4d121e0b19fb6fbef6ad88a532afc152398f31#egg=audio_test_tools&subdirectory=python \ No newline at end of file From c6133b3386c179d21726a12b0bcaf67695728d78 Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 23 Dec 2025 15:54:30 +0000 Subject: [PATCH 11/13] Support for odd PWM 0 offset test_idle now expects properly offsetted PWM vals --- tests/test_sigma_delta.py | 18 ++++++++++++++---- tests/test_sigma_delta_idle.py | 10 +++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/tests/test_sigma_delta.py b/tests/test_sigma_delta.py index 5921b8f..395c923 100644 --- a/tests/test_sigma_delta.py +++ b/tests/test_sigma_delta.py @@ -17,7 +17,7 @@ max_pcm = 32767 pwm_rate = 1500000 filter_cutoff = 48000 -pwm_idle_words = [0x0ff0, 0xfffffffffffff00f] +pwm_idle_words = [0x0ff0, 0xfffffffffffff00f, 0xfffffffff00ff00f, 0x0ff00ff0] def parse_output(stdout): pwm_lookup = {} @@ -44,6 +44,11 @@ def parse_output(stdout): hexval = int(m_pwm.group(2), 16) pwm_lookup[hexval] = idx max_pwm_magnitude = abs(idx) if abs(idx) > max_pwm_magnitude else max_pwm_magnitude + # handle case where we have even number of PWM entries - need offset due to no 0 word + if len(pwm_lookup) % 2 == 0: + print("EVEN") + for pwm_val in pwm_lookup.keys(): + pwm_lookup[pwm_val] = pwm_lookup[pwm_val] + 0.5 # Add idle words for pwd_idle_word in pwm_idle_words: @@ -81,7 +86,7 @@ def parse_xscope(filepath, pwm_lookup): pairs.append((left, right)) sample_num += 1 - return np.array(pairs, dtype=np.int32) + return np.array(pairs, dtype=np.float64) def check_hw_presence(): @@ -123,8 +128,9 @@ def run_on_hw(binary, xscope_file, burn, num_loops, pause_at): def test_sigma_delta(request, burn): test_name = "test_sigma_delta" + build = "CHAN" cwd = Path(request.fspath).parent - binary = Path(f'{cwd}/{test_name}/bin/{test_name}.xe') + binary = Path(f'{cwd}/{test_name}/bin/{build}/{test_name}_{build}.xe') assert Path(binary).exists(), f"Cannot find {binary}" tmp_binary = Path(f'{cwd}/{test_name}/bin/{test_name}_{burn}.xe') # Needed for xdist create_if_needed("logs") @@ -183,7 +189,11 @@ def test_sigma_delta(request, burn): # filtered = sosfilt(sos, one_channel) # like xms0021 HW - second order linear-phase-ish filtered = lfilter(brickwall, 1.0, one_channel) # brickwall - skip = 40 # There is a delay on the filter so skip the first few + if using_simuator: + skip = 40 + else: + skip = int(0.001 * pwm_rate) # There is a delay on the filter so skip the first millisecond + THDN, freq = THDN_and_freq(filtered[skip:], pwm_rate) print(f"Filtered channel {channel} THDN: {THDN} freq: {freq}") downsampled = resample_poly(filtered, up=up, down=down, axis=0) diff --git a/tests/test_sigma_delta_idle.py b/tests/test_sigma_delta_idle.py index 8c74ee6..81f7b7e 100644 --- a/tests/test_sigma_delta_idle.py +++ b/tests/test_sigma_delta_idle.py @@ -52,7 +52,7 @@ def test_sigma_delta_idle(request, burn): test_name = "test_sigma_delta_idle" # But rename it so we don't clash with other tests using xdist cwd = Path(request.fspath).parent - binary = Path(f'{cwd}/{dut_test_name}/bin/{dut_test_name}.xe') + binary = Path(f'{cwd}/{dut_test_name}/bin/CHAN/{dut_test_name}_CHAN.xe') assert Path(binary).exists(), f"Cannot find {binary}" tmp_binary = Path(f'{cwd}/{dut_test_name}/bin/{test_name}_{burn}.xe') # Needed for xdist create_if_needed("logs") @@ -114,9 +114,9 @@ def test_sigma_delta_idle(request, burn): sections = [[0, int(len_samples*0.333)], [int(len_samples*0.4), int(len_samples*0.6)], [int(len_samples*0.666), len_samples]] - expected = [[0.34, -0.12, 1000], # RMS, Mean, freq + expected = [[0.32, 0.0, 1000], # RMS, Mean, freq [0.0, 0.0, None], - [0.34, -0.12, 1000]] + [0.32, 0.0, 1000]] for section, expected in zip(sections, expected): start, end = section @@ -125,8 +125,8 @@ def test_sigma_delta_idle(request, burn): print(f"section {section_num} {section}, rms: {rms:.2f} ({exp_rms:.2f}), mean: {mean:.2f} ({exp_mean:.2f}), freq: {freq:.2f} ({exp_freq})") # Note because of short run, we have to be quite lax on RTOL - assert np.isclose(exp_rms, rms, rtol = 0.1), f"Expected RMS: {exp_rms} Actual RMS: {rms} - see {wave_name}" - assert np.isclose(exp_mean, mean, rtol = 0.1), f"Expected RMS: {exp_mean} Actual RMS: {mean} - see {wave_name}" + assert np.isclose(exp_rms, rms, atol = 0.01), f"Expected RMS: {exp_rms} Actual RMS: {rms} - see {wave_name}" + assert np.isclose(exp_mean, mean, atol = 0.01), f"Expected RMS: {exp_mean} Actual RMS: {mean} - see {wave_name}" if exp_freq is not None: assert np.isclose(exp_freq, freq, rtol = 0.05), f"Expected RMS: {exp_freq} Actual RMS: {freq} - see {wave_name}" From 142849d420b12cfcf6e488017e79378faef245bf Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 23 Dec 2025 16:13:39 +0000 Subject: [PATCH 12/13] Bump to version 1.0.0 assert on timedout in SD test Clean timeout at startup in SD test --- .gitignore | 1 + CHANGELOG.rst | 10 ++++++---- Jenkinsfile | 6 +++--- README.rst | 10 +++++----- doc/rst/lib_sw_dac.rst | 5 ----- lib_sw_dac/lib_build_info.cmake | 2 +- settings.yml | 2 +- tests/test_sigma_delta.py | 4 +++- tests/test_sigma_delta/src/main.c | 4 ++++ 9 files changed, 24 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index d23fc84..07cf174 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,4 @@ tests/trace.txt # editor **/.vscode */trace.gtkw +tests/*.gtkw diff --git a/CHANGELOG.rst b/CHANGELOG.rst index bad5144..a32d09f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,11 +1,13 @@ lib_sw_dac change log ===================== -UNRELEASED ----------- +1.0.0 +----- - * FIXED: Now sends PWM zero when not fed with samples - * ADDED: 44.1 , 88.2, 176.4 kHz support + * First public release + * ADDED: 44.1, 88.2, 176.4 kHz support + * FIXED: PWM outputs zero level when not fed with samples + * FIXED: PWM left and right outputs remain synchronised 0.1.0 ----- diff --git a/Jenkinsfile b/Jenkinsfile index 8e50eb6..5a3ab5e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,6 +1,6 @@ // This file relates to internal XMOS infrastructure and should be ignored by external users -@Library('xmos_jenkins_shared_library@v0.42.0') _ +@Library('xmos_jenkins_shared_library@v0.43.3') _ getApproval() pipeline { @@ -15,12 +15,12 @@ pipeline { ) string( name: 'XMOSDOC_VERSION', - defaultValue: 'v7.4.0', + defaultValue: 'v8.0.0', description: 'xmosdoc version' ) string( name: 'INFR_APPS_VERSION', - defaultValue: 'v3.1.1', + defaultValue: 'v3.2.0', description: 'The infr_apps version' ) } diff --git a/README.rst b/README.rst index ea36dcd..ef671fe 100644 --- a/README.rst +++ b/README.rst @@ -5,24 +5,24 @@ lib_sw_dac: Software DAC ######################## :vendor: XMOS -:version: 0.2.0 -:scope: Demo +:version: 1.0.0 +:scope: General Use :description: Software DAC for xcore.ai :category: Audio -:keywords: Audio, DAC, PWM +:keywords: DAC :devices: xcore.ai ******* Summary ******* -Software DAC for xcore comprising of upsampling, sigma-delta modulator and PWM generation. +Software DAC for ``xcore.ai`` comprising of upsampling, sigma-delta modulator and PWM generation. ******** Features ******** -* Supports 48000, 96000 and 192000 Hz sample rate, stereo output +* Supports 44100, 48000, 88200, 96000 and 176400 & 192000 Hz sample rate, stereo output * Uses two hardware threads, two one-bit ports, one timer, one clock-block and 32 kB of RAM ************ diff --git a/doc/rst/lib_sw_dac.rst b/doc/rst/lib_sw_dac.rst index 5ad1172..de238c8 100644 --- a/doc/rst/lib_sw_dac.rst +++ b/doc/rst/lib_sw_dac.rst @@ -104,11 +104,6 @@ latency) as needed. The number of filters can be changed too, although for pragm reasons it is good to start with 2x upsamplers as that creates natural input points for 96 and 192 kHz sample rates if multiple input sample rate is required. -In order to support the 44,100 family, one can either support: - -* A dual master clock configuration. -* A fractional sample rate converter at the input stage. -* A design the final filters to perform the fractional conversion at that stage. PWM pulse rate and levels ------------------------- diff --git a/lib_sw_dac/lib_build_info.cmake b/lib_sw_dac/lib_build_info.cmake index 0f63fd9..d903667 100644 --- a/lib_sw_dac/lib_build_info.cmake +++ b/lib_sw_dac/lib_build_info.cmake @@ -1,6 +1,6 @@ set(LIB_NAME lib_sw_dac) -set(LIB_VERSION 0.2.0) +set(LIB_VERSION 1.0.0) set(LIB_INCLUDES api src diff --git a/settings.yml b/settings.yml index 725c6e2..bc71031 100644 --- a/settings.yml +++ b/settings.yml @@ -3,7 +3,7 @@ lib_name: lib_sw_dac project: '{{lib_name}}' title: '{{lib_name}}: Software DAC' -version: 0.2.0 +version: 1.0.0 documentation: exclude_patterns_path: doc/exclude_patterns.inc diff --git a/tests/test_sigma_delta.py b/tests/test_sigma_delta.py index 395c923..9ff7e0d 100644 --- a/tests/test_sigma_delta.py +++ b/tests/test_sigma_delta.py @@ -46,7 +46,6 @@ def parse_output(stdout): max_pwm_magnitude = abs(idx) if abs(idx) > max_pwm_magnitude else max_pwm_magnitude # handle case where we have even number of PWM entries - need offset due to no 0 word if len(pwm_lookup) % 2 == 0: - print("EVEN") for pwm_val in pwm_lookup.keys(): pwm_lookup[pwm_val] = pwm_lookup[pwm_val] + 0.5 @@ -209,5 +208,8 @@ def test_sigma_delta(request, burn): wave_name = Path(f"logs/{test_name}_{sample_rate}_{burn}_{num_loops}.wav") wavfile.write(wave_name, sample_rate, np.int16(downsampled * max_pcm)) + + assert timedout == 0 + print() diff --git a/tests/test_sigma_delta/src/main.c b/tests/test_sigma_delta/src/main.c index aaec8f2..4b9f7bb 100644 --- a/tests/test_sigma_delta/src/main.c +++ b/tests/test_sigma_delta/src/main.c @@ -81,6 +81,10 @@ void test_producer(sw_dac_sf_t *sd, chanend_t c_sd_in, int burn, int n_loops, in chanend_out_word(c_sd_in, (int) &data[idx][0]); + if(loop_count == 0){ + sd->timeout_occurred = 0; // Ensure we start clean after startup + } + } running = 0; // Stop consuming samples in consumer app From 699cd14d9fef36a0afadd59711a2aee3d4e83c18 Mon Sep 17 00:00:00 2001 From: Ed Date: Fri, 9 Jan 2026 17:15:10 +0000 Subject: [PATCH 13/13] fix merge conflict --- README.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.rst b/README.rst index 6203d19..09e218c 100644 --- a/README.rst +++ b/README.rst @@ -30,13 +30,10 @@ Known issues ************ * Fixed configuration only currently (SF = Standard Fidelity) -<<<<<<< HEAD -======= * The API requires that the software DAC is continually fed samples at the input sample rate. Failure to do so will result in full scale DC output. Please ensure your application disables the hardware output stage before stopping feeding samples to the software DAC. ->>>>>>> develop **************** Development repo