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

490
lib_i2s/lib_i2s/api/i2s.h Normal file
View File

@@ -0,0 +1,490 @@
// Copyright 2014-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _i2s_h_
#define _i2s_h_
#include <xs1.h>
#include <stdint.h>
#include <stddef.h>
/** I2S mode.
*
* This type is used to describe the I2S mode.
*/
typedef enum i2s_mode_t {
I2S_MODE_I2S, ///< The LR clock transitions ahead of the data by one bit clock.
I2S_MODE_LEFT_JUSTIFIED, ///< The LR clock and data are phase aligned.
} i2s_mode_t;
/** I2S slave bit clock polarity.
*
* Standard I2S is positive, that is toggle data and LR clock on falling
* edge of bit clock and sample them on rising edge of bit clock. Some
* masters have it the other way around.
*/
typedef enum i2s_slave_bclk_polarity_t {
I2S_SLAVE_SAMPLE_ON_BCLK_RISING, ///<< Toggle falling, sample rising (default if not set)
I2S_SLAVE_SAMPLE_ON_BCLK_FALLING, ///<< Toggle rising, sample falling
} i2s_slave_bclk_polarity_t;
/** I2S configuration structure.
*
* This structure describes the configuration of an I2S bus.
*/
typedef struct i2s_config_t {
unsigned mclk_bclk_ratio; ///< The ratio between the master clock and bit clock signals.
i2s_mode_t mode; ///< The mode of the LR clock.
i2s_slave_bclk_polarity_t slave_bclk_polarity; ///< Slave bit clock polarity.
unsigned slave_frame_synch_error;///< Set if I2S slave restarted because of a frame synch error. No meaning for master.
} i2s_config_t;
/** TDM configuration structure.
*
* This structure describes the configuration of a TDM bus.
*/
typedef struct tdm_config_t {
int offset; ///< The number of bits that the FSYNC signal transitions before the data. Must be a value between -31 and 31.
unsigned sync_len; ///< The length that the FSYNC signal stays high counted as ticks of the bit clock.
unsigned channels_per_frame; ///< The number of channels in a TDM frame. This must be a power of 2.
} tdm_config_t;
/** Restart command type.
*
* Restart commands that can be signalled to the I2S or TDM component.
*/
typedef enum i2s_restart_t {
I2S_NO_RESTART = 0, ///< Do not restart.
I2S_RESTART, ///< Restart the bus (causes the I2S/TDM to stop and a new init callback to occur allowing reconfiguration of the BUS).
I2S_SHUTDOWN ///< Shutdown. This will cause the I2S/TDM component to exit.
} i2s_restart_t;
/** Interface representing callback events that can occur during the
* operation of the I2S task
*/
typedef interface i2s_callback_if {
/** I2S initialization event callback.
*
* The I2S component will call this
* when it first initializes on first run of after a restart.
*
* \param i2s_config This structure is provided if the connected
* component drives an I2S bus. The members
* of the structure should be set to the
* required configuration.
* \param tdm_config This structure is provided if the connected
* component drives an TDM bus. The members
* of the structure should be set to the
* required configuration.
*/
void init(i2s_config_t &?i2s_config, tdm_config_t &?tdm_config);
/** I2S restart check callback.
*
* This callback is called once per frame. The application must return the
* required restart behaviour.
*
* \return The return value should be set to
* ``I2S_NO_RESTART``, ``I2S_RESTART`` or
* ``I2S_SHUTDOWN``.
*/
i2s_restart_t restart_check();
/** Receive an incoming sample.
*
* This callback will be called when a new sample is read in by the I2S
* component.
*
* \param index The index of the sample in the frame.
* \param sample The sample data as a signed 32-bit value. The component
* may not use all 32 bits of the value (for example, many
* I2S codecs are 24-bit), in which case the bottom bits
* are ignored.
*/
void receive(size_t index, int32_t sample);
/** Request an outgoing sample.
*
* This callback will be called when the I2S component needs a new sample.
*
* \param index The index of the requested sample in the frame.
* \returns The sample data as a signed 32-bit value. The component
* may not have 32-bits of accuracy (for example, many
* I2S codecs are 24-bit), in which case the bottom bits
* will be arbitrary values.
*/
int32_t send(size_t index);
} i2s_callback_if;
/** Interface representing callback events that can occur during the
* operation of the I2S task. This is a more efficient interface
* and reccomended for new designs.
*/
typedef interface i2s_frame_callback_if {
/** I2S frame-based initialization event callback.
*
* The I2S component will call this
* when it first initializes on first run of after a restart.
*
* \param i2s_config This structure is provided if the connected
* component drives an I2S bus. The members
* of the structure should be set to the
* required configuration.
* \param tdm_config This structure is provided if the connected
* component drives an TDM bus. The members
* of the structure should be set to the
* required configuration.
*/
void init(i2s_config_t &?i2s_config, tdm_config_t &?tdm_config);
/** I2S frame-based restart check callback.
*
* This callback is called once per frame. The application must return the
* required restart behaviour.
*
* \return The return value should be set to
* ``I2S_NO_RESTART``, ``I2S_RESTART`` or
* ``I2S_SHUTDOWN``.
*/
i2s_restart_t restart_check();
/** Receive an incoming frame of samples.
*
* This callback will be called when a new frame of samples is read in by the I2S
* frame-based component.
*
* \param num_in The number of input channels contained within the array.
* \param samples The samples data array as signed 32-bit values. The component
* may not have 32-bits of accuracy (for example, many
* I2S codecs are 24-bit), in which case the bottom bits
* will be arbitrary values.
*/
void receive(size_t num_in, int32_t samples[num_in]);
/** Request an outgoing frame of samples.
*
* This callback will be called when the I2S frame-based component needs
* a new frame of samples.
*
* \param num_out The number of output channels contained within the array.
* \param samples The samples data array as signed 32-bit values. The component
* may not have 32-bits of accuracy (for example, many
* I2S codecs are 24-bit), in which case the bottom bits
* will be arbitrary values.
*/
void send(size_t num_out, int32_t samples[num_out]);
} i2s_frame_callback_if;
/** I2S master component.
*
* This task performs I2S on the provided pins. It will perform callbacks over
* the i2s_callback_if interface to get/receive data from the application
* using this component.
*
* The component performs I2S master so will drive the word clock and
* bit clock lines.
*
* \param i2s_i The I2S callback interface to connect to
* the application
* \param p_dout An array of data output ports
* \param num_out The number of output data ports
* \param p_din An array of data input ports
* \param num_in The number of input data ports
* \param p_bclk The bit clock output port
* \param p_lrclk The word clock output port
* \param bclk A clock that will get configured for use with
* the bit clock
* \param mclk The clock connected to the master clock frequency.
* Usually this should be configured to be driven by
* an incoming master system clock.
*/
void i2s_master(client i2s_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
out buffered port:32 p_bclk,
out buffered port:32 p_lrclk,
clock bclk,
const clock mclk);
#if defined(__XS2A__) || defined(__XS3A__) || defined(__DOXYGEN__)
/** I2S frame-based master component **for xCORE200 and xcore.ai only**
*
* This task performs I2S on the provided pins. It will perform callbacks over
* the i2s_frame_callback_if interface to get/receive frames of data from the
* application using this component.
*
* The component performs I2S master so will drive the word clock and
* bit clock lines.
*
* This is a more efficient version of i2s master which reduces callback
* frequency and allows useful processing to be done in distributable i2s handler tasks.
* It also uses xCORE200 and xcore.ai specific features to remove the need for software
* BCLK generation which decreases processor overhead.
*
* \param i2s_i The I2S frame callback interface to connect to
* the application
* \param p_dout An array of data output ports
* \param num_out The number of output data ports
* \param p_din An array of data input ports
* \param num_in The number of input data ports
* \param num_data_bits The number of bits per data word
* \param p_bclk The bit clock output port
* \param p_lrclk The word clock output port
* \param p_mclk Input port which supplies the master clock
* \param bclk A clock that will get configured for use with
* the bit clock
*/
void i2s_frame_master(client i2s_frame_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
static const size_t num_data_bits,
out port p_bclk,
out buffered port:32 p_lrclk,
in port p_mclk,
clock bclk);
/** I2S frame-based master component with 4-bit ports **for xCORE200 and xcore.ai only**
*
* This task performs I2S on the provided 4-bit ports. It will perform callbacks over
* the i2s_frame_callback_if interface to get/receive frames of data from the
* application using this component.
*
* The component performs I2S master so will drive the word clock and
* bit clock lines.
*
* This is a more efficient version of i2s master which reduces callback
* frequency and allows useful processing to be done in distributable i2s handler tasks.
* It also uses xCORE200 and xcore.ai specific features to remove the need for software
* BCLK generation which decreases processor overhead.
*
* This component can only operate with a 32-bit data word length.
*
* \param i2s_i The I2S frame callback interface to connect to
* the application
* \param p_dout A 4-bit data output port
* \param num_out The number of output data streams
* \param p_din A 4-bit data input port
* \param num_in The number of input data streams
* \param p_bclk The bit clock output port
* \param p_lrclk The word clock output port
* \param p_mclk Input port which supplies the master clock
* \param bclk A clock that will get configured for use with
* the bit clock
*/
void i2s_frame_master_4b(client i2s_frame_callback_if i2s_i,
out buffered port:32 ?p_dout,
static const size_t num_out,
in buffered port:32 ?p_din,
static const size_t num_in,
out port p_bclk,
out buffered port:32 p_lrclk,
in port p_mclk,
clock bclk);
/** I2S frame-based master component **for xCORE200 and xcore.ai only**
*
* This task performs I2S on the provided pins. It will perform callbacks over
* the i2s_frame_callback_if interface to get/receive frames of data from the
* application using this component.
*
* The component performs I2S master so will drive the word clock and
* bit clock lines.
*
* This is a more efficient version of i2s master which reduces callback
* frequency and allows useful processing to be done in distributable i2s handler tasks.
* It also uses xCORE200 and xcore.ai specific features to remove the need for software
* BCLK generation which decreases processor overhead.
*
* \param i2s_i The I2S frame callback interface to connect to
* the application
* \param p_dout An array of data output ports
* \param num_out The number of output data ports
* \param p_din An array of data input ports
* \param num_in The number of input data ports
* \param num_data_bits The number of bits per data word
* \param p_bclk The bit clock output port
* \param p_lrclk The word clock output port
* \param bclk A clock that is configured externally to be used as the bit clock
*
*/
void i2s_frame_master_external_clock(client i2s_frame_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
static const size_t num_data_bits,
out port p_bclk,
out buffered port:32 p_lrclk,
clock bclk);
/** I2S frame-based master component with 4-bit ports **for xCORE200 amd xcore.ai only**
*
* This task performs I2S on the provided 4-bit ports. It will perform callbacks over
* the i2s_frame_callback_if interface to get/receive frames of data from the
* application using this component.
*
* The component performs I2S master so will drive the word clock and
* bit clock lines.
*
* This is a more efficient version of i2s master which reduces callback
* frequency and allows useful processing to be done in distributable i2s handler tasks.
* It also uses xCORE200 and xcore.ai specific features to remove the need for software
* BCLK generation which decreases processor overhead.
*
* This component can only operate with a 32-bit data word length.
*
* \param i2s_i The I2S frame callback interface to connect to
* the application
* \param p_dout An array of data output ports
* \param num_out The number of output data ports
* \param p_din An array of data input ports
* \param num_in The number of input data ports
* \param p_bclk The bit clock output port
* \param p_lrclk The word clock output port
* \param bclk A clock that will get configured for use with
* the bit clock
*/
void i2s_frame_master_external_clock_4b(client i2s_frame_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
out port p_bclk,
out buffered port:32 p_lrclk,
clock bclk);
#endif // __XS2A__
/** I2S slave component.
*
* This task performs I2S on the provided pins. It will perform callbacks over
* the i2s_callback_if interface to get/receive data from the application
* using this component.
*
* The component performs I2S slave so will expect the word clock and
* bit clock to be driven externally.
*
* \param i2s_i The I2S callback interface to connect to
* the application
* \param p_dout An array of data output ports
* \param num_out The number of output data ports
* \param p_din An array of data input ports
* \param num_in The number of input data ports
* \param p_bclk The bit clock input port
* \param p_lrclk The word clock input port
* \param bclk A clock that will get configured for use with
* the bit clock
*/
void i2s_slave(client i2s_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
in port p_bclk,
in buffered port:32 p_lrclk,
clock bclk);
/** I2S High Efficiency slave component.
*
* This task performs I2S on the provided pins. It will perform callbacks over
* the i2s_callback_if interface to get/receive data from the application
* using this component.
*
* The component performs I2S slave so will expect the word clock and
* bit clock to be driven externally.
*
* \param i2s_i The I2S callback interface to connect to
* the application
* \param p_dout An array of data output ports
* \param num_out The number of output data ports
* \param p_din An array of data input ports
* \param num_in The number of input data ports
* \param num_data_bits The number of bits per data word
* \param p_bclk The bit clock input port
* \param p_lrclk The word clock input port
* \param bclk A clock that will get configured for use with
* the bit clock
*/
void i2s_frame_slave(client i2s_frame_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
static const size_t num_data_bits,
in port p_bclk,
in buffered port:32 p_lrclk,
clock bclk);
/** I2S High Efficiency slave component.
*
* This task performs I2S on the provided pins. It will perform callbacks over
* the i2s_callback_if interface to get/receive data from the application
* using this component.
*
* The component performs I2S slave so will expect the word clock and
* bit clock to be driven externally.
*
* \param i2s_i The I2S callback interface to connect to
* the application
* \param p_dout An array of data output ports
* \param num_out The number of output data ports
* \param p_din An array of data input ports
* \param num_in The number of input data ports
* \param p_bclk The bit clock input port
* \param p_lrclk The word clock input port
* \param bclk A clock that will get configured for use with
* the bit clock
*/
void i2s_frame_slave_4b(client i2s_frame_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
in port p_bclk,
in buffered port:32 p_lrclk,
clock bclk);
/** TDM master component.
*
* This task performs TDM on the provided pins. It will perform callbacks over
* the i2s_callback_if interface to get/receive data from the application
* using this component.
*
* The component performs as TDM master so will drive the fsync signal.
*
* \param tdm_i The TDM callback interface to connect to
* the application
* \param p_fsync The frame sync output port
* \param p_dout An array of data output ports
* \param num_out The number of output data ports
* \param p_din An array of data input ports
* \param num_in The number of input data ports
* \param clk The clock connected to the bit/master clock frequency.
* Usually this should be configured to be driven by
* an incoming master system clock.
*/
void tdm_master(client interface i2s_callback_if tdm_i,
out buffered port:32 p_fsync,
out buffered port:32 (&?p_dout)[num_out],
size_t num_out,
in buffered port:32 (&?p_din)[num_in],
size_t num_in,
clock clk);
#include <i2s_master_impl.h>
#include <i2s_frame_master_impl.h>
#include <i2s_frame_master_4b_impl.h>
#include <i2s_slave_impl.h>
#include <i2s_frame_slave_impl.h>
#include <i2s_frame_slave_4b_impl.h>
#include <tdm_master_impl.h>
#endif // _i2s_h_

View File

@@ -0,0 +1,7 @@
set(LIB_NAME lib_i2s)
set(LIB_VERSION 5.1.0)
set(LIB_INCLUDES api src)
set(LIB_COMPILER_FLAGS -O3)
set(LIB_DEPENDENT_MODULES "lib_xassert(4.2.0)")
XMOS_REGISTER_MODULE()

View File

@@ -0,0 +1,15 @@
VERSION = 5.1.0
DEPENDENT_MODULES = lib_xassert(>=4.2.0)
MODULE_XCC_FLAGS = $(XCC_FLAGS) \
-O3
OPTIONAL_HEADERS +=
EXPORT_INCLUDE_DIRS = api \
src
INCLUDE_DIRS = $(EXPORT_INCLUDE_DIRS)
SOURCE_DIRS = src

View File

@@ -0,0 +1,67 @@
// Copyright 2015-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <i2s.h>
#undef i2s_master
void i2s_master(client i2s_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
out buffered port:32 p_bclk,
out buffered port:32 p_lrclk,
clock bclk,
const clock mclk)
{}
#if defined(__XS2A__) || defined(__XS3A__)
#undef i2s_frame_master
void i2s_frame_master(client i2s_frame_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
static const size_t num_data_bits,
out port p_bclk,
out buffered port:32 p_lrclk,
in port p_mclk,
clock bclk)
{}
#endif // __XS2A__ || __XS3A__
#undef i2s_slave
void i2s_slave(client i2s_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
in port p_bclk,
in buffered port:32 p_lrclk,
clock bclk)
{
}
#undef i2s_frame_slave
void i2s_frame_slave(client i2s_frame_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
static const size_t num_data_bits,
in port p_bclk,
in buffered port:32 p_lrclk,
clock bclk)
{
}
#undef tdm_master
void tdm_master(client interface i2s_callback_if tdm_i,
out buffered port:32 p_fsync,
out buffered port:32 (&?p_dout)[num_out],
size_t num_out,
in buffered port:32 (&?p_din)[num_in],
size_t num_in,
clock clk){
}

View File

@@ -0,0 +1,259 @@
// Copyright 2016-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#if defined(__XS2A__) || defined(__XS3A__)
#include "limits.h"
#include <xs1.h>
#include <xclib.h>
#include "i2s.h"
#include "xassert.h"
void i2s_frame_master_4b_setup(
int32_t out_samps[],
int32_t in_samps[],
out buffered port:32 ?p_dout,
in buffered port:32 ?p_din,
clock bclk,
out buffered port:32 p_lrclk
);
void i2s_frame_master_4b_loop_part_1(
int32_t out_samps[],
int32_t in_samps[],
out buffered port:32 ?p_dout,
in buffered port:32 ?p_din,
out buffered port:32 p_lrclk
);
void i2s_frame_master_4b_loop_part_2(
int32_t out_samps[],
int32_t in_samps[],
out buffered port:32 ?p_dout,
in buffered port:32 ?p_din,
out buffered port:32 p_lrclk
);
static void i2s_setup_bclk_4b(
clock bclk,
in port p_mclk,
unsigned mclk_bclk_ratio
){
set_clock_on(bclk);
configure_clock_src_divide(bclk, p_mclk, mclk_bclk_ratio >> 1);
}
static void i2s_frame_init_ports_4b(
out buffered port:32 ?p_dout,
static const size_t num_out,
in buffered port:32 ?p_din,
static const size_t num_in,
out port p_bclk,
out buffered port:32 p_lrclk,
clock bclk
){
if (!isnull(p_dout))
{
configure_out_port(p_dout, bclk, 0);
clearbuf(p_dout);
}
if (!isnull(p_din))
{
configure_in_port(p_din, bclk);
clearbuf(p_din);
}
configure_out_port(p_lrclk, bclk, 1);
clearbuf(p_lrclk);
configure_port_clock_output(p_bclk, bclk);
}
#pragma unsafe arrays
static i2s_restart_t i2s_frame_ratio_n_4b(
client i2s_frame_callback_if i2s_i,
out buffered port:32 ?p_dout,
static const size_t num_out,
in buffered port:32 ?p_din,
static const size_t num_in,
out port p_bclk,
clock bclk,
out buffered port:32 p_lrclk,
i2s_mode_t mode){
const int offset = (mode == I2S_MODE_I2S) ? 1 : 0;
int32_t in_samps[16] = {0}; // Workaround: should be (num_in << 1) but compiler thinks that isn't const,
int32_t out_samps[16] = {0}; // so setting to 16 which should be big enough for most cases
// Since #pragma unsafe arrays is used need to ensure array won't overflow.
assert((num_in << 1) <= 16);
if (num_out)
{
i2s_i.send(num_out << 1, out_samps);
}
p_lrclk @ 1 <: 0;
if (!isnull(p_din))
{
asm volatile("setpt res[%0], %1"
:
:"r"(p_din), "r"(8 + offset));
}
if (!isnull(p_dout))
{
asm volatile("setpt res[%0], %1"
:
:"r"(p_dout), "r"(1 + offset));
}
i2s_frame_master_4b_setup(out_samps, in_samps, p_dout, p_din, bclk, p_lrclk);
while (1)
{
// Check for restart
i2s_restart_t restart = i2s_i.restart_check();
if (num_out)
{
i2s_i.send(num_out << 1, out_samps);
}
i2s_frame_master_4b_loop_part_1(out_samps, in_samps, p_dout, p_din, p_lrclk);
if (num_in)
{
i2s_i.receive(num_in << 1, in_samps);
}
if (restart == I2S_NO_RESTART)
{
i2s_frame_master_4b_loop_part_2(out_samps, in_samps, p_dout, p_din, p_lrclk);
}
else
{
if (!num_in)
{
// Prevent the clock from being stopped before the last word
// has been sent if there are no RX ports.
sync(p_dout);
}
stop_clock(bclk);
return restart;
}
}
return I2S_RESTART;
}
#define i2s_frame_master_4b i2s_frame_master0_4b
static void i2s_frame_master0_4b(
client i2s_frame_callback_if i2s_i,
out buffered port:32 ?p_dout,
static const size_t num_out,
in buffered port:32 ?p_din,
static const size_t num_in,
out port p_bclk,
out buffered port:32 p_lrclk,
in port p_mclk,
clock bclk){
while(1){
i2s_config_t config;
i2s_i.init(config, null);
if (isnull(p_dout) && isnull(p_din)) {
fail("Must provide non-null p_dout or p_din");
}
unsafe
{
if ((!isnull(p_din) && (XS1_RES_ID_PORTWIDTH((int)p_din) != 4)) ||
(!isnull(p_dout) && (XS1_RES_ID_PORTWIDTH((int)p_dout) != 4)))
{
fail("This function is designed only for use with 4b ports");
}
}
i2s_setup_bclk_4b(bclk, p_mclk, config.mclk_bclk_ratio);
//This ensures that the port time on all the ports is at 0
i2s_frame_init_ports_4b(p_dout, num_out, p_din, num_in, p_bclk, p_lrclk, bclk);
i2s_restart_t restart =
i2s_frame_ratio_n_4b(i2s_i, p_dout, num_out, p_din,
num_in, p_bclk, bclk, p_lrclk,
config.mode);
if (restart == I2S_SHUTDOWN)
return;
}
}
#define i2s_frame_master_external_clock_4b i2s_frame_master0_external_clock_4b
static void i2s_frame_master0_external_clock_4b(
client i2s_frame_callback_if i2s_i,
out buffered port:32 ?p_dout,
static const size_t num_out,
in buffered port:32 ?p_din,
static const size_t num_in,
out port p_bclk,
out buffered port:32 p_lrclk,
clock bclk){
while(1){
i2s_config_t config;
i2s_i.init(config, null);
if (isnull(p_dout) && isnull(p_din)) {
fail("Must provide non-null p_dout or p_din");
}
unsafe
{
if ((!isnull(p_din) && (XS1_RES_ID_PORTWIDTH((int)p_din) != 4)) ||
(!isnull(p_dout) && (XS1_RES_ID_PORTWIDTH((int)p_dout) != 4)))
{
fail("This function is designed only for use with 4b ports");
}
}
//This ensures that the port time on all the ports is at 0
i2s_frame_init_ports_4b(p_dout, num_out, p_din, num_in, p_bclk, p_lrclk, bclk);
i2s_restart_t restart =
i2s_frame_ratio_n_4b(i2s_i, p_dout, num_out, p_din,
num_in, p_bclk, bclk, p_lrclk,
config.mode);
if (restart == I2S_SHUTDOWN)
return;
}
}
// These functions are just to avoid unused static function warnings for i2s_frame_master0
// and i2s_frame_master0_external_clock. They should never be called.
inline void i2s_frame_master1_4b(client interface i2s_frame_callback_if i,
out buffered port:32 i2s_dout,
static const size_t num_i2s_out,
in buffered port:32 i2s_din,
static const size_t num_i2s_in,
out port i2s_bclk,
out buffered port:32 i2s_lrclk,
in port p_mclk,
clock clk_bclk) {
i2s_frame_master0_4b(i, i2s_dout, num_i2s_out, i2s_din, num_i2s_in,
i2s_bclk, i2s_lrclk, p_mclk, clk_bclk);
}
inline void i2s_frame_master1_external_clock_4b(client interface i2s_frame_callback_if i,
out buffered port:32 i2s_dout,
static const size_t num_i2s_out,
in buffered port:32 i2s_din,
static const size_t num_i2s_in,
out port i2s_bclk,
out buffered port:32 i2s_lrclk,
clock clk_bclk) {
i2s_frame_master0_external_clock_4b(i, i2s_dout, num_i2s_out, i2s_din, num_i2s_in,
i2s_bclk, i2s_lrclk, clk_bclk);
}
#endif // __XS2A__ || __XS3A__

View File

@@ -0,0 +1,168 @@
// Copyright 2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/*
void i2s_frame_master_4b_loop_part_1(
int32_t out_samps[],
int32_t in_samps[],
out buffered port:32 ?p_dout,
in buffered port:32 ?p_din,
out buffered port:32 p_lrclk
);
*/
#define NSTACKWORDS (8)
#define FUNCTION_NAME i2s_frame_master_4b_loop_part_1
#define out_array r0
#define inp_array r1
#define out_port r2
#define inp_port r3
#define lr_clock r4
#define a r5
#define b r6
#define c r7
#define d r8
#define e r9
#define f r10
.text
.issue_mode dual
.align 4
.cc_top FUNCTION_NAME.function,FUNCTION_NAME
FUNCTION_NAME:
dualentsp NSTACKWORDS
// Store registers r4 upwards to the stack.
stw lr_clock, sp[0]
std a, b, sp[1]
std c, d, sp[2]
std e, f, sp[3]
// Retrieve final argument to the function.
ldw lr_clock, sp[9]
// Split into input-only, output-only, or input-output body implementations
// If the input port is nulled (0), only output. Otherwise, proceed to input
bf inp_port, i2s_frame_master_4b_loop_part_1_out_body
i2s_frame_master_4b_loop_part_1_in_body:
// If we are here and the output port is not null (non-0), then both in and
// out are in use. Jump to the input-output implementation.
// Otherwise, just input.
bt out_port, i2s_frame_master_4b_loop_part_1_in_out_body
// Output 32 bits of 0 for lr clock
ldc e, 0
out res[lr_clock], e
// Retrieve the two odd samples we stored earlier, and input the other two
{ ldw f, inp_array[1] ; in d, res[inp_port] }
{ ldw e, inp_array[3] ; in c, res[inp_port] }
// Unzip the recieved odd samples as required
// aeim bfjn cgko dhlp -> (abcd) (efgh) (ijkl) (mnop)
unzip e, f, 0
unzip c, d, 0
unzip d, f, 0
unzip c, e, 0
// Bit-reverse and store the recieved odd samples. At this point, we have
// recieved all 8 samples and must call the recieve callback to pass these
// to the user.
{ ; bitrev f, f }
{ stw f, inp_array[7] ; bitrev e, e }
{ stw e, inp_array[5] ; bitrev d, d }
{ stw d, inp_array[3] ; bitrev c, c }
{ stw c, inp_array[1] ; }
// Jump to the common end section
bu i2s_frame_master_4b_loop_part_1_final
i2s_frame_master_4b_loop_part_1_out_body:
// Load and bit-reverse the even 32-bit samples we intend to send
// Generate and output 32 bits of 0 for the lrclock
{ ldw a, out_array[0] ; ldc e, 0 }
{ ldw b, out_array[2] ; out res[lr_clock], e }
{ ldw c, out_array[4] ; bitrev a, a }
{ ldw d, out_array[6] ; bitrev b, b }
{ bitrev c, c ; bitrev d, d }
// Zip the even samples as required to send in parallel on a 4b port
// (abcd) (efgh) (ijkl) (mnop) -> aeim bfjn cgko dhlp
zip a, c, 0
zip b, d, 0
zip a, b, 0
zip c, d, 0
// Output the first two even samples.
// Stash the 3rd and 4th even samples for transmission later.
{ stw a, out_array[0] ; out res[out_port], d }
{ stw b, out_array[2] ; out res[out_port], c }
// Jump to the common end section
bu i2s_frame_master_4b_loop_part_1_final
i2s_frame_master_4b_loop_part_1_in_out_body:
// Load and bit-reverse the even 32-bit samples we intend to send
// Generate and output 32 bits of 0 for the lrclock
{ ldw a, out_array[0] ; ldc e, 0 }
{ ldw b, out_array[2] ; out res[lr_clock], e }
{ ldw c, out_array[4] ; bitrev a, a }
{ ldw d, out_array[6] ; bitrev b, b }
{ bitrev c, c ; bitrev d, d }
// Zip the even samples as required to send in parallel on a 4b port
// (abcd) (efgh) (ijkl) (mnop) -> aeim bfjn cgko dhlp
zip a, c, 0
zip b, d, 0
zip a, b, 0
zip c, d, 0
// We stashed the 1st and 2nd recieved odd samples earlier - retrieve these.
// Input the 3rd and 4th odd samples.
// Stash the 3rd and 4th even samples for transmission later.
{ ldw f, inp_array[1] ; out res[out_port], d }
{ ldw e, inp_array[3] ; in d, res[inp_port] }
{ stw a, out_array[0] ; out res[out_port], c }
{ stw b, out_array[2] ; in c, res[inp_port] }
// Unzip the recieved odd samples as required
// aeim bfjn cgko dhlp -> (abcd) (efgh) (ijkl) (mnop)
unzip e, f, 0
unzip c, d, 0
unzip d, f, 0
unzip c, e, 0
// Bit-reverse and store the recieved odd samples. At this point, we have
// recieved all 8 samples and must call the recieve callback to pass these
// to the user.
{ ; bitrev f, f }
{ stw f, inp_array[7] ; bitrev e, e }
{ stw e, inp_array[5] ; bitrev d, d }
{ stw d, inp_array[3] ; bitrev c, c }
{ stw c, inp_array[1] ; }
// Continue to common end section
i2s_frame_master_4b_loop_part_1_final:
// Restore registers and return.
ldw lr_clock, sp[0]
ldd a, b, sp[1]
ldd c, d, sp[2]
ldd e, f, sp[3]
retsp NSTACKWORDS
.L_func_end:
.cc_bottom FUNCTION_NAME.function
.globl FUNCTION_NAME
.type FUNCTION_NAME,@function
.set FUNCTION_NAME.nstackwords,NSTACKWORDS; .global FUNCTION_NAME.nstackwords
.set FUNCTION_NAME.maxcores,1; .global FUNCTION_NAME.maxcores
.set FUNCTION_NAME.maxtimers,0; .global FUNCTION_NAME.maxtimers
.set FUNCTION_NAME.maxchanends,0; .global FUNCTION_NAME.maxchanends

View File

@@ -0,0 +1,202 @@
// Copyright 2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/*
void i2s_frame_master_4b_loop_part_2(
int32_t out_samps[],
int32_t in_samps[],
out buffered port:32 ?p_dout,
in buffered port:32 ?p_din,
out buffered port:32 p_lrclk
);
*/
#define NSTACKWORDS (8)
#define FUNCTION_NAME i2s_frame_master_4b_loop_part_2
#define out_array r0
#define inp_array r1
#define out_port r2
#define inp_port r3
#define lr_clock r4
#define a r5
#define b r6
#define c r7
#define d r8
#define e r9
#define f r10
.text
.issue_mode dual
.align 4
.cc_top FUNCTION_NAME.function,FUNCTION_NAME
FUNCTION_NAME:
dualentsp NSTACKWORDS
// Store registers r4 upwards to the stack.
stw lr_clock, sp[0]
std a, b, sp[1]
std c, d, sp[2]
std e, f, sp[3]
// Retrieve final argument to the function.
ldw lr_clock, sp[9]
// Split into input-only, output-only, or input-output body implementations
// If the input port is nulled (0), only output. Otherwise, proceed to input
bf inp_port, i2s_frame_master_4b_loop_part_2_out_body
i2s_frame_master_4b_loop_part_2_in_body:
// If we are here and the output port is not null (non-0), then both in and
// out are in use. Jump to the input-output implementation.
// Otherwise, just input.
bt out_port, i2s_frame_master_4b_loop_part_2_in_out_body
// Generate and output 32 bits of 1 to the LR clock
mkmsk e, 32
out res[lr_clock], e
// Recieve the even samples
in f, res[inp_port]
in e, res[inp_port]
in d, res[inp_port]
in c, res[inp_port]
// Unzip the recieved even samples as required
// aeim bfjn cgko dhlp -> (abcd) (efgh) (ijkl) (mnop)
unzip e, f, 0
unzip c, d, 0
unzip d, f, 0
unzip c, e, 0
// Bit-reverse and store the recieved even samples.
{ ; bitrev f, f }
{ stw f, inp_array[6] ; bitrev e, e }
{ stw e, inp_array[4] ; bitrev d, d }
{ stw d, inp_array[2] ; bitrev c, c }
{ stw c, inp_array[0] ; }
// Input the first two odd samples. As we are returning from this function,
// we must store these recieved samples in memory to pick up later.
{ ; in f, res[inp_port] }
{ stw f, inp_array[1] ; in e, res[inp_port] }
{ stw e, inp_array[3] ; }
// Jump to the common end section
bu i2s_frame_master_4b_loop_part_2_final
i2s_frame_master_4b_loop_part_2_out_body:
// We stashed the 3rd and 4th even samples for transmission earlier.
// Retrieve these and immediately transmit the 3rd.
{ ldw b, out_array[2] ; }
{ ldw a, out_array[0] ; out res[out_port], b }
// Generate and output 32 bits of 1 for the lrclock.
// Output the 4th even sample
// Load the odd samples we intend to send
// Output the 4th even sample
{ ldw d, out_array[7] ; mkmsk e, 32 }
{ ldw c, out_array[5] ; out res[lr_clock], e }
{ ldw b, out_array[3] ; }
{ ldw a, out_array[1] ; out res[out_port], a }
// Bit-reverse the odd samples
{ bitrev d, d ; bitrev c, c }
{ bitrev b, b ; bitrev a, a }
// Zip the odd samples as required to send in parallel on a 4b port
// (abcd) (efgh) (ijkl) (mnop) -> aeim bfjn cgko dhlp
zip a, c, 0
zip b, d, 0
zip a, b, 0
zip c, d, 0
// Output the odd samples
out res[out_port], d
out res[out_port], c
out res[out_port], b
out res[out_port], a
// Jump to the common end section
bu i2s_frame_master_4b_loop_part_2_final
i2s_frame_master_4b_loop_part_2_in_out_body:
// We stashed the 3rd and 4th even samples for transmission earlier.
// Retrieve these and immediately transmit the 3rd.
{ ldw b, out_array[2] ; }
{ ldw a, out_array[0] ; out res[out_port], b }
// Generate and output 32 bits of 1 for the lrclock.
// Output the 4th even sample
// Load the odd samples we intend to send
// Input the 1st even sample
{ ldw d, out_array[7] ; mkmsk e, 32 }
{ ldw c, out_array[5] ; out res[lr_clock], e }
{ ldw b, out_array[3] ; in f, res[inp_port] }
{ ldw a, out_array[1] ; out res[out_port], a }
// Bit-reverse the odd samples
{ bitrev d, d ; bitrev c, c }
{ bitrev b, b ; bitrev a, a }
// Zip the odd samples as required to send in parallel on a 4b port
// (abcd) (efgh) (ijkl) (mnop) -> aeim bfjn cgko dhlp
zip a, c, 0
zip b, d, 0
zip a, b, 0
zip c, d, 0
// Output the first two odd samples
// Input the remaining even samples
{ ; in e, res[inp_port] }
{ ; out res[out_port], d }
{ ; in d, res[inp_port] }
{ ; out res[out_port], c }
{ ; in c, res[inp_port] }
// Unzip the recieved even samples as required
// aeim bfjn cgko dhlp -> (abcd) (efgh) (ijkl) (mnop)
unzip e, f, 0
unzip c, d, 0
unzip d, f, 0
unzip c, e, 0
// Bit-reverse and store the recieved even samples.
{ ; bitrev f, f }
{ stw f, inp_array[6] ; bitrev e, e }
{ stw e, inp_array[4] ; bitrev d, d }
{ stw d, inp_array[2] ; bitrev c, c }
{ stw c, inp_array[0] ; }
// Output the remaining odd samples. At this point, we have transmitted all
// 8 samples, and must call the send callback to recieve the next batch.
// Input the first two odd samples. As we are returning from this function
// in order to call the send callback, we must store these recieved samples in
// memory to pick up later.
{ ; out res[out_port], b }
{ ; in f, res[inp_port] }
{ stw f, inp_array[1] ; out res[out_port], a }
{ ; in e, res[inp_port] }
{ stw e, inp_array[3] ; }
// Continue to the common end section
i2s_frame_master_4b_loop_part_2_final:
// Restore registers and return.
ldw lr_clock, sp[0]
ldd a, b, sp[1]
ldd c, d, sp[2]
ldd e, f, sp[3]
retsp NSTACKWORDS
.L_func_end:
.cc_bottom FUNCTION_NAME.function
.globl FUNCTION_NAME
.type FUNCTION_NAME,@function
.set FUNCTION_NAME.nstackwords,NSTACKWORDS; .global FUNCTION_NAME.nstackwords
.set FUNCTION_NAME.maxcores,1; .global FUNCTION_NAME.maxcores
.set FUNCTION_NAME.maxtimers,0; .global FUNCTION_NAME.maxtimers
.set FUNCTION_NAME.maxchanends,0; .global FUNCTION_NAME.maxchanends

View File

@@ -0,0 +1,229 @@
// Copyright 2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/*
void i2s_frame_master_4b_setup(
int32_t out_samps[],
int32_t in_samps[],
out buffered port:32 ?p_dout,
in buffered port:32 ?p_din,
clock bclk,
out buffered port:32 p_lrclk
);
*/
#define NSTACKWORDS (8)
#define FUNCTION_NAME i2s_frame_master_4b_setup
#define out_array r0
#define inp_array r1
#define out_port r2
#define inp_port r3
#define bt_clock r4
#define lr_clock r5
#define a r6
#define b r7
#define c r8
#define d r9
#define e r10
#define f r11
.text
.issue_mode dual
.align 4
.cc_top FUNCTION_NAME.function,FUNCTION_NAME
FUNCTION_NAME:
dualentsp NSTACKWORDS
// Store registers r4 upwards to the stack.
std bt_clock, lr_clock, sp[0]
std a, b, sp[1]
std c, d, sp[2]
std e, f, sp[3]
// Retrieve final two arguments to function
ldw bt_clock, sp[9]
ldw lr_clock, sp[10]
// Split into input-only, output-only, or input-output body implementations
// If the input port is nulled (0), only set up an output. Otherwise, proceed
// to set up an input.
bf inp_port, i2s_frame_master_4b_setup_out_body
i2s_frame_master_4b_setup_in_body:
// If we are here and the output port is not null (non-0), then both in and
// out are in use. Jump to the input-output implementation.
// Otherwise, just set up an input.
bt out_port, i2s_frame_master_4b_setup_in_out_body
// Start the bit clock
setc res[bt_clock], 0xF
// Output 32 1s to lr_clock
mkmsk e, 32
out res[lr_clock], e
// Input samples
{ ; in f, res[inp_port] }
{ ; in e, res[inp_port] }
{ ; in d, res[inp_port] }
{ ; in c, res[inp_port] }
// Unzip the recieved even samples as required
// aeim bfjn cgko dhlp -> (abcd) (efgh) (ijkl) (mnop)
unzip e, f, 0
unzip c, d, 0
unzip d, f, 0
unzip c, e, 0
// Bit-reverse and store the recieved even samples
{ ; bitrev f, f }
{ stw f, inp_array[6] ; bitrev e, e }
{ stw e, inp_array[4] ; bitrev d, d }
{ stw d, inp_array[2] ; bitrev c, c }
{ stw c, inp_array[0] ; }
// Input the first two odd samples. As we are returning from this function,
// we must store these recieved samples in memory to pick up later.
{ ; in f, res[inp_port] }
{ stw f, inp_array[1] ; in e, res[inp_port] }
{ stw e, inp_array[3] ; }
// Jump to the common end section
bu i2s_frame_master_4b_setup_final
i2s_frame_master_4b_setup_out_body:
// Load and bit-reverse the even 32-bit samples we intend to send
// Generate 32 bits of 1 for the lrclock
{ ldw a, out_array[0] ; }
{ ldw b, out_array[2] ; bitrev a, a }
{ ldw c, out_array[4] ; bitrev b, b }
{ ldw d, out_array[6] ; bitrev c, c }
{ mkmsk e, 32 ; bitrev d, d }
// Zip the even samples as required to send in parallel on a 4b port
// (abcd) (efgh) (ijkl) (mnop) -> aeim bfjn cgko dhlp
zip a, c, 0
zip b, d, 0
zip a, b, 0
zip c, d, 0
// Load and bit-reverse the odd 32-bit samples we intend to send
// Output 32 1s to the lrclock transfer register
// Output the even samples
// Start the bit clock
{ ldw d, out_array[7] ; out res[out_port], d }
setc res[bt_clock], 0xF
{ ldw c, out_array[5] ; out res[out_port], c }
{ bitrev d, d ; out res[lr_clock], e }
{ ldw b, out_array[3] ; out res[out_port], b }
{ bitrev c, c ; }
{ ldw a, out_array[1] ; out res[out_port], a }
{ bitrev b, b ; bitrev a, a }
// Zip the odd samples as required to send in parallel on a 4b port
zip a, c, 0
zip b, d, 0
zip a, b, 0
zip c, d, 0
// Output the odd samples. At this point, we have transmitted all
// 8 samples, and must call the send callback to recieve the next batch.
{ ; out res[out_port], d }
{ ; out res[out_port], c }
{ ; out res[out_port], b }
{ ; out res[out_port], a }
// Jump to the common end section
bu i2s_frame_master_4b_setup_final
i2s_frame_master_4b_setup_in_out_body:
// Load and bit-reverse the even 32-bit samples we intend to send
// Generate 32 bits of 1 for the lrclock
{ ldw a, out_array[0] ; }
{ ldw b, out_array[2] ; bitrev a, a }
{ ldw c, out_array[4] ; bitrev b, b }
{ ldw d, out_array[6] ; bitrev c, c }
{ mkmsk e, 32 ; bitrev d, d }
// Zip the even samples as required to send in parallel on a 4b port
// (abcd) (efgh) (ijkl) (mnop) -> aeim bfjn cgko dhlp
zip a, c, 0
zip b, d, 0
zip a, b, 0
zip c, d, 0
// Load and bit-reverse the odd 32-bit samples we intend to send
// Output 32 1s to the lrclock transfer register
// Output the even samples
// Start the bit clock
// Input the first even sample - this must happen here to meet timing
{ ldw d, out_array[7] ; out res[out_port], d }
setc res[bt_clock], 0xF
{ ldw c, out_array[5] ; out res[out_port], c }
{ bitrev d, d ; out res[lr_clock], e }
{ ldw b, out_array[3] ; out res[out_port], b }
{ bitrev c, c ; in f, res[inp_port] }
{ ldw a, out_array[1] ; out res[out_port], a }
{ bitrev b, b ; bitrev a, a }
// Zip the odd samples as required to send in parallel on a 4b port
zip a, c, 0
zip b, d, 0
zip a, b, 0
zip c, d, 0
// Output the first two odd samples
// Input the remaining even samples
{ ; in e, res[inp_port] }
{ ; out res[out_port], d }
{ ; in d, res[inp_port] }
{ ; out res[out_port], c }
{ ; in c, res[inp_port] }
// Unzip the recieved even samples as required
// aeim bfjn cgko dhlp -> (abcd) (efgh) (ijkl) (mnop)
unzip e, f, 0
unzip c, d, 0
unzip d, f, 0
unzip c, e, 0
// Bit-reverse and store the recieved even samples
{ ; bitrev f, f }
{ stw f, inp_array[6] ; bitrev e, e }
{ stw e, inp_array[4] ; bitrev d, d }
{ stw d, inp_array[2] ; bitrev c, c }
{ stw c, inp_array[0] ; }
// Output the remaining odd samples. At this point, we have transmitted all
// 8 samples, and must call the send callback to recieve the next batch.
// Input the first two odd samples. As we are returning from this function
// in order to call the send callback, we must store these recieved samples in
// memory to pick up later.
{ ; out res[out_port], b }
{ ; in f, res[inp_port] }
{ stw f, inp_array[1] ; out res[out_port], a }
{ ; in e, res[inp_port] }
{ stw e, inp_array[3] ; }
// Continue to common end section
i2s_frame_master_4b_setup_final:
// Restore registers and return.
ldd bt_clock, lr_clock, sp[0]
ldd a, b, sp[1]
ldd c, d, sp[2]
ldd e, f, sp[3]
retsp NSTACKWORDS
.L_func_end:
.cc_bottom FUNCTION_NAME.function
.globl FUNCTION_NAME
.type FUNCTION_NAME,@function
.set FUNCTION_NAME.nstackwords,NSTACKWORDS; .global FUNCTION_NAME.nstackwords
.set FUNCTION_NAME.maxcores,1; .global FUNCTION_NAME.maxcores
.set FUNCTION_NAME.maxtimers,0; .global FUNCTION_NAME.maxtimers
.set FUNCTION_NAME.maxchanends,0; .global FUNCTION_NAME.maxchanends

View File

@@ -0,0 +1,370 @@
// Copyright 2016-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#if defined(__XS2A__) || defined(__XS3A__)
#include "limits.h"
#include <xs1.h>
#include <xclib.h>
#include "i2s.h"
#include "xassert.h"
static void i2s_setup_bclk(
clock bclk,
in port p_mclk,
unsigned mclk_bclk_ratio
){
set_clock_on(bclk);
configure_clock_src_divide(bclk, p_mclk, mclk_bclk_ratio >> 1);
}
static void i2s_frame_init_ports(
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
out port p_bclk,
out buffered port:32 p_lrclk,
clock bclk
){
for (size_t i = 0; i < num_out; i++)
{
configure_out_port(p_dout[i], bclk, 0);
clearbuf(p_dout[i]);
}
for (size_t i = 0; i < num_in; i++)
{
configure_in_port(p_din[i], bclk);
clearbuf(p_din[i]);
}
configure_port_clock_output(p_bclk, bclk);
configure_out_port(p_lrclk, bclk, 1);
clearbuf(p_lrclk);
}
#pragma unsafe arrays
static i2s_restart_t i2s_frame_ratio_n(client i2s_frame_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
static const size_t num_data_bits,
out port p_bclk,
clock bclk,
out buffered port:32 p_lrclk,
i2s_mode_t mode){
const int offset = (mode == I2S_MODE_I2S) ? 1 : 0;
int32_t in_samps[16]; // Workaround: should be (num_in << 1) but compiler thinks that isn't const,
int32_t out_samps[16]; // so setting to 16 which should be big enough for most cases
// Since #pragma unsafe arrays is used need to ensure array won't overflow.
assert((num_in << 1) <= 16);
unsigned lr_mask = 0;
const unsigned data_bit_offset = 32 - num_data_bits;
const unsigned data_bit_mask = UINT_MAX >> data_bit_offset; // e.g. 00011111 for 5b data
if (num_out)
{
i2s_i.send(num_out << 1, out_samps);
}
// Start outputting evens (0,2,4..) data at correct point relative to the clock
if (num_data_bits == 32)
{
#pragma loop unroll
for (size_t i = 0, idx = 0; i < num_out; i++, idx += 2)
{
p_dout[i] @ (1 + offset) <: bitrev(out_samps[idx]);
}
p_lrclk @ 1 <: lr_mask;
}
else
{
#pragma loop unroll
for (size_t i = 0, idx = 0; i < num_out; i++, idx += 2)
{
partout_timed(p_dout[i], num_data_bits, bitrev(out_samps[idx] << data_bit_offset), (1 + offset));
}
partout_timed(p_lrclk, num_data_bits, lr_mask, 1);
}
start_clock(bclk);
// Pre-load the odds (1,3,5..) and setup timing on the input ports
if (num_data_bits == 32)
{
#pragma loop unroll
for (size_t i = 0, idx = 1; i < num_out; i++, idx += 2)
{
p_dout[i] <: bitrev(out_samps[idx]);
}
lr_mask = ~lr_mask;
p_lrclk <: lr_mask;
for (size_t i = 0; i < num_in; i++)
{
asm volatile("setpt res[%0], %1"
:
:"r"(p_din[i]), "r"(32 + offset));
}
}
else
{
#pragma loop unroll
for (size_t i = 0, idx = 1; i < num_out; i++, idx += 2)
{
partout(p_dout[i], num_data_bits, bitrev(out_samps[idx] << data_bit_offset));
}
lr_mask = ~lr_mask;
partout(p_lrclk, num_data_bits, lr_mask);
for (size_t i = 0; i < num_in; i++)
{
asm volatile("setpt res[%0], %1"
:
:"r"(p_din[i]), "r"(num_data_bits + offset));
set_port_shift_count(p_din[i], num_data_bits);
}
}
while (1)
{
// Check for restart
i2s_restart_t restart = i2s_i.restart_check();
if (restart == I2S_NO_RESTART)
{
if (num_out)
{
i2s_i.send(num_out << 1, out_samps);
}
// Output i2s evens (0,2,4..)
if (num_data_bits == 32)
{
#pragma loop unroll
for (size_t i = 0, idx = 0; i < num_out; i++, idx += 2)
{
p_dout[i] <: bitrev(out_samps[idx]);
}
}
else
{
#pragma loop unroll
for (size_t i = 0, idx = 0; i < num_out; i++, idx += 2)
{
partout(p_dout[i], num_data_bits, bitrev(out_samps[idx] << data_bit_offset));
}
}
}
// Input i2s evens (0,2,4..)
if (num_data_bits == 32)
{
#pragma loop unroll
for (size_t i = 0, idx = 0; i < num_in; i++, idx += 2)
{
int32_t data;
asm volatile("in %0, res[%1]"
:"=r"(data)
:"r"(p_din[i]));
in_samps[idx] = bitrev(data);
}
lr_mask = ~lr_mask;
p_lrclk <: lr_mask;
}
else
{
#pragma loop unroll
for (size_t i = 0, idx = 0; i < num_in; i++, idx += 2)
{
int32_t data;
asm volatile("in %0, res[%1]"
:"=r"(data)
:"r"(p_din[i]));
set_port_shift_count(p_din[i], num_data_bits);
in_samps[idx] = bitrev(data) & data_bit_mask;
}
lr_mask = ~lr_mask;
partout(p_lrclk, num_data_bits, lr_mask);
}
if (restart == I2S_NO_RESTART)
{
// Output i2s odds (1,3,5..)
if (num_data_bits == 32)
{
#pragma loop unroll
for (size_t i = 0, idx = 1; i < num_out; i++, idx += 2)
{
p_dout[i] <: bitrev(out_samps[idx]);
}
lr_mask = ~lr_mask;
p_lrclk <: lr_mask;
}
else
{
#pragma loop unroll
for (size_t i = 0, idx = 1; i < num_out; i++, idx += 2)
{
partout(p_dout[i], num_data_bits, bitrev(out_samps[idx] << data_bit_offset));
}
lr_mask = ~lr_mask;
partout(p_lrclk, num_data_bits, lr_mask);
}
}
// Input i2s odds (1,3,5..)
if (num_data_bits == 32)
{
#pragma loop unroll
for (size_t i = 0, idx = 1; i < num_in; i++, idx += 2)
{
int32_t data;
asm volatile("in %0, res[%1]"
:"=r"(data)
:"r"(p_din[i]));
in_samps[idx] = bitrev(data);
}
}
else
{
#pragma loop unroll
for (size_t i = 0, idx = 1; i < num_in; i++, idx += 2)
{
int32_t data;
asm volatile("in %0, res[%1]"
:"=r"(data)
:"r"(p_din[i])
:"memory");
set_port_shift_count(p_din[i], num_data_bits);
in_samps[idx] = bitrev(data) & data_bit_mask;
}
}
if (num_in)
{
i2s_i.receive(num_in << 1, in_samps);
}
if (restart != I2S_NO_RESTART)
{
if (!num_in)
{
// Prevent the clock from being stopped before the last word
// has been sent if there are no RX ports.
sync(p_dout[0]);
}
stop_clock(bclk);
return restart;
}
}
return I2S_RESTART;
}
#define i2s_frame_master i2s_frame_master0
static void i2s_frame_master0(client i2s_frame_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
static const size_t num_data_bits,
out port p_bclk,
out buffered port:32 p_lrclk,
in port p_mclk,
clock bclk){
while(1){
i2s_config_t config;
i2s_i.init(config, null);
if (isnull(p_dout) && isnull(p_din)) {
fail("Must provide non-null p_dout or p_din");
}
i2s_setup_bclk(bclk, p_mclk, config.mclk_bclk_ratio);
//This ensures that the port time on all the ports is at 0
i2s_frame_init_ports(p_dout, num_out, p_din, num_in, p_bclk, p_lrclk, bclk);
i2s_restart_t restart =
i2s_frame_ratio_n(i2s_i, p_dout, num_out, p_din,
num_in, num_data_bits, p_bclk, bclk, p_lrclk,
config.mode);
if (restart == I2S_SHUTDOWN)
return;
}
}
#define i2s_frame_master_external_clock i2s_frame_master0_external_clock
static void i2s_frame_master0_external_clock(client i2s_frame_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
static const size_t num_data_bits,
out port p_bclk,
out buffered port:32 p_lrclk,
clock bclk){
while(1){
i2s_config_t config;
i2s_i.init(config, null);
if (isnull(p_dout) && isnull(p_din)) {
fail("Must provide non-null p_dout or p_din");
}
//This ensures that the port time on all the ports is at 0
i2s_frame_init_ports(p_dout, num_out, p_din, num_in, p_bclk, p_lrclk, bclk);
i2s_restart_t restart =
i2s_frame_ratio_n(i2s_i, p_dout, num_out, p_din,
num_in, num_data_bits, p_bclk, bclk, p_lrclk,
config.mode);
if (restart == I2S_SHUTDOWN)
return;
}
}
// These functions is just to avoid unused static function warnings for i2s_frame_master0
// and i2s_frame_master0_external_clock. They should never be called.
inline void i2s_frame_master1(client interface i2s_frame_callback_if i,
out buffered port:32 i2s_dout[num_i2s_out],
static const size_t num_i2s_out,
in buffered port:32 i2s_din[num_i2s_in],
static const size_t num_i2s_in,
static const size_t num_data_bits,
out port i2s_bclk,
out buffered port:32 i2s_lrclk,
in port p_mclk,
clock clk_bclk) {
i2s_frame_master0(i, i2s_dout, num_i2s_out, i2s_din, num_i2s_in, num_data_bits,
i2s_bclk, i2s_lrclk, p_mclk, clk_bclk);
}
inline void i2s_frame_master1_external_clock(client interface i2s_frame_callback_if i,
out buffered port:32 i2s_dout[num_i2s_out],
static const size_t num_i2s_out,
in buffered port:32 i2s_din[num_i2s_in],
static const size_t num_i2s_in,
static const size_t num_data_bits,
out port i2s_bclk,
out buffered port:32 i2s_lrclk,
clock clk_bclk) {
i2s_frame_master0_external_clock(i, i2s_dout, num_i2s_out, i2s_din, num_i2s_in, num_data_bits,
i2s_bclk, i2s_lrclk, clk_bclk);
}
#endif // __XS2A__ || __XS3A__

View File

@@ -0,0 +1,187 @@
// Copyright 2015-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h>
#include <xclib.h>
#include "i2s.h"
#include <print.h>
#include "limits.h"
#include "xassert.h"
#include <stdlib.h>
#include <stdio.h>
unsigned i2s_frame_slave_4b_setup(
int32_t out_samps[],
int32_t in_samps[],
out buffered port:32 ?p_dout,
in buffered port:32 ?p_din,
in buffered port:32 p_lrclk
);
unsigned i2s_frame_slave_4b_loop_part_1(
int32_t out_samps[],
int32_t in_samps[],
out buffered port:32 ?p_dout,
in buffered port:32 ?p_din,
in buffered port:32 p_lrclk
);
unsigned i2s_frame_slave_4b_loop_part_2(
int32_t out_samps[],
int32_t in_samps[],
out buffered port:32 ?p_dout,
in buffered port:32 ?p_din,
in buffered port:32 p_lrclk
);
static void i2s_frame_slave_init_ports_4b(
out buffered port:32 ?p_dout,
size_t num_out,
in buffered port:32 ?p_din,
size_t num_in,
in port p_bclk,
in buffered port:32 p_lrclk,
clock bclk){
set_clock_on(bclk);
configure_clock_src(bclk, p_bclk);
configure_in_port(p_lrclk, bclk);
if (!isnull(p_din))
{
configure_in_port(p_din, bclk);
}
if (!isnull(p_dout))
{
configure_out_port(p_dout, bclk, 0);
}
start_clock(bclk);
}
#define i2s_frame_slave_4b i2s_frame_slave_4b0
#pragma unsafe arrays
static void i2s_frame_slave_4b0(client i2s_frame_callback_if i2s_i,
out buffered port:32 ?p_dout,
static const size_t num_out,
in buffered port:32 ?p_din,
static const size_t num_in,
in port p_bclk,
in buffered port:32 p_lrclk,
clock bclk){
unsigned port_time;
int32_t in_samps[16] = {0}; //Workaround: should be (num_in << 1) but compiler thinks that isn't const,
int32_t out_samps[16] = {0}; //so setting to 16 which should be big enough for most cases
// Since #pragma unsafe arrays is used need to ensure array won't overflow.
assert((num_in << 1) <= 16);
i2s_frame_slave_init_ports_4b(p_dout, num_out, p_din, num_in, p_bclk, p_lrclk, bclk);
while(1)
{
i2s_config_t config;
i2s_restart_t restart = I2S_NO_RESTART;
i2s_i.init(config, null);
unsigned mode = config.mode;
if (config.slave_bclk_polarity == I2S_SLAVE_SAMPLE_ON_BCLK_FALLING)
set_port_inv(p_bclk);
else
set_port_no_inv(p_bclk);
const unsigned expected_low = (mode == I2S_MODE_I2S ? 0x80000000 : 0x00000000);
const unsigned expected_high = (mode == I2S_MODE_I2S ? 0x7fffffff : 0xffffffff);
unsigned syncerror = 0;
unsigned lrval;
if (!isnull(p_dout))
{
clearbuf(p_dout);
}
if (!isnull(p_din))
{
clearbuf(p_din);
}
clearbuf(p_lrclk);
unsigned offset = (mode == I2S_MODE_I2S ? 1 : 0);
// Wait for LRCLK edge (in I2S LRCLK = 0 is left, TDM rising edge is start of frame)
p_lrclk when pinseq(1) :> void;
p_lrclk when pinseq(0) :> void @ port_time;
unsigned initial_lr_port_time = port_time + offset + ((I2S_CHANS_PER_FRAME*32)+32) - 1;
unsigned initial_out_port_time = port_time + offset + (I2S_CHANS_PER_FRAME*32);
unsigned initial_in_port_time = port_time + offset + ((I2S_CHANS_PER_FRAME*32)+8) - 1;
// XC doesn't have syntax for setting a timed input without waiting for the input
asm volatile("setpt res[%0], %1"
:
:"r"(p_lrclk),"r"(initial_lr_port_time));
if (!isnull(p_din))
{
asm volatile("setpt res[%0], %1"
:
:"r"(p_din),"r"(initial_in_port_time));
}
if (!isnull(p_dout))
{
asm volatile("setpt res[%0], %1"
:
:"r"(p_dout),"r"(initial_out_port_time));
}
//Get initial send data if output enabled
if (num_out)
{
i2s_i.send(num_out << 1, out_samps);
}
lrval = i2s_frame_slave_4b_setup(out_samps, in_samps, p_dout, p_din, p_lrclk);
syncerror += (lrval != expected_low);
//Main loop
while (!syncerror && (restart == I2S_NO_RESTART))
{
restart = i2s_i.restart_check();
if (num_out)
{
i2s_i.send(num_out << 1, out_samps);
}
lrval = i2s_frame_slave_4b_loop_part_1(out_samps, in_samps, p_dout, p_din, p_lrclk);
syncerror += (lrval != expected_high);
if (num_in)
{
i2s_i.receive(num_in << 1, in_samps);
}
if (restart == I2S_NO_RESTART)
{
lrval = i2s_frame_slave_4b_loop_part_2(out_samps, in_samps, p_dout, p_din, p_lrclk);
syncerror += (lrval != expected_low);
}
}// main loop, runs until user restart or synch error
}// while(1)
}
// This function is just to avoid unused static function warnings for
// i2s_frame_slave0,it should never be called.
inline void i2s_frame_slave_4b1(client i2s_frame_callback_if i2s_i,
out buffered port:32 ?p_dout,
static const size_t num_out,
in buffered port:32 ?p_din,
static const size_t num_in,
in port p_bclk,
in buffered port:32 p_lrclk,
clock bclk){
if (isnull(p_dout) && isnull(p_din)) {
fail("Must provide non-null p_dout or p_din");
}
i2s_frame_slave_4b0(i2s_i, p_dout, num_out, p_din, num_in, p_bclk, p_lrclk, bclk);
}

View File

@@ -0,0 +1,165 @@
// Copyright 2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/*
unsigned i2s_frame_slave_4b_loop_part_1(
int32_t out_samps[],
int32_t in_samps[],
out buffered port:32 ?p_dout,
in buffered port:32 ?p_din,
out buffered port:32 p_lrclk
);
*/
#define NSTACKWORDS (8)
#define FUNCTION_NAME i2s_frame_slave_4b_loop_part_1
#define out_array r0
#define inp_array r1
#define out_port r2
#define inp_port r3
#define lr_clock r4
#define a r5
#define b r6
#define c r7
#define d r8
#define e r9
#define f r10
.text
.issue_mode dual
.align 4
.cc_top FUNCTION_NAME.function,FUNCTION_NAME
FUNCTION_NAME:
dualentsp NSTACKWORDS
// Store registers r4 upwards to the stack.
stw lr_clock, sp[0]
std a, b, sp[1]
std c, d, sp[2]
std e, f, sp[3]
// Retrieve final argument to the function.
ldw lr_clock, sp[9]
// Split into input-only, output-only, or input-output body implementations
// If the input port is nulled (0), only set up an output. Otherwise, proceed
// to set up an input.
bf inp_port, i2s_frame_slave_4b_loop_part_1_out_body
i2s_frame_slave_4b_loop_part_1_in_body:
// If we are here and the output port is not null (non-0), then both in and
// out are in use. Jump to the input-output implementation.
// Otherwise, just set up an input.
bt out_port, i2s_frame_slave_4b_loop_part_1_in_out_body
// Retrieve the two odd samples we stored earlier, and input the other two
{ ldw f, inp_array[1] ; in d, res[inp_port] }
{ ldw e, inp_array[3] ; in c, res[inp_port] }
// Unzip the recieved odd samples as required
// aeim bfjn cgko dhlp -> (abcd) (efgh) (ijkl) (mnop)
unzip e, f, 0
unzip c, d, 0
unzip d, f, 0
unzip c, e, 0
// Bit-reverse and store the recieved odd samples. At this point, we have
// recieved all 8 samples and must call the recieve callback to pass these
// to the user.
// Input value of the lr_clock and return it
{ ; bitrev f, f }
{ stw f, inp_array[7] ; bitrev e, e }
{ stw e, inp_array[5] ; bitrev d, d }
{ stw d, inp_array[3] ; bitrev c, c }
{ stw c, inp_array[1] ; in r0, res[lr_clock] }
// Jump to the common end section
bu i2s_frame_slave_4b_loop_part_1_final
i2s_frame_slave_4b_loop_part_1_out_body:
// Load and bit-reverse the even 32-bit samples we intend to send
{ ldw a, out_array[0] ; }
{ ldw b, out_array[2] ; bitrev a, a }
{ ldw c, out_array[4] ; bitrev b, b }
{ ldw d, out_array[6] ; bitrev c, c }
{ ; bitrev d, d }
// Zip the even samples as required to send in parallel on a 4b port
// (abcd) (efgh) (ijkl) (mnop) -> aeim bfjn cgko dhlp
zip a, c, 0
zip b, d, 0
zip a, b, 0
zip c, d, 0
// Output the first two even samples.
// Stash the 3rd and 4th even samples for transmission later.
{ stw a, out_array[0] ; out res[out_port], d }
{ stw b, out_array[2] ; out res[out_port], c }
// Input value of the lr_clock and return it
in r0, res[lr_clock]
// Jump to the common end section
bu i2s_frame_slave_4b_loop_part_1_final
i2s_frame_slave_4b_loop_part_1_in_out_body:
// Load and bit-reverse the even 32-bit samples we intend to send
{ ldw a, out_array[0] ; }
{ ldw b, out_array[2] ; bitrev a, a }
{ ldw c, out_array[4] ; bitrev b, b }
{ ldw d, out_array[6] ; bitrev c, c }
{ ; bitrev d, d }
// Zip the even samples as required to send in parallel on a 4b port
// (abcd) (efgh) (ijkl) (mnop) -> aeim bfjn cgko dhlp
zip a, c, 0
zip b, d, 0
zip a, b, 0
zip c, d, 0
// We stashed the 1st and 2nd recieved odd samples earlier - retrieve these.
// Input the 3rd and 4th odd samples.
// Stash the 3rd and 4th even samples for transmission later.
{ ldw f, inp_array[1] ; out res[out_port], d }
{ ldw e, inp_array[3] ; in d, res[inp_port] }
{ stw a, out_array[0] ; out res[out_port], c }
{ stw b, out_array[2] ; in c, res[inp_port] }
// Unzip the recieved odd samples as required
// aeim bfjn cgko dhlp -> (abcd) (efgh) (ijkl) (mnop)
unzip e, f, 0
unzip c, d, 0
unzip d, f, 0
unzip c, e, 0
// Bit-reverse and store the recieved odd samples. At this point, we have
// recieved all 8 samples and must call the recieve callback to pass these
// to the user.
// Input value of the lr_clock and return it
{ ; bitrev f, f }
{ stw f, inp_array[7] ; bitrev e, e }
{ stw e, inp_array[5] ; bitrev d, d }
{ stw d, inp_array[3] ; bitrev c, c }
{ stw c, inp_array[1] ; in r0, res[lr_clock] }
// Continue to common end section
i2s_frame_slave_4b_loop_part_1_final:
// Restore registers and return.
ldw lr_clock, sp[0]
ldd a, b, sp[1]
ldd c, d, sp[2]
ldd e, f, sp[3]
retsp NSTACKWORDS
.L_func_end:
.cc_bottom FUNCTION_NAME.function
.globl FUNCTION_NAME
.type FUNCTION_NAME,@function
.set FUNCTION_NAME.nstackwords,NSTACKWORDS; .global FUNCTION_NAME.nstackwords
.set FUNCTION_NAME.maxcores,1; .global FUNCTION_NAME.maxcores
.set FUNCTION_NAME.maxtimers,0; .global FUNCTION_NAME.maxtimers
.set FUNCTION_NAME.maxchanends,0; .global FUNCTION_NAME.maxchanends

View File

@@ -0,0 +1,201 @@
// Copyright 2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/*
void i2s_frame_slave_4b_loop_part_2(
int32_t out_samps[],
int32_t in_samps[],
out buffered port:32 ?p_dout,
in buffered port:32 ?p_din,
out buffered port:32 p_lrclk
);
*/
#define NSTACKWORDS (8)
#define FUNCTION_NAME i2s_frame_slave_4b_loop_part_2
#define out_array r0
#define inp_array r1
#define out_port r2
#define inp_port r3
#define lr_clock r4
#define a r5
#define b r6
#define c r7
#define d r8
#define e r9
#define f r10
.text
.issue_mode dual
.align 4
.cc_top FUNCTION_NAME.function,FUNCTION_NAME
FUNCTION_NAME:
dualentsp NSTACKWORDS
// Store registers r4 upwards to the stack.
stw lr_clock, sp[0]
std a, b, sp[1]
std c, d, sp[2]
std e, f, sp[3]
// Retrieve final argument to the function.
ldw lr_clock, sp[9]
// Split into input-only, output-only, or input-output body implementations
// If the input port is nulled (0), only set up an output. Otherwise, proceed
// to set up an input.
bf inp_port, i2s_frame_slave_4b_loop_part_2_out_body
i2s_frame_slave_4b_loop_part_2_in_body:
// If we are here and the output port is not null (non-0), then both in and
// out are in use. Jump to the input-output implementation.
// Otherwise, just set up an input.
bt out_port, i2s_frame_slave_4b_loop_part_2_in_out_body
// Recieve the even samples
in f, res[inp_port]
in e, res[inp_port]
in d, res[inp_port]
in c, res[inp_port]
// Unzip the recieved even samples as required
// aeim bfjn cgko dhlp -> (abcd) (efgh) (ijkl) (mnop)
unzip e, f, 0
unzip c, d, 0
unzip d, f, 0
unzip c, e, 0
// Bit-reverse and store the recieved even samples.
{ ; bitrev f, f }
{ stw f, inp_array[6] ; bitrev e, e }
{ stw e, inp_array[4] ; bitrev d, d }
{ stw d, inp_array[2] ; bitrev c, c }
{ stw c, inp_array[0] ; }
// Input the first two odd samples. As we are returning from this function,
// we must store these recieved samples in memory to pick up later.
// Input the value of the lr clock and return it
{ ; in f, res[inp_port] }
{ stw f, inp_array[1] ; in e, res[inp_port] }
{ stw e, inp_array[3] ; in r0, res[lr_clock] }
// Jump to the common end section
bu i2s_frame_slave_4b_loop_part_2_final
i2s_frame_slave_4b_loop_part_2_out_body:
// We stashed the 3rd and 4th even samples for transmission earlier.
// Retrieve these and immediately transmit the 3rd.
{ ldw b, out_array[2] ; }
{ ldw a, out_array[0] ; out res[out_port], b }
// Output the 4th even sample
// Load the odd samples we intend to send
{ ldw d, out_array[7] ; }
{ ldw c, out_array[5] ; }
{ ldw b, out_array[3] ; }
{ ldw a, out_array[1] ; out res[out_port], a }
// Bit-reverse the odd samples
{ bitrev d, d ; bitrev c, c }
{ bitrev b, b ; bitrev a, a }
// Zip the odd samples as required to send in parallel on a 4b port
// (abcd) (efgh) (ijkl) (mnop) -> aeim bfjn cgko dhlp
zip a, c, 0
zip b, d, 0
zip a, b, 0
zip c, d, 0
// Output the odd samples
out res[out_port], d
out res[out_port], c
out res[out_port], b
out res[out_port], a
// Input the value of the lr clock and return it
in r0, res[lr_clock]
// Jump to the common end section
bu i2s_frame_slave_4b_loop_part_2_final
i2s_frame_slave_4b_loop_part_2_in_out_body:
// We stashed the 3rd and 4th even samples for transmission earlier.
// Retrieve these and immediately transmit the 3rd.
{ ldw b, out_array[2] ; }
{ ldw a, out_array[0] ; out res[out_port], b }
// Output the 4th even sample
// Load the odd samples we intend to send
// Input the 1st even sample
{ ldw d, out_array[7] ; }
{ ldw c, out_array[5] ; }
{ ldw b, out_array[3] ; in f, res[inp_port] }
{ ldw a, out_array[1] ; out res[out_port], a }
// Bit-reverse the odd samples
{ bitrev d, d ; bitrev c, c }
{ bitrev b, b ; bitrev a, a }
// Zip the odd samples as required to send in parallel on a 4b port
// (abcd) (efgh) (ijkl) (mnop) -> aeim bfjn cgko dhlp
zip a, c, 0
zip b, d, 0
zip a, b, 0
zip c, d, 0
// Output the first two odd samples
// Input the remaining even samples
{ ; in e, res[inp_port] }
{ ; out res[out_port], d }
{ ; in d, res[inp_port] }
{ ; out res[out_port], c }
{ ; in c, res[inp_port] }
// Unzip the recieved even samples as required
// aeim bfjn cgko dhlp -> (abcd) (efgh) (ijkl) (mnop)
unzip e, f, 0
unzip c, d, 0
unzip d, f, 0
unzip c, e, 0
// Bit-reverse and store the recieved even samples.
{ ; bitrev f, f }
{ stw f, inp_array[6] ; bitrev e, e }
{ stw e, inp_array[4] ; bitrev d, d }
{ stw d, inp_array[2] ; bitrev c, c }
{ stw c, inp_array[0] ; }
// Output the remaining odd samples. At this point, we have transmitted all
// 8 samples, and must call the send callback to recieve the next batch.
// Input the first two odd samples. As we are returning from this function
// in order to call the send callback, we must store these recieved samples in
// memory to pick up later.
// Input value of the lr_clock and return it
{ ; out res[out_port], b }
{ ; in f, res[inp_port] }
{ stw f, inp_array[1] ; out res[out_port], a }
{ ; in e, res[inp_port] }
{ stw e, inp_array[3] ; in r0, res[lr_clock] }
// Continue to common end section
i2s_frame_slave_4b_loop_part_2_final:
// Restore registers and return.
ldw lr_clock, sp[0]
ldd a, b, sp[1]
ldd c, d, sp[2]
ldd e, f, sp[3]
retsp NSTACKWORDS
.L_func_end:
.cc_bottom FUNCTION_NAME.function
.globl FUNCTION_NAME
.type FUNCTION_NAME,@function
.set FUNCTION_NAME.nstackwords,NSTACKWORDS; .global FUNCTION_NAME.nstackwords
.set FUNCTION_NAME.maxcores,1; .global FUNCTION_NAME.maxcores
.set FUNCTION_NAME.maxtimers,0; .global FUNCTION_NAME.maxtimers
.set FUNCTION_NAME.maxchanends,0; .global FUNCTION_NAME.maxchanends

View File

@@ -0,0 +1,215 @@
// Copyright 2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/*
unsigned i2s_frame_slave_4b_setup(
int32_t out_samps[],
int32_t in_samps[],
out buffered port:32 ?p_dout,
in buffered port:32 ?p_din,
in buffered port:32 p_lrclk
);
*/
#define NSTACKWORDS (8)
#define FUNCTION_NAME i2s_frame_slave_4b_setup
#define out_array r0
#define inp_array r1
#define out_port r2
#define inp_port r3
#define a r4
#define b r5
#define c r6
#define d r7
#define e r8
#define f r9
#define lr_clock r10
.text
.issue_mode dual
.align 4
.cc_top FUNCTION_NAME.function,FUNCTION_NAME
FUNCTION_NAME:
dualentsp NSTACKWORDS
// Store registers r4 upwards to the stack.
std a, b, sp[0]
std c, d, sp[1]
std e, f, sp[2]
stw lr_clock, sp[6]
// Retrieve final argument to the function.
ldw lr_clock, sp[9]
// Split into input-only, output-only, or input-output body implementations
// If the input port is nulled (0), only set up an output. Otherwise, proceed
// to set up an input.
bf inp_port, i2s_frame_slave_4b_setup_out_body
i2s_frame_slave_4b_setup_in_body:
// If we are here and the output port is not null (non-0), then both in and
// out are in use. Jump to the input-output implementation.
// Otherwise, just set up an input.
bt out_port, i2s_frame_slave_4b_setup_in_out_body
// Input samples
{ ; in f, res[inp_port] }
{ ; in e, res[inp_port] }
{ ; in d, res[inp_port] }
{ ; in c, res[inp_port] }
// Unzip the recieved even samples as required
// aeim bfjn cgko dhlp -> (abcd) (efgh) (ijkl) (mnop)
unzip e, f, 0
unzip c, d, 0
unzip d, f, 0
unzip c, e, 0
// Bit-reverse and store the recieved even samples
{ ; bitrev f, f }
{ stw f, inp_array[6] ; bitrev e, e }
{ stw e, inp_array[4] ; bitrev d, d }
{ stw d, inp_array[2] ; bitrev c, c }
{ stw c, inp_array[0] ; }
// Input the first two odd samples. As we are returning from this function,
// we must store these recieved samples in memory to pick up later.
// Input the value of the lr_clock and return it
{ ; in f, res[inp_port] }
{ stw f, inp_array[1] ; in e, res[inp_port] }
{ stw e, inp_array[3] ; in r0, res[lr_clock] }
// Jump to the common end section
bu i2s_frame_slave_4b_setup_final
i2s_frame_slave_4b_setup_out_body:
// Load and bit-reverse the even 32-bit samples we intend to send
{ ldw a, out_array[0] ; }
{ ldw b, out_array[2] ; bitrev a, a }
{ ldw c, out_array[4] ; bitrev b, b }
{ ldw d, out_array[6] ; bitrev c, c }
{ ; bitrev d, d }
// Zip the even samples as required to send in parallel on a 4b port
// (abcd) (efgh) (ijkl) (mnop) -> aeim bfjn cgko dhlp
zip a, c, 0
zip b, d, 0
zip a, b, 0
zip c, d, 0
// Load and bit-reverse the odd 32-bit samples we intend to send
// Output the even samples
{ ldw d, out_array[7] ; out res[out_port], d }
{ ldw c, out_array[5] ; out res[out_port], c }
{ bitrev d, d ; bitrev c, c }
{ ldw b, out_array[3] ; out res[out_port], b }
{ ldw a, out_array[1] ; out res[out_port], a }
{ bitrev b, b ; bitrev a, a }
// Zip the odd samples as required to send in parallel on a 4b port
zip a, c, 0
zip b, d, 0
zip a, b, 0
zip c, d, 0
// Output the odd samples. At this point, we have transmitted all
// 8 samples, and must call the send callback to recieve the next batch.
{ ; out res[out_port], d }
{ ; out res[out_port], c }
{ ; out res[out_port], b }
{ ; out res[out_port], a }
// Input the value of the lr_clock and return it
in r0, res[lr_clock]
// Jump to the common end section
bu i2s_frame_slave_4b_setup_final
i2s_frame_slave_4b_setup_in_out_body:
// Load and bit-reverse the even 32-bit samples we intend to send
{ ldw a, out_array[0] ; }
{ ldw b, out_array[2] ; bitrev a, a }
{ ldw c, out_array[4] ; bitrev b, b }
{ ldw d, out_array[6] ; bitrev c, c }
{ ; bitrev d, d }
// Zip the even samples as required to send in parallel on a 4b port
// (abcd) (efgh) (ijkl) (mnop) -> aeim bfjn cgko dhlp
zip a, c, 0
zip b, d, 0
zip a, b, 0
zip c, d, 0
// Load and bit-reverse the odd 32-bit samples we intend to send
// Output the even samples
// Input the first even sample - this must happen here to meet timing
{ ldw d, out_array[7] ; out res[out_port], d }
{ ldw c, out_array[5] ; out res[out_port], c }
{ ldw b, out_array[3] ; out res[out_port], b }
{ ; in f, res[inp_port] }
{ ldw a, out_array[1] ; out res[out_port], a }
{ bitrev d, d ; bitrev c, c }
{ bitrev b, b ; bitrev a, a }
// Zip the odd samples as required to send in parallel on a 4b port
zip a, c, 0
zip b, d, 0
zip a, b, 0
zip c, d, 0
// Output the first two odd samples
// Input the remaining even samples
{ ; in e, res[inp_port] }
{ ; out res[out_port], d }
{ ; in d, res[inp_port] }
{ ; out res[out_port], c }
{ ; in c, res[inp_port] }
// Unzip the recieved even samples as required
// aeim bfjn cgko dhlp -> (abcd) (efgh) (ijkl) (mnop)
unzip e, f, 0
unzip c, d, 0
unzip d, f, 0
unzip c, e, 0
// Bit-reverse and store the recieved even samples
{ ; bitrev f, f }
{ stw f, inp_array[6] ; bitrev e, e }
{ stw e, inp_array[4] ; bitrev d, d }
{ stw d, inp_array[2] ; bitrev c, c }
{ stw c, inp_array[0] ; }
// Output the remaining odd samples. At this point, we have transmitted all
// 8 samples, and must call the send callback to recieve the next batch.
// Input the first two odd samples. As we are returning from this function
// in order to call the send callback, we must store these recieved samples in
// memory to pick up later.
// Input the value of the lr_clock and return it
{ ; out res[out_port], b }
{ ; in f, res[inp_port] }
{ stw f, inp_array[1] ; out res[out_port], a }
{ ; in e, res[inp_port] }
{ stw e, inp_array[3] ; in r0, res[lr_clock] }
// Continue to common end section
i2s_frame_slave_4b_setup_final:
// Restore registers and return.
ldd a, b, sp[0]
ldd c, d, sp[1]
ldd e, f, sp[2]
ldw lr_clock, sp[6]
retsp NSTACKWORDS
.L_func_end:
.cc_bottom FUNCTION_NAME.function
.globl FUNCTION_NAME
.type FUNCTION_NAME,@function
.set FUNCTION_NAME.nstackwords,NSTACKWORDS; .global FUNCTION_NAME.nstackwords
.set FUNCTION_NAME.maxcores,1; .global FUNCTION_NAME.maxcores
.set FUNCTION_NAME.maxtimers,0; .global FUNCTION_NAME.maxtimers
.set FUNCTION_NAME.maxchanends,0; .global FUNCTION_NAME.maxchanends

View File

@@ -0,0 +1,361 @@
// Copyright 2015-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h>
#include <xclib.h>
#include "i2s.h"
#include <print.h>
#include "limits.h"
#include "xassert.h"
#include <stdlib.h>
#include <stdio.h>
static void i2s_frame_slave_init_ports(
out buffered port:32 (&?p_dout)[num_out],
size_t num_out,
in buffered port:32 (&?p_din)[num_in],
size_t num_in,
in port p_bclk,
in buffered port:32 p_lrclk,
clock bclk)
{
set_clock_on(bclk);
configure_clock_src(bclk, p_bclk);
configure_in_port(p_lrclk, bclk);
for (size_t i = 0; i < num_out; i++)
configure_out_port(p_dout[i], bclk, 0);
for (size_t i = 0; i < num_in; i++)
configure_in_port(p_din[i], bclk);
start_clock(bclk);
}
#define i2s_frame_slave i2s_frame_slave0
#pragma unsafe arrays
static void i2s_frame_slave0(client i2s_frame_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
static const size_t num_data_bits,
in port p_bclk,
in buffered port:32 p_lrclk,
clock bclk)
{
unsigned port_time;
int32_t in_samps[16]; //Workaround: should be (num_in << 1) but compiler thinks that isn't const,
int32_t out_samps[16];//so setting to 16 which should be big enough for most cases
// Since #pragma unsafe arrays is used need to ensure array won't overflow.
assert((num_in << 1) <= 16);
i2s_config_t config;
config.slave_frame_synch_error = 0;
if (num_data_bits == 32)
{
while(1){
i2s_frame_slave_init_ports(p_dout, num_out, p_din, num_in, p_bclk, p_lrclk, bclk);
i2s_restart_t restart = I2S_NO_RESTART;
i2s_i.init(config, null);
config.slave_frame_synch_error = 0;
//Get initial send data if output enabled
if (num_out) i2s_i.send(num_out << 1, out_samps);
unsigned mode = config.mode;
if (config.slave_bclk_polarity == I2S_SLAVE_SAMPLE_ON_BCLK_FALLING)
set_port_inv(p_bclk);
else
set_port_no_inv(p_bclk);
const unsigned expected_low = (mode == I2S_MODE_I2S ? 0x80000000 : 0x00000000);
const unsigned expected_high = (mode == I2S_MODE_I2S ? 0x7fffffff : 0xffffffff);
unsigned syncerror = 0;
unsigned lrval;
for (size_t i=0;i<num_out;i++)
clearbuf(p_dout[i]);
for (size_t i=0;i<num_in;i++)
clearbuf(p_din[i]);
clearbuf(p_lrclk);
unsigned offset = 0;
if (mode==I2S_MODE_I2S) {
offset = 1;
}
// Wait for LRCLK edge (in I2S LRCLK = 0 is left, TDM rising edge is start of frame)
p_lrclk when pinseq(1) :> void;
p_lrclk when pinseq(0) :> void @ port_time;
unsigned initial_out_port_time = port_time + offset + (I2S_CHANS_PER_FRAME*32);
unsigned initial_in_port_time = port_time + offset + ((I2S_CHANS_PER_FRAME*32)+32) - 1;
//Start outputting evens (0,2,4..) data at correct point relative to the clock
for (size_t i=0, idx=0; i<num_out; i++, idx+=2){
p_dout[i] @ initial_out_port_time <: bitrev(out_samps[idx]);
}
// XC doesn't have syntax for setting a timed input without waiting for the input
asm("setpt res[%0], %1"::"r"(p_lrclk),"r"(initial_in_port_time));
for (size_t i=0;i<num_in;i++) {
asm("setpt res[%0], %1"::"r"(p_din[i]),"r"(initial_in_port_time));
}
//And pre-load the odds (1,3,5..) to follow immediately afterwards
for (size_t i=0, idx=1; i<num_out; i++, idx+=2){
p_dout[i] <: bitrev(out_samps[idx]);
}
//Main loop
while (!syncerror && (restart == I2S_NO_RESTART)) {
restart = i2s_i.restart_check();
if (num_out && (restart == I2S_NO_RESTART)){
i2s_i.send(num_out << 1, out_samps);
//Output i2s evens (0,2,4..)
#pragma loop unroll
for (size_t i=0, idx=0; i<num_out; i++, idx+=2){
p_dout[i] <: bitrev(out_samps[idx]);
}
}
//Read lrclk value
p_lrclk :> lrval;
//Input i2s evens (0,2,4..)
#pragma loop unroll
for (size_t i=0, idx=0; i<num_in; i++, idx+=2){
int32_t data;
asm volatile("in %0, res[%1]":"=r"(data):"r"(p_din[i]):"memory");
in_samps[idx] = bitrev(data);
}
syncerror += (lrval != expected_low);
//Read lrclk value
p_lrclk :> lrval;
//Output i2s odds (1,3,5..)
#pragma loop unroll
if (num_out && (restart == I2S_NO_RESTART)){
for (size_t i=0, idx=1; i<num_out; i++, idx+=2){
p_dout[i] <: bitrev(out_samps[idx]);
}
}
//Input i2s odds (1,3,5..)
#pragma loop unroll
for (size_t i=0, idx=1; i<num_in; i++, idx+=2){
int32_t data;
asm volatile("in %0, res[%1]":"=r"(data):"r"(p_din[i]):"memory");
in_samps[idx] = bitrev(data);
}
syncerror += (lrval != expected_high);
if (num_in && (restart == I2S_NO_RESTART))
{
i2s_i.receive(num_in << 1, in_samps);
}//main loop, runs until user restart or synch error
}
if(restart == I2S_SHUTDOWN)
{
return;
}
if(syncerror)
{
config.slave_frame_synch_error = 1;
}
}// while(1)
}
else // else if num_data_bits != 32
{
const unsigned data_bit_offset = 32 - num_data_bits;
const unsigned data_bit_mask = UINT_MAX >> data_bit_offset; // e.g. 00011111 for 5b data
while (1)
{
i2s_frame_slave_init_ports(p_dout, num_out, p_din, num_in, p_bclk, p_lrclk, bclk);
i2s_config_t config;
i2s_restart_t restart = I2S_NO_RESTART;
i2s_i.init(config, null);
config.slave_frame_synch_error = 0;
// Get initial send data if output enabled
if (num_out)
{
i2s_i.send(num_out << 1, out_samps);
}
unsigned mode = config.mode;
if (config.slave_bclk_polarity == I2S_SLAVE_SAMPLE_ON_BCLK_FALLING)
set_port_inv(p_bclk);
else
set_port_no_inv(p_bclk);
const unsigned expected_low = (mode == I2S_MODE_I2S ? 0x80000000 : 0x00000000) & !data_bit_mask;
const unsigned expected_high = (mode == I2S_MODE_I2S ? 0x7fffffff : 0xffffffff) & !data_bit_mask;
unsigned syncerror = 0;
unsigned lrval;
for (size_t i = 0; i < num_out; i++)
clearbuf(p_dout[i]);
for (size_t i = 0; i < num_in; i++)
clearbuf(p_din[i]);
clearbuf(p_lrclk);
unsigned offset = 0;
if (mode == I2S_MODE_I2S)
{
offset = 1;
}
// Wait for LRCLK edge (in I2S LRCLK = 0 is left, TDM rising edge is start of frame)
p_lrclk when pinseq(1) :> void;
p_lrclk when pinseq(0) :> void @ port_time;
unsigned initial_out_port_time = port_time + offset + (I2S_CHANS_PER_FRAME * num_data_bits);
unsigned initial_in_port_time = port_time + offset + ((I2S_CHANS_PER_FRAME * num_data_bits) + num_data_bits) - 1;
// Start outputting evens (0,2,4..) data at correct point relative to the clock
for (size_t i = 0, idx = 0; i < num_out; i++, idx += 2)
{
partout_timed(p_dout[i], num_data_bits, bitrev(out_samps[idx] << data_bit_offset), initial_out_port_time);
}
// XC doesn't have syntax for setting a timed input without waiting for the input
asm volatile("setpt res[%0], %1"
:
: "r"(p_lrclk), "r"(initial_in_port_time));
set_port_shift_count(p_lrclk, num_data_bits);
for (size_t i = 0; i < num_in; i++)
{
asm volatile("setpt res[%0], %1"
:
: "r"(p_din[i]), "r"(initial_in_port_time));
set_port_shift_count(p_din[i], num_data_bits);
}
// And pre-load the odds (1,3,5..) to follow immediately afterwards
for (size_t i = 0, idx = 1; i < num_out; i++, idx += 2)
{
partout(p_dout[i], num_data_bits, bitrev(out_samps[idx] << data_bit_offset));
}
// Main loop
while (!syncerror && (restart == I2S_NO_RESTART))
{
restart = i2s_i.restart_check();
if (num_out && (restart == I2S_NO_RESTART))
{
i2s_i.send(num_out << 1, out_samps);
// Output i2s evens (0,2,4..)
for (size_t i = 0, idx = 0; i < num_out; i++, idx += 2)
{
partout(p_dout[i], num_data_bits, bitrev(out_samps[idx] << data_bit_offset));
}
}
// Read lrclk value
asm volatile("in %0, res[%1]"
: "=r"(lrval)
: "r"(p_lrclk));
set_port_shift_count(p_lrclk, num_data_bits);
// Input i2s evens (0,2,4..)
for (size_t i = 0, idx = 0; i < num_in; i++, idx += 2)
{
int32_t data;
asm volatile("in %0, res[%1]"
: "=r"(data)
: "r"(p_din[i]));
set_port_shift_count(p_din[i], num_data_bits);
in_samps[idx] = bitrev(data) & data_bit_mask;
}
syncerror += ((lrval & !data_bit_mask) != expected_low);
// Read lrclk value
asm volatile("in %0, res[%1]"
: "=r"(lrval)
: "r"(p_lrclk));
set_port_shift_count(p_lrclk, num_data_bits);
// Output i2s odds (1,3,5..)
#pragma loop unroll
if (num_out && (restart == I2S_NO_RESTART))
{
for (size_t i = 0, idx = 1; i < num_out; i++, idx += 2)
{
partout(p_dout[i], num_data_bits, bitrev(out_samps[idx] << data_bit_offset));
}
}
// Input i2s odds (1,3,5..)
#pragma loop unroll
for (size_t i = 0, idx = 1; i < num_in; i++, idx += 2)
{
int32_t data;
asm volatile("in %0, res[%1]"
: "=r"(data)
: "r"(p_din[i]));
set_port_shift_count(p_din[i], num_data_bits);
in_samps[idx] = bitrev(data) & data_bit_mask;
}
syncerror += ((lrval & !data_bit_mask) != expected_high);
if (num_in && (restart == I2S_NO_RESTART))
{
i2s_i.receive(num_in << 1, in_samps);
}
} // main loop, runs until user restart or synch error
if(restart == I2S_SHUTDOWN)
{
return;
}
if(syncerror)
{
config.slave_frame_synch_error = 1;
}
}// while(1)
} // if num_data_bits == 32
}
// This function is just to avoid unused static function warnings for
// i2s_frame_slave0,it should never be called.
inline void i2s_frame_slave1(client i2s_frame_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
static const size_t num_data_bits,
in port p_bclk,
in buffered port:32 p_lrclk,
clock bclk)
{
if (isnull(p_dout) && isnull(p_din))
{
fail("Must provide non-null p_dout or p_din");
}
i2s_frame_slave0(i2s_i, p_dout, num_out, p_din, num_in, num_data_bits, p_bclk, p_lrclk, bclk);
}

View File

@@ -0,0 +1,229 @@
// Copyright 2015-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h>
#include <xclib.h>
#include "i2s.h"
#include "xassert.h"
#define FRAME_WORDS (2)
static const unsigned i2s_clk_mask_lookup[5] = {
0xaaaaaaaa, //div 2
0xcccccccc, //div 4
0xf0f0f0f0, //div 8
0xff00ff00, //div 16
0xffff0000, //div 32
};
static void i2s_init_ports(
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
out buffered port:32 p_bclk,
out buffered port:32 p_lrclk,
clock bclk,
const clock mclk){
set_clock_on(bclk);
configure_clock_src(bclk, p_bclk);
configure_out_port(p_bclk, mclk, 1);
configure_out_port(p_lrclk, bclk, 1);
for (size_t i = 0; i < num_out; i++)
configure_out_port(p_dout[i], bclk, 0);
for (size_t i = 0; i < num_in; i++)
configure_in_port(p_din[i], bclk);
start_clock(bclk);
}
static void inline i2s_output_clock_pair(out buffered port:32 p_bclk,unsigned clk_mask){
p_bclk <: clk_mask;
p_bclk <: clk_mask;
}
#pragma unsafe arrays
static void output_word(
out buffered port:32 p_lrclk,
unsigned &lr_mask,
unsigned total_clk_pairs,
client i2s_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
out buffered port:32 p_bclk,
unsigned clk_mask,
unsigned calls_per_pair,
unsigned offset){
//This is non-blocking
lr_mask = ~lr_mask;
p_lrclk <: lr_mask;
unsigned if_call_num = 0;
for(unsigned clk_pair=0; clk_pair < total_clk_pairs;clk_pair++){
for(unsigned i=0;i<calls_per_pair;i++){
if(if_call_num < num_in){
unsigned data;
asm volatile("in %0, res[%1]":"=r"(data):"r"(p_din[if_call_num]):"memory");
i2s_i.receive(if_call_num*FRAME_WORDS + offset, bitrev(data));
} else if(if_call_num < num_in + num_out){
unsigned index = if_call_num - num_in;
p_dout[index] <: bitrev(i2s_i.send(index*FRAME_WORDS + offset));
}
if_call_num++;
}
//This is blocking
i2s_output_clock_pair(p_bclk, clk_mask);
}
}
#pragma unsafe arrays
static i2s_restart_t i2s_ratio_n(client i2s_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
out buffered port:32 p_bclk,
out buffered port:32 p_lrclk,
unsigned ratio,
i2s_mode_t mode){
unsigned clk_mask = i2s_clk_mask_lookup[ratio-1];
unsigned lr_mask = 0;
int32_t data;
unsigned total_clk_pairs = (1<<(ratio-1));
unsigned calls_per_pair = ((num_in + num_out) + (1<<(ratio-1))-1)>>(ratio-1);
for(size_t i=0;i<num_out;i++)
clearbuf(p_dout[i]);
for(size_t i=0;i<num_in;i++)
clearbuf(p_din[i]);
clearbuf(p_lrclk);
clearbuf(p_bclk);
//Preload word 0
if(mode == I2S_MODE_I2S){
for(size_t i=0;i<num_out;i++)
p_dout[i] @ 2 <: bitrev(i2s_i.send(i*FRAME_WORDS));
partout(p_lrclk, 1, 0);
for(size_t i=0;i<num_in;i++)
asm("setpt res[%0], %1"::"r"(p_din[i]), "r"(32+1));
lr_mask = 0x80000000;
partout(p_bclk, 1<<ratio, clk_mask);
} else {
for(size_t i=0;i<num_out;i++)
p_dout[i] <: bitrev(i2s_i.send(i*FRAME_WORDS));
}
p_lrclk <: lr_mask;
i2s_output_clock_pair(p_bclk, clk_mask);
//This is non-blocking
lr_mask = ~lr_mask;
p_lrclk <: lr_mask;
//Now preload word 1
unsigned if_call_num = 0;
for(unsigned clk_pair=0; clk_pair < total_clk_pairs;clk_pair++){
for(unsigned i=0;i<calls_per_pair;i++){
if(if_call_num < num_out)
p_dout[if_call_num] <: bitrev(i2s_i.send(if_call_num*FRAME_WORDS+1));
if_call_num++;
}
//This is blocking
i2s_output_clock_pair(p_bclk, clk_mask);
}
for(unsigned frm_word_no=1;frm_word_no < FRAME_WORDS - 1; frm_word_no++){
output_word(p_lrclk, lr_mask, total_clk_pairs, i2s_i, p_dout, num_out,
p_din, num_in, p_bclk, clk_mask, calls_per_pair, (1 + frm_word_no)%FRAME_WORDS);
}
//body
while(1){
// Do the first (FRAME_WORDS-1) words of the frame
i2s_restart_t restart = i2s_i.restart_check();
// The final word of each frame is special as it might terminate the transfer
if (restart != I2S_NO_RESTART) {
if_call_num = 0;
for(unsigned clk_pair=0; clk_pair < total_clk_pairs;clk_pair++){
for(unsigned i=0;i<calls_per_pair;i++){
if(if_call_num < num_in){
asm volatile("in %0, res[%1]":"=r"(data):"r"(p_din[if_call_num]):"memory");
i2s_i.receive(if_call_num*FRAME_WORDS + FRAME_WORDS - 2, bitrev(data));
}
if_call_num++;
}
if(clk_pair < total_clk_pairs-1)
i2s_output_clock_pair(p_bclk, clk_mask);
}
sync(p_bclk);
for(size_t i=0;i<num_in;i++){
asm volatile("in %0, res[%1]":"=r"(data):"r"(p_din[i]):"memory");
i2s_i.receive(i*FRAME_WORDS + FRAME_WORDS - 1, bitrev(data));
}
return restart;
} else {
output_word(p_lrclk, lr_mask, total_clk_pairs, i2s_i, p_dout, num_out,
p_din, num_in, p_bclk, clk_mask, calls_per_pair, 0);
}
for(unsigned frm_word_no=0;frm_word_no < FRAME_WORDS-1; frm_word_no++){
output_word(p_lrclk, lr_mask, total_clk_pairs, i2s_i, p_dout, num_out,
p_din, num_in, p_bclk, clk_mask, calls_per_pair, (1 + frm_word_no)%FRAME_WORDS);
}
}
return I2S_RESTART;
}
#define i2s_master i2s_master0
static void i2s_master0(client i2s_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
out buffered port:32 p_bclk,
out buffered port:32 p_lrclk,
clock bclk,
const clock mclk){
while(1){
i2s_config_t config;
unsigned mclk_bclk_ratio_log2;
i2s_i.init(config, null);
if (isnull(p_dout) && isnull(p_din)) {
fail("Must provide non-null p_dout or p_din");
}
mclk_bclk_ratio_log2 = clz(bitrev(config.mclk_bclk_ratio));
//This ensures that the port time on all the ports is at 0
i2s_init_ports(p_dout, num_out, p_din, num_in,
p_bclk, p_lrclk, bclk, mclk);
i2s_restart_t restart =
i2s_ratio_n(i2s_i, p_dout, num_out, p_din,
num_in, p_bclk, p_lrclk,
mclk_bclk_ratio_log2, config.mode);
if (restart == I2S_SHUTDOWN)
return;
}
}
// This function is just to avoid unused static function warnings for i2s_master0,
// it should never be called.
inline void i2s_master1(client interface i2s_callback_if i,
out buffered port:32 i2s_dout[num_i2s_out],
static const size_t num_i2s_out,
in buffered port:32 i2s_din[num_i2s_in],
static const size_t num_i2s_in,
out buffered port:32 i2s_bclk,
out buffered port:32 i2s_lrclk,
clock clk_bclk,
const clock clk_mclk) {
i2s_master0(i, i2s_dout, num_i2s_out, i2s_din, num_i2s_in,
i2s_bclk, i2s_lrclk,
clk_bclk, clk_mclk);
}

View File

@@ -0,0 +1,148 @@
// Copyright 2015-2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h>
#include <xclib.h>
#include "i2s.h"
#define I2S_CHANS_PER_FRAME (2)
static void i2s_slave_init_ports(
out buffered port:32 (&?p_dout)[num_out],
size_t num_out,
in buffered port:32 (&?p_din)[num_in],
size_t num_in,
in port p_bclk,
in buffered port:32 p_lrclk,
clock bclk){
set_clock_on(bclk);
configure_clock_src(bclk, p_bclk);
configure_in_port(p_lrclk, bclk);
for (size_t i = 0; i < num_out; i++) {
configure_out_port(p_dout[i], bclk, 0);
}
for (size_t i = 0; i < num_in; i++) {
configure_in_port(p_din[i], bclk);
}
start_clock(bclk);
}
static void i2s_slave_send(client i2s_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
size_t num_out, unsigned frame_word){
for(size_t i=0;i<num_out;i++) {
p_dout[i] <: bitrev(i2s_i.send(i*2+frame_word));
}
}
static void i2s_slave_receive(client i2s_callback_if i2s_i,
in buffered port:32 (&?p_din)[num_in],
size_t num_in, unsigned frame_word){
for (size_t i=0;i<num_in;i++) {
unsigned data;
p_din[i] :> data;
i2s_i.receive(i*2 + frame_word, bitrev(data));
}
}
#define i2s_slave i2s_slave0
static void i2s_slave0(client i2s_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
in port p_bclk,
in buffered port:32 p_lrclk,
clock bclk){
unsigned syncerror;
unsigned lrval;
unsigned port_time;
i2s_slave_init_ports(p_dout, num_out, p_din, num_in, p_bclk, p_lrclk, bclk);
i2s_config_t config;
config.slave_frame_synch_error = 0;
while(1) {
i2s_mode_t m;
i2s_restart_t restart = I2S_NO_RESTART;
config.slave_bclk_polarity = I2S_SLAVE_SAMPLE_ON_BCLK_RISING;
i2s_i.init(config, null);
config.slave_frame_synch_error = 0;
m = config.mode;
if (config.slave_bclk_polarity == I2S_SLAVE_SAMPLE_ON_BCLK_FALLING)
set_port_inv(p_bclk);
else
set_port_no_inv(p_bclk);
unsigned expected_low = (m==I2S_MODE_I2S) ? 0 : 0x80000000;
unsigned expected_high = (m==I2S_MODE_I2S) ? 0xffffffff : 0x7fffffff;
syncerror = 0;
clearbuf(p_lrclk);
/* Wait for LRCLK edge (in I2S LRCLK = 0 is left, TDM rising edge is start of frame) */
p_lrclk when pinseq(1) :> void;
p_lrclk when pinseq(0) :> void @ port_time;
for (size_t i=0;i<num_out;i++) {
p_dout[i] @ (port_time+32+32+(m==I2S_MODE_I2S)) <: bitrev(i2s_i.send(i*2));
}
/* Setup input for next frame. Account for the buffering in port */
port_time += ((I2S_CHANS_PER_FRAME*32)+32);
/* XC doesn't have syntax for setting a timed input without waiting for the input */
/* -1 on LRClock makes checking a lot easier since data is offset with LRclock by 1 clk */
asm("setpt res[%0], %1"::"r"(p_lrclk),"r"(port_time-(m==I2S_MODE_I2S)));
for (size_t i=0;i<num_in;i++) {
asm("setpt res[%0], %1"::"r"(p_din[i]),"r"(port_time-(m!=I2S_MODE_I2S)));
}
while (!syncerror && (restart == I2S_NO_RESTART)) {
i2s_slave_send(i2s_i, p_dout, num_out, 1);
i2s_slave_receive(i2s_i, p_din, num_in, 0);
p_lrclk :> lrval;
syncerror += (lrval != expected_low);
restart = i2s_i.restart_check();
if (restart == I2S_NO_RESTART) {
i2s_slave_send(i2s_i, p_dout, num_out, 0);
}
i2s_slave_receive(i2s_i, p_din, num_in, 1);
p_lrclk :> lrval;
syncerror += (lrval != expected_high);
}
if (restart == I2S_SHUTDOWN) {
return;
}
if(syncerror){
config.slave_frame_synch_error = 1;
}
}
}
// This function is just to avoid unused static function warnings for
// i2s_slave0,it should never be called.
inline void i2s_slave1(client i2s_callback_if i2s_i,
out buffered port:32 (&?p_dout)[num_out],
static const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
static const size_t num_in,
in port p_bclk,
in buffered port:32 p_lrclk,
clock bclk){
if (isnull(p_dout) && isnull(p_din)) {
fail("Must provide non-null p_dout or p_din");
}
i2s_slave0(i2s_i, p_dout, num_out, p_din, num_in, p_bclk, p_lrclk, bclk);
}

View File

@@ -0,0 +1,33 @@
// Copyright 2015-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef TDM_COMMON_H_
#define TDM_COMMON_H_
#include <xclib.h>
#define TDM_MAX_CHANNELS_PER_DATA_LINE (16)
static void make_fsync_mask(
unsigned fsync_mask[],
int offset,
unsigned sclk_edge_count,
unsigned channels_per_data_line){
unsigned hi_edge = (offset)%(channels_per_data_line*32);
unsigned lo_edge = (sclk_edge_count+offset)%(channels_per_data_line*32);
unsigned bit_no = 0;
unsigned w;
for(unsigned i=0;i<channels_per_data_line;i++){
for(unsigned j=0;j<32;j++){
w<<=1;
if(lo_edge > hi_edge)
w += ((bit_no>=hi_edge)&&(bit_no < lo_edge));
else
w += ((bit_no>=hi_edge)||(bit_no < lo_edge));
bit_no++;
}
fsync_mask[i] = bitrev(w);
}
}
#endif /* TDM_COMMON_H_ */

View File

@@ -0,0 +1,167 @@
// Copyright 2015-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <i2s.h>
#include <xs1.h>
#include <xclib.h>
#include "tdm_common.h"
static void tdm_init_ports(
out buffered port:32 (&?p_dout)[num_out],
const size_t num_out,
in buffered port:32 (&?p_din)[num_in],
const size_t num_in,
out buffered port:32 p_fsync,
clock clk){
stop_clock(clk);
configure_out_port(p_fsync, clk, 0);
for (size_t i = 0; i < num_out; i++)
configure_out_port(p_dout[i], clk, 0);
for (size_t i = 0; i < num_in; i++)
configure_in_port(p_din[i], clk);
start_clock(clk);
}
[[always_inline]]
static void tdm_send(client i2s_callback_if tdm_i,
out buffered port:32 (&?p_dout)[num_out],
size_t num_out,
unsigned channels_per_data_line,
unsigned word){
for(size_t i=0;i<num_out;i++)
p_dout[i] <: bitrev(tdm_i.send(i*channels_per_data_line + word));
}
[[always_inline]]
static void tdm_receive(client i2s_callback_if tdm_i,
in buffered port:32 (&?p_din)[num_in],
size_t num_in,
unsigned channels_per_data_line,
unsigned word){
for(size_t i=0;i<num_in;i++){
uint32_t data;
asm volatile("in %0, res[%1]":"=r"(data):"r"(p_din[i]):"memory");
tdm_i.receive(i*channels_per_data_line + word, bitrev(data));
}
}
[[always_inline]]
static i2s_restart_t do_tdm(client i2s_callback_if tdm_i,
out buffered port:32 (&?p_dout)[num_out],
size_t num_out,
in buffered port:32 (&?p_din)[num_in],
size_t num_in,
out buffered port:32 p_fsync,
int offset,
unsigned sclk_edge_count,
unsigned channels_per_data_line){
i2s_restart_t restart = I2S_NO_RESTART;
unsigned fsync_mask[TDM_MAX_CHANNELS_PER_DATA_LINE] ={0};
p_fsync <: 0;
make_fsync_mask(fsync_mask, offset, sclk_edge_count, channels_per_data_line);
for(size_t i=0;i<num_out;i++){
clearbuf(p_dout[i]);
p_dout[i] <: 0;
}
for(size_t i=0;i<num_in;i++)
clearbuf(p_din[i]);
unsigned port_time;
p_fsync <: 0 @ port_time;
port_time += 80;//lots!
if(offset < 0)
partout_timed(p_fsync, -offset, bitrev(fsync_mask[channels_per_data_line-1]), port_time + offset);
for(size_t i=0;i<num_in;i++)
asm("setpt res[%0], %1"::"r"(p_din[i]), "r"(port_time+32-1));
for(size_t i=0;i<num_out;i++)
p_dout[i] @ port_time <: bitrev(tdm_i.send(i*channels_per_data_line + 0));
p_fsync @ port_time <: fsync_mask[0];
restart = tdm_i.restart_check();
p_fsync <: fsync_mask[1];
tdm_send(tdm_i, p_dout, num_out, channels_per_data_line, 1);
for(unsigned frm_word_no = 2;frm_word_no < channels_per_data_line; frm_word_no++){
tdm_receive(tdm_i, p_din, num_in, channels_per_data_line, frm_word_no-2);
p_fsync <: fsync_mask[frm_word_no];
tdm_send(tdm_i, p_dout, num_out, channels_per_data_line, frm_word_no);
}
while(1){
if (restart != I2S_NO_RESTART){
tdm_receive(tdm_i, p_din, num_in, channels_per_data_line, channels_per_data_line-2);
tdm_receive(tdm_i, p_din, num_in, channels_per_data_line, channels_per_data_line-1);
sync(p_din[0]);
p_fsync <: 0;
return restart;
}
tdm_receive(tdm_i, p_din, num_in, channels_per_data_line, channels_per_data_line-2);
p_fsync <: fsync_mask[0];
tdm_send(tdm_i, p_dout, num_out, channels_per_data_line, 0);
restart = tdm_i.restart_check();
for(unsigned frm_word_no=1;frm_word_no < channels_per_data_line; frm_word_no++){
tdm_receive(tdm_i, p_din, num_in, channels_per_data_line,
(frm_word_no-2)&(channels_per_data_line-1));
p_fsync <: fsync_mask[frm_word_no];
tdm_send(tdm_i, p_dout, num_out, channels_per_data_line, frm_word_no);
}
}
return I2S_RESTART;
}
#define tdm_master tdm_master0
static void tdm_master0(client interface i2s_callback_if tdm_i,
out buffered port:32 p_fsync,
out buffered port:32 (&?p_dout)[num_out],
size_t num_out,
in buffered port:32 (&?p_din)[num_in],
size_t num_in,
clock clk){
if (isnull(p_dout) && isnull(p_din)) {
fail("Must provide non-null p_dout or p_din");
}
tdm_init_ports(p_dout, num_out, p_din, num_in, p_fsync, clk);
while(1){
int offset;
unsigned sclk_edge_count;
unsigned channels_per_data_line;
tdm_config_t config;
tdm_i.init(null, config);
i2s_restart_t restart =
do_tdm(tdm_i,
p_dout,num_out,
p_din, num_in,
p_fsync,
config.offset, config.sync_len, config.channels_per_frame);
if (restart == I2S_SHUTDOWN)
return;
}
}
// This function is just to avoid unused static function warnings for
// tdm_master0,it should never be called.
inline void tdm_master1(client interface i2s_callback_if tdm_i,
out buffered port:32 p_fsync,
out buffered port:32 (&?p_dout)[num_out],
size_t num_out,
in buffered port:32 (&?p_din)[num_in],
size_t num_in,
clock clk){
tdm_master0(tdm_i, p_fsync, p_dout, num_out, p_din, num_in, clk);
}