This commit is contained in:
Steven Dan
2025-12-11 09:43:42 +08:00
commit d8b2974133
1822 changed files with 280037 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
// Copyright 2024-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include "app_dsp.h"
#include "stdbool.h"
#include "xcore/chanend.h"
#include "xcore/parallel.h"
#include <stages/adsp_pipeline.h>
#include <adsp_generated_auto.h>
#if SEND_TEST_CONTROL_COMMANDS
#include "run_control_cmds.h"
#endif
static adsp_pipeline_t * m_dsp;
// send data to dsp
void app_dsp_source(int32_t** data) {
adsp_pipeline_source(m_dsp, data);
}
// read output
void app_dsp_sink(int32_t** data) {
adsp_pipeline_sink(m_dsp, data);
}
DECLARE_JOB(dsp_control_thread, (chanend_t, module_instance_t*, size_t));
void dsp_control_thread(chanend_t c_control, module_instance_t* modules, size_t num_modules)
{
chan_in_word(c_control);
#if SEND_TEST_CONTROL_COMMANDS
send_control_cmds(m_dsp, c_control);
#endif
chan_out_word(c_control, 0);
}
// do dsp
void app_dsp_main(chanend_t c_control) {
m_dsp = adsp_auto_pipeline_init();
PAR_JOBS(
PJOB(adsp_auto_pipeline_main, (m_dsp)),
PJOB(dsp_control_thread, (c_control, m_dsp->modules, m_dsp->n_modules))
);
}
int app_dsp_frame_size() {
while(!m_dsp);
return m_dsp->input_mux.chan_cfg[0].frame_size;
}

View File

@@ -0,0 +1,28 @@
// Copyright 2024-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#pragma once
#include <xccompat.h>
#include <stdint.h>
#define MAX_CHANNELS (4)
// XC safe wrapper for adsp
void app_dsp_init(void);
// send data to dsp
void app_dsp_source(int32_t** data);
// read output
void app_dsp_sink(int32_t** data);
// do dsp
void app_dsp_main(chanend c_control);
// get frame size
int app_dsp_frame_size();
// TODO control

View File

@@ -0,0 +1,86 @@
// Copyright 2024-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "fileio_task.h"
static void show_usage()
{
puts(
"args: Specify in a single line in args.txt file.\n\n"
" -i input wav file (eg. input.wav)\n\n"
" -o output wav file (eg. -o output.wav)\n\n"
" -n no. of output channels (eg. -o 2. If not specified, set to be same as input channels)\n\n"
" -h Show this usage message and abort\n\n"
" -t Input/output offset, number of samples to throw out at boot to account for pipeline length\n\n"
);
exit(0);
}
static void parse_one_arg(char *arg_part_1, test_config_t *test_config)
{
char *arg_part_2 = strtok(NULL, " ");
if(strcmp(arg_part_1, "-h") == 0)
{
show_usage();
}
else
{
assert(arg_part_2 != NULL);
}
if(strcmp(arg_part_1, "-n") == 0)
{
test_config->num_output_channels = atoi(arg_part_2);
}
else if(strcmp(arg_part_1, "-t") == 0)
{
test_config->num_discard_frames = atoi(arg_part_2);
}
else if(strcmp(arg_part_1, "-i") == 0)
{
test_config->input_filename = malloc(strlen(arg_part_2)+1);
strcpy(test_config->input_filename, arg_part_2);
}
else if(strcmp(arg_part_1, "-o") == 0)
{
test_config->output_filename = malloc(strlen(arg_part_2)+1);
strcpy(test_config->output_filename, arg_part_2);
}
else
{
printf("Error: Invalid argument %s\n", arg_part_1);
assert(0);
}
}
void parse_args(const char *args_file, test_config_t *test_config)
{
FILE *fp = fopen(args_file, "r");
// Get the length of the line first
int count = 0;
int ch = fgetc(fp);
while((ch != '\n') && (ch != EOF)) {
count += 1;
ch = fgetc(fp);
}
fseek(fp, 0, SEEK_SET);
char *args = malloc((count+1)*sizeof(char));
// Read the full line
assert(fgets(args, count+1, fp) != NULL);
printf("args = %s\n", args);
char *pch;
pch = strtok(args, " ");
while(pch != NULL)
{
parse_one_arg(pch, test_config);
pch = strtok(NULL, " ");
}
}

View File

@@ -0,0 +1,12 @@
// Copyright 2024-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#pragma once
#include <xcore/chanend.h>
#include <xcore/channel.h>
#include <xcore/parallel.h>
#include <stddef.h>
#include "stages/adsp_module.h"
DECLARE_JOB(dsp_control_thread, (chanend_t, module_instance_t **, size_t));
DECLARE_JOB(dsp_thread, (chanend_t, chanend_t, module_instance_t**, size_t));

View File

@@ -0,0 +1,93 @@
// Copyright 2024-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include "fileio.h"
int file_open(file_t *fp, const char* name, const char *mode) {
#if TEST_WAV_XSCOPE
fp->xscope_file = xscope_open_file(name, (char*)mode);
#else
if(!strcmp(mode, "rb")) {
fp->file = open(name, O_RDONLY);
if(fp->file == -1) {return -1;}
}
else if(!strcmp(mode, "wb")) {
fp->file = open(name, O_WRONLY|O_CREAT, 0644);
if(fp->file == -1) {return -1;}
}
else
{
assert((0) && "invalid file open mode specified. Only 'rb' and 'wb' modes supported");
}
#endif
return 0;
}
void file_seek(file_t *fp, long int offset, int origin) {
#if TEST_WAV_XSCOPE
xscope_fseek(&fp->xscope_file, offset, origin);
#else
lseek(fp->file, offset, origin);
#endif
}
int get_current_file_offset(file_t *fp) {
#if TEST_WAV_XSCOPE
int current_offset = xscope_ftell(&fp->xscope_file);
#else
int current_offset = lseek(fp->file, 0, SEEK_CUR);
#endif
return current_offset;
}
int get_file_size(file_t *fp) {
#if TEST_WAV_XSCOPE
//find the current offset in the file
int current_offset = xscope_ftell(&fp->xscope_file);
//go to the end
xscope_fseek(&fp->xscope_file, 0, SEEK_END);
//get offset which will be file size
int size = xscope_ftell(&fp->xscope_file);
//return back to the original offset
xscope_fseek(&fp->xscope_file, current_offset, SEEK_SET);
#else
//find the current offset in the file
int current_offset = lseek(fp->file, 0, SEEK_CUR);
//get file size
int size = lseek(fp->file, 0, SEEK_END);
//go back to original offset
lseek(fp->file, current_offset, SEEK_SET);
#endif
return size;
}
void file_read(file_t *fp, void *buf, size_t count) {
#if TEST_WAV_XSCOPE
xscope_fread(&fp->xscope_file, (uint8_t*)buf, count);
#else
read(fp->file, buf, count);
#endif
}
void file_write(file_t *fp, void *buf, size_t count) {
#if TEST_WAV_XSCOPE
xscope_fwrite(&fp->xscope_file, (uint8_t*)buf, count);
#else
write(fp->file, buf, count);
#endif
}
void file_close(file_t *fp) {
#if !TEST_WAV_XSCOPE
close(fp->file);
#else
//files are closed by a single call to xscope_close_all_files()
#endif
}
void shutdown_session() {
//Needed for XSCOPE_ID_HOST_QUIT in xscope_close_all_files()
#if TEST_WAV_XSCOPE
xscope_close_all_files();
#endif
}

View File

@@ -0,0 +1,31 @@
// Copyright 2024-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#if TEST_WAV_XSCOPE
#include "xscope_io_device.h"
#endif
#include <fcntl.h>
#include <unistd.h>
typedef union {
int file;
#if TEST_WAV_XSCOPE
xscope_file_t xscope_file;
#endif
}file_t;
int file_open(file_t *fp, const char* name, const char *mode);
void file_read(file_t *fp, void *buf, size_t count);
void file_write(file_t *fp, void *buf, size_t count);
void file_seek(file_t *fp, long int offset, int origin);
void file_close(file_t *fp);
void shutdown_session(); //Needed for XSCOPE_ID_HOST_QUIT in xscope_close_all_files()
int get_current_file_offset(file_t *fp);
int get_file_size(file_t *fp);

View File

@@ -0,0 +1,135 @@
// Copyright 2022-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "fileio.h"
#include "wav_utils.h"
#define RIFF_SECTION_SIZE (12)
#define FMT_SUBCHUNK_MIN_SIZE (24)
#define EXTENDED_FMT_GUID_SIZE (16)
static const char wav_default_header[WAV_HEADER_BYTES] = {
0x52, 0x49, 0x46, 0x46,
0x00, 0x00, 0x00, 0x00,
0x57, 0x41, 0x56, 0x45,
0x66, 0x6d, 0x74, 0x20,
0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x64, 0x61, 0x74, 0x61,
0x00, 0x00, 0x00, 0x00,
};
int get_wav_header_details(file_t *input_file, wav_header *s, unsigned *header_size){
//Assume file is already open here. First rewind.
file_seek(input_file, 0, SEEK_SET);
//read riff header section (12 bytes)
file_read(input_file, (uint8_t*)(&s->riff_header[0]), RIFF_SECTION_SIZE);
if(memcmp(s->riff_header, "RIFF", sizeof(s->riff_header)) != 0)
{
printf("Error: couldn't find RIFF: 0x%x, 0x%x, 0x%x, 0x%x\n", s->riff_header[0], s->riff_header[1], s->riff_header[2], s->riff_header[3]);
return 1;
}
if(memcmp(s->wave_header, "WAVE", sizeof(s->wave_header)) != 0)
{
printf("couldn't find WAVE:, 0x%x, 0x%x, 0x%x, 0x%x\n", s->wave_header[0], s->wave_header[1], s->wave_header[2], s->wave_header[3]);
return 1;
}
file_read(input_file, (uint8_t*)&s->fmt_header[0], FMT_SUBCHUNK_MIN_SIZE);
if(memcmp(s->fmt_header, "fmt ", sizeof(s->fmt_header)) != 0)
{
printf("Error: couldn't find fmt: 0x%x, 0x%x, 0x%x, 0x%x\n", s->fmt_header[0], s->fmt_header[1], s->fmt_header[2], s->fmt_header[3]);
return 1;
}
unsigned fmt_subchunk_actual_size = s->fmt_chunk_size + sizeof(s->fmt_header) + sizeof(s->fmt_chunk_size); //fmt_chunk_size doesn't include the fmt_header(4) and size(4) bytes
unsigned fmt_subchunk_remaining_size = fmt_subchunk_actual_size - FMT_SUBCHUNK_MIN_SIZE;
if(s->audio_format == (short)0xfffe)
{
//seek to the end of fmt subchunk and rewind 16bytes to the beginning of GUID
file_seek(input_file, fmt_subchunk_remaining_size - EXTENDED_FMT_GUID_SIZE, SEEK_CUR);
//The first 2 bytes of GUID is the audio_format.
file_read(input_file, (uint8_t *)&s->audio_format, sizeof(s->audio_format));
//skip the rest of GUID
file_seek(input_file, EXTENDED_FMT_GUID_SIZE - sizeof(s->audio_format), SEEK_CUR);
}
else
{
//go to the end of fmt subchunk
file_seek(input_file, fmt_subchunk_remaining_size, SEEK_CUR);
}
if(s->audio_format != 1)
{
printf("Error: audio format(%d) is not PCM\n", s->audio_format);
return 1;
}
//read header (4 bytes) for the next subchunk
file_read(input_file, (uint8_t*)&s->data_header[0], sizeof(s->data_header));
//if next subchunk is fact, read subchunk size and skip it
if(memcmp(s->data_header, "fact", sizeof(s->data_header)) == 0)
{
uint32_t chunksize;
file_read(input_file, (uint8_t *)&chunksize, sizeof(s->data_bytes));
file_seek(input_file, chunksize, SEEK_CUR);
file_read(input_file, (uint8_t*)(&s->data_header[0]), sizeof(s->data_header));
}
//only thing expected at this point is the 'data' subchunk. Throw error if not found.
if(memcmp(s->data_header, "data", sizeof(s->data_header)) != 0)
{
printf("Error: couldn't find data: 0x%x, 0x%x, 0x%x, 0x%x\n", s->data_header[0], s->data_header[1], s->data_header[2], s->data_header[3]);
return 1;
}
//read data subchunk size.
file_read(input_file, (uint8_t *)&s->data_bytes, sizeof(s->data_bytes));
*header_size = get_current_file_offset(input_file); //total file size should be header_size + data_bytes
//No need to close file - handled by caller
return 0;
}
int wav_form_header(wav_header *header,
short audio_format,
short num_channels,
int sample_rate,
short bit_depth,
int num_frames){
memcpy((char*)header, wav_default_header, WAV_HEADER_BYTES);
header->audio_format = audio_format;
header->num_channels = num_channels;
header->sample_rate = sample_rate;
header->bit_depth = bit_depth;
header->byte_rate = sample_rate*bit_depth*num_channels/8;
header->sample_alignment = num_channels* (bit_depth/8);
int data_bytes = num_frames * num_channels * (bit_depth/8);
header->data_bytes = data_bytes;
header->wav_size = data_bytes + WAV_HEADER_BYTES - 8;
return 0;
}
unsigned wav_get_num_bytes_per_frame(const wav_header *s){
int bytes_per_sample = s->bit_depth/CHAR_BIT;
return (unsigned)(bytes_per_sample * s->num_channels);
}
int wav_get_num_frames(const wav_header *s){
unsigned bytes_per_frame = wav_get_num_bytes_per_frame(s);
return s->data_bytes / bytes_per_frame;
}
long wav_get_frame_start(const wav_header *s, unsigned frame_number, uint32_t wavheader_size){
return wavheader_size + frame_number * wav_get_num_bytes_per_frame(s);
}

View File

@@ -0,0 +1,44 @@
// Copyright 2022-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#pragma once
#include <stdint.h>
#define WAV_HEADER_BYTES 44
typedef struct wav_header {
// RIFF Header
char riff_header[4]; // Should be "RIFF"
int wav_size; // File size - 8 = data_bytes + WAV_HEADER_BYTES - 8
char wave_header[4]; // Should be "WAVE"
// Format Subsection
char fmt_header[4]; // Should be "fmt "
int fmt_chunk_size; // Size of the rest of this subchunk
short audio_format;
short num_channels;
int sample_rate;
int byte_rate; // sample_rate * num_channels * (bit_depth/8)
short sample_alignment; // num_channels * (bit_depth/8)
short bit_depth; // bits per sample
// Data Subsection
char data_header[4]; // Should be "data"
int data_bytes; // frame count * num_channels * (bit_depth/8)
} wav_header;
int get_wav_header_details(file_t *input_file, wav_header *s, unsigned *header_size);
int wav_form_header(wav_header *header,
short audio_format,
short num_channels,
int sample_rate,
short bit_depth,
int num_frames);
unsigned wav_get_num_bytes_per_frame(const wav_header *s);
int wav_get_num_frames(const wav_header *s);
long wav_get_frame_start(const wav_header *s, unsigned frame_number, uint32_t wavheader_size);

View File

@@ -0,0 +1,234 @@
// Copyright 2024-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <xcore/assert.h>
#include <xcore/channel.h>
#include <xcore/chanend.h>
#include <xcore/select.h>
#include "fileio.h"
#include "wav_utils.h"
#include "app_dsp.h"
#include "fileio_task.h"
#include "adsp_generated_auto.h"
#define FILEREAD_CHUNK_SIZE (1024)
// Token to start control operations
#define START_CONTROL_TOKEN (0x12345678)
/// @brief Read a chunk of data from the input file.
/// Takes care of different bit-depths and reads the data in left justified 32bit format.
/// @param input_file Input file handle
/// @param input Buffer to read into
/// @param input_header_struct wav header structure
static void read_input_frame(file_t *input_file, int32_t *input, wav_header *input_header_struct)
{
unsigned bytes_per_frame = wav_get_num_bytes_per_frame(input_header_struct);
if(input_header_struct->bit_depth == 32)
{
// Read directly to input
file_read (input_file, (uint8_t*)&input[0], bytes_per_frame * FILEREAD_CHUNK_SIZE); // Read in FILEREAD_CHUNK_SIZE chunks otherwise it's really slow
}
else if(input_header_struct->bit_depth == 16)
{
int16_t input16[ FILEREAD_CHUNK_SIZE * MAX_CHANNELS];
file_read (input_file, (uint8_t*)&input16[0], bytes_per_frame * FILEREAD_CHUNK_SIZE);
for(int i=0; i<FILEREAD_CHUNK_SIZE*input_header_struct->num_channels; i++)
{
input[i] = input16[i] << 16;
}
}
else if(input_header_struct->bit_depth == 24)
{
uint8_t input8[FILEREAD_CHUNK_SIZE * MAX_CHANNELS * 3];
uint8_t *inptr = input8;
file_read (input_file, (uint8_t*)&input8[0], bytes_per_frame * FILEREAD_CHUNK_SIZE);
for(int i=0; i<FILEREAD_CHUNK_SIZE; i++)
{
for(int ch=0; ch<input_header_struct->num_channels; ch++)
{
int32_t temp = 0;
for(int b=0; b<3; b++)
{
uint32_t val = (uint32_t)(*inptr++);
temp = temp | ((uint32_t)val << ((b+1)*8));
}
input[i*input_header_struct->num_channels + ch] = temp;
}
}
}
else
{
printf("ERROR: Unsupported bit depth %d\n", input_header_struct->bit_depth);
assert(0);
}
}
static void prepare_input(int32_t** dsp_input, int32_t* data, int n_ch, int frame_size) {
for(int f = 0; f < frame_size; ++f) {
for(int ch=0; ch < n_ch; ++ch) {
dsp_input[ch][f] = *data;
++data;
}
}
}
static void prepare_output(int32_t* data, int32_t** dsp_input, int n_ch, int frame_size) {
for(int f = 0; f < frame_size; ++f) {
for(int ch=0; ch < n_ch; ++ch) {
*data = dsp_input[ch][f];
++data;
}
}
}
/// @brief Task responsible for sending data read from a wav file to the pipeline and writing the output received
/// from the pipeline to another file. File operations are done using xscope_fileio functions.
/// @param c_control Channel used for control operations.
void fileio_task(chanend_t c_control)
{
printf("In test app!\n");
test_config_t test_config = {0};
parse_args("args.txt", &test_config);
printf("After parse_args: Input file %s, Output file %s, num channels %d\n", test_config.input_filename, test_config.output_filename, test_config.num_output_channels);
assert(test_config.input_filename != NULL);
assert(test_config.output_filename != NULL);
file_t input_file, output_file;
int ret = file_open(&input_file, test_config.input_filename, "rb");
assert((!ret) && "Failed to open file");
ret = file_open(&output_file, test_config.output_filename, "wb");
assert((!ret) && "Failed to open file");
wav_header input_header_struct, output_header_struct;
unsigned input_header_size;
if(get_wav_header_details(&input_file, &input_header_struct, &input_header_size) != 0){
printf("error in get_wav_header_details()\n");
_Exit(1);
}
if(test_config.num_output_channels == 0)
{
test_config.num_output_channels = input_header_struct.num_channels;
}
assert(input_header_struct.num_channels <= MAX_CHANNELS);
assert(test_config.num_output_channels <= MAX_CHANNELS);
unsigned frame_count = wav_get_num_frames(&input_header_struct);
// Calculate number of frames in the wav file
unsigned block_count = frame_count / (FILEREAD_CHUNK_SIZE);
xassert(0 != block_count);
wav_form_header(&output_header_struct,
input_header_struct.audio_format,
test_config.num_output_channels,
input_header_struct.sample_rate,
32, // Output always 32 bits
block_count*FILEREAD_CHUNK_SIZE);
file_write(&output_file, (uint8_t*)(&output_header_struct), WAV_HEADER_BYTES);
unsigned bytes_per_frame = wav_get_num_bytes_per_frame(&input_header_struct);
printf("Num input channels = %d\n", input_header_struct.num_channels);
printf("Num output channels = %d\n", test_config.num_output_channels);
printf("Discard frames = %d\n", test_config.num_discard_frames);
printf("bytes_per_frame = %d\n", bytes_per_frame);
printf("Block count = %d\n", block_count);
printf("Frame size = %d\n", app_dsp_frame_size());
int32_t input[ FILEREAD_CHUNK_SIZE * MAX_CHANNELS] = {0}; // Array for storing interleaved input read from wav file
int32_t output[FILEREAD_CHUNK_SIZE * MAX_CHANNELS] = {0};
xassert(FILEREAD_CHUNK_SIZE % app_dsp_frame_size() ==0); // frame size must be a factor of read size
int32_t** dsp_input = malloc(sizeof(int32_t*) * input_header_struct.num_channels);
for(int i = 0; i < input_header_struct.num_channels; ++i) {
dsp_input[i] = calloc(app_dsp_frame_size(), sizeof(int32_t));
}
int32_t** dsp_output = malloc(sizeof(int32_t*) * test_config.num_output_channels);
for(int i = 0; i < test_config.num_output_channels; ++i) {
dsp_output[i] = calloc(app_dsp_frame_size(), sizeof(int32_t));
}
// Send a token to indicate that the control parameters, if any, can be sent
chan_out_word(c_control, START_CONTROL_TOKEN);
const int pretest_zero_count = 64000; // MUST MATCH test_stages.py !!!!!
for(int i = 0; i < pretest_zero_count/app_dsp_frame_size(); ++i) {
// push data through pipeline so the control works.
// the amount of data is fixed so that it matches
// the test.
app_dsp_source(dsp_input);
app_dsp_sink(dsp_output);
}
// check that control is done.
SELECT_RES(CASE_THEN(c_control, success), DEFAULT_THEN(error)) {
success:
chan_in_word(c_control);
break;
error:
printf("Error Control should be done by now\n");
_Exit(1);
break;
}
int discard = test_config.num_discard_frames;
for(int i=0; i<block_count; i++)
{
printf("block %d\n", i);
read_input_frame(&input_file, input, &input_header_struct);
int32_t* block_input = input;
int32_t* block_output = output;
// 1. discard initial outputs which will be 0
for(int d = 0; d < discard; d++) {
prepare_input(dsp_input, block_input, input_header_struct.num_channels, app_dsp_frame_size());
app_dsp_source(dsp_input);
block_input += input_header_struct.num_channels * app_dsp_frame_size();
// discard these outputs
app_dsp_sink(dsp_output);
}
// 2. source and sink pipeline
int loop_n = FILEREAD_CHUNK_SIZE/app_dsp_frame_size();//- test_config.num_discard_frames;
for(int j=0; j < loop_n; j++)
{
prepare_input(dsp_input, block_input, input_header_struct.num_channels, app_dsp_frame_size());
app_dsp_source(dsp_input);
app_dsp_sink(dsp_output);
prepare_output(block_output, dsp_output, test_config.num_output_channels, app_dsp_frame_size());
block_input += input_header_struct.num_channels * app_dsp_frame_size();
block_output += test_config.num_output_channels * app_dsp_frame_size();
}
// 3. read only, should empty the pipeline
for(int d = 0; d < discard; d++) {
app_dsp_sink(dsp_output);
prepare_output(block_output, dsp_output, test_config.num_output_channels, app_dsp_frame_size());
block_output += test_config.num_output_channels * app_dsp_frame_size();
}
file_write(&output_file, (uint8_t*)&output[0], test_config.num_output_channels * FILEREAD_CHUNK_SIZE * sizeof(int32_t));
discard = 0; // synchronisation complete
}
file_close(&input_file);
file_close(&output_file);
shutdown_session();
adsp_auto_print_thread_max_ticks();
printf("DONE\n");
_Exit(0);
}

View File

@@ -0,0 +1,14 @@
// Copyright 2024-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#pragma once
typedef struct
{
char *input_filename;
char *output_filename;
int num_discard_frames;
int num_output_channels;
}test_config_t;
void parse_args(const char *args_file, test_config_t *test_config);

View File

@@ -0,0 +1,25 @@
// Copyright 2024-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <xcore/assert.h>
#include <xcore/parallel.h>
#include <xcore/channel.h>
#include <xcore/chanend.h>
DECLARE_JOB(fileio_task, (chanend_t));
DECLARE_JOB(app_dsp_main, (chanend_t));
int main_c()
{
channel_t c_control = chan_alloc();
PAR_JOBS(
PJOB(fileio_task, (c_control.end_a)),
PJOB(app_dsp_main, (c_control.end_b))
);
return 0;
}

View File

@@ -0,0 +1,29 @@
// Copyright 2024-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <platform.h>
#include <xs1.h>
#include <xscope.h>
#include <stdlib.h>
#ifdef __XC__
#define chanend_t chanend
#else
#include <xcore/chanend.h>
#endif
#include "xscope_io_device.h"
extern int main_c();
int main (void)
{
chan xscope_chan;
par
{
xscope_host_data(xscope_chan);
on tile[0]: {
xscope_io_init(xscope_chan);
exit(main_c());
}
}
return 0;
}

View File

@@ -0,0 +1,93 @@
// Copyright 2024-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#if SEND_TEST_CONTROL_COMMANDS
#include <string.h>
#include "adsp_control.h"
#include "adsp_instance_id_auto.h"
#include "xcore/hwtimer.h"
#include <xcore/assert.h>
#include <debug_print.h>
#include "run_control_cmds.h"
#include "control_test_params.h"
#include "print.h"
#define CONTROL_COMMAND_TIMEOUT_TICKS 1000000 // one tick is 10ns
#define CONTROL_COMMAND_DELAY_TICKS 1000 // one tick is 10ns
void send_control_cmds(adsp_pipeline_t * m_dsp, chanend_t c_control) {
adsp_stage_control_cmd_t cmd;
int8_t payload_buf[CMD_PAYLOAD_MAX_SIZE];
cmd.instance_id = control_stage_index;
uint8_t values_write[CMD_PAYLOAD_MAX_SIZE];
uint8_t values_read[CMD_PAYLOAD_MAX_SIZE];
adsp_controller_t ctrl;
adsp_controller_init(&ctrl, m_dsp);
// Iterate through all the commands in the stage config
for (int cmd_idx = 0; cmd_idx<CMD_TOTAL_NUM; cmd_idx++)
{
// Fill up the command fields
cmd.cmd_id = control_config[cmd_idx].cmd_id;
cmd.payload_len = control_config[cmd_idx].cmd_size;
cmd.payload = payload_buf;
memset(cmd.payload, 0, cmd.payload_len);
// Write control command to the stage
memcpy(cmd.payload, control_config[cmd_idx].payload, cmd.payload_len);
// Save the payload values for the final check
memcpy(values_write, cmd.payload, cmd.payload_len);
adsp_control_status_t ret = ADSP_CONTROL_BUSY;
uint32_t time_start = get_reference_time();
// Write the data
do {
ret = adsp_write_module_config(&ctrl, &cmd);
// Assert if operation is taking too long
if (get_reference_time() > time_start + CONTROL_COMMAND_TIMEOUT_TICKS) {
xassert(0 && "Timer expired while writing control command");
}
}while(ret == ADSP_CONTROL_BUSY);
xassert(ret == ADSP_CONTROL_SUCCESS);
memset(cmd.payload, 0, cmd.payload_len);
// Add a delay of 10 ms to address the bug described in https://xmosjira.atlassian.net/browse/LCD-257
hwtimer_t t_delay = hwtimer_alloc();
hwtimer_delay(t_delay, CONTROL_COMMAND_DELAY_TICKS);
hwtimer_free(t_delay);
// Read back the written data
ret = ADSP_CONTROL_BUSY;
time_start = get_reference_time();
do {
ret = adsp_read_module_config(&ctrl, &cmd);
// Assert if operation is taking too long
if (get_reference_time() > time_start + CONTROL_COMMAND_TIMEOUT_TICKS) {
xassert(0 && "Timer expired while reading control command");
}
}while(ret == ADSP_CONTROL_BUSY);
xassert(ret == ADSP_CONTROL_SUCCESS);
memcpy(values_read, cmd.payload, cmd.payload_len);
// Check that read and written values match
for(int i=0; i<cmd.payload_len; i++)
{
if(values_read[i] != values_write[i])
{
debug_printf("Command %d: mismatch at index %d. Expected %d, found %d\n", cmd.cmd_id, i, values_write[i], values_read[i]);
xassert(0);
}
}
}
}
#endif

View File

@@ -0,0 +1,8 @@
// Copyright 2024-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include "adsp_pipeline.h"
// Function to write and read back control parameters.
// Only the commands included in the stage config are used.
void send_control_cmds(adsp_pipeline_t * m_dsp, chanend_t c_control);