init
This commit is contained in:
490
lib_i2s/lib_i2s/api/i2s.h
Normal file
490
lib_i2s/lib_i2s/api/i2s.h
Normal 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_
|
||||
7
lib_i2s/lib_i2s/lib_build_info.cmake
Normal file
7
lib_i2s/lib_i2s/lib_build_info.cmake
Normal 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()
|
||||
15
lib_i2s/lib_i2s/module_build_info
Normal file
15
lib_i2s/lib_i2s/module_build_info
Normal 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
|
||||
67
lib_i2s/lib_i2s/src/i2s_dummy_impl.xc
Normal file
67
lib_i2s/lib_i2s/src/i2s_dummy_impl.xc
Normal 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){
|
||||
}
|
||||
259
lib_i2s/lib_i2s/src/i2s_frame_master_4b_impl.h
Normal file
259
lib_i2s/lib_i2s/src/i2s_frame_master_4b_impl.h
Normal 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__
|
||||
168
lib_i2s/lib_i2s/src/i2s_frame_master_4b_loop_part_1.S
Normal file
168
lib_i2s/lib_i2s/src/i2s_frame_master_4b_loop_part_1.S
Normal 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
|
||||
202
lib_i2s/lib_i2s/src/i2s_frame_master_4b_loop_part_2.S
Normal file
202
lib_i2s/lib_i2s/src/i2s_frame_master_4b_loop_part_2.S
Normal 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
|
||||
229
lib_i2s/lib_i2s/src/i2s_frame_master_4b_setup.S
Normal file
229
lib_i2s/lib_i2s/src/i2s_frame_master_4b_setup.S
Normal 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
|
||||
370
lib_i2s/lib_i2s/src/i2s_frame_master_impl.h
Normal file
370
lib_i2s/lib_i2s/src/i2s_frame_master_impl.h
Normal 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__
|
||||
187
lib_i2s/lib_i2s/src/i2s_frame_slave_4b_impl.h
Normal file
187
lib_i2s/lib_i2s/src/i2s_frame_slave_4b_impl.h
Normal 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);
|
||||
}
|
||||
165
lib_i2s/lib_i2s/src/i2s_frame_slave_4b_loop_part_1.S
Normal file
165
lib_i2s/lib_i2s/src/i2s_frame_slave_4b_loop_part_1.S
Normal 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
|
||||
201
lib_i2s/lib_i2s/src/i2s_frame_slave_4b_loop_part_2.S
Normal file
201
lib_i2s/lib_i2s/src/i2s_frame_slave_4b_loop_part_2.S
Normal 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
|
||||
215
lib_i2s/lib_i2s/src/i2s_frame_slave_4b_setup.S
Normal file
215
lib_i2s/lib_i2s/src/i2s_frame_slave_4b_setup.S
Normal 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
|
||||
361
lib_i2s/lib_i2s/src/i2s_frame_slave_impl.h
Normal file
361
lib_i2s/lib_i2s/src/i2s_frame_slave_impl.h
Normal 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);
|
||||
}
|
||||
229
lib_i2s/lib_i2s/src/i2s_master_impl.h
Normal file
229
lib_i2s/lib_i2s/src/i2s_master_impl.h
Normal 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);
|
||||
}
|
||||
|
||||
148
lib_i2s/lib_i2s/src/i2s_slave_impl.h
Normal file
148
lib_i2s/lib_i2s/src/i2s_slave_impl.h
Normal 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);
|
||||
}
|
||||
33
lib_i2s/lib_i2s/src/tdm_common.h
Normal file
33
lib_i2s/lib_i2s/src/tdm_common.h
Normal 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_ */
|
||||
167
lib_i2s/lib_i2s/src/tdm_master_impl.h
Normal file
167
lib_i2s/lib_i2s/src/tdm_master_impl.h
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user