init
This commit is contained in:
163
sw_usb_audio/app_usb_aud_phaten_golden/CMakeLists.txt
Normal file
163
sw_usb_audio/app_usb_aud_phaten_golden/CMakeLists.txt
Normal 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()
|
||||
91
sw_usb_audio/app_usb_aud_phaten_golden/src/core/PHATEN_GS.xn
Normal file
91
sw_usb_audio/app_usb_aud_phaten_golden/src/core/PHATEN_GS.xn
Normal 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>
|
||||
177
sw_usb_audio/app_usb_aud_phaten_golden/src/core/xua_conf.h
Normal file
177
sw_usb_audio/app_usb_aud_phaten_golden/src/core/xua_conf.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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_
|
||||
@@ -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_ */
|
||||
586
sw_usb_audio/app_usb_aud_phaten_golden/src/extensions/audiohw.xc
Normal file
586
sw_usb_audio/app_usb_aud_phaten_golden/src/extensions/audiohw.xc
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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_ */
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
1095
sw_usb_audio/app_usb_aud_phaten_golden/src/extensions/dsp.c
Normal file
1095
sw_usb_audio/app_usb_aud_phaten_golden/src/extensions/dsp.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
296
sw_usb_audio/app_usb_aud_phaten_golden/src/extensions/ex3d_0.h
Normal file
296
sw_usb_audio/app_usb_aud_phaten_golden/src/extensions/ex3d_0.h
Normal 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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
915
sw_usb_audio/app_usb_aud_phaten_golden/src/extensions/qspi_io.h
Normal file
915
sw_usb_audio/app_usb_aud_phaten_golden/src/extensions/qspi_io.h
Normal 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
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
155
sw_usb_audio/app_usb_aud_phaten_golden/src/extensions/sfdp.h
Normal file
155
sw_usb_audio/app_usb_aud_phaten_golden/src/extensions/sfdp.h
Normal 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_ */
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
Reference in New Issue
Block a user