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

View File

@@ -0,0 +1,163 @@
cmake_minimum_required(VERSION 3.21)
include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake)
project(app_usb_aud_phaten_gs)
set(APP_HW_TARGET PHATEN_GS.xn)
include(${CMAKE_CURRENT_LIST_DIR}/../deps.cmake)
set(APP_PCA_ENABLE ON)
###=========================================================================###
set(TARGET_HW_PHATEN_GSV2 ON)
set(CODEC_IS_AIC3204 ON)
# Configure host os detection here... (Windows or Non-Windows)
#-----------------------------------------------------------------------------#
# Host OS detection, set HOST_OS_DETECTION to ON
set(HOST_OS_DETECTION ON)
###=========================================================================###
# Customer
if (TARGET_HW_PHATEN_GSV2)
message("-- Building for PHATEN_GSv2 ---")
set(TARGET_BOARD PHATEN_GSV2)
set(APP_HW_TARGET PHATEN_GS.xn)
endif()
# CODEC IC
if (CODEC_IS_AIC3204)
message("-- DAC: AIC3204 ---")
set(CODEC_IC CODEC_AIC3204)
endif()
# Host OS detection
if (HOST_OS_DETECTION)
message("-- Host OS detection (Windows/Non-Windows) enabled ---")
set(EXTRA_BUILD_FLAGS ${EXTRA_BUILD_FLAGS} -DHOST_OS_DETECTION)
endif()
set(SW_USB_AUDIO_FLAGS ${EXTRA_BUILD_FLAGS} -O3
-report
-L${CMAKE_CURRENT_LIST_DIR}/../../lib_ex3d/lib_ex3d/lib
-lquadflash
-g
-fxscope
-DUSB_TILE=tile[0]
-DADAT_TX_USE_SHARED_BUFF=1
-DXUA_QUAD_SPI_FLASH=1
-D${TARGET_BOARD}
-D${CODEC_IC}
-DWINDOWS_OS_DESCRIPTOR_SUPPORT
-DDEBUG_PRINT_ENABLE=1)
LINK_DIRECTORIES(${CMAKE_CURRENT_LIST_DIR}/../../lib_dnr/lib_dnr)
set(APP_COMPILER_FLAGS_ex3d_stereo_2k ${SW_USB_AUDIO_FLAGS} -DI2S_CHANS_DAC=2
-DI2S_CHANS_ADC=2
-DAUDIO_CLASS=1
-DMIN_FREQ=48000
-DMAX_FREQ=48000
-DUSE_EX3D
-DMIXER=0
-DAIZIP_DNR=0 -ldnr_50ms
-llib_ex3d_stereo_2k
-DNUM_USB_CHAN_OUT=2
-DNUM_USB_CHAN_IN=2
-DSTREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS=16
-DSTREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS=16
-DSTREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS=16
-DSTREAM_FORMAT_INPUT_1_RESOLUTION_BITS=16
-DSTREAM_FORMAT_INPUT_2_RESOLUTION_BITS=16
-DSTREAM_FORMAT_INPUT_3_RESOLUTION_BITS=16
-DNUM_EX3D_CHAN_OUT=2
-DINPUT_VOLUME_CONTROL=0
-DOUTPUT_VOLUME_CONTROL=0
-DSTEREO_2K
-DHID_CONTROLS=1)
set(APP_COMPILER_FLAGS_ex3d_stereo_8k ${SW_USB_AUDIO_FLAGS} -DI2S_CHANS_DAC=2
-DI2S_CHANS_ADC=2
-DAUDIO_CLASS=1
-DMIN_FREQ=48000
-DMAX_FREQ=48000
-DUSE_EX3D
-DMIXER=0
-DAIZIP_DNR=0 -ldnr_50ms
-llib_ex3d_stereo_8k
-DNUM_USB_CHAN_OUT=2
-DNUM_USB_CHAN_IN=2
-DSTREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS=16
-DSTREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS=16
-DSTREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS=16
-DSTREAM_FORMAT_INPUT_1_RESOLUTION_BITS=16
-DSTREAM_FORMAT_INPUT_2_RESOLUTION_BITS=16
-DSTREAM_FORMAT_INPUT_3_RESOLUTION_BITS=16
-DNUM_EX3D_CHAN_OUT=2
-DINPUT_VOLUME_CONTROL=0
-DOUTPUT_VOLUME_CONTROL=0
-DSTEREO_8K
-DHID_CONTROLS=1)
set(APP_COMPILER_FLAGS_ex3d_71_game ${SW_USB_AUDIO_FLAGS} -DI2S_CHANS_DAC=2
-DI2S_CHANS_ADC=2
-DMIN_FREQ=48000
-DMAX_FREQ=48000
-DUSE_EX3D
-DMIXER=0
-DAIZIP_DNR=0 -ldnr_50ms
-llib_ex3d_game
-DNUM_USB_CHAN_OUT=8
-DNUM_USB_CHAN_IN=2
-DNUM_EX3D_CHAN_OUT=2
-DMIN_VOLUME=0xE000
-DSPATIAL_GAME
-DHID_CONTROLS=1)
set(APP_COMPILER_FLAGS_ex3d_71_music ${SW_USB_AUDIO_FLAGS} -DI2S_CHANS_DAC=2
-DI2S_CHANS_ADC=2
-DMIN_FREQ=48000
-DMAX_FREQ=48000
-DUSE_EX3D
-DMIXER=0
-DAIZIP_DNR=0 -ldnr_50ms
-llib_ex3d_music
-DNUM_USB_CHAN_OUT=8
-DNUM_USB_CHAN_IN=2
-DNUM_EX3D_CHAN_OUT=2
-DMIN_VOLUME=0xE000
-DSPATIAL_MUSIC
-DHID_CONTROLS=1)
set(APP_COMPILER_FLAGS_ex3d_71_movie ${SW_USB_AUDIO_FLAGS} -DI2S_CHANS_DAC=2
-DI2S_CHANS_ADC=2
-DMIN_FREQ=48000
-DMAX_FREQ=48000
-DUSE_EX3D
-DMIXER=0
-DAIZIP_DNR=0 -ldnr_50ms
-llib_ex3d_movie
-DNUM_USB_CHAN_OUT=8
-DNUM_USB_CHAN_IN=2
-DNUM_EX3D_CHAN_OUT=2
-DMIN_VOLUME=0xE000
-DSPATIAL_MOVIE
-DHID_CONTROLS=1)
set(APP_COMPILER_FLAGS_ex3d_71_drama ${SW_USB_AUDIO_FLAGS} -DI2S_CHANS_DAC=2
-DI2S_CHANS_ADC=2
-DMIN_FREQ=48000
-DMAX_FREQ=48000
-DUSE_EX3D
-DMIXER=0
-DAIZIP_DNR=0 -ldnr_50ms
-llib_ex3d_drama
-DNUM_USB_CHAN_OUT=8
-DNUM_USB_CHAN_IN=2
-DNUM_EX3D_CHAN_OUT=2
-DMIN_VOLUME=0xE000
-DSPATIAL_DRAMA
-DHID_CONTROLS=1)
set(APP_INCLUDES src src/core src/extensions ../../lib_dnr/lib_dnr)
set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)
XMOS_REGISTER_APP()

View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Network xmlns="http://www.xmos.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.xmos.com http://www.xmos.com">
<Declarations>
<Declaration>tileref tile[2]</Declaration>
<Declaration>tileref usb_tile</Declaration>
</Declarations>
<Packages>
<Package id="0" Type="XS3-UnA-1024-QF60A">
<Nodes>
<!-- Note that this clock setting is overridden by the app by writing directly to the PLL -->
<Node Id="0" InPackageId="0" Type="XS3-L16A-1024" SystemFrequency="600MHz" Oscillator="24MHz" referencefrequency="100MHz">
<Boot>
<Source Location="bootFlash"/>
</Boot>
<!-- <Extmem sizeMbit="1024" Frequency="175MHz">
<Padctrl clk="0x0" cke="0x0" cs_n="0x0" we_n="0x0" cas_n="0x0" ras_n="0x0" addr="0x0" ba="0x0" dq="0x0" dqs="0x0" dm="0x0"/>
<Lpddr lmr_opcode="0x0" emr_opcode="0x0"/>
</Extmem> -->
<Tile Number="0" Reference="tile[0]">
<!-- QSPI ports -->
<Port Location="XS1_PORT_1B" Name="PORT_SQI_CS_0"/>
<Port Location="XS1_PORT_1C" Name="PORT_SQI_SCLK_0"/>
<Port Location="XS1_PORT_4B" Name="PORT_SQI_SIO_0"/>
<!-- SPI ports -->
<Port Location="XS1_PORT_1A" Name="PORT_SSB"/>
<Port Location="XS1_PORT_1C" Name="PORT_SQI_SCLK_0"/>
<Port Location="XS1_PORT_1D" Name="PORT_SPI_MOSI"/>
<Port Location="XS1_PORT_1P" Name="PORT_SPI_MISO"/>
<!-- I2C ports -->
<Port Location="XS1_PORT_1N" Name="PORT_I2C_SCL"/>
<Port Location="XS1_PORT_1O" Name="PORT_I2C_SDA"/>
<!-- GPIO ports -->
<Port Location="XS1_PORT_8C" Name="PORT_GPO"/>
<Port Location="XS1_PORT_8D" Name="PORT_GPI"/>
<Port Location="XS1_PORT_1P" Name="PORT_ADC_RST"/>
<!-- Used for keeping XUA happy only -->
<Port Location="XS1_PORT_1G" Name="PORT_NOT_IN_PACKAGE_0"/>
<Port Location="XS1_PORT_16B" Name="PORT_MCLK_COUNT"/>
<Port Location="XS1_PORT_1D" Name="PORT_MCLK_IN_USB"/>
</Tile>
<Tile Number="1" Reference="tile[1]">
<!-- MIC related ports -->
<Port Location="XS1_PORT_1G" Name="PORT_PDM_CLK"/>
<Port Location="XS1_PORT_1F" Name="PORT_PDM_DATA"/>
<!-- Audio ports -->
<Port Location="XS1_PORT_1D" Name="PORT_MCLK_IN"/>
<Port Location="XS1_PORT_1C" Name="PORT_I2S_BCLK"/>
<Port Location="XS1_PORT_1B" Name="PORT_I2S_LRCLK"/>
<Port Location="XS1_PORT_1K" Name="PORT_I2S_ADC0"/>
<Port Location="XS1_PORT_1A" Name="PORT_I2S_DAC0"/>
</Tile>
</Node>
</Nodes>
</Package>
</Packages>
<Nodes>
<Node Id="2" Type="device:" RoutingId="0x8000">
<Service Id="0" Proto="xscope_host_data(chanend c);">
<Chanend Identifier="c" end="3"/>
</Service>
</Node>
</Nodes>
<Links>
<Link Encoding="2wire" Delays="5clk" Flags="XSCOPE">
<LinkEndpoint NodeId="0" Link="XL0"/>
<LinkEndpoint NodeId="2" Chanend="1"/>
</Link>
</Links>
<ExternalDevices>
<Device NodeId="0" Tile="0" Class="SQIFlash" Name="bootFlash">
<Attribute Name="PORT_SQI_CS" Value="PORT_SQI_CS_0"/>
<Attribute Name="PORT_SQI_SCLK" Value="PORT_SQI_SCLK_0"/>
<Attribute Name="PORT_SQI_SIO" Value="PORT_SQI_SIO_0"/>
</Device>
</ExternalDevices>
<JTAGChain>
<JTAGDevice NodeId="0"/>
</JTAGChain>
</Network>

View File

@@ -0,0 +1,177 @@
/**
* @file xua_conf.h
* @brief Defines relating to device configuration and customisation.
* For xCORE.ai Audio MC Board
* @author Ross Owen, XMOS Limited
*/
#ifndef _XUA_CONF_H_
#define _XUA_CONF_H_
#include "../../../shared/version.h"
/*
* Device configuration option defines to override default defines found in lib_xua/api/xua_conf_default.h
*
* Build can be customised but changing and adding defines here
*
* Note, we check if they are already defined in Makefile
*/
/*** Defines relating to basic functionality ***/
/* Enable/Disable MIDI - Default is MIDI off */
#ifndef MIDI
#define MIDI (0)
#endif
/* Enable/Disable S/PDIF output - Default is S/PDIF off */
#ifndef XUA_SPDIF_TX_EN
#define XUA_SPDIF_TX_EN (0)
#endif
/* Enable/Disable S/PDIF input - Default is S/PDIF off */
#ifndef XUA_SPDIF_RX_EN
#define XUA_SPDIF_RX_EN (0)
#endif
/* Enable/Disable ADAT output - Default is ADAT off */
#ifndef XUA_ADAT_TX_EN
#define XUA_ADAT_TX_EN (0)
#endif
/* Enable/Disable ADAT input - Default is ADAT off */
#ifndef XUA_ADAT_RX_EN
#define XUA_ADAT_RX_EN (0)
#endif
/* Enable/Disable Mixing core(s) - Default is on */
#ifndef MIXER
#define MIXER (1)
#endif
/* Set the number of mixes to perform - Default is 0 i.e mixing disabled */
#ifndef MAX_MIX_COUNT
#define MAX_MIX_COUNT (0)
#endif
/* Audio Class version - Default is 2.0 */
#ifndef AUDIO_CLASS
#define AUDIO_CLASS (2)
#endif
/*** Defines relating to channel counts ***/
/* Number of I2S channels to DACs*/
#ifndef I2S_CHANS_DAC
#define I2S_CHANS_DAC (8)
#endif
/* Number of I2S channels from ADCs */
#ifndef I2S_CHANS_ADC
#define I2S_CHANS_ADC (8)
#endif
/* Number of USB streaming channels - by default calculate by counting audio interfaces */
#ifndef NUM_USB_CHAN_IN
#define NUM_USB_CHAN_IN (I2S_CHANS_ADC + 2*XUA_SPDIF_RX_EN + 8*XUA_ADAT_RX_EN) /* Device to Host */
#endif
#ifndef NUM_USB_CHAN_OUT
#define NUM_USB_CHAN_OUT (I2S_CHANS_DAC + 2*XUA_SPDIF_TX_EN + 8*XUA_ADAT_TX_EN) /* Host to Device */
#endif
/*** Defines relating to channel arrangement/indices ***/
/* Channel index of S/PDIF Tx channels: separate channels after analogue channels (if they fit) */
#ifndef SPDIF_TX_INDEX
#if (I2S_CHANS_DAC + 2*XUA_SPDIF_TX_EN) <= NUM_USB_CHAN_OUT
#define SPDIF_TX_INDEX (I2S_CHANS_DAC)
#else
#define SPDIF_TX_INDEX (0)
#endif
#endif
/* Channel index of S/PDIF Rx channels: separate channels after analogue channels */
#ifndef SPDIF_RX_INDEX
#define SPDIF_RX_INDEX (I2S_CHANS_ADC)
#endif
/* Channel index of ADAT Tx channels: separate channels after S/PDIF channels (if they fit) */
#ifndef ADAT_TX_INDEX
#define ADAT_TX_INDEX (I2S_CHANS_DAC + 2*XUA_SPDIF_TX_EN)
#endif
/* Channel index of ADAT Rx channels: separate channels after S/PDIF channels */
#ifndef ADAT_RX_INDEX
#define ADAT_RX_INDEX (I2S_CHANS_ADC + 2*XUA_SPDIF_RX_EN)
#endif
/*** Defines relating to audio frequencies ***/
/* Master clock defines (in Hz) */
#ifndef MCLK_441
#define MCLK_441 (512*44100) /* 44.1, 88.2 etc */
#endif
#ifndef MCLK_48
#define MCLK_48 (512*48000) /* 48, 96 etc */
#endif
/* Minumum sample frequency device runs at */
#ifndef MIN_FREQ
#define MIN_FREQ (48000) //(44100)
#endif
/* Maximum sample frequency device runs at */
#ifndef MAX_FREQ
#define MAX_FREQ (48000)
#endif
/*** Defines relating to feature placement regarding tiles ***/
#define XUD_TILE (0)
#define PLL_REF_TILE (0)
#define AUDIO_IO_TILE (1)
#define MIDI_TILE (1)
/*** Defines relating to USB descriptor strings and ID's ***/
#define VENDOR_ID (0x20B1) /* XMOS Vendor ID*/
#ifndef PID_AUDIO_2
#define PID_AUDIO_2 (0x0016)
#endif
#ifndef PID_AUDIO_1
#define PID_AUDIO_1 (0x0017)
#endif
#if defined (SPATIAL_GAME)
#define PRODUCT_STR_A2 "XMOS V7.1 Game"
#define PRODUCT_STR_A1 "XMOS V7.1 Game"
#elif defined (SPATIAL_DRAMA)
#define PRODUCT_STR_A2 "XMOS V7.1 Drama"
#define PRODUCT_STR_A1 "XMOS V7.1 Drama"
#elif defined (SPATIAL_MOVIE)
#define PRODUCT_STR_A2 "XMOS V7.1 Movie"
#define PRODUCT_STR_A1 "XMOS V7.1 Movie"
#elif defined (SPATIAL_MUSIC)
#define PRODUCT_STR_A2 "XMOS V7.1 Music"
#define PRODUCT_STR_A1 "XMOS V7.1 Music"
#elif defined (STEREO_2K)
#define PRODUCT_STR_A2 "XMOS Stereo 2K"
#define PRODUCT_STR_A1 "XMOS V7.1 Game"
#elif defined (STEREO_8K)
#define PRODUCT_STR_A2 "XMOS Stereo 8K"
#define PRODUCT_STR_A1 "XMOS Stereo 8K"
#else
#define PRODUCT_STR_A2 "XMOS Gold Sample (UAC2.0)"
#define PRODUCT_STR_A1 "XMOS Gold Sample (UAC1.0)"
#endif
/* Board power source - Default is bus-powered */
#ifndef XUA_POWERMODE
#define XUA_POWERMODE XUA_POWERMODE_BUS
#endif
/* Enable/Disable example HID code - Default is off */
#ifndef HID_CONTROLS
#define HID_CONTROLS (0)
#endif
#include "user_main.h"
#endif

View File

@@ -0,0 +1,12 @@
#include <print.h>
#include "xua.h"
on tile[XUD_TILE]: in port p_vbus = XS1_PORT_4C;
unsigned int XUD_HAL_GetVBusState(void)
{
unsigned vBus;
p_vbus :> vBus;
return !vBus;
}

View File

@@ -0,0 +1,7 @@
// Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
void agc_init_interface(void);

View File

@@ -0,0 +1,64 @@
#ifndef AZP_H_
#define AZP_H_
#include "stdint.h"
typedef enum
{
errAZP_NoError = 0,
errAZP_NotReady,
errAZP_InvalidParam,
errAZP_InvalidLicense,
errAZP_BufferOverflow,
errAZP_BufferTooSmall,
}AZP_STATUS;
typedef struct
{
int8_t *buffer1;
int8_t *buffer2;
int32_t buffer1_size;
int32_t buffer2_size;
int32_t sample_rate;
}azp_dnr_pram_t;
/// @brief DNR initialization
/// @param param Configuration parameters
/// @return License status
/// @brief DNR initialization
/// @param param Configuration parameters
/// @return License status
AZP_STATUS AI_DNR_init(azp_dnr_pram_t *pram);
/// @brief DNR processing
/// @param audio Q31 input and output
/// @return License status
AZP_STATUS AI_DNR_Processing(int32_t *audio);
/// @brief Get version number
/// @return If not licensed, returns NULL
char * AI_DNR_Version();
/// @brief 初始化
/// @param buffer 公共区buffer
/// @param buf_size 公共区buffer size
/// @param buffer2 私有区buffer
/// @param buffer2_size 私有区buffer size
/// @return
//AZP_STATUS azp_dnr_init(int8_t *buffer, int32_t buf_size,int8_t *buffer2,int32_t buffer2_size);
/// @brief DNR 处理
/// @param audio 32bit Q31 单声道
/// @return
//AZP_STATUS Aizip_DNR_Processing(int32_t *audio);
/// @brief 获取工作buffer大小
/// @param buf_size
/// @param buf_size2
void getBufferSize(int32_t *buf_size, int32_t *buf_size2);/// @brief 设置是否开启EQ ,默认开启
/// @param status
//void setEQ_Status(int8_t status);
/// @brief 设置是否开启AGC ,默认开启
/// @param status
//void setAGC_Status(int8_t status);
/// @brief Set the noise reduction depth
/// @param dB Range (-200 to 0 dB)
/// -200: Maximum noise reduction
/// 0: No noise reduction
void setNoisy_mix_factor(float dB);
#endif // AZP_H_

View File

@@ -0,0 +1,17 @@
// Copyright (c) 2022 XMOS LIMITED. This Software is subject to the terms of the
// XMOS Public License: Version 1
#ifndef AUDIO_PIPELINE_DSP_H_
#define AUDIO_PIPELINE_DSP_H_
#include <stdint.h>
//#include "agc_api.h"
//typedef struct agc_stage_ctx {
// agc_meta_data_t md;
// agc_state_t state;
//} agc_stage_ctx_t;
#endif /* AUDIO_PIPELINE_DSP_H_ */

View File

@@ -0,0 +1,586 @@
#include <xs1.h>
#include <assert.h>
#include <platform.h>
#include "xassert.h"
#include "i2c.h"
#include "xua.h"
#include <print.h>
#if defined (CODEC_AIC3204)
#include "codec_ti3204.h"
#else
#error "No codec selected"
#endif
extern "C" {
#include "sw_pll.h"
}
#if (XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM) && (XUA_I2S_N_BITS != 32)
#warning ADC only supports TDM operation at 32 bits
#endif
#ifndef I2S_LOOPBACK
#define I2S_LOOPBACK (0)
#endif
port p_scl = PORT_I2C_SCL;
port p_sda = PORT_I2C_SDA;
port p_adc_rst = PORT_ADC_RST;
port p_ctrl = PORT_GPI;
/* Board setup for XU316 MC Audio (1v1) */
void board_setup()
{
p_adc_rst <: 0;
p_ctrl <: 0;
delay_milliseconds(10);
p_ctrl <: 0x90; //0XFF;
p_adc_rst <: 1;
printstrln("board_setup OK.");
}
#if 0
//Tile 0
unsafe client interface i2c_master_if i_i2c_client_t0;
static uint8_t mic_vol = 0x2b;
#define MIN_MIC_VOL_LEVEL 0
#define MAX_MIC_VOL_LEVEL 0x37
//static inline int CODEC_REGWRITE(uint8_t reg, uint8_t val);
i2c_regop_res_t i2c_reg_write_t0(uint8_t device_addr, uint8_t reg, uint8_t data)
{
uint8_t a_data[2] = {reg, data};
size_t n;
unsafe
{
i_i2c_client_t0.write(device_addr, a_data, 2, n, 1);
}
if (n == 0)
{
return I2C_REGOP_DEVICE_NACK;
}
if (n < 2)
{
return I2C_REGOP_INCOMPLETE;
}
return I2C_REGOP_SUCCESS;
}
static inline int CODEC_REGWRITE_t0(uint8_t reg, uint8_t val)
{
i2c_regop_res_t ret;
ret = i2c_reg_write_t0(AIC3204_I2C_DEVICE_ADDR, reg, val);
//ret = rtos_i2c_master_reg_write(i2c_master_ctx, AIC3204_I2C_DEVICE_ADDR, reg, val);
if (ret == I2C_REGOP_SUCCESS) {
return 0;
} else {
return -1;
}
}
int i2c_codec_mic_vol_up(void)
{
//delay_milliseconds(20);
if (mic_vol < MAX_MIC_VOL_LEVEL) {
mic_vol++;
if ( (CODEC_REGWRITE_t0(AIC3204_PAGE_CTRL, 1) == 0) && (CODEC_REGWRITE_t0(AIC3204_OP_PWR_CTRL, 0x00) == 0) ) {
return 0;
} else {
return -1;
}
}
return 0;
}
//------------------------------------------------------------------------------------
/* Working around not being able to extend an unsafe interface (Bugzilla #18670)*/
i2c_regop_res_t i2c_reg_write(uint8_t device_addr, uint8_t reg, uint8_t data)
{
uint8_t a_data[2] = {reg, data};
size_t n;
unsafe
{
i_i2c_client.write(device_addr, a_data, 2, n, 1);
}
if (n == 0)
{
return I2C_REGOP_DEVICE_NACK;
}
if (n < 2)
{
return I2C_REGOP_INCOMPLETE;
}
return I2C_REGOP_SUCCESS;
}
uint8_t i2c_reg_read(uint8_t device_addr, uint8_t reg, i2c_regop_res_t &result)
{
uint8_t a_reg[1] = {reg};
uint8_t data[1] = {0};
size_t n;
i2c_res_t res;
unsafe
{
res = i_i2c_client.write(device_addr, a_reg, 1, n, 0);
if (n != 1)
{
result = I2C_REGOP_DEVICE_NACK;
i_i2c_client.send_stop_bit();
return 0;
}
res = i_i2c_client.read(device_addr, data, 1, 1);
}
if (res == I2C_ACK)
{
result = I2C_REGOP_SUCCESS;
}
else
{
result = I2C_REGOP_DEVICE_NACK;
}
return data[0];
}
unsafe client interface i2c_master_if i_i2c_client;
#if 0
uint8_t CODEC_REGREAD(int regAddr)
{
i2c_regop_res_t result ;
return i2c_reg_read(AIC3204_I2C_DEVICE_ADDR, regAddr, &result);
}
#endif
void CODEC_REGWRITE(int regAddr, int regData)
{
i2c_regop_res_t result = i2c_reg_write(AIC3204_I2C_DEVICE_ADDR, regAddr, regData);
// asert(result == I2C_REGOP_SUCCESS && msg("I2C write reg failed"));
}
/*
* Writes a value to a register in the AIC3204 DAC chip.
*/
static inline int CODEC_REGWRITE(uint8_t reg, uint8_t val)
{
i2c_regop_res_t ret;
ret = i2c_reg_write(AIC3204_I2C_DEVICE_ADDR, reg, val);
//ret = rtos_i2c_master_reg_write(i2c_master_ctx, AIC3204_I2C_DEVICE_ADDR, reg, val);
if (ret == I2C_REGOP_SUCCESS) {
return 0;
} else {
return -1;
}
}
#endif
unsafe chanend uc_audiohw;
typedef enum {
AUDIOHW_CMD_REGWR,
AUDIOHW_CMD_REGRD
} audioHwCmd_t;
static inline void CODEC_REGWRITE(unsigned reg, unsigned val)
{
unsafe {
uc_audiohw <: (unsigned) AUDIOHW_CMD_REGWR;
uc_audiohw <: reg;
uc_audiohw <: val;
}
}
static inline void CODEC_REGREAD(unsigned reg, unsigned val)
{
unsafe {
uc_audiohw <: (unsigned) AUDIOHW_CMD_REGRD;
uc_audiohw <: reg;
uc_audiohw :> val;
}
}
static inline void CODEC_IC_REGREAD(unsigned reg, unsigned &val, client interface i2c_master_if i2c)
{
i2c_regop_res_t result;
uint8_t a_reg[1] = {reg};
uint8_t data[1] = {0};
size_t n;
i2c_res_t res;
unsafe
{
res = i2c.write(CODEC_I2C_DEVICE_ADDR, a_reg, 1, n, 0);
if (n != 1)
{
result = I2C_REGOP_DEVICE_NACK;
i2c.send_stop_bit();
return 0;
}
res = i2c.read(CODEC_I2C_DEVICE_ADDR, data, 1, 1);
}
data[0] = val;
if (res == I2C_ACK)
{
result = I2C_REGOP_SUCCESS;
}
else
{
result = I2C_REGOP_DEVICE_NACK;
}
// return data[0];
}
static inline void CODEC_IC_REGWRITE(unsigned reg, unsigned val, client interface i2c_master_if i2c)
{
uint8_t a_data[2] = {reg, val};
size_t n;
unsafe
{
i2c.write(CODEC_I2C_DEVICE_ADDR, a_data, 2, n, 1);
}
if (n == 0)
{
return I2C_REGOP_DEVICE_NACK;
}
if (n < 2)
{
return I2C_REGOP_INCOMPLETE;
}
return I2C_REGOP_SUCCESS;
}
// tile 1
static uint8_t mic_vol = 40; //84; // default +42dB as 20250408 //56; //0x2b;
#define MIN_MIC_VOL_LEVEL 0
#define MAX_MIC_VOL_LEVEL 84 // max +42dB as 20250408 //0x37
#define MIC_VOL_STEP 4
#define MIC_VOL_THRD MIC_VOL_STEP - 1
int i2c_codec_mic_vol_up(void)
{
#if defined (CODEC_AIC3204)
//delay_milliseconds(20);
if (mic_vol < MAX_MIC_VOL_LEVEL) {
mic_vol=mic_vol+MIC_VOL_STEP;
if (mic_vol > MAX_MIC_VOL_LEVEL) {
mic_vol = MAX_MIC_VOL_LEVEL;
}
CODEC_REGWRITE(AIC3204_PAGE_CTRL, 1);
CODEC_REGWRITE(0x3c, mic_vol);
}
printf("vol %d\n", mic_vol);
delay_milliseconds(10);
#endif
return 0;
}
int i2c_codec_mic_vol_down(void)
{
#if defined (CODEC_AIC3204)
//delay_milliseconds(20);
if ((mic_vol > MIN_MIC_VOL_LEVEL) && (mic_vol > MIC_VOL_THRD)) {
// if (mic_vol == MAX_MIC_VOL_LEVEL) {
// mic_vol = MAX_MIC_VOL_LEVEL - 3;
// } else {
mic_vol=mic_vol-MIC_VOL_STEP;
// }
CODEC_REGWRITE(AIC3204_PAGE_CTRL, 1);
CODEC_REGWRITE(0x3c, mic_vol);
}
printf("vol %d\n", mic_vol);
delay_milliseconds(10);
#endif
return 0;
}
// tile 0
void AudioHwRemote2(chanend c, client interface i2c_master_if i2c);
void AudioHwRemote(chanend c)
{
i2c_master_if i2c[1];
par {
i2c_master(i2c, 1, p_scl, p_sda, 100);
AudioHwRemote2(c, i2c[0]);
}
}
void AudioHwRemote2(chanend c, client interface i2c_master_if i2c)
{
unsigned cmd;
while (1) {
select {
case c :> cmd:
if (cmd == AUDIOHW_CMD_REGRD) {
unsigned regAddr, regVal;
c:>regAddr;
CODEC_IC_REGREAD(regAddr, regVal, i2c);
c<:regVal;
} else {
unsigned regAddr, regVal;
c:>regAddr;
c:>regVal;
CODEC_IC_REGWRITE(regAddr, regVal, i2c);
}
break;
}
}
}
int adc_2ch_48K_highPerformance(void)
{
#if defined (CODEC_AIC3204)
printstrln("adc_2ch_48K_highPerformance");
//0==CODEC_REGWRITE( AIC3204_PAGE_CTRL, 0) // page0
//CODEC_REGWRITE( AIC3204_SW_RST, 1); //Set
//&& 0== CODEC_REGWRITE( 0x12, 0x81) //NADC
//&& 0== CODEC_REGWRITE( 0x13, 0x84) //MADC
//&& 0== CODEC_REGWRITE( 0x14, 0x40) //AOSR
//&& 0== CODEC_REGWRITE( 0x1d, 0x10) // dac route2 adc-----jian added ---LoopBack
//&&
CODEC_REGWRITE( AIC3204_PAGE_CTRL, 1); //page1
//&& 0== CODEC_REGWRITE( 0x0c, 0x04) //IN1L-route2--HPL----------
//&& 0== CODEC_REGWRITE( 0x01, 0x08)
//&& 0== CODEC_REGWRITE( 0x02, 0)
//&& 0== CODEC_REGWRITE( 0x0a, 0x40)// ---- 0x00
//&& 0== CODEC_REGWRITE( 0x3d, 0xff)// ---- 0x00
CODEC_REGWRITE( 0x47, 0x32); //analog input quick charging configure
CODEC_REGWRITE( 0x7b, 0x01); //power up config
//CODEC_REGWRITE(AIC3204_LPGA_P_ROUTE, 0x20) == 0 &&
// Route IN2_R to LEFT_M with 20K input impedance
//CODEC_REGWRITE(AIC3204_LPGA_N_ROUTE, 0x20) == 0 &&
CODEC_REGWRITE( 0x33, 0x60); //---------0x78 used fail-------jian added ----MICBIAS
//Route IN1L to LEFT_P with 20K input impedance
//&& 0== CODEC_REGWRITE( 0x34, 0x00)
//&& 0== CODEC_REGWRITE( 0x34, 0x80) // 0x80-fail
//&& 0== CODEC_REGWRITE( 0x36, 0x80) // 0x80-fail
CODEC_REGWRITE( 0x37, 0x80);
CODEC_REGWRITE( 0x39, 0x20); // IN1L to RIGHT MICPGA with 20k ohm
//&& 0== CODEC_REGWRITE( 0x3b, 0x5f) // Left MICPGA VOL--------------
CODEC_REGWRITE( 0x3c, mic_vol/*40*/); // Right MICPGA VOL --5a-45DB 5e--47db :3d--ap实际测试25DB增益---跟客户样品一致 一般30DB
#if 1 //added _jian
//&& 0== CODEC_REGWRITE( 0x3e, 0x02)
//&& 0== CODEC_REGWRITE( AIC3204_PAGE_CTRL, 8) //page1
//&& 0== CODEC_REGWRITE( 1, 4) // adc adaptive filter
#endif
CODEC_REGWRITE( AIC3204_PAGE_CTRL, 0); // page0
//CODEC_REGWRITE( 0x35, 0x02); //default-0x00 DOUT: 02:loopbak
//CODEC_REGWRITE( 0x35, 0x12); //default-0x02 DOUT
//CODEC_REGWRITE( 0x53, 0x08); // _jian L_ADC volume 20DB 录到一条直线
//CODEC_REGWRITE( 0x54, 0x28); // _jian R_ADC volume 20DB
CODEC_REGWRITE( 0x51, 0xc0); //
CODEC_REGWRITE( 0x52, 0x00); //
#endif
return 0;
}
/// @brief
/// @param
/// @return
int i2c_codec_setup(void)
{
// Take dac out of reset
// dac_reset_inactive(i2c);---------
// Reset recovery time
delay_milliseconds(20);
//const rtos_gpio_port_id_t codec_rst_port = rtos_gpio_port(PORT_CODEC_RST_N);
//rtos_gpio_port_enable(gpio_ctx_t1, codec_rst_port);
//rtos_gpio_port_out(gpio_ctx_t1, codec_rst_port, 0xF);
#if defined (CODEC_AIC3204)
// Set register page to 0
CODEC_REGWRITE(AIC3204_PAGE_CTRL, 0x00);
// Initiate SW reset (PLL is powered off as part of reset)
CODEC_REGWRITE(AIC3204_SW_RST, 0x01);
// Program clock settings
// Default is CODEC_CLKIN is from MCLK pin. Don't need to change this.
// Power up NDAC and set to 1
CODEC_REGWRITE(AIC3204_NDAC, 0x81);
// Power up MDAC and set to 4
CODEC_REGWRITE(AIC3204_MDAC, 0x84);
// Power up NADC and set to 1
CODEC_REGWRITE(AIC3204_NADC, 0x81);
// Power up MADC and set to 4
CODEC_REGWRITE(AIC3204_MADC, 0x84);
// Program DOSR = 128
CODEC_REGWRITE(AIC3204_DOSR_LSB, 0x80);
// Program AOSR = 128
CODEC_REGWRITE(AIC3204_AOSR, 0x80);
// Set Audio Interface Config: I2S, 24 bits, slave mode, DOUT always driving.
CODEC_REGWRITE(AIC3204_CODEC_IF, 0x20);
// Program the DAC processing block to be used - PRB_P1
CODEC_REGWRITE(AIC3204_DAC_SIG_PROC, 0x01);
// Program the ADC processing block to be used - PRB_R1
CODEC_REGWRITE(AIC3204_ADC_SIG_PROC, 0x01); // 3D-------------------------
// Select Page 1
CODEC_REGWRITE(AIC3204_PAGE_CTRL, 0x01);
// Enable the internal AVDD_LDO:
CODEC_REGWRITE(AIC3204_LDO_CTRL, 0x09);
//
// Program Analog Blocks
// ---------------------
//
// Disable Internal Crude AVdd in presence of external AVdd supply or before powering up internal AVdd LDO
CODEC_REGWRITE(AIC3204_PWR_CFG, 0x08);
// Enable Master Analog Power Control
CODEC_REGWRITE(AIC3204_LDO_CTRL, 0x01);
// Set Common Mode voltages: Full Chip CM to 0.9V and Output Common Mode for Headphone to 1.65V and HP powered from LDOin @ 3.3V.
CODEC_REGWRITE(AIC3204_CM_CTRL, 0x33);
// Set PowerTune Modes
// Set the Left & Right DAC PowerTune mode to PTM_P3/4. Use Class-AB driver.
CODEC_REGWRITE(AIC3204_PLAY_CFG1, 0x00);
CODEC_REGWRITE(AIC3204_PLAY_CFG2, 0x00);
// Set ADC PowerTune mode PTM_R4.
CODEC_REGWRITE(AIC3204_ADC_PTM, 0x00);
// Set MicPGA startup delay to 3.1ms
CODEC_REGWRITE(AIC3204_AN_IN_CHRG, 0x31);
// Set the REF charging time to 40ms
CODEC_REGWRITE(AIC3204_REF_STARTUP, 0x01);
// HP soft stepping settings for optimal pop performance at power up
// Rpop used is 6k with N = 6 and soft step = 20usec. This should work with 47uF coupling
// capacitor. Can try N=5,6 or 7 time constants as well. Trade-off delay vs “pop” sound.
CODEC_REGWRITE(AIC3204_HP_START, 0x25);
// Route Left DAC to HPL
CODEC_REGWRITE(AIC3204_HPL_ROUTE, 0x08);
// Route Right DAC to HPR
CODEC_REGWRITE(AIC3204_HPR_ROUTE, 0x08);
CODEC_REGWRITE(0x0e, 0x08);
CODEC_REGWRITE(0x0f, 0x08);
CODEC_REGWRITE(0x12, 0x3a);
CODEC_REGWRITE(0x13, 0x3a); //Gain-0DB
// We are using Line input with low gain for PGA so can use 40k input R but lets stick to 20k for now.
// Route IN2_L to LEFT_P with 20K input impedance
CODEC_REGWRITE(AIC3204_LPGA_P_ROUTE, 0x20); //---
// Route IN2_R to LEFT_M with 20K input impedance
CODEC_REGWRITE(AIC3204_LPGA_N_ROUTE, 0x20); //---
// Route IN1_R to RIGHT_P with 20K input impedance
CODEC_REGWRITE(AIC3204_RPGA_P_ROUTE, 0x80); //---
// Route IN1_L to RIGHT_M with 20K input impedance
CODEC_REGWRITE(AIC3204_RPGA_N_ROUTE, 0x20); //---
// Unmute HPL and set gain to 0dB
CODEC_REGWRITE(AIC3204_HPL_GAIN, 0x06);
// Unmute HPR and set gain to 0dB
CODEC_REGWRITE(AIC3204_HPR_GAIN, 0x06);
// Unmute Left MICPGA, Set Gain to 0dB.
CODEC_REGWRITE(AIC3204_LPGA_VOL, 0x00);
// Unmute Right MICPGA, Set Gain to 0dB.
CODEC_REGWRITE(AIC3204_RPGA_VOL, 0x00);
// Power up HPL and HPR drivers
CODEC_REGWRITE(AIC3204_OP_PWR_CTRL, 0x30); // HP powerUp
//CODEC_REGWRITE(AIC3204_OP_PWR_CTRL, 0x0C); // LO powerUP
// Wait for 2.5 sec for soft stepping to take effect
//vTaskDelay(pdMS_TO_TICKS(2500));
delay_milliseconds(10);
// while(1)CODEC_REGWRITE(AIC3204_PAGE_CTRL, 0x00); //for Test _jian 20250317
printstrln("i2c_codec_setup");
//
// Power Up DAC/ADC
// ----------------
//
// Select Page 0
CODEC_REGWRITE(AIC3204_PAGE_CTRL, 0x00);
// Power up the Left and Right DAC Channels. Route Left data to Left DAC and Right data to Right DAC.
// DAC Vol control soft step 1 step per DAC word clock.
CODEC_REGWRITE(AIC3204_DAC_CH_SET1, 0xd4);
// Power up Left and Right ADC Channels, ADC vol ctrl soft step 1 step per ADC word clock.
CODEC_REGWRITE(AIC3204_ADC_CH_SET, 0xc0);
// Unmute Left and Right DAC digital volume control
CODEC_REGWRITE(AIC3204_DAC_CH_SET2, 0x00);
// Unmute Left and Right ADC Digital Volume Control.
CODEC_REGWRITE(AIC3204_ADC_FGA_MUTE, 0x00);
return 0;
#endif
}
/* Configures the external audio hardware at startup */
void AudioHwInit()
{
int result=0;
// Wait for power supply to come up.
delay_milliseconds(100);
#if 0
/* Wait until global is set */
unsafe
{
while(!(unsigned) i_i2c_client);
}
#endif
/* Use xCORE Secondary PLL to generate *fixed* master clock */
if (DEFAULT_FREQ % 22050 == 0)
{
sw_pll_fixed_clock(MCLK_441);
}
else
{
sw_pll_fixed_clock(MCLK_48);
}
result = i2c_codec_setup();
if(result)
printstrln("CODEC DAC Init Err");
else
printstrln("CODEC DAC Init OK.");
result = adc_2ch_48K_highPerformance();
if(result)
printstrln("CODEC ADC Init Err");
else
printstrln("CODEC ADC Init OK.");
}
/* Configures the external audio hardware for the required sample frequency */
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC)
{
{
sw_pll_fixed_clock(mClk);
}
}

View File

@@ -0,0 +1,16 @@
#include <platform.h>
on tile[0]: out port p_leds = XS1_PORT_4F;
void UserAudioStreamStart(void)
{
/* Turn all LEDs on */
// p_leds <: 0xF;
}
void UserAudioStreamStop(void)
{
/* Turn all LEDs off */
// p_leds <: 0x0;
}

View File

@@ -0,0 +1,22 @@
#ifndef _C_DSP_H_
#define _C_DSP_H_
#include <stdint.h>
// #define AUDIO_T_16
#if defined(AUDIO_T_16)
typedef int16_t AUDIO_T;
#else // defined(AUDIO_T_32)
typedef int32_t AUDIO_T;
#endif
#define FRAME_SIZE 8 //128 samples / 48kHz = 2.6ms
void hid_update(unsigned char hid_data);
// mic level
#define MIN_MIC_LEVEL 0
#define MAX_MIC_LEVEL 9
#define DEFAULT_MIC_LEVEL 5
#endif

View File

@@ -0,0 +1,77 @@
// Copyright 2020-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef AIC3204_H_
#define AIC3204_H_
// TLV320AIC3204 Device I2C Address
#define AIC3204_I2C_DEVICE_ADDR 0x18
#define CODEC_I2C_DEVICE_ADDR AIC3204_I2C_DEVICE_ADDR
// TLV320AIC3204 Register Addresses
// Page 0
#define AIC3204_PAGE_CTRL 0x00 // Register 0 - Page Control
#define AIC3204_SW_RST 0x01 // Register 1 - Software Reset
#define AIC3204_NDAC 0x0B // Register 11 - NDAC Divider Value
#define AIC3204_MDAC 0x0C // Register 12 - MDAC Divider Value
#define AIC3204_DOSR_MSB 0x0d // Register 14 - DOSR Divider Value (MS Byte)
#define AIC3204_DOSR_LSB 0x0e // Register 14 - DOSR Divider Value (LS Byte)
#define AIC3204_NADC 0x12 // Register 18 - NADC Divider Value
#define AIC3204_MADC 0x13 // Register 19 - MADC Divider Value
#define AIC3204_AOSR 0x14 // Register 20 - AOSR Divider Value
#define AIC3204_CLK_MUL 0x19 // Register 25
#define AIC3204_CLK_DIV 0x1A // Register 26
#define AIC3204_CODEC_IF 0x1B // Register 27 - CODEC Interface Control
#define AIC3204_D_OFFSET 0x1C // Register 28 -
#define AIC3204_LOOPBACK 0x1D // Register 29 -
#define AIC3204_BCLK_DIV 0x1E // Register 30 - --------
#define AIC3204_CLK_MUL 0x1F // Register 31 -
#define AIC3204_BCLK_SEL 0x20 // Register 32 - 0:Primary; 1:Second
#define AIC3204_DAC_SIG_PROC 0x3C // Register 60 - DAC Sig Processing Block Control
#define AIC3204_ADC_SIG_PROC 0x3D // Register 61 - ADC Sig Processing Block Control
#define AIC3204_DAC_CH_SET1 0x3F // Register 63 - DAC Channel Setup 1
#define AIC3204_DAC_CH_SET2 0x40 // Register 64 - DAC Channel Setup 2
#define AIC3204_DACL_VOL_D 0x41 // Register 65 - DAC Left Digital Vol Control
#define AIC3204_DACR_VOL_D 0x42 // Register 66 - DAC Right Digital Vol Control
#define AIC3204_HEADSET_DET 0x43 // Register 67 - Headset Detection configuration
#define AIC3204_DRC_CON1 0x44 // Register 68 - DRC control reg1
#define AIC3204_DRC_CON2 0x45 // Register 69 - DRC control reg2
#define AIC3204_DRC_CON3 0x46 // Register 70 - DRC control reg3
#define AIC3204_ADC_CH_SET 0x51 // Register 81 - ADC Channel Setup
#define AIC3204_ADC_FGA_MUTE 0x52 // Register 82 - ADC Fine Gain Adjust/Mute
#define AIC3204_ADC_CH1_SET 0x53 // Register 83 - ADC L_CH vol
#define AIC3204_ADC_CH2_SET 0x54 // Register 84 - ADC R_CH vol
#define AIC3204_ADC_PHA_SET 0x55 // Register 84 - ADC Phase Adjust Reg
//0x56~0x5c: L-CH agc control regs
//0x5e~0x65: R-CH agc control regs
///0x66~0x67: DC measurement regs
//0x68~0x6d: DC Measurement output regs
// Page 1
#define AIC3204_PAGE_SEL1 0x00 // Register 0 - Page Select Reg
#define AIC3204_PWR_CFG 0x01 // Register 1 - Power Config
#define AIC3204_LDO_CTRL 0x02 // Register 2 - LDO Control
#define AIC3204_PLAY_CFG1 0x03 // Register 3 - Playback Config 1
#define AIC3204_PLAY_CFG2 0x04 // Register 4 - Playback Config 2
#define AIC3204_OP_PWR_CTRL 0x09 // Register 9 - Output Driver Power Control
#define AIC3204_CM_CTRL 0x0A // Register 10 - Common Mode Control
#define AIC3204_HPL_ROUTE 0x0C // Register 12 - HPL Routing Select
#define AIC3204_HPR_ROUTE 0x0D // Register 13 - HPR Routing Select
#define AIC3204_HPL_GAIN 0x10 // Register 16 - HPL Driver Gain
#define AIC3204_HPR_GAIN 0x11 // Register 17 - HPR Driver Gain
#define AIC3204_HP_START 0x14 // Register 20 - Headphone Driver Startup
#define AIC3204_LPGA_P_ROUTE 0x34 // Register 52 - Left PGA Positive Input Route
#define AIC3204_LPGA_N_ROUTE 0x36 // Register 54 - Left PGA Negative Input Route
#define AIC3204_RPGA_P_ROUTE 0x37 // Register 55 - Right PGA Positive Input Route
#define AIC3204_RPGA_N_ROUTE 0x39 // Register 57 - Right PGA Negative Input Route
#define AIC3204_LPGA_VOL 0x3B // Register 59 - Left PGA Volume
#define AIC3204_RPGA_VOL 0x3C // Register 60 - Right PGA Volume
#define AIC3204_ADC_PTM 0x3D // Register 61 - ADC Power Tune Config
#define AIC3204_ADC_VOL 0x3E // Register 62 - ADC Analog Gain Control Flag
#define AIC3204_AN_IN_CHRG 0x47 // Register 71 - Analog Input Quick Charging Config
#define AIC3204_REF_STARTUP 0x7B // Register 123 - Reference Power Up Config
int aic3204_init(void);
#endif /* AIC3204_H_ */

View File

@@ -0,0 +1,391 @@
// Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <xs1.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <platform.h>
#include <string.h>
#include "dnr_dsp_buf.h"
#include "aizip_dnr.h"
#include "xassert.h"
//#include "agc_interface.h"
#include "audio_pipeline_dsp.h"
//#include "agc_profiles.h"
#include <xcore/channel.h>
#include <xcore/port.h>
#include "xc_ptr.h"
extern unsigned g_host_os;
#if AIZIP_DNR == 1
// for AGC
//static agc_stage_ctx_t DWORD_ALIGNED agc_stage_state = {};
static unsigned __attribute__((aligned(4))) dsp_dnr_frame_buf[BUF_SLOT_NUM][DSP_CH_NUM][DNR_DSP_FRAME_SIZE];
unsigned sample_in_buf_slot = 0;
unsigned processing_buf_slot = 1;
unsigned sample_out_buf_slot = 2;
//void agc_init_interface(void)
//{
// agc_init(&agc_stage_state.state, &AGC_PROFILE_ASR);
//}
void dsp_buf_init()
{
sample_in_buf_slot = 0;
sample_out_buf_slot = 1;
processing_buf_slot = 2;
}
static void buf_slot_inc(unsigned *buf_slot_input)
{
if (*buf_slot_input == BUF_SLOT_NUM - 1)
{
*buf_slot_input = 0;
}
else
{
*buf_slot_input = *buf_slot_input + 1;
}
}
void buf_slot_rotate()
{
buf_slot_inc(&sample_in_buf_slot);
buf_slot_inc(&sample_out_buf_slot);
buf_slot_inc(&processing_buf_slot);
}
void write_to_sample_in_buf(unsigned data, unsigned index, unsigned ch)
{
dsp_dnr_frame_buf[sample_in_buf_slot][ch][index] = data;
}
void read_from_sample_out_buf(unsigned *read_data, unsigned index, unsigned ch)
{
*read_data = dsp_dnr_frame_buf[sample_out_buf_slot][ch][index];
}
#if (DNR_50MS == 1)
int8_t buffer[21416] __attribute__((aligned(4)));
int8_t buffer2[31712] __attribute__((aligned(4)));
#else
int8_t buffer[4208] __attribute__((aligned(4)));
int8_t buffer2[31136] __attribute__((aligned(4)));
#endif
azp_dnr_pram_t pram = {
.buffer1 = buffer,
.buffer1_size = sizeof(buffer),
.buffer2 = buffer2,
.buffer2_size = sizeof(buffer2),
.sample_rate = 48000};
//uint32_t get_reference_time();
void Aizip_DNR_init()
{
int sta = AI_DNR_init(&pram);
printf("AI_DNR_init: %d\n",sta);
}
int g_dnr_enable = 1;
int g_dnr_level = 200;
// extern void read_flash_data_partition_offset(uint8_t *data_buf, int32_t start, int32_t len);
void start_dsp_processing(unsigned samFreq)
{
uint32_t t0, t1;
static uint32_t start = 0;
uint8_t data_buf[1024];
static int count = 0;
// read_flash_data_partition_offset(data_buf, 0, 100);
// printf("Timer --> %d \n\n", get_reference_time()-start);
float level = -200;
if (g_dnr_enable == 1) {
if (g_dnr_level >= 0 && g_dnr_level <= 200)
level = 0 - g_dnr_level;
} else
level = 0l;
// printf("level %d \n", level);
setNoisy_mix_factor(level);
start = t0;
#if AIZIP_DNR
// Aizip_DNR_Processing((int32_t *)&dsp_dnr_frame_buf[processing_buf_slot][0][0]);
//new dnr_50ms lib
AI_DNR_Processing((int32_t *)&dsp_dnr_frame_buf[processing_buf_slot][0][0]);
#if 0
printf("\nbefore \n");
for (int j = 0; j < 200; j++)
{
printf("%x ",dsp_dnr_frame_buf[proc essing_buf_slot][0][j]);
if (j % 16 == 0)
printf("\n");
}
printf("=================\n sam rate %d \n",samFreq);
#endif
// t0 = get_reference_time();
// Aizip_DNR_Processing((int32_t *)&dsp_dnr_frame_buf[processing_buf_slot][0][0], samFreq);
#if 0
t1= get_reference_time();
printf("================= \nprocess time %d \n\n", t1 - t0);
for (int j = 0; j < 200; j++)
{
printf("%x ",dsp_dnr_frame_buf[processing_buf_slot][0][j]);
if (j % 16 == 0)
printf("\n");
}
#endif
#else
#endif
}
//chanend_t uc_dsp_to_dnr_t0;
//tile 0
// all GPO should be handled here
/* Write HID Report Data into hidData array
*
* Bits are as follows:
* 0: Play/Pause
* 1: Scan Next Track
* 2: Scan Prev Track
* 3: Volume Up
* 4: Volume Down
* 5: Mute
*/
#define HID_CONTROL_PLAYPAUSE 0x01
#define HID_CONTROL_NEXT 0x02
#define HID_CONTROL_PREV 0x04
#define HID_CONTROL_VOLUP 0x08
#define HID_CONTROL_VOLDN 0x10
#define HID_CONTROL_MUTE 0x20
extern void hid_update(unsigned char hid_data);
#if defined(__XS2A__)
/* Note range 0x7FFC8 - 0x7FFFF guarenteed to be untouched by tools */
#warning Building xCORE-200 compatible loader
#define FLAG_ADDRESS 0x7ffcc
#else
/* Note range 0xFFFC8 - 0xFFFFF guarenteed to be untouched by tools */
#warning Building xcore.ai compatible loader
#define FLAG_ADDRESS 0xfffcc
#endif
/* Store Flag to fixed address */
void SetRoleSwitchFlag(unsigned x)
{
asm volatile("stw %0, %1[0]" :: "r"(x), "r"(FLAG_ADDRESS));
}
enum { OS_WIN = 1, OS_OTHERS = 2 };
extern void device_reboot(void);
void dnr_dsp_buffer_task(chanend_t cc_dsp_in, chanend_t cc_dsp_eof, chanend_t cc_mic_level)
{
port_t p_ctrl_keys = XS1_PORT_8C;
// port_t p_ctrl_keys2 = XS1_PORT_8D;
uint32_t port_ctrl_keys = 0, curr_ctrl_keys = 0, last_ctrl_keys = 0, keys_changed = 0;
uint32_t port_ctrl_keys2 =0, curr_ctrl_keys2 = 0, last_ctrl_keys2 = 0, keys_changed2 = 0;
int debounce_cnt = 0;
static unsigned isMute = 0;
uint8_t tmp = 0;
#if (HID_CONTROLS == 1)
unsigned hidData0;
// int hid_evt_rst = 0;
#endif
// channel_t cc_mic = chan_alloc();
// chanend_t cc_mic_key = cc_mic.end_a;
// cc_mic_level = cc_mic.end_b;
#if defined (DEBUG_DNR)
uint32_t test_in_buf[3][2][DNR_DSP_FRAME_SIZE] = {0};
uint32_t test_out_buf[DNR_DSP_FRAME_SIZE] = {0}; //{0x10000000, 0x30000000, 0x50000000, 0x70000000, 0x90000000, 0xB0000000, 0xD0000000, 0xF0000000};
#endif
uint32_t muted_buf[DNR_DSP_FRAME_SIZE] = {0};
dsp_buf_init();
//unsigned samFreq = 48000;
//int i;
//int j;
printf("AGC and DNR dsp_task started\n");
port_enable(p_ctrl_keys);
// port_enable(p_ctrl_keys2);
port_ctrl_keys = port_in(p_ctrl_keys);
// port_ctrl_keys2 = port_in(p_ctrl_keys2);
if ((port_ctrl_keys & KEY_MUTE) == 0) {
// mute switch on
printf("unmuted\n");
isMute = 0;
// hid_update(HID_CONTROL_MUTE);
} else {
printf("muted\n");
isMute = 1;
// hid_update(HID_CONTROL_VOLUP);
}
// chan_out_byte(cc_mic_level, (uint8_t) isMute);
last_ctrl_keys = ((port_ctrl_keys) & KEY_BITS);
// last_ctrl_keys2 = ((port_ctrl_keys2) & KEY_PLAY_VOL_UP);
last_ctrl_keys ^= KEY_MUTE;
unsigned host_os = 0;
while (1) {
unsigned buf_size = DNR_DSP_FRAME_SIZE;
if (isMute) {
for (int i = 0; i < DNR_DSP_FRAME_SIZE/FRAME_SIZE; i++){
#if !defined (DEBUG_DNR)
chan_in_buf_word(cc_dsp_in, (uint32_t *)(&(dsp_dnr_frame_buf[sample_in_buf_slot][0][i*FRAME_SIZE])), FRAME_SIZE);
chan_in_buf_word(cc_dsp_in, (uint32_t *)(&(dsp_dnr_frame_buf[sample_in_buf_slot][1][i*FRAME_SIZE])), FRAME_SIZE);
#else
chan_in_buf_word(cc_dsp_in, (uint32_t *)(&(test_in_buf[sample_in_buf_slot][0][i*FRAME_SIZE])), FRAME_SIZE);
chan_in_buf_word(cc_dsp_in, (uint32_t *)(&(test_in_buf[sample_in_buf_slot][1][i*FRAME_SIZE])), FRAME_SIZE);
#endif
chan_out_buf_word(cc_dsp_in, (const uint32_t *)(&(muted_buf[i*FRAME_SIZE])), FRAME_SIZE);
chan_out_buf_word(cc_dsp_in, (const uint32_t *)(&(muted_buf[i*FRAME_SIZE])), FRAME_SIZE);
}
} else {
for (int i = 0; i < DNR_DSP_FRAME_SIZE/FRAME_SIZE; i++){
#if !defined (DEBUG_DNR)
chan_in_buf_word(cc_dsp_in, (uint32_t *)(&(dsp_dnr_frame_buf[sample_in_buf_slot][0][i*FRAME_SIZE])), FRAME_SIZE);
chan_in_buf_word(cc_dsp_in, (uint32_t *)(&(dsp_dnr_frame_buf[sample_in_buf_slot][1][i*FRAME_SIZE])), FRAME_SIZE);
chan_out_buf_word(cc_dsp_in, (const uint32_t *)(&(dsp_dnr_frame_buf[sample_out_buf_slot][0][i*FRAME_SIZE])), FRAME_SIZE);
chan_out_buf_word(cc_dsp_in, (const uint32_t *)(&(dsp_dnr_frame_buf[sample_out_buf_slot][1][i*FRAME_SIZE])), FRAME_SIZE);
#else
chan_in_buf_word(cc_dsp_in, (uint32_t *)(&(test_in_buf[sample_in_buf_slot][0][i*FRAME_SIZE])), FRAME_SIZE);
chan_in_buf_word(cc_dsp_in, (uint32_t *)(&(test_in_buf[sample_in_buf_slot][1][i*FRAME_SIZE])), FRAME_SIZE);
chan_out_buf_word(cc_dsp_in, (const uint32_t *)(&(test_in_buf[sample_in_buf_slot][0][i*FRAME_SIZE])), FRAME_SIZE);
chan_out_buf_word(cc_dsp_in, (const uint32_t *)(&(test_in_buf[sample_in_buf_slot][1][i*FRAME_SIZE])), FRAME_SIZE);
#endif
}
}
// chan_in_buf_word(cc_dsp_in, (uint32_t *)(dsp_dnr_frame_buf[sample_in_buf_slot]), DSP_CH_NUM * DNR_DSP_FRAME_SIZE);
// chan_out_buf_word(cc_dsp_out, (const uint32_t *)(dsp_dnr_frame_buf[sample_out_buf_slot]), DSP_CH_NUM * DNR_DSP_FRAME_SIZE);
buf_slot_rotate();
//cc_dsp_eof <: 1; //finished data transfer of 1 frame, notify dnr_dsp_proc_task to start processing with slot number
chan_out_word(cc_dsp_eof, 1);
// keys polling (on tile0)
// loop here every 2.6ms = 128 samples at 48kHz
if (debounce_cnt == DEBOUNCE_TIMEOUT) { // 26ms pollint interval
#if defined (HOST_OS_DETECTION)
GET_SHARED_GLOBAL(host_os, g_host_os);
//#if defined (PHATEN_GSV2)
#if (AUDIO_CLASS == 1)
if (host_os == OS_WIN) {
SetRoleSwitchFlag(OS_WIN);
delay_milliseconds(10);
device_reboot();
}
#endif
#if (AUDIO_CLASS == 2)
if (host_os == OS_OTHERS) {
SetRoleSwitchFlag(OS_OTHERS);
delay_milliseconds(10);
device_reboot();
}
#endif
#endif
debounce_cnt = 0;
hidData0 = 0;
curr_ctrl_keys = (port_in(p_ctrl_keys)) & KEY_BITS;
// curr_ctrl_keys2 = (port_in(p_ctrl_keys2)) & KEY_PLAY_VOL_UP;
if (curr_ctrl_keys != last_ctrl_keys) {
keys_changed = curr_ctrl_keys ^ last_ctrl_keys;
tmp = 0;
switch (keys_changed) {
case KEY_MIC_VOL_DN:
case KEY_MIC_VOL_UP:
if ((isMute == 0) && ((curr_ctrl_keys & KEY_MIC_VOL_DN) == 0)) {
tmp = KEY_MIC_VOL_DN;
//chan_out_byte(cc_mic_level, KEY_MIC_VOL_DN);
printf("dn\n");
} else {
if ((isMute == 0) && ((curr_ctrl_keys & KEY_MIC_VOL_UP) == 0)) {
tmp = KEY_MIC_VOL_UP;
//chan_out_byte(cc_mic_level, KEY_MIC_VOL_UP);
printf("up\n");
//int ret = i2c_codec_mic_vol_up();
//if (ret == 0) printf("ok\n");
}
}
if (tmp) {
chan_out_byte(cc_mic_level, tmp);
}
break;
case KEY_MUTE:
if ((curr_ctrl_keys & KEY_MUTE) == 0) {
// mute switch off
//printf("t0 unmuted\n");
isMute = 0;
chan_out_byte(cc_mic_level, (uint8_t) UNMUTED_MIC);
// hidData0 = HID_CONTROL_MUTE;
} else {
//printf("t0 muted\n");
isMute = 1;
chan_out_byte(cc_mic_level, (uint8_t) MUTED_MIC);
// hidData0 = HID_CONTROL_VOLUP;
}
// chan_out_byte(cc_mic_level, (uint8_t) isMute);
//hidData0 = HID_CONTROL_MUTE;
// hid_evt_rst = 1;
break;
case KEY_PLAY_VOL_DN:
if ((curr_ctrl_keys & KEY_PLAY_VOL_DN) == 0) {
hidData0 = HID_CONTROL_VOLDN;
}
//printf("vol down\n");
break;
case KEY_PLAY_VOL_UP:
if ((curr_ctrl_keys & KEY_PLAY_VOL_UP) == 0) {
hidData0 = HID_CONTROL_VOLUP;
}
//printf("vol up\n");
break;
default:
break;
}
hid_update(hidData0);
}
last_ctrl_keys = curr_ctrl_keys;
} else {
debounce_cnt++;
}
}
}
extern unsigned g_samFreq;
extern int g_reduct_dep;
void flash_setup(void);
void Aizip_DNR_init();
void dnr_dsp_proc_task(chanend_t cc_dsp_eof)
{
unsigned processing_slot_pt;
unsigned samFreq = 48000;
int reduct_dep = 0, old_rd = 0;
// timer tmr;
// uint32_t start_time, end_time;
// int i;
#if AIZIP_DNR
Aizip_DNR_init();
// flash_setup();
#endif
while(1) {
//cc_dsp_eof :> processing_slot_pt;
processing_slot_pt = chan_in_word(cc_dsp_eof);
// GET_SHARED_GLOBAL(samFreq, g_samFreq);
// tmr :> start_time;
start_dsp_processing(samFreq);
// tmr :> end_time;
}
}
#endif //(AIZIP_DNR == 1)

View File

@@ -0,0 +1,61 @@
// Copyright 2011-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef __DNR_DSP_BUF_H__
#define __DNR_DSP_BUF_H__
#include <xs1.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <platform.h>
#include "c_dsp.h"
#define DNR_50MS 1
//#define DEBUG_DNR
#if (DNR_50MS == 1)
#define DNR_DSP_FRAME_SIZE 512
#else
#define DNR_DSP_FRAME_SIZE 128
#endif
#define BUF_SLOT_NUM 3 //one for input, one for output, one for processing
#define DSP_CH_NUM 2
#if __XC__
extern "C" {
#endif
void dsp_buf_init();
void buf_slot_rotate();
void write_to_sample_in_buf(unsigned data, unsigned index, unsigned ch);
void read_from_sample_out_buf(unsigned* read_data, unsigned index, unsigned ch);
void start_dsp_processing(unsigned samFreq);
#if __XC__
} //extern c
#endif
#define AGC_ENABLED 1
#define DNR_ENABLED 1
#define KEY_MIC_VOL_DN 0x08
#define KEY_MUTE 0x10
#define KEY_MIC_VOL_UP 0x20
#define KEY_PLAY_VOL_DN 0x40
#define KEY_PLAY_VOL_UP 0x80
#define KEY_BITS (KEY_MIC_VOL_DN | KEY_MUTE | KEY_MIC_VOL_UP | KEY_PLAY_VOL_DN | KEY_PLAY_VOL_UP)
#if defined (PHATEN_GSV2)
#define DEBOUNCE_TIMEOUT 10 // in units of 2.6ms; 128 samples/frame
#else
#error "Please define DEBOUNCE_TIMEOUT"
#endif
#define MUTED_MIC 0x5A
#define UNMUTED_MIC 0xA5
#endif //RINGBUFFER_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
#define DSP_BLOCK_LENGTH (8) //DO NOT CHANGE
#define DSP_TILE_0_CHANNEL_IN_COUNT (1)
#define DSP_TILE_1_CHANNEL_IN_COUNT (1)
#define DSP_TILE_0_CHANNEL_OUT_COUNT (DSP_TILE_0_CHANNEL_IN_COUNT*2)
#define DSP_TILE_1_CHANNEL_OUT_COUNT (DSP_TILE_1_CHANNEL_IN_COUNT*2)
#define DSP_TILE_0_WORKER_COUNT (DSP_TILE_0_CHANNEL_IN_COUNT)
#define DSP_TILE_1_WORKER_COUNT (DSP_TILE_1_CHANNEL_IN_COUNT)
#define DSP_MIXER_OUTPUT_CHANNEL_COUNT (2)

View File

@@ -0,0 +1,296 @@
#include "dsp/td_block_fir.h"
int32_t __attribute__((aligned (8))) coefs_ex3d_0[1024] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 536870912
};
td_block_fir_filter_t td_block_fir_filter_ex3d_0 = {
.coefs = coefs_ex3d_0,
.block_count = 128,
.accu_shr = 0,
.accu_shl = -1,
};
td_block_fir_filter_t td_block_fir_filter_ex3d_1 = {
.coefs = coefs_ex3d_0,
.block_count = 128,
.accu_shr = 0,
.accu_shl = -1,
};
td_block_fir_filter_t td_block_fir_filter_ex3d_2 = {
.coefs = coefs_ex3d_0,
.block_count = 128,
.accu_shr = 0,
.accu_shl = -1,
};
td_block_fir_filter_t td_block_fir_filter_ex3d_3 = {
.coefs = coefs_ex3d_0,
.block_count = 128,
.accu_shr = 0,
.accu_shl = -1,
};
//This is the count of int32_t words to allocate for one data channel.
//i.e. int32_t channel_data[ex3d_0_DATA_BUFFER_ELEMENTS] = { 0 };
#define ex3d_0_DATA_BUFFER_ELEMENTS (1040)
#define ex3d_0_TD_BLOCK_LENGTH (8)
#define ex3d_0_BLOCK_COUNT (128)
#define ex3d_0_FRAME_ADVANCE (8)
#define ex3d_0_FRAME_OVERLAP (0)

View File

@@ -0,0 +1,102 @@
#ifndef _HOST_OS_DETECT_H_
#define _HOST_OS_DETECT_H_
#include "xua.h"
#include "xc_ptr.h"
#include "xassert.h"
#if XUA_USB_EN
#ifndef WINDOWS_OS_DESCRIPTOR_SUPPORT
#define WINDOWS_OS_DESCRIPTOR_SUPPORT
#endif
#define EP0_MAX_REQUEST_SIZE 2048
#include "xud.h"
#include "vendorrequests.h"
unsigned g_host_os = 0; // 1 -> Windows, 0 -> Others
int VendorAudioRequests(XUD_ep ep0_out, XUD_ep ep0_in, unsigned char bRequest, unsigned char cs, unsigned char cn,
unsigned short unitId, unsigned char direction, NULLABLE_RESOURCE(chanend, c_aud_ctl),
NULLABLE_RESOURCE(chanend, c_mix_ctl),
NULLABLE_RESOURCE(chanend, c_clk_ctL))
{
return XUD_RES_ERR;
}
enum { OS_WIN = 1, OS_OTHERS = 2 };
#if 1
int VendorRequests(XUD_ep ep0_out, XUD_ep ep0_in, REFERENCE_PARAM(USB_SetupPacket_t, sp) VENDOR_REQUESTS_PARAMS_DEC_)
{
XUD_Result_t result = XUD_RES_ERR;
unsigned char request_data[EP0_MAX_REQUEST_SIZE];
unsigned len;
int a = 0;
unsigned k = 5;
//SET_SHARED_GLOBAL(g_host_os, k);
switch ((sp->bmRequestType.Direction << 7) | (sp->bmRequestType.Type << 5) | (sp->bmRequestType.Recipient)) {
case USB_BMREQ_H2D_VENDOR_DEV:
result = XUD_GetBuffer(ep0_out, request_data, len);
if (result == XUD_RES_OKAY) {
if (a/*control_process_usb_set_request(sp.wIndex, sp.wValue, sp.wLength, request_data, i_control) == CONTROL_SUCCESS*/) {
/* zero length data to indicate success
* on control error, go to standard requests, which will issue STALL
*/
result = XUD_DoSetRequestStatus(ep0_in);
} else {
result = XUD_RES_ERR;
}
}
break;
case USB_BMREQ_D2H_VENDOR_DEV:
/* application retrieval latency inside the control library call
* XUD task defers further calls by NAKing USB transactions
*/
// if (a/*control_process_usb_get_request(sp.wIndex, sp.wValue, sp.wLength, request_data, i_control) == CONTROL_SUCCESS*/) {
// len = sp->wLength;
// result = XUD_DoGetRequest(ep0_out, ep0_in, request_data, len, len);
// /* on control error, go to standard requests, which will issue STALL */
// }
#ifdef WINDOWS_OS_DESCRIPTOR_SUPPORT
// else
if(sp->bRequest == 0x01) // GET_MS_DESCRIPTOR, same as the OS_STR_DESC last byte
{
// dwLength, 4bytes: 16 bytes
request_data[0] = 0x10; request_data[1] = 0x00; request_data[2] = 0x00; request_data[3] = 0x00;
// bcdVersion, 2bytes: For version 1.00, bcdVersion is set to 0x0100.
request_data[4] = 0x00; request_data[5] = 0x01;
// wIndex, 2bytes: 0x04 for extended compat ID descriptors
request_data[6] = 0x04; request_data[7] = 0x00;
// bCount, 1byte: 0
request_data[8] = 0x00;
// RESERVED, 7bytes: all 0
request_data[9] = 0x00; request_data[10] = 0x00; request_data[11] = 0x00;
request_data[12] = 0x00; request_data[13] = 0x00; request_data[14] = 0x00; request_data[15] = 0x00;
result = XUD_DoGetRequest(ep0_out, ep0_in, request_data, 16, sp->wLength);
//xassert(busSpeed == XUD_SPEED_HS);
k = OS_WIN;
SET_SHARED_GLOBAL(g_host_os, k);
}
else {
//xassert(busSpeed == XUD_SPEED_FS);
k = OS_OTHERS;
SET_SHARED_GLOBAL(g_host_os, k);
result = XUD_RES_ERR;
}
#endif
break;
}
return result;
}
#endif
void VendorRequests_Init(VENDOR_REQUESTS_PARAMS_DEC)
{
}
#endif /* XUA_USB_EN */
#endif

View File

@@ -0,0 +1,21 @@
#include <platform.h>
/* This is provided as simple example but disabled due to the port clash with audiostream.xc */
#if 0
on tile[0]: out port p_leds = XS1_PORT_4F;
void UserHostActive(int active)
{
if(active)
{
/* Turn all LEDs on */
p_leds <: 0xF;
}
else
{
/* Turn all LEDs off */
p_leds <: 0x0;
}
}
#endif

View File

@@ -0,0 +1,529 @@
// Copyright 2020-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#pragma once
/** \file
* \brief API for QSPI Flash
*/
#include <stdbool.h>
#include "qspi_io.h"
void flash_id(uint8_t* buf);
void read_flash_data_partition(uint8_t* data_buf);
/**
* \addtogroup hil_qspi_flash hil_qspi_flash
*
* The public API for using HIL QSPI flash I/O.
* @{
*/
#if !defined(QSPI_FLASH_SANITY_CHECKS)
/**
* When QSPI_FLASH_SANITY_CHECKS is true then some
* run-time sanity checks are made at the expense
* of some extra overhead.
*/
#define QSPI_FLASH_SANITY_CHECKS 0
#endif
typedef enum {
/**
* Programs pages using only MOSI. Most, if not all, QSPI flashes support
* this command. Use this if in doubt.
*/
qspi_flash_page_program_1_1_1,
/**
* Programs pages by sending the command and address over just SIO0,
* but the data over all four data lines. Many QSPI flashes support either
* this or qspi_flash_page_program_1_4_4, but not both. Check the datasheet.
* If the particular flash chip that will be used is unknown, then
* qspi_flash_page_program_1_1_1 should be used.
*/
qspi_flash_page_program_1_1_4,
/**
* Programs pages by sending the command over just SIO0, but the
* address and data over all four data lines. Many QSPI flashes support either
* this or qspi_flash_page_program_1_1_4, but not both. Check the datasheet.
* If the particular flash chip that will be used is unknown, then
* qspi_flash_page_program_1_1_1 should be used.
*/
qspi_flash_page_program_1_4_4,
} qspi_flash_page_program_cmd_t;
/**
* The context structure that must be passed to each of the qspi_flash functions.
*/
typedef struct {
/**
* The context for the QSPI I/O interface that is used by the
* QSPI flash. At a minimum, the ports and clock block must be
* set prior to calling qspi_flash_init().
*/
qspi_io_ctx_t qspi_io_ctx;
/**
* The source clock to use for the QSPI I/O interface. Must be
* either qspi_io_source_clock_ref or qspi_io_source_clock_xcore.
* This must be set prior to calling qspi_flash_init().
*/
qspi_io_source_clock_t source_clock;
/**
* If set to false, then qspi_flash_init() will setup safe default
* values for the QSPI I/O clock configuration. If set to true, then
* the application must supply the clock setup values.
*/
int custom_clock_setup;
/**
* The type of page program command that will be used when qspi_flash_write() is
* called. See qspi_flash_page_program_cmd_t.
*/
qspi_flash_page_program_cmd_t quad_page_program_cmd;
/*
* The following members are all set automatically by qspi_flash_init() if
* SFDP is supported. These may be set prior to calling qspi_flash_init()
* in the event that SFDP is not supported.
* Additionally, sfdp_skip may be set to skip SFDP and use manually set
* parameters.
*/
bool sfdp_skip;
bool sfdp_supported;
size_t page_size_bytes;
size_t page_count;
size_t flash_size_kbytes;
/* should be 3 or 4 */
int address_bytes;
struct {
uint32_t size_log2;
uint32_t cmd;
} erase_info[4];
uint32_t busy_poll_cmd;
uint8_t busy_poll_bit;
uint8_t busy_poll_ready_value;
/* 1 or 2. 0 means quad mode is automatically detected and cannot be explicitly entered */
uint8_t qe_reg;
uint8_t qe_bit;
uint32_t sr2_read_cmd; /* if 0, then read 2 bytes with the standard status register read command */
uint32_t sr2_write_cmd;/* if 0, then write 2 bytes with the standard status register write command */
} qspi_flash_ctx_t;
/**
* Most QSPI flashes allow data to be erased in 4k, 32k or 64k chunks,
* as well as the entire chip. However, these values are not always
* available on all chips. The erase info table in the qspi flash context
* structure defines the size of each erasable sector size. This is typically
* populated automatically by qspi_flash_init() from the SFDP data in the flash.
*/
typedef enum {
qspi_flash_erase_1, /**< Erase the first available sector size. This should always be available and will be the smallest available erasable sector size. */
qspi_flash_erase_2, /**< Erase the second available sector size. This should be smaller than qspi_flash_erase_3 if both are available. */
qspi_flash_erase_3, /**< Erase the third available sector size. This should be smaller than qspi_flash_erase_4 if both are available. */
qspi_flash_erase_4, /**< Erase the fourth available sector size. This is typically not available, but will be the largest available erasable sector size if it is. */
qspi_flash_erase_chip, /**< Erase the entire chip */
} qspi_flash_erase_length_t;
/**
* The bit mask for the status register's write
* in progress bit.
*/
#define QSPI_FLASH_STATUS_REG_WIP_BM 0x01
/**
* The bit mask for the status register's write
* enable latch bit.
*/
#define QSPI_FLASH_STATUS_REG_WEL_BM 0x02
/*
* Returns the erase size in bytes associated with the given erase type.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param erase_type The erase type to return the size of.
*
* \returns The erase size in bytes of \p erase type. If \p erase_type
* is qspi_flash_erase_chip then SIZE_MAX is returned. If \p erase_type
* is invalid or not available on the flash chip, then 0 is returned.
*/
inline size_t qspi_flash_erase_type_size(qspi_flash_ctx_t *ctx, qspi_flash_erase_length_t erase_type)
{
if (erase_type >= qspi_flash_erase_1 && erase_type <= qspi_flash_erase_4) {
uint32_t size_log2 = ctx->erase_info[erase_type].size_log2;
return size_log2 > 0 ? (1 << size_log2) : 0;
} else if (erase_type == qspi_flash_erase_chip) {
return SIZE_MAX;
} else {
return 0;
}
}
/*
* Returns log2 of the erase size in bytes associated with the given erase type.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param erase_type The erase type to return the size of.
*
* \returns The log2 of the erase size in bytes of \p erase type. If \p erase_type
* is qspi_flash_erase_chip then UINT32_MAX is returned. If \p erase_type
* is invalid or not available on the flash chip, then 0 is returned.
*/
inline uint32_t qspi_flash_erase_type_size_log2(qspi_flash_ctx_t *ctx, qspi_flash_erase_length_t erase_type)
{
if (erase_type >= qspi_flash_erase_1 && erase_type <= qspi_flash_erase_4) {
return ctx->erase_info[erase_type].size_log2;
} else if (erase_type == qspi_flash_erase_chip) {
return UINT32_MAX;
} else {
return 0;
}
}
/**
* Sets or clears the quad enable bit in the flash.
*
* \note The quad enable bit is fixed to '1' in some QSPI flash
* chips, and cannot be cleared.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param set When true, the quad enable bit is set. When false,
* the quad enable bit is cleared if possible.
*
* \retval true if the QE bit was already at the requested value,
* or if the write was successful.
* \retval false if the write did not complete successfully. This
* can happen when trying to clear the QE bit on parts where
* it is fixed to '1'.
*/
bool qspi_flash_quad_enable_write(qspi_flash_ctx_t *ctx, bool set);
/**
* Sets the write enable latch in the QSPI flash. This must be called
* prior to erasing, programming, or writing to a register.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
*/
void qspi_flash_write_enable(qspi_flash_ctx_t *ctx);
/**
* This clears the write enable latch in the QSPI flash. This is cleared
* automatically at the end of write operations, so normally does not need
* to be called.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
*/
void qspi_flash_write_disable(qspi_flash_ctx_t *ctx);
/**
* This checks to see if the QSPI flash has a write operation in progress.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
*
* \retval true if there is a flash write in progress.
* \retval false if the flash is not writing and is ready to accept another
* read or write command.
*/
bool qspi_flash_write_in_progress(qspi_flash_ctx_t *ctx);
/**
* This waits while the QSPI flash has a write operation in progress.
* It returns when the write operation is complete, or immediately if
* there is not one in progress to begin with.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
*/
void qspi_flash_wait_while_write_in_progress(qspi_flash_ctx_t *ctx);
/**
* This performs an erase operation. qspi_flash_write_enable() must be called
* prior to this.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param address Any byte address within the data block to erase.
* \param erase_length The data block size to erase. See qspi_flash_erase_length_t.
* If qspi_flash_erase_chip, then \p address is ignored.
*/
void qspi_flash_erase(qspi_flash_ctx_t *ctx,
uint32_t address,
qspi_flash_erase_length_t erase_length);
/**
* This writes to a register in the QSPI flash. This allows an application
* to write to non-standard registers specific to its flash chip.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param cmd The command required for writing to the desired register.
Must be the value returned by QSPI_IO_BYTE_TO_MOSI().
* \param val Pointer to the data to write to the register.
* \param len The number of bytes from \p val to write to the register.
*/
void qspi_flash_write_register(qspi_flash_ctx_t *ctx,
uint32_t cmd,
const uint8_t *val,
size_t len);
/**
* This writes to the status register in the QSPI flash. qspi_flash_write_enable()
* must be called prior to this.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param val Pointer to the data to write to the register.
* \param len The number of bytes from \p val to write to the register.
*/
void qspi_flash_write_status_register(qspi_flash_ctx_t *ctx,
const uint8_t *val,
size_t len);
/**
* This reads from a register in the QSPI flash. This allows an application
* to read from non-standard registers specific to its flash chip.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param cmd The command required for reading from the desired register.
Must be the value returned by QSPI_IO_BYTE_TO_MOSI().
* \param val Pointer to the buffer to store the data read from the register.
* \param len The number of bytes to read from the register and save to \p val.
*/
void qspi_flash_read_register(qspi_flash_ctx_t *ctx,
uint32_t cmd,
uint8_t *val,
size_t len);
/**
* This reads from the status register in the QSPI flash.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param val Pointer to the buffer to store the data read from the register.
* \param len The number of bytes to read from the register and save to \p val.
*/
void qspi_flash_read_status_register(qspi_flash_ctx_t *ctx,
uint8_t *val,
size_t len);
/**
* This reads the JEDEC ID of the QSPI flash.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param val Pointer to the buffer to write the ID to.
* \param len The number of ID bytes to read and save to \p val.
*/
void qspi_flash_read_id(qspi_flash_ctx_t *ctx,
uint8_t *val,
size_t len);
/**
* This polls a register in the QSPI flash. This allows an application to
* poll non-standard registers specific to its flash chip. The register must
* be one byte and repeatedly sent out over MISO following the read register
* command.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param cmd The command required for reading from the desired register.
Must be the value returned by QSPI_IO_BYTE_TO_MOSI().
* \param mask The bitmask to apply to the received register value before
* comparing it to \p val;
* \param val The value that the register value, masked with \p mask, must
* match before returning.
*/
void qspi_flash_poll_register(qspi_flash_ctx_t *ctx,
uint32_t cmd,
const uint8_t mask,
const uint8_t val);
/**
* This polls the status register in the QSPI flash.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param mask The bitmask to apply to the received register value before
* comparing it to \p val;
* \param val The value that the register value, masked with \p mask, must
* match before returning.
*/
void qspi_flash_poll_status_register(qspi_flash_ctx_t *ctx,
const uint8_t mask,
const uint8_t val);
/**
* This reads data from the flash in fast mode. This is a normal SPI read,
* using only SIO0 (MOSI) and SIO1 (MOSI), but includes eight dummy cycles
* between the address and data.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param data Pointer to the buffer to save the read data to.
* \param address The byte address in the flash to begin reading at.
* Only bits 23:0 contain the address. The byte in bits 31:24
* is not sent.
* \param len The number of bytes to read and save to \p data.
*/
void qspi_flash_fast_read(qspi_flash_ctx_t *ctx,
uint8_t *data,
uint32_t address,
size_t len);
/**
* This reads data from the flash in quad I/O mode. All four lines are
* used to send the address and to read the data.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param data Pointer to the buffer to save the read data to.
* \param address The byte address in the flash to begin reading at.
* Only bits 23:0 contain the address. Bits 31:24 are actually
* transmitted to the flash during the first two dummy cycles
* following the three address bytes. Some flashes read the SIO
* lines during these first two dummy cycles to enable certain
* features, so this might be useful for some applications.
* \param len The number of bytes to read and save to \p data.
*/
void qspi_flash_read(qspi_flash_ctx_t *ctx,
uint8_t *data,
uint32_t address,
size_t len);
/**
* This is the same as qspi_flash_read() except that the nibbles in each
* byte of the data returned are swapped.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param data Pointer to the buffer to save the read data to.
* \param address The byte address in the flash to begin reading at.
* Only bits 23:0 contain the address. Bits 31:24 are actually
* transmitted to the flash during the first two dummy cycles
* following the three address bytes. Some flashes read the SIO
* lines during these first two dummy cycles to enable certain
* features, so this might be useful for some applications.
* \param len The number of bytes to read and save to \p data.
*/
void qspi_flash_read_nibble_swapped(qspi_flash_ctx_t *ctx,
uint8_t *data,
uint32_t address,
size_t len);
/**
* This reads data from the flash in quad I/O "eXecute In Place" mode.
* All four lines are used to send the address and to read the data.
* No command is sent. The flash must already have been put into "XIP" mode.
* The method used to put the flash into XIP mode, as well as to take it out,
* is flash dependent. See your flash's datasheet.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param data Pointer to the buffer to save the read data to.
* \param address The byte address in the flash to begin reading at.
* Only bits 23:0 contain the address. Bits 31:24 are actually
* transmitted to the flash during the first two dummy cycles
* following the three address bytes. Some flashes read the SIO
* lines during these first two dummy cycles to enable certain
* features, so this might be useful for some applications.
* \param len The number of bytes to read and save to \p data.
*/
void qspi_flash_xip_read(qspi_flash_ctx_t *ctx,
uint8_t *data,
uint32_t address,
size_t len);
/**
* This is the same as qspi_flash_xip_read() except that the nibbles in each
* byte of the data returned are swapped.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param data Pointer to the buffer to save the read data to.
* \param address The byte address in the flash to begin reading at.
* Only bits 23:0 contain the address. Bits 31:24 are actually
* transmitted to the flash during the first two dummy cycles
* following the three address bytes. Some flashes read the SIO
* lines during these first two dummy cycles to enable certain
* features, so this might be useful for some applications.
* \param len The number of bytes to read and save to \p data.
*/
void qspi_flash_xip_read_nibble_swapped(qspi_flash_ctx_t *ctx,
uint8_t *data,
uint32_t address,
size_t len);
/**
* This writes data to a page in the QSPI flash. If ctx->quad_page_program_enable
* is true, then the command in ctx->quad_page_program_cmd is sent and all
* four SIO lines are used to send the address and data. Otherwise, the standard
* page program command is sent and only SIO0 (MOSI) is used to send the address
* and data.
*
* qspi_flash_write_enable() must be called prior to this.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param data Pointer to the data to write to the flash.
* \param address The byte address in the flash to begin writing at.
* Only bits 23:0 contain the address. The byte in bits 31:24
* is not sent.
* \param len The number of bytes to write to the flash.
*/
void qspi_flash_write(qspi_flash_ctx_t *ctx,
const uint8_t *data,
uint32_t address,
size_t len);
/**
* This is the same as qspi_flash_write() except that the nibbles in each
* byte of the data written are swapped.
*
* qspi_flash_write_enable() must be called prior to this.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param data Pointer to the data to write to the flash.
* \param address The byte address in the flash to begin writing at.
* Only bits 23:0 contain the address. The byte in bits 31:0 is
* not sent.
* \param len The number of bytes to write to the flash.
*/
void qspi_flash_write_nibble_swapped(qspi_flash_ctx_t *ctx,
const uint8_t *data,
uint32_t address,
size_t len);
/**
* This reads the SFDP data from the flash in 1-1-1 mode.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* \param data Pointer to the buffer to save the read data to.
* \param address The byte address in the SFDP area to begin reading at.
* Only bits 23:0 contain the address. The byte in bits 31:24
* is not sent.
* \param len The number of bytes to read and save to \p data.
*/
void qspi_flash_sfdp_read(qspi_flash_ctx_t *ctx,
uint8_t *data,
uint32_t address,
size_t len);
/**
* Deinitializes the QSPI I/O interface associated with the QSPI flash.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
* This should have been previously initialized with
* qspi_flash_init().
*/
void qspi_flash_deinit(qspi_flash_ctx_t *ctx);
/**
* Initializes the QSPI I/O interface associated with the QSPI flash.
* The ports and clock block in the ctx->qspi_io_ctx must be set prior
* to calling this.
*
* If ctx->custom_clock_setup is false, then the QSPI I/O clock configuration
* is set to safe default values that should work for all QSPI flashes.
* Otherwise, the clock configuration values must be supplied by the application.
*
* \param ctx The QSPI flash context associated with the QSPI flash.
*/
void qspi_flash_init(qspi_flash_ctx_t *ctx);
/**@}*/ // END: addtogroup hil_qspi_flash

View File

@@ -0,0 +1,915 @@
// Copyright 2020-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#pragma once
/** \file
* \brief API for QSPI I/O
*/
#include <stdlib.h> /* for size_t */
#include <stdint.h>
#include <xclib.h> /* for byterev() */
#include <xcore/port.h>
#include <xcore/clock.h>
#include <xcore/thread.h>
/**
* \addtogroup hil_qspi_io hil_qspi_io
*
* The public API for using HIL QSPI I/O.
* @{
*/
/**
* Enum type used to set which SCLK edge SIO is sampled on.
*/
typedef enum {
qspi_io_sample_edge_rising = 0, /**< Sample SIO on the rising edge */
qspi_io_sample_edge_falling /**< Sample SIO on the falling edge */
} qspi_io_sample_edge_t;
/**
* Enum type used to set which of the two clock sources SCLK is derived from.
*/
typedef enum {
qspi_io_source_clock_ref = 0, /**< SCLK is derived from the 100 MHz reference clock */
qspi_io_source_clock_xcore /**< SCLK is derived from the core clock */
} qspi_io_source_clock_t;
/**
* Enum type used to specify whether or not nibbles should be swapped during transfers.
*/
typedef enum {
qspi_io_transfer_normal = 0, /**< Do not swap nibbles */
qspi_io_transfer_nibble_swap /**< Swap nibbles */
} qspi_io_transfer_mode_t;
/**
* Enum type used to specify whether the next transaction will be a full speed
* QSPI transaction with dummy cycles, or a lower speed SPI read transaction
* without dummy cycles.
*/
typedef enum {
qspi_io_full_speed = 0, /**< The transaction will be full speed with dummy cycles */
qspi_io_spi_read /**< The transaction will be low speed without dummy cycles */
} qspi_io_transaction_type_t;
/**
* This macro may be used when sending out bytes
* that are only transmitted over the single data
* line MOSI (SIO0). The returned word should be
* transmitted using either qspi_io_start_transaction()
* or qspi_io_words_out(). Typically the byte argument
* to this macro is a constant known at compile time,
* like commands, as the compiler can perform this
* computation at compile time. For arrays of data,
* it may be more appropriate to use qspi_io_mosi_out()
* which more efficiently computes this transformation
* at run time on the fly.
*
* When writing a single byte out in SPI mode,
* the byte needs to be transformed such that
* each nibble in the word that is sent out on
* SIO contains one bit from the byte in bit 0
* (which corresponds to SIO0, or MOSI).
*
* \param x The byte to send out to MOSI.
*/
#define QSPI_IO_BYTE_TO_MOSI(x) ( \
((((x) >> 0) & 1) | 0xE) << (0 * 4) | \
((((x) >> 1) & 1) | 0xE) << (1 * 4) | \
((((x) >> 2) & 1) | 0xE) << (2 * 4) | \
((((x) >> 3) & 1) | 0xE) << (3 * 4) | \
((((x) >> 4) & 1) | 0xE) << (4 * 4) | \
((((x) >> 5) & 1) | 0xE) << (5 * 4) | \
((((x) >> 6) & 1) | 0xE) << (6 * 4) | \
((((x) >> 7) & 1) | 0xE) << (7 * 4) )
/**
* The context structure that must be passed to each of the qspi_io functions.
* Several of the members in this structure must be set by the application prior
* to calling either qspi_io_init() or qspi_io_start_transaction().
*/
typedef struct {
/**
* The clock block to use for the qspi_io interface.
*
* This must be set prior to calling qspi_io_init()
* and must not change.
*/
xclock_t clock_block;
/**
* The chip select port. MUST be a 1-bit port.
*
* This must be set prior to calling qspi_io_init()
* and must not change.
*/
port_t cs_port;
/**
* The SCLK port. MUST be a 1-bit port.
*
* This must be set prior to calling qspi_io_init()
* and must not change.
*/
port_t sclk_port;
/**
* The SIO port. MUST be a 4-bit port.
*
* This must be set prior to calling qspi_io_init()
* and must not change.
*/
port_t sio_port;
/**
* The divisor to use for QSPI reads and writes as well as SPI writes.
*
* The frequency of SCLK will be set to:
* (F_src) / (2 * full_speed_clk_divisor)
* Where F_src is the frequency of the source clock specified
* by the source_clock parameter of qspi_io_init().
*
* This must be set prior to the beginning of a transaction
* and may change between transactions.
*/
int full_speed_clk_divisor;
/**
* The divisor to use for the clock when performing a SPI read. This
* may need to be slower than the clock used for writes and QSPI reads.
* This is because a small handful of instructions must execute to turn
* the SIO port around from output to input and they must execute within
* a single SCLK period during a SPI read. QSPI reads have dummy cycles
* where these instructions may execute which allows for a higher clock
* frequency.
*
* The frequency of SCLK will be set to:
* (F_src) / (2 * spi_read_clk_divisor)
* Where F_src is the frequency of the source clock specified
* by the source_clock parameter of qspi_io_init().
*
* This must be set prior to the beginning of a transaction
* and may change between transactions.
*/
int spi_read_clk_divisor;
/**
* Number of SCLK cycles to delay the sampling of SIO on input
* during a full speed transaction.
*
* Usually either 0 or 1 depending on the SCLK frequency.
*
* This must be set prior to the beginning of a transaction
* and may change between transactions.
*/
uint32_t full_speed_sclk_sample_delay;
/**
* Number of SCLK cycles to delay the sampling of SIO on input
* during a SPI read transaction.
*
* Usually either 0 or 1 depending on the SCLK frequency.
*
* This must be set prior to the beginning of a transaction
* and may change between transactions.
*/
uint32_t spi_read_sclk_sample_delay;
/**
* The SCLK edge to sample the SIO input on during a full speed
* transaction. May be either qspi_io_sample_edge_rising or
* qspi_io_sample_edge_falling.
*
* This must be set prior to the beginning of a transaction
* and may change between transactions.
*/
qspi_io_sample_edge_t full_speed_sclk_sample_edge;
/**
* The SCLK edge to sample the SIO input on during a SPI read
* transaction. May be either qspi_io_sample_edge_rising or
* qspi_io_sample_edge_falling.
*
* This must be set prior to the beginning of a transaction
* and may change between transactions.
*/
qspi_io_sample_edge_t spi_read_sclk_sample_edge;
/**
* Number of core clock cycles to delay sampling the SIO pads during
* a full speed transaction. This allows for more fine grained adjustment
* of sampling time. The value may be between 0 and 5.
*
* This must be set prior to the beginning of a transaction
* and may change between transactions.
*/
uint32_t full_speed_sio_pad_delay;
/**
* Number of core clock cycles to delay sampling the SIO pads during
* a SPI read transaction. This allows for more fine grained adjustment
* of sampling time. The value may be between 0 and 5.
*
* This must be set prior to the beginning of a transaction
* and may change between transactions.
*/
uint32_t spi_read_sio_pad_delay;
/* The following are used internally and should not be set by the application */
size_t transaction_length;
uint32_t transaction_start;
uint32_t sample_delay;
uint32_t sample_edge;
uint32_t sio_pad_delay;
uint32_t sio_drive;
thread_mode_t thread_mode;
} qspi_io_ctx_t;
/**
* This function should only be called internally
* by qspi_io_mosi_out(). It performs the same
* transformation as QSPI_IO_BYTE_TO_MOSI() but
* also integrates the byte reversal and nibble
* swap performed by qspi_io_words_out().
*
* \param x The byte to send out to MOSI.
*
* \returns the word to output to SIO.
*/
__attribute__((always_inline))
inline uint32_t qspi_io_byte_to_mosi(uint32_t x)
{
uint32_t ones = 0xFFFFFFFF;
x |= 0xFFFFFF00;
asm volatile (
"zip %0, %1, 0\n"
"zip %1, %0, 0\n"
"bitrev %0, %0\n"
:"+r"(x), "+r"(ones)
);
return x;
}
/**
* This function should only be called internally
* by qspi_io_miso_in().
*
* When reading in a single byte in SPI mode,
* the word that is received on SIO needs to
* be transformed such that bit one from each
* nibble (which corresponds to SIO1, or MISO)
* is shifted into the correct bit position.
*
* \param x The word received on SIO.
*
* \returns the byte received on MISO.
*/
__attribute__((always_inline))
inline uint32_t qspi_io_miso_to_byte(uint32_t x)
{
uint32_t unzipped = 0;
asm volatile (
"bitrev %1, %1\n"
"unzip %0, %1, 0\n"
"unzip %0, %1, 0\n"
: "+r"(unzipped), "+r"(x)
);
return unzipped;
}
/**
* This function swaps the nibbles in each of the
* four bytes of \p word.
*
* \param word The word to nibble swap.
*
* \returns the nibble swapped word.
*/
__attribute__((always_inline))
inline uint32_t qspi_io_nibble_swap(uint32_t word)
{
uint32_t tmp;
/* word = ((word & 0x0F0F0F0F) << 4) | ((word & 0xF0F0F0F0) >> 4) */
asm volatile (
"{and %0,%0,%2 ; and %1,%0,%3}\n"
"{shl %0,%0,4 ; shr %1,%1,4}\n"
: "+r"(word), "=r"(tmp)
: "r"(0x0F0F0F0F), "r"(0xF0F0F0F0)
);
return word | tmp;
}
/* The SETC constant for pad delay is missing from xs2a_user.h */
#define QSPI_IO_SETC_PAD_DELAY(n) (0x7007 | ((n) << 3))
/* These appear to be missing from the public API of lib_xcore */
#define QSPI_IO_RESOURCE_SETCI(res, c) asm volatile( "setc res[%0], %1" :: "r" (res), "n" (c))
#define QSPI_IO_RESOURCE_SETC(res, r) asm volatile( "setc res[%0], %1" :: "r" (res), "r" (r))
/**
* Begins a new QSPI I/O transaction by starting the clock,
* asserting CS, and sending out the first word which is
* typically a command.
*
* \note If more words or bytes need to be sent or received as
* part of this transaction, then the appropriate functions will
* need to be called immediately following this one. For example,
* qspi_io_bytes_out() then qspi_io_sio_direction_input() then
* qspi_io_bytes_in(). The "out" or "in" instruction in each must
* be executed within eight SCLK cycles of the preceding one,
* including the OUT instruction in qspi_io_start_transaction().
* Some analysis may be necessary depending on the frequency of SCLK.
* These functions are all marked as inline to help keep them closer
* together by removing the overhead associated with function calls
* and to allow better optimization.
*
* \note It is likely not possible to follow an input with an output
* within a single transaction unless the frequency of SCLK is
* sufficiently slow. Fortunately in practice this rarely, if ever,
* required.
*
* \param ctx Pointer to the QSPI I/O context.
* \param first_word The first word to output.
* \param len The total number of clock cycles in the transaction.
* CS will at some point during the transaction be setup
* to deassert automatically after this number of cycles.
* \param transaction_type Set to qspi_io_spi_read if the transaction will be a SPI
* read with no dummy cycles. This may run at a slower clock
* frequency in order to turn around SIO from output to input
* in time. Otherwise set to qspi_io_full_speed.
*/
__attribute__((always_inline))
inline void qspi_io_start_transaction(qspi_io_ctx_t *ctx,
uint32_t first_word,
size_t len,
qspi_io_transaction_type_t transaction_type)
{
/* Save thread bits on entry */
ctx->thread_mode = local_thread_mode_get_bits();
/* enable fast mode and high priority */
local_thread_mode_set_bits(thread_mode_fast | thread_mode_high_priority);
ctx->transaction_length = len;
port_set_master(ctx->sio_port);
port_set_no_ready(ctx->sio_port);
if (transaction_type != qspi_io_full_speed) {
clock_set_divide(ctx->clock_block, ctx->spi_read_clk_divisor);
ctx->sample_delay = ctx->spi_read_sclk_sample_delay;
ctx->sample_edge = ctx->spi_read_sclk_sample_edge;
ctx->sio_pad_delay = QSPI_IO_SETC_PAD_DELAY(ctx->spi_read_sio_pad_delay);
ctx->sio_drive = XS1_SETC_DRIVE_PULL_UP; /* enable pullups during the read */
} else {
clock_set_divide(ctx->clock_block, ctx->full_speed_clk_divisor);
ctx->sample_delay = ctx->full_speed_sclk_sample_delay;
ctx->sample_edge = ctx->full_speed_sclk_sample_edge;
ctx->sio_pad_delay = QSPI_IO_SETC_PAD_DELAY(ctx->full_speed_sio_pad_delay);
ctx->sio_drive = XS1_SETC_DRIVE_DRIVE; /* disable pullups during the read */
}
first_word = byterev(first_word);
first_word = qspi_io_nibble_swap(first_word);
/* ensure pullups are disabled during output */
QSPI_IO_RESOURCE_SETCI(ctx->sio_port, XS1_SETC_DRIVE_DRIVE);
port_out(ctx->sio_port, first_word);
port_out(ctx->cs_port, 1);
clock_start(ctx->clock_block);
}
/**
* Outputs a byte array to the QSPI interface. The byte array
* must start on a 4-byte boundary. This call must be made in
* time such that its OUT instruction executes within 8 SCLK
* cycles of the previous OUT instruction.
*
* \param ctx Pointer to the QSPI I/O context.
* \param transfer_mode Can be either qspi_io_transfer_normal or qspi_io_transfer_nibble_swap.
* When qspi_io_transfer_nibble_swap, each byte transferred out is
* nibble swapped. Because the data is inherently sent out nibble swapped
* over the port, setting this to qspi_io_transfer_nibble_swap
* actually removes a nibble swap operation.
* \param data Pointer to the byte array to output. This MUST
* begin on a 4-byte boundary.
* \param len The number of bytes in \p data to output.
*/
__attribute__((always_inline))
inline void qspi_io_bytes_out(const qspi_io_ctx_t *ctx,
const qspi_io_transfer_mode_t transfer_mode,
const uint8_t *data,
size_t len)
{
const uint32_t *data_words = (const uint32_t *) data;
size_t word_len = len / sizeof(uint32_t);
uint32_t word;
int i;
/*
* Each iteration of this loop must execute within
* no more than eight SCLK cycles.
*/
for (i = 0; i < word_len; i++) {
word = data_words[i];
if (transfer_mode == qspi_io_transfer_normal) {
word = qspi_io_nibble_swap(word);
}
port_out(ctx->sio_port, word);
}
len &= sizeof(uint32_t) - 1; /* get the byte remainder */
if (len) {
word = data_words[i];
len *= 8;
if (transfer_mode == qspi_io_transfer_normal) {
word = qspi_io_nibble_swap(word);
}
port_set_shift_count(ctx->sio_port, len);
port_out(ctx->sio_port, word);
}
}
/**
* Outputs a word array to the QSPI interface. This call must be
* made in time such that its OUT instruction executes within 8 SCLK
* cycles of the previous OUT instruction.
*
* \param ctx Pointer to the QSPI I/O context.
* \param transfer_mode Can be either qspi_io_transfer_normal or qspi_io_transfer_nibble_swap.
* When qspi_io_transfer_nibble_swap, each byte transferred out is
* nibble swapped. Because the data is inherently sent out nibble swapped
* over the port, setting this to qspi_io_transfer_nibble_swap
* actually removes a nibble swap operation.
* \param data Pointer to the word array to output.
* \param len The number of words in \p data to output.
*/
__attribute__((always_inline))
inline void qspi_io_words_out(const qspi_io_ctx_t *ctx,
const qspi_io_transfer_mode_t transfer_mode,
const uint32_t *data,
size_t len)
{
uint32_t word;
/*
* Each iteration of this loop must execute within
* no more than eight SCLK cycles.
*/
for (int i = 0; i < len; i++) {
word = byterev(data[i]);
if (transfer_mode == qspi_io_transfer_normal) {
word = qspi_io_nibble_swap(word);
}
port_out(ctx->sio_port, word);
}
}
/**
* Outputs a byte array to the QSPI interface over the single data
* line MOSI (SIO0). This call must be made in time such that its
* OUT instruction executes within 8 SCLK cycles of the previous OUT
* instruction.
*
* \param ctx Pointer to the QSPI I/O context.
* \param transfer_mode Can be either qspi_io_transfer_normal or qspi_io_transfer_nibble_swap.
* When qspi_io_transfer_nibble_swap, each byte transferred out is
* nibble swapped.
* \param data Pointer to the byte array to output.
* \param len The number of words in \p data to output.
*/
__attribute__((always_inline))
inline void qspi_io_mosi_out(const qspi_io_ctx_t *ctx,
const qspi_io_transfer_mode_t transfer_mode,
const uint8_t *data,
size_t len)
{
uint32_t word;
/*
* Each iteration of this loop must execute within
* no more than eight SCLK cycles.
*/
for (int i = 0; i < len; i++) {
word = data[i];
if (transfer_mode != qspi_io_transfer_normal) {
word = qspi_io_nibble_swap(word);
}
word = qspi_io_byte_to_mosi(word);
port_out(ctx->sio_port, word);
}
}
/**
* This must be called to change the direction of SIO from output
* to input before calling either qspi_io_bytes_in() or qspi_io_words_in().
* This call must be made in time such that the call to port_set_buffered()
* completes before the sample edge of SCLK shifts in the first nibble of
* the next data word to be read.
* This also will setup CS to deassert at the end of the transaction
* while waiting for the previous output to complete.
*
* \note This is probably the most fragile function. Ensure that the port
* direction is turned around on time, and that the subsequent read IN
* instruction executes on time, by inspecting a VCD trace with both ports
* and instructions.
*
* \param ctx Pointer to the QSPI I/O context.
*/
__attribute__((always_inline))
inline void qspi_io_sio_direction_input(qspi_io_ctx_t *ctx)
{
/*
* If CS has not yet been setup to deassert at the end of
* the transaction then do that now. It is happening here
* because there should still be time while waiting for the
* previous output to complete.
*
* This cannot be done at the end of a transaction after
* the input begins, as it will not be able to complete
* in time after the last IN instruction.
*/
if (ctx->transaction_length != 0) {
uint32_t cs_start;
port_sync(ctx->cs_port);
cs_start = port_get_trigger_time(ctx->cs_port);
port_out_at_time(ctx->cs_port, cs_start + ctx->transaction_length, 0);
ctx->transaction_length = 0;
ctx->transaction_start = cs_start + ctx->sample_delay;
}
if (ctx->sample_delay) {
/*
* Applying a 5 cycle pad delay to the CS pin when delaying the sample
* time by one SCLK cycle helps to ensure that CS is still seen as
* active at that time, which is necessary since CS is SIO's ready signal.
* This does only work at higher frequencies (~50+ MHz), but these are the
* frequencies that require sampling be delayed by a clock cycle.
*/
QSPI_IO_RESOURCE_SETCI(ctx->cs_port, QSPI_IO_SETC_PAD_DELAY(5));
}
/*
* This mess is to ensure that immediately following the sync,
* the port is put into buffered mode and the sample edge is
* set to falling if required as quickly as possible. Not sure
* at this time how to achieve this without using inline assembly.
* If the syncr+setc instructions are not grouped together in a
* single asm volatile() group, then they get merged, resulting
* in a single syncr instruction, followed by checking sample_edge,
* then executing the appropriate setc instructions.
*/
if (ctx->sample_edge == qspi_io_sample_edge_falling) {
//port_sync(ctx->sio_port);
//port_reset(ctx->sio_port);
//port_set_sample_falling_edge(ctx->sio_port);
//port_set_buffered(ctx->sio_port);
asm volatile (
"syncr res[%0]\n"
"setc res[%0], %1\n"
"setc res[%0], %2\n"
"setc res[%0], %3\n"
: :
"r"(ctx->sio_port),
"n"(XS1_SETC_INUSE_ON),
"n"(XS1_SETC_SDELAY_SDELAY),
"n"(XS1_SETC_BUF_BUFFERS)
);
} else {
//port_sync(ctx->sio_port);
//port_reset(ctx->sio_port);
//port_set_buffered(ctx->sio_port);
asm volatile (
"syncr res[%0]\n"
"setc res[%0], %1\n"
"setc res[%0], %2\n"
: :
"r"(ctx->sio_port),
"n"(XS1_SETC_INUSE_ON),
"n"(XS1_SETC_BUF_BUFFERS)
);
}
QSPI_IO_RESOURCE_SETC(ctx->sio_port, ctx->sio_pad_delay);
QSPI_IO_RESOURCE_SETC(ctx->sio_port, ctx->sio_drive);
port_set_transfer_width(ctx->sio_port, 32);
port_set_ready_strobed(ctx->sio_port);
port_set_slave(ctx->sio_port);
}
/**
* Inputs a byte array from the QSPI interface. The byte array
* must start on a 4-byte boundary. qspi_io_sio_direction_input()
* must have been called previously. This call must be made in time
* such that its IN instruction executes before \p start_time.
*
* \note The number of bytes input may be any number. However, if
* it is NOT a multiple of four, then this likely will need to be
* the last call in the transaction. This is because the shorter
* length of the last input chunk plus the extra overhead required
* to deal with the sub-word accesses will not allow subsequent I/O
* to keep up unless the SCLK frequency is significantly slower than
* the core clock.
*
* \param ctx Pointer to the QSPI I/O context.
* \param transfer_mode Can be either qspi_io_transfer_normal or qspi_io_transfer_nibble_swap.
* When qspi_io_transfer_nibble_swap, each byte transferred in is
* nibble swapped. Because the data is inherently received nibble swapped
* over the port, setting this to qspi_io_transfer_nibble_swap
* actually removes a nibble swap operation.
* \param data Pointer to the byte array to save the received data to.
* This MUST begin on a 4-byte boundary.
* \param start_time The port time, relative to the beginning of the transfer,
* at which to input the first group of four bytes. This must
* line up with the last nibble of the fourth byte. If \p len
* is less than four, then it must line up with the last nibble
* of the last byte.
* \param len The number of bytes to input.
*/
__attribute__((always_inline))
inline void qspi_io_bytes_in(const qspi_io_ctx_t *ctx,
const qspi_io_transfer_mode_t transfer_mode,
uint8_t *data,
uint32_t start_time,
size_t len)
{
uint32_t *data_words = (uint32_t *) data;
size_t word_len = len / sizeof(uint32_t);
uint32_t word;
int i;
port_set_trigger_time(ctx->sio_port, ctx->transaction_start + start_time);
/*
* Each iteration of this loop must execute within
* no more than eight SCLK cycles.
*/
for (i = 0; i < word_len; i++) {
word = port_in(ctx->sio_port);
if (transfer_mode == qspi_io_transfer_normal) {
word = qspi_io_nibble_swap(word);
}
data_words[i] = word;
}
/*
* Note: Some of the following code, including the final IN
* instruction, may execute well after the data has already
* shifted in. This is ok provided this is the end of the
* transaction. Also note that the SETPSC and IN instructions
* executing after the data has already shifted in only works
* correctly when the port is in strobed slave mode and the ready
* signal is deasserted immediately following the last byte. This
* ensures that the data stops shifting in at the end of the
* transaction and that the number of bits shifted in after the last
* full word matches the number of remaining bytes to be read in.
*
* None of this not applies if len is a multiple of four.
*/
len &= sizeof(uint32_t) - 1; /* get the byte remainder */
if (len) {
if (word_len > 0) {
/*
* There appears to be a problem (bug?) where if there is
* a port time set on a port, and then a port shift
* count is set within one or two cycles prior to this
* port time, then the subsequent IN instruction hangs
* indefinitely. If only either a port time or a port
* shift count is used, there appears to be no problem.
* So we only set the port shift count here if there
* is not an active port time.
*/
port_set_shift_count(ctx->sio_port, len * 8);
}
word = port_in(ctx->sio_port);
word >>= 8 * (4 - len);
data = (uint8_t *) &data_words[i];
if (transfer_mode == qspi_io_transfer_normal) {
word = qspi_io_nibble_swap(word);
}
for (i = 0; i < len; i++) {
*data++ = word & 0xFF;
word >>= 8;
}
}
}
/**
* Inputs a word array from the QSPI interface. qspi_io_sio_direction_input()
* must have been called previously. This call must be made in time such
* that its IN instruction executes before \p start_time.
*
* \param ctx Pointer to the QSPI I/O context.
* \param transfer_mode Can be either qspi_io_transfer_normal or qspi_io_transfer_nibble_swap.
* When qspi_io_transfer_nibble_swap, each byte transferred in is
* nibble swapped. Because the data is inherently received nibble swapped
* over the port, setting this to qspi_io_transfer_nibble_swap
* actually removes a nibble swap operation.
* \param data Pointer to the word array to save the received data to.
* \param start_time The time, relative to the beginning of the transfer, at which
* to input the first word. This must line up with the last nibble
* of the first word.
* \param len The number of words to input.
*/
__attribute__((always_inline))
inline void qspi_io_words_in(const qspi_io_ctx_t *ctx,
const qspi_io_transfer_mode_t transfer_mode,
uint32_t *data,
uint32_t start_time,
size_t len)
{
uint32_t word;
port_set_trigger_time(ctx->sio_port, ctx->transaction_start + start_time);
/*
* Each iteration of this loop must execute within
* no more than eight SCLK cycles.
*/
for (int i = 0; i < len; i++) {
word = port_in(ctx->sio_port);
if (transfer_mode == qspi_io_transfer_normal) {
word = qspi_io_nibble_swap(word);
}
data[i] = byterev(word);
}
}
/**
* Inputs a byte array from the QSPI interface over the single data
* line MISO (SIO1). qspi_io_sio_direction_input() must have been
* called previously. This call must be made in time such that its
* IN instruction executes before \p start_time.
*
* \param ctx Pointer to the QSPI I/O context.
* \param transfer_mode Can be either qspi_io_transfer_normal or qspi_io_transfer_nibble_swap.
* When qspi_io_transfer_nibble_swap, each byte transferred in is
* nibble swapped.
* \param data Pointer to the byte array to save the received data to.
* \param start_time The time, relative to the beginning of the transfer, at which
* to input the first byte. This must line up with the last bit
* of the first byte.
* \param len The number of words to input.
*/
__attribute__((always_inline))
inline void qspi_io_miso_in(const qspi_io_ctx_t *ctx,
const qspi_io_transfer_mode_t transfer_mode,
uint8_t *data,
uint32_t start_time,
size_t len)
{
uint32_t word;
port_set_trigger_time(ctx->sio_port, ctx->transaction_start + start_time);
/*
* Each iteration of this loop must execute within
* no more than eight SCLK cycles.
*/
for (int i = 0; i < len; i++) {
word = qspi_io_miso_to_byte(port_in(ctx->sio_port));
if (transfer_mode != qspi_io_transfer_normal) {
word = qspi_io_nibble_swap(word);
}
data[i] = word;
}
}
/**
* Polls the SPI interface by repeatedly receiving a byte over MISO until
* a specified condition is met. For each time the received byte does not meet
* the condition, the deassertion of CS is extended by eight SCLK cycles.
* qspi_io_sio_direction_input() must have been called previously. This call
* must be made in time such that its IN instruction executes before \p start_time.
*
* \param ctx Pointer to the QSPI I/O context.
* \param mask The bitmask to apply to the received byte before comparing
* it to \p val;
* \param val The value that the received byte, masked with \p mask, must
* match before returning.
* \param start_time The time, relative to the beginning of the transfer, at which
* to input the first byte. This must line up with the last bit
* of the first byte.
*/
__attribute__((always_inline))
inline void qspi_io_miso_poll(const qspi_io_ctx_t *ctx,
const uint8_t mask,
const uint8_t val,
uint32_t start_time)
{
uint32_t word;
port_set_trigger_time(ctx->sio_port, ctx->transaction_start + start_time);
/*
* Each iteration of this loop must execute within
* no more than eight SCLK cycles.
*/
while (1) {
word = qspi_io_miso_to_byte(port_in(ctx->sio_port));
if ((word & mask) == val) {
break;
}
port_clear_buffer(ctx->cs_port);
port_out_at_time(ctx->cs_port, port_get_trigger_time(ctx->cs_port) + 8, 0);
}
}
/**
* This sets up CS to deassert at the end of the transaction if it has
* not already, waits for the current QSPI transaction to complete, and
* then stops SCLK.
*
* \param ctx Pointer to the QSPI I/O context.
*/
__attribute__((always_inline))
inline void qspi_io_end_transaction(const qspi_io_ctx_t *ctx)
{
/*
* If the transaction included input, then CS should already
* have been setup to deassert at the end. If not, then do it
* now as there should still be time while waiting for the
* previous output to complete. A sync on CS is necessary in
* case this is immediately following a call to
* qspi_io_start_transaction().
*/
if (ctx->transaction_length != 0) {
uint32_t cs_start;
port_sync(ctx->cs_port);
cs_start = port_get_trigger_time(ctx->cs_port);
port_out_at_time(ctx->cs_port, cs_start + ctx->transaction_length, 0);
}
port_sync(ctx->cs_port);
clock_stop(ctx->clock_block);
/*
* Ensure the SIO port is back to being sampled on the falling
* edge with no pad delay, and that the CS port has no pad delay.
*/
QSPI_IO_RESOURCE_SETCI(ctx->sio_port, QSPI_IO_SETC_PAD_DELAY(0));
QSPI_IO_RESOURCE_SETCI(ctx->cs_port, QSPI_IO_SETC_PAD_DELAY(0));
port_set_sample_falling_edge(ctx->sio_port);
/*
* Also enable pull-ups on the SIO lines between transactions. If the
* transaction ended with an input, then SIO is still in input mode so
* this prevents them from floating.
*/
QSPI_IO_RESOURCE_SETCI(ctx->sio_port, XS1_SETC_DRIVE_PULL_UP);
/* disable fast mode and high priority */
local_thread_mode_clear_bits(thread_mode_fast | thread_mode_high_priority);
/* Restore original thread bits on exit */
local_thread_mode_set_bits(ctx->thread_mode);
}
/**
* This disables and frees the clock block and all the ports associated with
* the QSPI I/O interface.
*
* Note: To guarantee timing in all situations, the QSPI I/O interface
* implicitly sets the fast mode and high priority status register bits
* for the duration of flash operations. This may reduce the MIPS of other
* threads based on overall system setup.
*
* \param ctx Pointer to the QSPI I/O context. This should have been
* previously initialized with qspi_io_init().
*/
void qspi_io_deinit(const qspi_io_ctx_t *ctx);
/**
* This sets up the clock block and all the ports associated with
* the QSPI I/O interface. This must be called first prior to any
* other QSPI I/O function.
*
* \param ctx Pointer to the QSPI I/O context. This must be initialized
* with the clock block and ports to use.
* \param source_clock Set to qspi_io_source_clock_ref to use the 100 MHz reference
* clock as the source for SCLK. Set to qspi_io_source_clock_xcore
* to use the xcore clock.
*/
void qspi_io_init(const qspi_io_ctx_t *ctx,
qspi_io_source_clock_t source_clock);
/**@}*/ // END: addtogroup hil_qspi_io

View File

@@ -0,0 +1,710 @@
// Copyright 2020-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#define DEBUG_UNIT QSPI_FLASH
#include "qspi_flash.h"
#include "sfdp.h"
#if QSPI_FLASH_SANITY_CHECKS
/*
* Ensure NDEBUG is not defined when the
* flash sanity checks are enabled.
*/
#if defined(NDEBUG)
#undef NDEBUG
#endif
#endif
#include <xcore/assert.h>
#include <xcore/hwtimer.h>
/*
* TODO: This isn't fully supported yet
*/
#define FOUR_BYTE_ADDRESS_SUPPORT 0
#define BARRIER() asm volatile("": : :"memory")
#define WRITE_ENABLE_COMMAND QSPI_IO_BYTE_TO_MOSI(0x06)
#define WRITE_DISABLE_COMMAND QSPI_IO_BYTE_TO_MOSI(0x04)
#define ERASE_CHIP_COMMAND QSPI_IO_BYTE_TO_MOSI(0xC7)
#define PP_1_1_1_COMMAND QSPI_IO_BYTE_TO_MOSI(0x02)
#define PP_1_1_4_COMMAND QSPI_IO_BYTE_TO_MOSI(0x32)
#define PP_1_4_4_COMMAND QSPI_IO_BYTE_TO_MOSI(0x38)
#define READ_STATUS_REG_COMMAND QSPI_IO_BYTE_TO_MOSI(0x05)
#define READ_ID_COMMAND QSPI_IO_BYTE_TO_MOSI(0x9F)
#define WRITE_STATUS_REG_COMMAND QSPI_IO_BYTE_TO_MOSI(0x01)
#define FAST_READ_COMMAND QSPI_IO_BYTE_TO_MOSI(0x0B)
#define QUAD_IO_READ_CMD_VAL 0xEB
#define QUAD_IO_READ_COMMAND QSPI_IO_BYTE_TO_MOSI(QUAD_IO_READ_CMD_VAL)
#if FOUR_BYTE_ADDRESS_SUPPORT
#define QUAD_IO_READ_DUMMY_CYCLES 6
#else
#define QUAD_IO_READ_DUMMY_CYCLES 4
#endif
#define FAST_READ_DUMMY_CYCLES 8
bool qspi_flash_quad_enable_write(qspi_flash_ctx_t *ctx, bool set)
{
uint8_t status[2];
uint8_t quad_enable_bitmask;
const uint32_t no_cmd = QSPI_IO_BYTE_TO_MOSI(0x00);
if (ctx->qe_reg == 0) {
return true;
}
xassert(ctx->qe_reg == 1 || ctx->qe_reg == 2);
xassert(ctx->qe_bit >= 0 && ctx->qe_bit <= 7);
quad_enable_bitmask = 1 << ctx->qe_bit;
if (ctx->qe_reg == 1 || ctx->sr2_read_cmd == no_cmd) {
qspi_flash_read_status_register(ctx, status, ctx->qe_reg);
} else {
qspi_flash_read_status_register(ctx, &status[0], 1);
qspi_flash_read_register(ctx, ctx->sr2_read_cmd, &status[1], 1);
}
if (!!(status[ctx->qe_reg - 1] & quad_enable_bitmask) != set) {
if (set) {
status[ctx->qe_reg - 1] |= quad_enable_bitmask;
} else {
status[ctx->qe_reg - 1] &= ~quad_enable_bitmask;
}
qspi_flash_write_enable(ctx);
if (ctx->qe_reg == 1 || ctx->sr2_write_cmd == no_cmd) {
qspi_flash_write_status_register(ctx, status, ctx->qe_reg);
} else {
qspi_flash_write_register(ctx, ctx->sr2_write_cmd, &status[1], 1);
}
qspi_flash_wait_while_write_in_progress(ctx);
status[0] = 0;
status[1] = 0;
if (ctx->qe_reg == 1 || ctx->sr2_read_cmd == no_cmd) {
qspi_flash_read_status_register(ctx, status, ctx->qe_reg);
} else {
qspi_flash_read_register(ctx, ctx->sr2_read_cmd, &status[1], 1);
}
return !!(status[ctx->qe_reg - 1] & quad_enable_bitmask) == set;
} else {
return true;
}
}
void qspi_flash_write_enable(qspi_flash_ctx_t *ctx)
{
qspi_io_ctx_t *qspi_io_ctx = &ctx->qspi_io_ctx;
uint8_t status;
do {
qspi_io_start_transaction(qspi_io_ctx, WRITE_ENABLE_COMMAND, 8, qspi_io_full_speed);
qspi_io_end_transaction(qspi_io_ctx);
qspi_flash_read_status_register(ctx, &status, 1);
} while ((status & QSPI_FLASH_STATUS_REG_WEL_BM) == 0);
}
void qspi_flash_write_disable(qspi_flash_ctx_t *ctx)
{
qspi_io_ctx_t *qspi_io_ctx = &ctx->qspi_io_ctx;
uint8_t status;
do {
qspi_io_start_transaction(qspi_io_ctx, WRITE_DISABLE_COMMAND, 8, qspi_io_full_speed);
qspi_io_end_transaction(qspi_io_ctx);
qspi_flash_read_status_register(ctx, &status, 1);
} while ((status & QSPI_FLASH_STATUS_REG_WEL_BM) != 0);
}
bool qspi_flash_write_in_progress(qspi_flash_ctx_t *ctx)
{
uint8_t status_reg;
qspi_flash_read_register(ctx, ctx->busy_poll_cmd, &status_reg, 1);
return ((status_reg >> ctx->busy_poll_bit) & 1) != ctx->busy_poll_ready_value;
}
void qspi_flash_wait_while_write_in_progress(qspi_flash_ctx_t *ctx)
{
qspi_flash_poll_register(ctx, ctx->busy_poll_cmd, (1 << ctx->busy_poll_bit), ctx->busy_poll_ready_value << ctx->busy_poll_bit);
}
void qspi_flash_erase(qspi_flash_ctx_t *ctx,
uint32_t address,
qspi_flash_erase_length_t erase_length)
{
qspi_io_ctx_t *qspi_io_ctx = &ctx->qspi_io_ctx;
size_t cycles;
uint32_t cmd;
uint8_t *address_bytes;
#if QSPI_FLASH_SANITY_CHECKS
uint8_t status_reg;
qspi_flash_read_status_register(ctx, &status_reg, 1);
xassert(status_reg & QSPI_FLASH_STATUS_REG_WEL_BM);
#endif
/*
* Manipulate the address so that in memory it is:
* {byte 2, byte 1, byte 0, XX}
* it will get sent out as 3 bytes.
*/
address = byterev(address << 8);
address_bytes = (uint8_t *) &address;
xassert(erase_length >= 0 && erase_length <= qspi_flash_erase_chip);
if (erase_length > qspi_flash_erase_chip) {
return;
}
if (erase_length == qspi_flash_erase_chip) {
cmd = ERASE_CHIP_COMMAND;
cycles = 8;
} else {
xassert(ctx->erase_info[erase_length].size_log2 > 0);
if (ctx->erase_info[erase_length].size_log2 <= 0) {
return;
}
cmd = ctx->erase_info[erase_length].cmd;
cycles = 32;
}
qspi_io_start_transaction(qspi_io_ctx, cmd, cycles, qspi_io_full_speed);
if (cycles == 32) {
qspi_io_mosi_out(qspi_io_ctx, qspi_io_transfer_normal, address_bytes, 3);
}
qspi_io_end_transaction(qspi_io_ctx);
}
void qspi_flash_write_register(qspi_flash_ctx_t *ctx,
uint32_t cmd,
const uint8_t *val,
size_t len)
{
qspi_io_ctx_t *qspi_io_ctx = &ctx->qspi_io_ctx;
size_t cycles = 8 + /* 8 cycles for the command */
8 * len; /* 2 cycles per byte */
qspi_io_start_transaction(qspi_io_ctx, cmd, cycles, qspi_io_full_speed);
qspi_io_mosi_out(qspi_io_ctx, qspi_io_transfer_normal, val, len);
qspi_io_end_transaction(qspi_io_ctx);
}
void qspi_flash_write_status_register(qspi_flash_ctx_t *ctx,
const uint8_t *val,
size_t len)
{
#if QSPI_FLASH_SANITY_CHECKS
uint8_t status_reg;
qspi_flash_read_status_register(ctx, &status_reg, 1);
xassert(status_reg & QSPI_FLASH_STATUS_REG_WEL_BM);
#endif
qspi_flash_write_register(ctx, WRITE_STATUS_REG_COMMAND, val, len);
}
void qspi_flash_read_register(qspi_flash_ctx_t *ctx,
uint32_t cmd,
uint8_t *val,
size_t len)
{
qspi_io_ctx_t *qspi_io_ctx = &ctx->qspi_io_ctx;
size_t cycles = 8 + /* 8 cycles for the command */
8 * len; /* 8 cycles per byte */
qspi_io_start_transaction(qspi_io_ctx, cmd, cycles, qspi_io_spi_read);
qspi_io_sio_direction_input(qspi_io_ctx);
qspi_io_miso_in(qspi_io_ctx, qspi_io_transfer_normal, val,
8 + /* 8 cycles for the command */
7, /* input on the last cycle of the first byte */
len);
// }
qspi_io_end_transaction(qspi_io_ctx);
}
void qspi_flash_read_status_register(qspi_flash_ctx_t *ctx,
uint8_t *val,
size_t len)
{
qspi_flash_read_register(ctx, READ_STATUS_REG_COMMAND, val, len);
}
void qspi_flash_read_id(qspi_flash_ctx_t *ctx,
uint8_t *val,
size_t len)
{
qspi_flash_read_register(ctx, READ_ID_COMMAND, val, len);
}
void qspi_flash_poll_register(qspi_flash_ctx_t *ctx,
uint32_t cmd,
const uint8_t mask,
const uint8_t val)
{
qspi_io_ctx_t *qspi_io_ctx = &ctx->qspi_io_ctx;
size_t cycles = 8 + /* 8 cycles for the command */
8 * 2; /* Read the register at least twice */
qspi_io_start_transaction(qspi_io_ctx, cmd, cycles, qspi_io_spi_read);
qspi_io_sio_direction_input(qspi_io_ctx);
qspi_io_miso_poll(qspi_io_ctx, mask, val,
8 + /* 8 cycles for the command */
7); /* input on the last cycle of the first byte */
qspi_io_end_transaction(qspi_io_ctx);
}
void qspi_flash_poll_status_register(qspi_flash_ctx_t *ctx,
const uint8_t mask,
const uint8_t val)
{
qspi_flash_poll_register(ctx, READ_STATUS_REG_COMMAND, mask, val);
}
__attribute__((always_inline))
static void qspi_flash_fast_read_i(qspi_flash_ctx_t *ctx,
uint32_t cmd,
uint8_t *data,
uint32_t address,
size_t len)
{
qspi_io_ctx_t *qspi_io_ctx = &ctx->qspi_io_ctx;
uint8_t *address_bytes;
size_t cycles = 8 + /* 8 cycles for the command */
24 + /* 24 cycles for the address */
FAST_READ_DUMMY_CYCLES + /* dummy cycles */
8 * len; /* 8 cycles per byte */
size_t input_cycle = 8 + /* 8 cycles for the command */
24 + /* 24 cycles for the address */
FAST_READ_DUMMY_CYCLES + /* dummy cycles */
7; /* input on the last cycle of the first byte */
/*
* Manipulate the address so that in memory it is:
* {byte 2, byte 1, byte 0, XX}
* it will get sent out as 3 bytes.
*/
address = byterev(address << 8);
address_bytes = (uint8_t *) &address;
qspi_io_start_transaction(qspi_io_ctx, cmd, cycles, qspi_io_full_speed);
qspi_io_mosi_out(qspi_io_ctx, qspi_io_transfer_normal, address_bytes, 3);
qspi_io_sio_direction_input(qspi_io_ctx);
qspi_io_miso_in(qspi_io_ctx, qspi_io_transfer_normal, data, input_cycle, len);
qspi_io_end_transaction(qspi_io_ctx);
}
void qspi_flash_fast_read(qspi_flash_ctx_t *ctx,
uint8_t *data,
uint32_t address,
size_t len)
{
qspi_flash_ctx_t local_ctx = *ctx;
qspi_flash_fast_read_i(&local_ctx, QSPI_IO_BYTE_TO_MOSI(FAST_READ_COMMAND), data, address, len);
}
__attribute__ ((noinline))
static void qspi_flash_read_calc(const int xip,
#if FOUR_BYTE_ADDRESS_SUPPORT
const int four_byte_address,
#endif
uint32_t *address,
size_t len,
size_t *cycles,
size_t *input_cycle)
{
/* The first input should occur on either the fourth
* byte, or the last byte, whichever is first. */
const size_t first_input_byte = len > 4 ? 4 : len;
#if FOUR_BYTE_ADDRESS_SUPPORT
if (xip) {
*cycles = (four_byte_address ? 8 : 6) + /* 6 or 8 cycles for address */
QUAD_IO_READ_DUMMY_CYCLES + /* dummy cycles */
2 * len; /* 2 cycles per byte */
*input_cycle = (four_byte_address ? 8 : 6) + /* 6 or 8 cycles for address */
QUAD_IO_READ_DUMMY_CYCLES + /* dummy cycles */
2 * first_input_byte - 1; /* input on the last cycle of the first input byte */
} else {
*cycles = 8 + /* 8 cycles each for command */
(four_byte_address ? 8 : 6) + /* 6 or 8 cycles for address */
QUAD_IO_READ_DUMMY_CYCLES + /* dummy cycles */
2 * len; /* 2 cycles per byte */
*input_cycle = 8 + /* 8 cycles each for command */
(four_byte_address ? 8 : 6) + /* 6 or 8 cycles for address */
QUAD_IO_READ_DUMMY_CYCLES + /* dummy cycles */
2 * first_input_byte - 1; /* input on the last cycle of the first input byte */
}
#else
if (xip) {
*cycles = 8 + /* 8 cycles for address */
QUAD_IO_READ_DUMMY_CYCLES + /* dummy cycles */
2 * len; /* 2 cycles per byte */
*input_cycle = 8 + /* 8 cycles for address */
QUAD_IO_READ_DUMMY_CYCLES + /* dummy cycles */
2 * first_input_byte - 1; /* input on the last cycle of the first input byte */
} else {
*cycles = 8 * 2 + /* 8 cycles each for command and address */
QUAD_IO_READ_DUMMY_CYCLES + /* dummy cycles */
2 * len; /* 2 cycles per byte */
*input_cycle = 8 * 2 + /* 8 cycles each for command and address */
QUAD_IO_READ_DUMMY_CYCLES + /* dummy cycles */
2 * first_input_byte - 1; /* input on the last cycle of the first input byte */
}
#endif
#if FOUR_BYTE_ADDRESS_SUPPORT
if (!four_byte_address) {
#endif
/*
* The address is really contained in the upper 24 bits.
* The lower 8 bits are essentially 2 dummy cycles.
* Rotate, such that the MSB of address is sent out during
* the first two dummy cycles. Some flashes use this to
* enter "performance" or "XIP" mode.
*/
*address = (*address << 8) | (*address >> 24);
#if FOUR_BYTE_ADDRESS_SUPPORT
}
#endif
}
__attribute__((always_inline))
inline void qspi_flash_read_i(qspi_flash_ctx_t *ctx,
const qspi_io_transfer_mode_t transfer_mode,
const int xip,
#if FOUR_BYTE_ADDRESS_SUPPORT
const int four_byte_address,
#endif
uint8_t *data,
uint32_t address,
size_t len)
{
qspi_io_ctx_t *qspi_io_ctx = &ctx->qspi_io_ctx;
size_t cycles;
size_t input_cycle;
qspi_flash_read_calc(xip,
#if FOUR_BYTE_ADDRESS_SUPPORT
four_byte_address,
#endif
&address,
len,
&cycles,
&input_cycle);
if (xip) {
qspi_io_start_transaction(qspi_io_ctx, address, cycles, qspi_io_full_speed);
} else {
qspi_io_start_transaction(qspi_io_ctx, QUAD_IO_READ_COMMAND, cycles, qspi_io_full_speed);
qspi_io_words_out(qspi_io_ctx, qspi_io_transfer_normal, &address, 1);
}
#if FOUR_BYTE_ADDRESS_SUPPORT
if (four_byte_address) {
uint8_t mode = 0xFF;
qspi_io_bytes_out(qspi_io_ctx, transfer_mode, &mode, 1);
}
#endif
qspi_io_sio_direction_input(qspi_io_ctx);
qspi_io_bytes_in(qspi_io_ctx, transfer_mode, data, input_cycle, len);
qspi_io_end_transaction(qspi_io_ctx);
}
void qspi_flash_read(qspi_flash_ctx_t *ctx,
uint8_t *data,
uint32_t address,
size_t len)
{
qspi_flash_ctx_t local_ctx = *ctx;
qspi_flash_read_i(&local_ctx, qspi_io_transfer_normal, 0,
#if FOUR_BYTE_ADDRESS_SUPPORT
0,
#endif
data, address, len);
}
void qspi_flash_read_nibble_swapped(qspi_flash_ctx_t *ctx,
uint8_t *data,
uint32_t address,
size_t len)
{
qspi_flash_ctx_t local_ctx = *ctx;
qspi_flash_read_i(&local_ctx, qspi_io_transfer_nibble_swap, 0,
#if FOUR_BYTE_ADDRESS_SUPPORT
0,
#endif
data, address, len);
}
void qspi_flash_xip_read(qspi_flash_ctx_t *ctx,
uint8_t *data,
uint32_t address,
size_t len)
{
qspi_flash_ctx_t local_ctx = *ctx;
qspi_flash_read_i(&local_ctx, qspi_io_transfer_normal, 1,
#if FOUR_BYTE_ADDRESS_SUPPORT
0,
#endif
data, address, len);
}
void qspi_flash_xip_read_nibble_swapped(qspi_flash_ctx_t *ctx,
uint8_t *data,
uint32_t address,
size_t len)
{
qspi_flash_ctx_t local_ctx = *ctx;
qspi_flash_read_i(&local_ctx, qspi_io_transfer_nibble_swap, 1,
#if FOUR_BYTE_ADDRESS_SUPPORT
0,
#endif
data, address, len);
}
__attribute__((always_inline))
inline void qspi_flash_write_i(qspi_flash_ctx_t *ctx,
const qspi_io_transfer_mode_t transfer_mode,
const uint8_t *data,
uint32_t address,
size_t len)
{
qspi_io_ctx_t *qspi_io_ctx = &ctx->qspi_io_ctx;
uint8_t *address_bytes;
uint32_t pp_cmd;
size_t cycles;
#if QSPI_FLASH_SANITY_CHECKS
uint8_t status_reg;
qspi_flash_read_status_register(ctx, &status_reg, 1);
xassert(status_reg & QSPI_FLASH_STATUS_REG_WEL_BM);
#endif
switch (ctx->quad_page_program_cmd) {
case qspi_flash_page_program_1_1_4:
pp_cmd = PP_1_1_4_COMMAND;
cycles = 8 + /* 8 cycles for command */
24 + /* 24 cycles for address */
2 * len; /* 2 cycles per byte */
break;
case qspi_flash_page_program_1_4_4:
pp_cmd = PP_1_4_4_COMMAND;
cycles = 8 + /* 8 cycles for command */
6 + /* 6 cycles for address */
2 * len; /* 2 cycles per byte */
break;
case qspi_flash_page_program_1_1_1:
default:
pp_cmd = PP_1_1_1_COMMAND;
cycles = 8 + /* 8 cycles for command */
24 + /* 24 cycles for address */
8 * len; /* 8 cycles per byte */
break;
}
/*
* Manipulate the address so that in memory it is:
* {byte 2, byte 1, byte 0, XX}
* it will get sent out as 3 bytes.
*/
address = byterev(address << 8);
address_bytes = (uint8_t *) &address;
qspi_io_start_transaction(qspi_io_ctx, pp_cmd, cycles, qspi_io_full_speed);
switch (ctx->quad_page_program_cmd) {
case qspi_flash_page_program_1_1_4:
qspi_io_mosi_out(qspi_io_ctx, qspi_io_transfer_normal, address_bytes, 3);
qspi_io_bytes_out(qspi_io_ctx, transfer_mode, data, len);
break;
case qspi_flash_page_program_1_4_4:
qspi_io_bytes_out(qspi_io_ctx, qspi_io_transfer_normal, address_bytes, 3);
qspi_io_bytes_out(qspi_io_ctx, transfer_mode, data, len);
break;
case qspi_flash_page_program_1_1_1:
default:
qspi_io_mosi_out(qspi_io_ctx, qspi_io_transfer_normal, address_bytes, 3);
qspi_io_mosi_out(qspi_io_ctx, transfer_mode, data, len);
break;
}
qspi_io_end_transaction(qspi_io_ctx);
}
void qspi_flash_write(qspi_flash_ctx_t *ctx,
const uint8_t *data,
uint32_t address,
size_t len)
{
qspi_flash_ctx_t local_ctx = *ctx;
qspi_flash_write_i(&local_ctx, qspi_io_transfer_normal, data, address, len);
}
void qspi_flash_write_nibble_swapped(qspi_flash_ctx_t *ctx,
const uint8_t *data,
uint32_t address,
size_t len)
{
qspi_flash_ctx_t local_ctx = *ctx;
qspi_flash_write_i(&local_ctx, qspi_io_transfer_nibble_swap, data, address, len);
}
SFDP_READ_CALLBACK_ATTR
void qspi_flash_sfdp_read(qspi_flash_ctx_t *ctx,
uint8_t *data,
uint32_t address,
size_t len)
{
qspi_flash_ctx_t local_ctx = *ctx;
qspi_flash_fast_read_i(&local_ctx, QSPI_IO_BYTE_TO_MOSI(SFDP_READ_INSTRUCTION), data, address, len);
}
void qspi_flash_deinit(qspi_flash_ctx_t *ctx)
{
qspi_io_ctx_t *qspi_io_ctx = &ctx->qspi_io_ctx;
qspi_io_deinit(qspi_io_ctx);
}
void qspi_flash_init(qspi_flash_ctx_t *ctx)
{
sfdp_info_t sfdp_info;
qspi_io_ctx_t *qspi_io_ctx = &ctx->qspi_io_ctx;
if (!ctx->custom_clock_setup) {
ctx->source_clock = qspi_io_source_clock_ref;
/* 100 / (2 * 2) = 25 MHz */
qspi_io_ctx->full_speed_clk_divisor = 2;
qspi_io_ctx->full_speed_sclk_sample_delay = 0;
qspi_io_ctx->full_speed_sclk_sample_edge = qspi_io_sample_edge_falling;
qspi_io_ctx->full_speed_sio_pad_delay = 0;
/* 100 / (2 * 2) = 25 MHz */
qspi_io_ctx->spi_read_clk_divisor = 2;
qspi_io_ctx->spi_read_sclk_sample_delay = 0;
qspi_io_ctx->spi_read_sclk_sample_edge = qspi_io_sample_edge_falling;
qspi_io_ctx->spi_read_sio_pad_delay = 0;
}
/* configure the QSPI I/O interface */
qspi_io_init(qspi_io_ctx, ctx->source_clock);
if ((ctx->sfdp_skip == false) && sfdp_discover(&sfdp_info, ctx, (sfdp_read_cb_t) qspi_flash_sfdp_read)) {
int ret;
int erase_table_entries;
uint8_t read_instruction;
uint8_t write_instruction;
ctx->sfdp_supported = true;
/* Parameters from SFDP used:
* 1) Flash size.
* 2) Page size.
* 3) Address bytes - 3, 4, or both
* Set the "current" address bytes to 3 if it's 3 only.
* Otherwise set to 4.
* If both modes are allowed, then should switch to 4 byte mode.
* 4) Supports_144_fast_read. This is required. Ensure that its command
* is 0xEB.
* 5) Ensure that the quad i/o read's mode plus dummy clocks equals 6.
* 6) All the erase sizes and commands. Sort them and save to a table
* inside the flash ctx.
* 7) The busy poll method.
* 8) The quad enable method.
*
* TODO:
* 9) If XIP mode is supported, The XIP entry/exit methods and implement them.
* Should have xip enter/xip exit functions. Reads will need issue the
* proper mode bits. Continue to use the xip read function?
*/
/* Verify that the QSPI flash chip supports quad I/O read mode with 6 dummy cycles */
xassert(sfdp_info.basic_parameter_table.supports_144_fast_read && "Quad I/O Read mode support is required");
xassert(sfdp_info.basic_parameter_table.quad_144_read_cmd == QUAD_IO_READ_CMD_VAL && "Unsupported Quad I/O Read command");
xassert(sfdp_info.basic_parameter_table.quad_144_read_mode_clocks + sfdp_info.basic_parameter_table.quad_144_read_dummy_clocks == 6 && "Unsupported number of dummy clocks");
/* Save the page and flash sizes. Calculate the page count */
ctx->page_size_bytes = sfdp_flash_page_size_bytes(&sfdp_info);
ctx->flash_size_kbytes = sfdp_flash_size_kbytes(&sfdp_info);
if (ctx->flash_size_kbytes) {
ctx->page_count = (ctx->flash_size_kbytes >> sfdp_info.basic_parameter_table.page_size) << 10;
}
xassert(ctx->flash_size_kbytes != 0 && "Unsupported flash size");
/* Save the supported busy poll method */
ret = sfdp_busy_poll_method(&sfdp_info, &read_instruction, &ctx->busy_poll_bit, &ctx->busy_poll_ready_value);
if (ret == 0) {
ctx->busy_poll_cmd = QSPI_IO_BYTE_TO_MOSI(read_instruction);
}
xassert(ret == 0 && "Unsupported busy poll method");
/* Save the supported quad enable method */
ret = sfdp_quad_enable_method(&sfdp_info, &ctx->qe_reg, &ctx->qe_bit, &read_instruction, &write_instruction);
if (ret == 0) {
ctx->sr2_read_cmd = QSPI_IO_BYTE_TO_MOSI(read_instruction);
ctx->sr2_write_cmd = QSPI_IO_BYTE_TO_MOSI(write_instruction);
}
xassert(ret == 0 && "Unsupported QE enable method");
/* Parse and save the erase table */
erase_table_entries = 0;
for (int i = 0; i < 4; i++) {
if (sfdp_info.basic_parameter_table.erase_info[i].size != 0) {
ctx->erase_info[erase_table_entries].size_log2 = sfdp_info.basic_parameter_table.erase_info[i].size;
ctx->erase_info[erase_table_entries].cmd = QSPI_IO_BYTE_TO_MOSI(sfdp_info.basic_parameter_table.erase_info[i].cmd);
} else {
ctx->erase_info[erase_table_entries].size_log2 = 0;
}
erase_table_entries++;
}
xassert(erase_table_entries > 0 && "Erase table found in SFDP is empty!");
/* Determine the number of address bytes. If 4 byte mode is available, switch to it */
switch (sfdp_info.basic_parameter_table.address_bytes) {
case sfdp_3_or_4_byte_address:
ctx->address_bytes = 3; break; /* leave it in 3 byte address mode for now */
/* TODO: enable 4 byte address mode now and fall thru to next case */
// @suppress("No break at end of case")
case sfdp_4_byte_address:
ctx->address_bytes = 4;
xassert(0 && "4 byte address mode entry not yet implemented");
break;
case sfdp_3_byte_address:
default:
ctx->address_bytes = 3;
break;
}
} else {
ctx->sfdp_supported = false;
xassert((ctx->address_bytes == 3 || ctx->address_bytes == 4) && ctx->busy_poll_bit <= 7 && (ctx->busy_poll_ready_value == 0 || ctx->busy_poll_ready_value == 1));
}
}

View File

@@ -0,0 +1,88 @@
// Copyright 2020-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdint.h>
#include "qspi_io.h"
void qspi_io_deinit(const qspi_io_ctx_t *ctx)
{
port_disable(ctx->cs_port);
port_disable(ctx->sio_port);
port_reset(ctx->sclk_port);
port_start_buffered(ctx->sclk_port,32);
clock_disable(ctx->clock_block);
}
void qspi_io_init(const qspi_io_ctx_t *ctx,
qspi_io_source_clock_t source_clock)
{
/* Setup the clock block */
clock_enable(ctx->clock_block);
if (source_clock == qspi_io_source_clock_ref) {
clock_set_source_clk_ref(ctx->clock_block);
} else {
clock_set_source_clk_xcore(ctx->clock_block);
}
/* Setup the chip select port */
port_enable(ctx->cs_port);
port_set_clock(ctx->cs_port, ctx->clock_block);
/*
* CS is used internally as the ready signal for SIO, so it must be
* driven high when it is asserted. However, it must be active low
* on the chip pin itself for the external QSPI device. Therefore the
* CS port is put into invert mode.
* NOTE: invert mode is not modeled correctly in xsim VCD trace.
*/
port_set_invert(ctx->cs_port);
/* Set chip select as the ready source for the clock */
clock_set_ready_src(ctx->clock_block, ctx->cs_port);
/* Setup the SIO port */
port_enable(ctx->sio_port);
/*
* Ensure SIO begins outputing high on all lines.
* This requires an active clock.
*/
port_set_clock(ctx->sio_port, XS1_CLKBLK_REF);
port_out(ctx->sio_port, 0xF);
port_sync(ctx->sio_port);
/* Now set SIO to use the desired clock block. */
port_set_clock(ctx->sio_port, ctx->clock_block);
/*
* Always sample on the falling edge. This allows for
* faster SCLK frequencies, but in general works with
* any frequency that meets timing. This also ensures
* that the internal CS ready signal is captured on time
* at higher frequencies, so is important both when SIO
* is input and output.
*/
port_set_sample_falling_edge(ctx->sio_port);
/*
* SIO is put into strobed slave mode. CS is already
* setup as the ready signal for SCLK. This ensures that
* the data does not begin shifting out out until CS
* is asserted.
*/
port_set_buffered(ctx->sio_port);
port_set_transfer_width(ctx->sio_port, 32);
port_set_ready_strobed(ctx->sio_port);
port_set_slave(ctx->sio_port);
/* Ensure the buffer is clear before the first transaction. */
port_clear_buffer(ctx->sio_port);
/* Setup the SCLK port */
port_enable(ctx->sclk_port);
port_set_clock(ctx->sclk_port, ctx->clock_block);
port_set_out_clock(ctx->sclk_port);
}

View File

@@ -0,0 +1,168 @@
// Copyright 2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#define DEBUG_UNIT SFDP
#include "sfdp.h"
size_t sfdp_flash_size_kbytes(sfdp_info_t *sfdp_info)
{
size_t flash_size_kbytes;
if (sfdp_info->basic_parameter_table.memory_density_is_exponent) {
if (sfdp_info->basic_parameter_table.memory_density >=32 && sfdp_info->basic_parameter_table.memory_density <= 44) {
flash_size_kbytes = 1 << (sfdp_info->basic_parameter_table.memory_density - 13);
} else {
flash_size_kbytes = 0;
}
} else {
flash_size_kbytes = (1 + sfdp_info->basic_parameter_table.memory_density) >> 13;
}
return flash_size_kbytes;
}
size_t sfdp_flash_page_size_bytes(sfdp_info_t *sfdp_info)
{
return 1 << sfdp_info->basic_parameter_table.page_size;
}
int sfdp_busy_poll_method(sfdp_info_t *sfdp_info,
uint8_t *instruction,
uint8_t *bit,
uint8_t *ready_value)
{
uint32_t method_set = sfdp_info->basic_parameter_table.busy_poll_methods;
if (method_set & SFDP_BUSY_POLL_ALT1_BM) {
*instruction = 0x70;
*bit = 7;
*ready_value = 1;
} else if (method_set & SFDP_BUSY_POLL_LEGACY_BM) {
*instruction = 0x05;
*bit = 0;
*ready_value = 0;
} else {
return -1;
}
return 0;
}
int sfdp_quad_enable_method(sfdp_info_t *sfdp_info,
uint8_t *qe_reg,
uint8_t *qe_bit,
uint8_t *sr2_read_instruction,
uint8_t *sr2_write_instruction)
{
switch (sfdp_info->basic_parameter_table.quad_enable_method) {
case 0:
*qe_reg = 0;
*qe_bit = 0;
*sr2_read_instruction = 0;
*sr2_write_instruction = 0;
break;
case 1:
*qe_reg = 2;
*qe_bit = 1;
*sr2_read_instruction = 0;
*sr2_write_instruction = 0;
break;
case 2:
*qe_reg = 1;
*qe_bit = 6;
*sr2_read_instruction = 0;
*sr2_write_instruction = 0;
break;
case 3:
*qe_reg = 2;
*qe_bit = 7;
*sr2_read_instruction = 0x3F;
*sr2_write_instruction = 0x3E;
break;
case 4:
*qe_reg = 2;
*qe_bit = 1;
*sr2_read_instruction = 0;
*sr2_write_instruction = 0;
break;
case 5:
*qe_reg = 2;
*qe_bit = 1;
*sr2_read_instruction = 0x35;
*sr2_write_instruction = 0;
break;
case 6:
*qe_reg = 2;
*qe_bit = 1;
*sr2_read_instruction = 0x35;
*sr2_write_instruction = 0x31;
break;
default:
return -1;
}
return 0;
}
static void sfdp_erase_table_sort(sfdp_info_t *sfdp_info)
{
sfdp_parameter_table_t *t = &sfdp_info->basic_parameter_table;
const int n = 4;
int i, j;
for (i = 0; i < n - 1; i++) {
for (j = 0; j < n - i - 1; j++) {
/* Treat size 0 as a maximum value to keep them at the end of the table */
if (t->erase_info[j].size == 0 || (t->erase_info[j + 1].size != 0 && t->erase_info[j].size > t->erase_info[j + 1].size)) {
uint8_t size = t->erase_info[j].size;
uint8_t cmd = t->erase_info[j].cmd;
t->erase_info[j].size = t->erase_info[j + 1].size;
t->erase_info[j].cmd = t->erase_info[j + 1].cmd;
t->erase_info[j + 1].size = size;
t->erase_info[j + 1].cmd = cmd;
}
}
}
}
bool sfdp_discover(sfdp_info_t *sfdp_info,
void *serial_flash_ctx,
SFDP_READ_CALLBACK_ATTR sfdp_read_cb_t sfdp_read)
{
const uint32_t sfdp_signature = 0x50444653;
const uint8_t req_major_revision = 1;
const uint8_t req_min_minor_revision = 5;
size_t table_read_length;
sfdp_read(serial_flash_ctx, sfdp_info, 0x000000, sizeof(sfdp_header_t) + sizeof(sfdp_parameter_header_t));
table_read_length = sizeof(uint32_t) * sfdp_info->basic_parameter_header.length;
if (sfdp_info->sfdp_header.signature == sfdp_signature &&
sfdp_info->sfdp_header.major_revision == req_major_revision &&
sfdp_info->basic_parameter_header.major_revision == req_major_revision &&
sfdp_info->sfdp_header.minor_revision >= req_min_minor_revision &&
sfdp_info->basic_parameter_header.minor_revision >= req_min_minor_revision &&
table_read_length >= sizeof(sfdp_parameter_table_t)) {
// debug_printf("Supported SFDP flash device found\n");
} else {
if (sfdp_info->sfdp_header.signature != sfdp_signature) {
// debug_printf("No SFDP flash device found\n");
} else {
// debug_printf("Unsupported SFDP flash device found\n");
}
return false;
}
if (sizeof(sfdp_parameter_table_t) < table_read_length) {
table_read_length = sizeof(sfdp_parameter_table_t);
}
sfdp_read(serial_flash_ctx, &sfdp_info->basic_parameter_table, sfdp_info->basic_parameter_header.table_address, table_read_length);
/* Ensure that the erase table is sorted by size, with unused entries at the end. */
sfdp_erase_table_sort(sfdp_info);
return true;
}

View File

@@ -0,0 +1,155 @@
// Copyright 2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef SFDP_H_
#define SFDP_H_
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#define SFDP_READ_INSTRUCTION 0x5A
typedef struct {
uint32_t signature;
uint8_t minor_revision;
uint8_t major_revision;
uint8_t nph;
uint8_t : 8;
} sfdp_header_t;
typedef struct {
uint8_t id_lsb;
uint8_t minor_revision;
uint8_t major_revision;
uint8_t length;
uint32_t table_address : 24;
uint8_t id_msb;
} sfdp_parameter_header_t;
enum {
sfdp_3_byte_address = 0,
sfdp_3_or_4_byte_address = 1,
sfdp_4_byte_address = 2,
};
#define SFDP_BUSY_POLL_LEGACY_BM 0x01
#define SFDP_BUSY_POLL_ALT1_BM 0x02
typedef struct {
/* 1st DWORD */
uint32_t : 2; /* legacy */
uint32_t : 1; /* legacy */
uint32_t : 1; /* legacy */
uint32_t : 1; /* legacy */
uint32_t : 3; /* unused */
uint32_t : 8; /* erase table used instead */
uint32_t : 1; /* supports 1-1-2 fast read */
uint32_t address_bytes : 2;
uint32_t : 1; /* DTR clocking */
uint32_t : 1; /* supports 1-2-2 fast read */
uint32_t supports_144_fast_read: 1;
uint32_t supports_114_fast_read: 1;
uint32_t : 9; /* unused */
/* 2nd DWORD */
uint32_t memory_density : 30;
uint32_t memory_density_is_exponent : 1;
/* 3rd DWORD */
uint8_t quad_144_read_dummy_clocks : 5;
uint8_t quad_144_read_mode_clocks : 3;
uint8_t quad_144_read_cmd;
uint8_t quad_114_read_dummy_clocks : 5;
uint8_t quad_114_read_mode_clocks : 3;
uint8_t quad_114_read_cmd;
/* 4th DWORD */
uint32_t : 32;
/* 5th DWORD */
uint32_t : 32;
/* 6th DWORD */
uint32_t : 32;
/* 7th DWORD */
uint32_t : 32;
/* 8th and 9th DWORD */
struct {
uint8_t size;
uint8_t cmd;
} erase_info[4];
/* 10th DWORD */
uint32_t : 32; /* typical and maximum erase times */
/* TODO could be nice for timeouts */
/* 11th DWORD */
/* typical and max chip erase and program times. page size. */
uint32_t typ_to_max_prog_time_multiplier : 4;
uint32_t page_size : 4;
uint32_t page_prog_time_typ : 6;
uint32_t byte_prog_time_first_typ : 5;
uint32_t byte_prog_time_addl_typ : 5;
uint32_t chip_erase_time_typ : 7;
uint32_t : 1; /* reserved */
/* 12th DWORD */
uint32_t : 32; /* suspend/resume info */
/* 13th DWORD */
uint32_t : 32; /* suspend/resume instructions */
/* 14th DWORD */
uint32_t : 2; /* reserved */
uint32_t busy_poll_methods : 6;
uint32_t : 7; /* powerdown info */
uint32_t : 8; /* powerdown info */
uint32_t : 8; /* powerdown info */
uint32_t : 1; /* powerdown info */
/* 15th DWORD */
uint32_t : 4; /* 4-4-4 mode disable */
uint32_t : 5; /* 4-4-4 mode enable */
uint32_t xip_mode_supported : 1;
uint32_t xip_mode_exit_method : 6;
uint32_t xip_mode_entry_method : 4;
uint32_t quad_enable_method : 3;
uint32_t hold_reset_disable : 1;
uint32_t : 8; /* reserved */
/* 16th DWORD */
uint32_t status_reg_1_info : 7;
uint32_t : 1; /* reserved */
uint32_t soft_reset_sequence : 6;
uint32_t four_byte_address_exit_method : 10;
uint32_t four_byte_address_enter_method : 8;
} sfdp_parameter_table_t;
typedef struct {
sfdp_header_t sfdp_header;
sfdp_parameter_header_t basic_parameter_header;
sfdp_parameter_table_t basic_parameter_table;
} sfdp_info_t;
#define SFDP_READ_CALLBACK_ATTR __attribute__((fptrgroup("sfdp_read_cb_fptr_grp")))
typedef void (*sfdp_read_cb_t)(void *flash_ctx, void *data, uint32_t address, size_t len);
size_t sfdp_flash_size_kbytes(sfdp_info_t *sfdp_info);
size_t sfdp_flash_page_size_bytes(sfdp_info_t *sfdp_info);
int sfdp_busy_poll_method(sfdp_info_t *sfdp_info,
uint8_t *instruction,
uint8_t *bit,
uint8_t *ready_value);
int sfdp_quad_enable_method(sfdp_info_t *sfdp_info,
uint8_t *qe_reg,
uint8_t *qe_bit,
uint8_t *sr2_read_instruction,
uint8_t *sr2_write_instruction);
bool sfdp_discover(sfdp_info_t *sfdp_info,
void *serial_flash_ctx,
sfdp_read_cb_t sfdp_read);
#endif /* SFDP_H_ */

View File

@@ -0,0 +1,127 @@
#ifndef _USER_MAIN_H_
#define _USER_MAIN_H_
#ifdef __XC__
#include "i2c.h"
#include <print.h>
#include <xs1.h>
#include <platform.h>
#include "DSBuild.h"
//extern unsafe client interface i2c_master_if i_i2c_client;
//extern unsafe client interface i2c_master_if i_i2c_client_t0;
extern void interface_saver(client interface i2c_master_if i);
extern void board_setup();
/* I2C interface ports */
extern port p_scl;
extern port p_sda;
extern int dsp_worker_tile(chanend c_dsp_to_ex3d, int worker_id);
//extern int dsp_worker_tile_1(chanend c_dsp_to_ex3d, int worker_id);
extern void button_task(chanend c_hidSendData);
extern void ex3d_task();
extern void hid_button_task(chanend cc_mic_level, chanend c_hid, chanend c_hidSendData);
extern void AudioHwRemote(chanend c);
#if AIZIP_DNR == 1
extern void dnr_dsp_buffer_task(chanend cc_dsp_in, chanend cc_dsp_eof, chanend cc_mic_level);
extern void dnr_dsp_proc_task(chanend cc_dsp_eof);
#else
//#define dnr_dsp_buffer_task(cc_dsp_in, cc_dsp_eof, cc_mic_level)
//#define dnr_dsp_proc_task(cc_dsp_eof)
#endif
//XUA_DFU_EN do i need it?
// tile[0]
// 3 DSP processors
// XUD_Main
// Core USB audio task, buffering, USB etc
// Endpoint 0 Core
// i2c_master
// DFUHandler???
//IAP??
//tile[1]:
// ex3d
// hid + buttons
// 4 DSP processors
// usb_audio_io
//
extern unsafe chanend uc_dsp_to_ex3d[DSP_WORKER_COUNT];
extern unsafe chanend uc_dsp_to_dnr_t1;
extern unsafe chanend uc_key_to_ubm_t0;
extern unsafe chanend uc_audiohw;
#if AIZIP_DNR == 1
#define USER_MAIN_DECLARATIONS \
chan c_dsp_to_ex3d[DSP_WORKER_COUNT]; \
chan cc_dsp_in; chan cc_dsp_eof; chan cc_mic_level; chan c;
#define USER_MAIN_CORES on tile[1]: {\
par\
{\
ex3d_task();\
par(int i=0;i<DSP_WORKER_COUNT;i++) dsp_worker_tile(c_dsp_to_ex3d[i], i);\
unsafe\
{\
uc_audiohw = (chanend) c;\
for(int i=0;i<DSP_WORKER_COUNT;i++)\
uc_dsp_to_ex3d[i] = (chanend) c_dsp_to_ex3d[i];\
uc_dsp_to_dnr_t1 = (chanend) cc_dsp_in;\
}\
hid_button_task(cc_mic_level, c_hid, c_hidSendData);\
}\
}\
on tile[0]: {\
par\
{\
unsafe\
{\
board_setup();\
}\
AudioHwRemote(c);\
dnr_dsp_buffer_task(cc_dsp_in, cc_dsp_eof, cc_mic_level);\
dnr_dsp_proc_task(cc_dsp_eof);\
}\
}
#else
#define USER_MAIN_DECLARATIONS \
chan c_dsp_to_ex3d[DSP_WORKER_COUNT]; chan c; chan cc_mic_level; chan c_hidSendData; chan c_hidRcvData;
#define USER_MAIN_CORES on tile[1]: {\
par\
{\
ex3d_task();\
par(int i=0;i<DSP_WORKER_COUNT;i++) dsp_worker_tile(c_dsp_to_ex3d[i], i);\
unsafe\
{\
uc_audiohw = (chanend) c;\
for(int i=0;i<DSP_WORKER_COUNT;i++)\
uc_dsp_to_ex3d[i] = (chanend) c_dsp_to_ex3d[i];\
}\
hid_button_task(cc_mic_level, c_hidRcvData, c_hidSendData);\
}\
}\
on tile[0]: {\
par\
{\
unsafe\
{\
board_setup();\
}\
AudioHwRemote(c);\
button_task(c_hidSendData);\
}\
}
#endif // AIZIP_DNR
#endif // __XC__
#endif // _USER_MAIN_H_

View File

@@ -0,0 +1,425 @@
// Copyright 2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef _hid_report_descriptor_h_
#define _hid_report_descriptor_h_
#include "xua_hid_report.h"
#if 0
/* Existing static report descriptor kept for reference */
unsigned char hidReportDescriptor[] =
{
0x05, 0x0c, /* Usage Page (Consumer Device) */
0x09, 0x01, /* Usage (Consumer Control) */
0xa1, 0x01, /* Collection (Application) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x09, 0xb0, /* Usage (Play) */
0x09, 0xb5, /* Usage (Scan Next Track) */
0x09, 0xb6, /* Usage (Scan Previous Track) */
0x09, 0xe9, /* Usage (Volume Up) */
0x09, 0xea, /* Usage (Volume Down) */
0x09, 0xe2, /* Usage (Mute) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x06, /* Report Count (6) */
0x81, 0x02, /* Input (Data, Var, Abs) */
0x95, 0x02, /* Report Count (2) */
0x81, 0x01, /* Input (Cnst, Ary, Abs) */
0xc0 /* End collection */
};
#endif
#if 0//AIZIP_DNR == 1
/*
* Define non-configurable items in the HID Report descriptor.
*/
static const USB_HID_Short_Item_t hidCollectionApplication = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_COLLECTION),
.data = { 0x01, 0x00 } };
static const USB_HID_Short_Item_t hidCollectionEnd = {
.header = HID_REPORT_SET_HEADER(0, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_END_COLLECTION),
.data = { 0x00, 0x00 } };
static const USB_HID_Short_Item_t hidInputConstArray = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_INPUT),
.data = { 0x01, 0x00 } };
static const USB_HID_Short_Item_t hidInputDataVar = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_INPUT),
.data = { 0x02, 0x00 } };
static const USB_HID_Short_Item_t hidOutputDataVar = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_OUTPUT),
.data = { 0x02, 0x00 } };
static const USB_HID_Short_Item_t hidOutputConstArray = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_OUTPUT),
.data = { 0x01, 0x00 } };
static const USB_HID_Short_Item_t hidReportId0 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_ID),
.data = { 0x00, 0x00 } };
static const USB_HID_Short_Item_t hidReportId1 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_ID),
.data = { 0x01, 0x00 } };
static const USB_HID_Short_Item_t hidLogicalMaximum0 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM),
.data = { 0x00, 0x00 } };
static const USB_HID_Short_Item_t hidLogicalMaximum1 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM),
.data = { 0x01, 0x00 } };
static const USB_HID_Short_Item_t hidLogicalMinimum0 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MINIMUM),
.data = { 0x00, 0x00 } };
static const USB_HID_Short_Item_t hidLogicalMaximum0F = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM),
.data = { 0x0f, 0x00 } };
static const USB_HID_Short_Item_t hidLogicalMaximumFF = {
.header = HID_REPORT_SET_HEADER(2, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM),
.data = { 0xFF, 0x00 } };
static const USB_HID_Short_Item_t hidReportCount1 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT),
.data = { 0x01, 0x00 } };
static const USB_HID_Short_Item_t hidReportCount2 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT),
.data = { 0x02, 0x00 } };
static const USB_HID_Short_Item_t hidReportCount6 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT),
.data = { 0x06, 0x00 } };
static const USB_HID_Short_Item_t hidReportCount63 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT),
.data = { 0x3F, 0x00 } };
static const USB_HID_Short_Item_t hidReportSize1 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_SIZE),
.data = { 0x01, 0x00 } };
static const USB_HID_Short_Item_t hidReportSize8 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_SIZE),
.data = { 0x08, 0x00 } };
static const USB_HID_Short_Item_t hidUsageConsumerControl = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.data = { 0x01, 0x00 } };
static const USB_HID_Short_Item_t hidUsageLED = { // BUG: potential bug
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.data = { 0x08, 0x00 } };
static const USB_HID_Short_Item_t hidUsageSpatialControl = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.data = { 0x00, 0x00 } };
/*
* Define the HID Report Descriptor Item, Usage Page, Report ID and length for each HID Report
* For internal purposes, a report element with ID of 0 must be included if report IDs are not being used.
*/
static const USB_HID_Report_Element_t hidReportPageConsumer = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_USAGE_PAGE),
.item.data = { USB_HID_USAGE_PAGE_ID_CONSUMER, 0x00 },
.location = HID_REPORT_SET_LOC( 0, 1, 0, 0 )
};
static const USB_HID_Report_Element_t hidReportPageSpatial = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_USAGE_PAGE),
.item.data = { USB_HID_USAGE_PAGE_ID_GENERIC_DESKTOP, 0x00 },
.location = HID_REPORT_SET_LOC( 0, 1, 0, 0 )
};
/*
* Define configurable items in the HID Report descriptor.
*/
static USB_HID_Report_Element_t hidUsageByte0Bit5 = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.item.data = { 0xE2, 0x00 },
.location = HID_REPORT_SET_LOC(0, 0, 0, 5)
}; // Mute
static USB_HID_Report_Element_t hidUsageByte0Bit4 = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.item.data = { 0xEA, 0x00 },
.location = HID_REPORT_SET_LOC(0, 0, 0, 4)
}; // Vol-
static USB_HID_Report_Element_t hidUsageByte0Bit3 = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.item.data = { 0xE9, 0x00 },
.location = HID_REPORT_SET_LOC(0, 0, 0, 3)
}; // Vol+
static USB_HID_Report_Element_t hidUsageByte0Bit2 = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.item.data = { 0xB6, 0x00 },
.location = HID_REPORT_SET_LOC(0, 0, 0, 2)
}; // Scan Prev
static USB_HID_Report_Element_t hidUsageByte0Bit1 = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.item.data = { 0xB5, 0x00 },
.location = HID_REPORT_SET_LOC(0, 0, 0, 1)
}; // Scan Next
static USB_HID_Report_Element_t hidUsageByte0Bit0 = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.item.data = { 0xCD, 0x00 },
.location = HID_REPORT_SET_LOC(0, 0, 0, 0)
}; // Play/Pause
static USB_HID_Report_Element_t hidUsageUnassigned = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.item.data = { 0x00, 0x00 },
.location = HID_REPORT_SET_LOC(1, 64, 0, 0)
}; //
/*
* List the configurable elements in the HID Report descriptor.
*/
static USB_HID_Report_Element_t* const hidConfigurableElements[] = {
&hidUsageByte0Bit0,
&hidUsageByte0Bit1,
&hidUsageByte0Bit2,
&hidUsageByte0Bit3,
&hidUsageByte0Bit4,
&hidUsageByte0Bit5,
&hidUsageUnassigned
};
/*
* List HID Reports, one per Report ID.
* If not using report IDs - still have one with report ID 0
*/
static const USB_HID_Report_Element_t* const hidReports[] = {
&hidReportPageConsumer
};
/*
* List all items in the HID Report descriptor.
*/
static const USB_HID_Short_Item_t* const hidReportDescriptorItems[] = {
#if 1
&(hidReportPageConsumer.item),
&hidUsageConsumerControl,
&hidCollectionApplication,
// &hidReportId1,
&hidLogicalMinimum0,
&hidLogicalMaximum1,
&(hidUsageByte0Bit0.item),
&(hidUsageByte0Bit1.item),
&(hidUsageByte0Bit2.item),
&(hidUsageByte0Bit3.item),
&(hidUsageByte0Bit4.item),
&(hidUsageByte0Bit5.item),
&hidReportSize1,
&hidReportCount6,
&hidInputDataVar,
&hidLogicalMaximum0,
&hidReportCount2,
&hidInputConstArray,
// LED Output Report
&hidUsageLED, //0x05, 0x08, // Usage Page (LED)
&hidLogicalMinimum0, //0x15, 0x00, // Logical Minimum (0)
&hidLogicalMaximum0F, //0x25, 0x0F, // Logical Minimum (16) - 8 bits */
&hidReportSize8, //0x75, 0x08, // Report Size (8 bits)
&hidReportCount1, //0x95, 0x01, // Report Count (1)
&hidOutputDataVar, //0x91, 0x02, // Output (Data, Var, Abs) - 1 byte for LED control
&hidCollectionEnd,
#endif
#if 0
&(hidReportPageConsumer.item),
&hidUsageConsumerControl,
&hidCollectionApplication,
&hidReportId1,
&hidLogicalMinimum0,
&hidLogicalMaximumFF,
&(hidUsageUnassigned.item),
&hidReportSize8,
&hidReportCount63,
&hidOutputDataVar,
&(hidUsageUnassigned.item),
&hidInputDataVar,
&hidCollectionEnd
#endif
};
#else
/*
* Define non-configurable items in the HID Report descriptor.
*/
static const USB_HID_Short_Item_t hidCollectionApplication = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_COLLECTION),
.data = { 0x01, 0x00 } };
static const USB_HID_Short_Item_t hidCollectionEnd = {
.header = HID_REPORT_SET_HEADER(0, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_END_COLLECTION),
.data = { 0x00, 0x00 } };
static const USB_HID_Short_Item_t hidInputConstArray = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_INPUT),
.data = { 0x01, 0x00 } };
static const USB_HID_Short_Item_t hidInputDataVar = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_INPUT),
.data = { 0x02, 0x00 } };
static const USB_HID_Short_Item_t hidOutputDataVar = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_OUTPUT),
.data = { 0x02, 0x00 } };
static const USB_HID_Short_Item_t hidOutputConstArray = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_OUTPUT),
.data = { 0x01, 0x00 } };
static const USB_HID_Short_Item_t hidReportId1 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_ID),
.data = { 0x01, 0x00 } };
static const USB_HID_Short_Item_t hidReportId24 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_ID),
.data = { 0x18, 0x00 } };
static const USB_HID_Short_Item_t hidReportId25 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_ID),
.data = { 0x19, 0x00 } };
static const USB_HID_Short_Item_t hidLogicalMaximum0 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM),
.data = { 0x00, 0x00 } };
static const USB_HID_Short_Item_t hidLogicalMaximum1 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM),
.data = { 0x01, 0x00 } };
static const USB_HID_Short_Item_t hidLogicalMaximum0F = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM),
.data = { 0x0f, 0x00 } };
static const USB_HID_Short_Item_t hidLogicalMaximumFF = {
.header = HID_REPORT_SET_HEADER(2, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM),
.data = { 0xFF, 0x00 } };
static const USB_HID_Short_Item_t hidLogicalMinimum0 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MINIMUM),
.data = { 0x00, 0x00 } };
static const USB_HID_Short_Item_t hidReportCount1 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT),
.data = { 0x01, 0x00 } };
static const USB_HID_Short_Item_t hidReportCount2 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT),
.data = { 0x02, 0x00 } };
static const USB_HID_Short_Item_t hidReportCount6 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT),
.data = { 0x06, 0x00 } };
static const USB_HID_Short_Item_t hidReportCount63 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT),
.data = { 0x3F, 0x00 } };
static const USB_HID_Short_Item_t hidReportSize1 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_SIZE),
.data = { 0x01, 0x00 } };
static const USB_HID_Short_Item_t hidReportSize8 = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_SIZE),
.data = { 0x08, 0x00 } };
static const USB_HID_Short_Item_t hidUsageConsumerControl = {
.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.data = { 0x01, 0x00 } };
/*
* Define the HID Report Descriptor Item, Usage Page, Report ID and length for each HID Report
* For internal purposes, a report element with ID of 0 must be included if report IDs are not being used.
*/
static const USB_HID_Report_Element_t hidReportPageConsumer = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_USAGE_PAGE),
.item.data = { USB_HID_USAGE_PAGE_ID_CONSUMER, 0x00 },
.location = HID_REPORT_SET_LOC( 1, 64, 0, 0 )
};
/*
* Define configurable items in the HID Report descriptor.
*/
static USB_HID_Report_Element_t hidUsageByte0Bit5 = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.item.data = { 0xE2, 0x00 },
.location = HID_REPORT_SET_LOC(0, 0, 0, 5)
}; // Mute
static USB_HID_Report_Element_t hidUsageByte0Bit4 = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.item.data = { 0xEA, 0x00 },
.location = HID_REPORT_SET_LOC(0, 0, 0, 4)
}; // Vol-
static USB_HID_Report_Element_t hidUsageByte0Bit3 = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.item.data = { 0xE9, 0x00 },
.location = HID_REPORT_SET_LOC(0, 0, 0, 3)
}; // Vol+
static USB_HID_Report_Element_t hidUsageByte0Bit2 = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.item.data = { 0xB6, 0x00 },
.location = HID_REPORT_SET_LOC(0, 0, 0, 2)
}; // Scan Prev
static USB_HID_Report_Element_t hidUsageByte0Bit1 = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.item.data = { 0xB5, 0x00 },
.location = HID_REPORT_SET_LOC(0, 0, 0, 1)
}; // Scan Next
static USB_HID_Report_Element_t hidUsageByte0Bit0 = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.item.data = { 0xCD, 0x00 },
.location = HID_REPORT_SET_LOC(0, 0, 0, 0)
}; // Play/Pause
static USB_HID_Report_Element_t hidUsageUnassigned = {
.item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE),
.item.data = { 0x00, 0x00 },
.location = HID_REPORT_SET_LOC(1, 64, 0, 0)
}; //
/*
* List the configurable elements in the HID Report descriptor.
*/
static USB_HID_Report_Element_t* const hidConfigurableElements[] = {
// &hidUsageByte0Bit0,
// &hidUsageByte0Bit1,
// &hidUsageByte0Bit2,
// &hidUsageByte0Bit3,
// &hidUsageByte0Bit4,
// &hidUsageByte0Bit5
&hidUsageUnassigned
};
/*
* List HID Reports, one per Report ID.
* If not using report IDs - still have one with report ID 0
*/
static const USB_HID_Report_Element_t* const hidReports[] = {
&hidReportPageConsumer
};
/*
* List all items in the HID Report descriptor.
*/
static const USB_HID_Short_Item_t* const hidReportDescriptorItems[] = {
&(hidReportPageConsumer.item),
&hidUsageConsumerControl,
&hidCollectionApplication,
&hidReportId1,
&hidLogicalMinimum0,
&hidLogicalMaximumFF,
&(hidUsageUnassigned.item),
&hidReportSize8,
&hidReportCount63,
&hidOutputDataVar,
&(hidUsageUnassigned.item),
&hidInputDataVar,
// &hidLogicalMinimum0,
// &hidLogicalMaximum1,
// &(hidUsageByte0Bit0.item),
// &(hidUsageByte0Bit1.item),
// &(hidUsageByte0Bit2.item),
// &(hidUsageByte0Bit3.item),
// &(hidUsageByte0Bit4.item),
// &(hidUsageByte0Bit5.item),
// &hidReportSize1,
// &hidReportCount6,
// &hidInputDataVar,
// &hidLogicalMaximum0,
// &hidReportCount2,
// &hidInputConstArray,
&hidCollectionEnd
};
#endif // AIZIP_DNR
/*
* Define the number of HID Reports
* Due to XC not supporting designated initializers, this constant has a hard-coded value.
* It must equal ( sizeof hidReports / sizeof ( USB_HID_Report_Element_t* ))
*/
#define HID_REPORT_COUNT ( 1 )
#endif // _hid_report_descriptor_h_