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

1828
sw_usb_audio/CHANGELOG.rst Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.21)
include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake)
project(sw_usb_audio)
add_subdirectory(app_usb_aud_xk_316_mc)
add_subdirectory(app_usb_aud_xk_216_mc)
add_subdirectory(app_usb_aud_xk_evk_xu316)
add_subdirectory(app_usb_aud_xk_evk_xu316_extrai2s)

84
sw_usb_audio/LICENSE.rst Normal file
View File

@@ -0,0 +1,84 @@
*******************************
XMOS PUBLIC LICENCE: Version 1
*******************************
Subject to the conditions and limitations below, permission is hereby granted by XMOS LIMITED (“XMOS”), free of charge, to any person or entity obtaining a copy of the XMOS Software.
**1. Definitions**
**“Applicable Patent Rights”** means: (a) where XMOS is the grantor of the rights, (i) claims of patents that are now or in future owned by or assigned to XMOS and (ii) that cover subject matter contained in the Software, but only to the extent it is necessary to use, reproduce or distribute the Software without infringement; and (b) where you are the grantor of the rights, (i) claims of patents that are now or in future owned by or assigned to you and (ii) that cover the subject matter contained in your Derivatives, taken alone or in combination with the Software.
**“Compiled Code”** means any compiled, binary, machine readable or executable version of the Source Code.
**“Contributor”** means any person or entity that creates or contributes to the creation of Derivatives.
**“Derivatives”** means any addition to, deletion from and/or change to the substance, structure of the Software, any previous Derivatives, the combination of the Derivatives and the Software and/or any respective portions thereof.
**“Source Code”** means the human readable code that is suitable for making modifications but excluding any Compiled Code.
**“Software”** means the software and associated documentation files which XMOS makes available and which contain a notice identifying the software as original XMOS software and referring to the software being subject to the terms of this XMOS Public Licence.
This Licence refers to XMOS Software and does not relate to any XMOS hardware or devices which are protected by intellectual property rights (including patent and trade marks) which may be sold to you under a separate agreement.
**2. Licence**
**Permitted Uses, Conditions and Restrictions.** Subject to the conditions below, XMOS grants you a worldwide, royalty free, non-exclusive licence, to the extent of any Patent Rights to do the following:
2.1 **Unmodified Software.** You may use, copy, display, publish, distribute and make available unmodified copies of the Software:
2.1.1 for personal or academic, non-commercial purposes; or
2.1.2 for commercial purposes provided the Software is at all times used on a device designed, licensed or developed by XMOS and, provided that in each instance (2.1.1 and 2.1.2):
(a) you must retain and reproduce in all copies of the Software the copyright and proprietary notices and disclaimers of XMOS as they appear in the Software, and keep intact all notices and disclaimers in the Software files that refer to this Licence; and
(b) you must include a copy of this Licence with every copy of the Software and documentation you publish, distribute and make available and you may not offer or impose any terms on such Software that alter or restrict this Licence or the intent of such Licence, except as permitted below (Additional Terms).
The licence above does not include any Compiled Code which XMOS may make available under a separate support and licence agreement.
2.2 **Derivatives.** You may create and modify Derivatives and use, copy, display, publish, distribute and make available Derivatives:
2.2.1 for personal or academic, non-commercial purposes; or
2.2.2 for commercial purposes, provided the Derivatives are at all times used on a device designed, licensed or developed by XMOS and, provided that in each instance (2.2.1 and 2.2.2):
(a) you must comply with the terms of clause 2.1 with respect to the Derivatives;
(b) you must copy (to the extent it doesnt already exist) the notice below in each file of the Derivatives, and ensure all the modified files carry prominent notices stating that you have changed the files and the date of any change; and
(c) if you sublicence, distribute or otherwise make the Software and/or the Derivatives available for commercial purposes, you must provide that the Software and Derivatives are at all times used on a device designed, licensed or developed by XMOS.
Without limitation to these terms and clause 3 below, the Source Code and Compiled Code to your Derivatives may at your discretion (but without obligation) be released, copied, displayed, published, distributed and made available; and if you elect to do so, it must be under the terms of this Licence including the terms of the licence at clauses 2.2.1, 2.2.2 and clause 3 below.
2.3 **Distribution of Executable Versions.** If you distribute or make available Derivatives, you must include a prominent notice in the code itself as well as in all related documentation, stating that the Source Code of the Software from which the Derivatives are based is available under the terms of this Licence, with information on how and where to obtain such Source Code.
**3. Your Grant of Rights.** In consideration and as a condition to this Licence, you grant to any person or entity receiving or distributing any Derivatives, a non-exclusive, royalty free, perpetual, irrevocable license under your Applicable Patent Rights and all other intellectual property rights owned or controlled by you, to use, copy, display, publish, distribute and make available your Derivatives of the same scope and extent as XMOSs licence under clause 2.2 above.
**4. Combined Products.** You may create a combined product by combining Software, Derivatives and other code not covered by this Licence as a single application or product. In such instance, you must comply with the requirements of this Licence for any portion of the Software and/or Derivatives.
**5. Additional Terms.** You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations and/or other rights consistent with the term of this Licence (“Additional Terms”) to any legitimate recipients of the Software and/or Derivatives. The terms on which you provide such Additional Terms are on your sole responsibility and you shall indemnify, defend and hold XMOS harmless against any claims asserted against XMOS.
**6. New Versions.** XMOS may publish revised and/or new versions of this Licence from time to time to accommodate changes to the Licence terms, new versions, updates and bug fixes of the Software. Each version will be given a distinguishing version number. Once Software has been published under a particular version of this Licence, you may continue to use it under the terms of that version. You may also choose to use the latest version of the Software under any subsequent version published by XMOS. Only XMOS shall have the right to modify these terms.
**7. IPR and Ownership**
Any rights, including all intellectual property rights and all trademarks not expressly granted herein are reserved in full by the authors or copyright holders. Any requests for additional permissions by XMOS including any rights to use XMOS trademarks, should be made (without obligation) to XMOS at **support@xmos.com**
Nothing herein shall limit any rights that XMOS is otherwise entitled to under the doctrines of patent exhaustion, implied license, or legal estoppel. Neither the name of the authors, the copyright holders or any contributors may be used to endorse or promote any Derivatives from this Software without specific written permission. Any attempt to deal with the Software which does not comply with this Licence shall be void and shall automatically terminate any rights granted under this licence (including any licence of any intellectual property rights granted herein).
Subject to the licences granted under this Licence any Contributor retains all rights, title and interest in and to any Derivatives made by Contributor subject to the underlying rights of XMOS in the Software. XMOS shall retain all rights, title and interest in the Software and any Derivatives made by XMOS (“XMOS Derivatives”). XMOS Derivatives will not automatically be subject to this Licence and XMOS shall be entitled to licence such rights on any terms (without obligation) as it sees fit.
**8. Termination**
8.1 This Licence will automatically terminate immediately, without notice to you, if:
(a) you fail to comply with the terms of this Licence; and/or
(b) you directly or indirectly commence any action for patent or intellectual property right infringement against XMOS, or any parent, group, affiliate or subsidiary of XMOS; provided XMOS did not first commence an action or patent infringement against you in that instance; and/or
(c) the terms of this Licence are held by any court of competent jurisdiction to be unenforceable in whole or in part.
**9. Critical Applications.** Unless XMOS has agreed in writing with you an agreement specifically governing use of the Goods in military, aerospace, automotive or medically related functions (collectively and individually hereinafter referred to as "Special Use"), any permitted use of the Software excludes Special Use. Notwithstanding any agreement between XMOS and you for Special Use, Special Use shall be at your own risk, and you shall fully indemnify XMOS against any damages, losses, costs and claims (direct and indirect) arising out of any Special Use.
**10. NO WARRANTY OR SUPPORT.** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL XMOS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, WARRANTY, CIVIL TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE INCLUDING GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES EVEN IF SUCH PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES AND NOT WITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE. IN SOME JURISDICTIONS PARTIES ARE UNABLE TO LIMIT LIABILTY IN THIS WAY, IF THIS APPLIES TO YOUR JURISDICTION THIS LIABILITY CLAUSE ABOVE MAY NOT APPLY. NOTWITHSTANDING THE ABOVE, IN NO EVENT SHALL XMOSs TOTAL LIABILITY TO YOU FOR ALL DAMAGES, LOSS OR OTHERWISE EXCEED $50.
**11. Governing Law and Jurisdiction.** This Licence constitutes the entire agreement between the parties with respect to the subject matter hereof. The Licence shall be governed by the laws of England and the conflict of laws and UN Convention on Contracts for the International Sale of Goods, shall not apply.

31
sw_usb_audio/Makefile Normal file
View File

@@ -0,0 +1,31 @@
# This variable should contain a space separated list of all
# the directories containing buildable applications (usually
# prefixed with the app_ prefix)
BUILD_SUBDIRS = ./app_usb_aud_xk_evk_xu316_extrai2s \
./app_usb_aud_xk_216_mc \
./app_usb_aud_xk_316_mc \
./app_usb_aud_xk_evk_xu316
# This variable should contain a space separated list of all
# the directories containing buildable plugins (usually
# prefixed with the plugin_ prefix)
PLUGIN_SUBDIRS =
# This variable should contain a space separated list of all
# the directories containing applications with a 'test' make target
TEST_SUBDIRS =
# Provided that the above variables are set you shouldn't need to modify
# the targets below here.
%.all:
cd $* && xmake BUILD_TEST_CONFIGS=1 all
%.clean:
cd $* && xmake BUILD_TEST_CONFIGS=1 clean
all: $(foreach x, $(BUILD_SUBDIRS), $x.all)
plugins: $(foreach x, $(PLUGIN_SUBDIRS), $x.all)
clean: $(foreach x, $(BUILD_SUBDIRS), $x.clean)
clean_plugins: $(foreach x, $(PLUGIN_SUBDIRS), $x.clean)
test: $(foreach x, $(TEST_SUBDIRS), $x.test)

184
sw_usb_audio/README.rst Normal file
View File

@@ -0,0 +1,184 @@
XMOS USB Audio 2.0 Reference Design README
##########################################
:Version: 8.1.0
:Vendor: XMOS
Please note, Alpha and Beta releases may not accurately reflect the final release and documentation may not be complete. These early releases are not suitable for a production context, and are provided for evaluation purposes only. See 'Release Quality & QA'.
Please see CHANGELOG.rst for detailed change listing.
For full software documentation please see the USB Audio User Guide document.
This release is built and tested using version 15.2.1 of the XMOS tool set. Build or functionality issues could be experienced with any other version.
This repository contains applications (or instances) of the XMOS USB Audio Reference Design framework. These applications
typically relate to a specific hardware platform. This repository contains the following:
+--------------------------+--------------------------+------------------------------------------------------------+
| App Name | Relevant Board(s) | Description |
+==========================+==========================+============================================================+
| app_usb_aud_xk_216_mc | xk-audio-216-mc | xcore-200 Multi-channel Audio Board |
+--------------------------+--------------------------+------------------------------------------------------------+
| app_usb_aud_xk_316_mc | xk-audio-316-mc | xcore.ai Multi-channel Audio Board |
+--------------------------+--------------------------+------------------------------------------------------------+
| app_usb_aud_xk_evk_xu316 | xk-evk-xu316 | xcore.ai Explorer Board |
+--------------------------+--------------------------+------------------------------------------------------------+
Please refer to individual README files in these apps for more detailed information.
Each application contains a "core" folder, this folder contains items that are required to use and run the USB Audio application framework.
Mandatory files per application include:
- An XN file describing the board including required port defines. The XN file is referenced in the application makefile.
- xua_conf.h header file containing configuration items such as channel count, strings etc.
Each application also contains an "extensions" folder which includes board specific extensions such as CODEC configuration etc.
Additionally some options are contained in Makefiles for building multiple configurations of an application. For example an application may provide configurations with and without MIDI enabled. See the USB Audio Software User Guide for full details.
Key Features
============
Key features of the various applications in this repository are listed below. Please refer to the application README the specific feature set supported by that application.
- USB Audio Class 1.0/2.0 Compliant
- Fully Asynchronous operation (Synchronous mode as an option)
- Support for the following sample frequencies: 8, 11.025, 12, 16, 32, 44.1, 48, 88.2, 96, 176.4, 192, 352.8, 384kHz
- Input/output channel and individual volume/mute controls supported
- Support for dynamically selectable output audio formats (e.g. resolution)
- Field firmware upgrade compliant to the USB Device Firmware Upgrade (DFU) Class Specification
- S/PDIF output
- S/PDIF input
- ADAT output
- ADAT input
- MIDI input/output (Compliant to USB Class Specification for MIDI devices)
- DSD output ("Native" and DoP mode) at DSD64 and DSD128 rates
- Mixer with flexible routing
- Simple playback controls via Human Interface Device (HID)
Note, not all features may be supported at all sample frequencies, simultaneously or on all devices. Some features also require specific host driver support.
Release Quality & QA
====================
+---------------------------+--------------------------+
| Feature | Quality |
+===========================+==========================+
| Audio Class 1.0 Streaming | Release |
+---------------------------+--------------------------+
| Audio Class 2.0 Streaming | Release |
+---------------------------+--------------------------+
| I2S Master | Release |
+---------------------------+--------------------------+
| I2S Slave | Release |
+---------------------------+--------------------------+
| TDM Master | Release |
+---------------------------+--------------------------+
| TDM Slave | Release |
+---------------------------+--------------------------+
| S/PDIF Receive | Release |
+---------------------------+--------------------------+
| S/PDIF Transmit | Release |
+---------------------------+--------------------------+
| ADAT Receive | Release |
+---------------------------+--------------------------+
| ADAT Transmit | Release |
+---------------------------+--------------------------+
| MIDI I/O | Release |
+---------------------------+--------------------------+
| DSD Playback | Beta |
+---------------------------+--------------------------+
| Mixer | Release |
+---------------------------+--------------------------+
| HID Controls | Beta |
+---------------------------+--------------------------+
Known Issues
============
General known issues with this release are listed below. For board/application specific known issues please see README in relevant app directory
- (xmos/sw_usb_audio#54) When DFU flash access fails the xcore sends NAKs to the USB host forever, rather than a STALL
- (xmos/sw_usb_audio#97) Documentation missing for XK-EVK-316
- (xmos/sw_usb_audio#99) Input via TDM master unreliable due to low-level timing issues (xcore-200 only)
- (xmos/sw_usb_audio#120) Playback glitches experienced at 44.1/48kHz when using ASIO4ALL (v2.15) with built-in windows drivers. USB bus traces prove that these originate from the host driver.
- (#14762) When in DSD mode with S/PDIF output enabled, DSD samples are transmitted over S/PDIF if the DSD and S/PDIF channels are shared, this may or may not be desired
- (#14173) I2S input is completely disabled when DSD output is active - any input stream to the host will contain 0 samples
- (#14780) Operating the design at a sample rate of less than or equal to the SOF rate (i.e. 8kHz at HS, 1kHz at FS) may expose a corner case relating to 0 length packet handling in both the driver and device and should be considered unsupported at this time.
- (#14883) Before DoP mode is detected a small number of DSD samples will be played out as PCM via I2S
- (#14887) Volume control settings currently affect samples in both DSD and PCM modes. This results in invalid DSD output if volume control not set to 0
- Windows XP volume control very sensitive. The Audio 1.0 driver built into Windows XP (usbaudio.sys) does not properly support master volume AND channel volume controls, leading to a very sensitive control. Descriptors can be easily modified to disable master volume control if required (one byte - bmaControls(0) in Feature Unit descriptors)
- 88.2kHz and 176.4kHz sample frequencies are not exposed in Windows control panels. These are known OS restrictions in Windows 7 and earlier.
- Compatibility issues exist with the Microsoft built in UAC1.0 driver (usbaudio.sys) and Intel Smart Sound Technology (SST) can result in audible distortions. This can be worked around by disabling the SST driver.
Host System Requirements
========================
USB Audio Class 1.0
-------------------
- macOS version 10.6 or later
- Windows XP, Vista, 7, 8, 10, or 11 with built-in USB Audio Class 1.0 driver.
USB Audio Class 2.0
-------------------
- macOS version 10.6 or later
- Windows 10 or 11 with built-in USB Audio Class 2.0 driver.
- Windows 10 or 11 using built-in or Thesycon Audio Class 2.0 driver for Windows (Tested against version 5.5)
In Field Firmware Upgrade
=========================
The firmware provides a Device Firmware Upgrade (DFU) interface compliant to the USB DFU Device Class. An example host application is provided for OSX. See README in example application for usage. The Thesycon USB Audio Class 2.0 driver for Windows provides DFU functionality and includes an example application.
Required Software (dependencies)
================================
* lib_sw_pll (www.github.com/xmos/lib_sw_pll)
* lib_xua (www.github.com/xmos/lib_xua)
* lib_adat (www.github.com/xmos/lib_adat)
* lib_locks (www.github.com/xmos/lib_locks)
* lib_logging (www.github.com/xmos/lib_logging)
* lib_mic_array (www.github.com/xmos/lib_mic_array)
* lib_xassert (www.github.com/xmos/lib_xassert)
* lib_dsp (www.github.com/xmos/lib_dsp)
* lib_spdif (www.github.com/xmos/lib_spdif)
* lib_xud (www.github.com/xmos/lib_xud)
* lib_i2c (www.github.com/xmos/lib_i2c)
* lib_i2s (www.github.com/xmos/lib_i2s)
Documentation
=============
You can find the documentation for this software in the /doc directory of the package.
Support
=======
This package is supported by XMOS Ltd. Issues can be raised against the software at: http://www.xmos.com/support

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

12
sw_usb_audio/deps.cmake Normal file
View File

@@ -0,0 +1,12 @@
set(APP_DEPENDENT_MODULES "lib_xua(5.0.0)"
"lib_mic_array(5.5.0)"
"lib_xud(2.4.0)"
"lib_adat(2.0.1)"
"lib_locks(2.3.1)"
"lib_i2c(6.2.0)"
"lib_i2s(5.1.0)"
"lib_audio_dsp(1.4.0)"
"lib_agc"
"lib_dnr")
include_directories("${CMAKE_CURRENT_LIST_DIR}/../lib_ex3d/lib_ex3d/api")

View File

@@ -0,0 +1,93 @@
#ifndef CS2100_I2C_DEVICE_ADDR
#define CS2100_I2C_DEVICE_ADDR (0x9c>>1)
#endif
#define CS2100_DEVICE_CONTROL (0x02)
#define CS2100_DEVICE_CONFIG_1 (0x03)
#define CS2100_GLOBAL_CONFIG (0x05)
#define CS2100_RATIO_1 (0x06)
#define CS2100_RATIO_2 (0x07)
#define CS2100_RATIO_3 (0x08)
#define CS2100_RATIO_4 (0x09)
#define CS2100_FUNC_CONFIG_1 (0x16)
#define CS2100_FUNC_CONFIG_2 (0x17)
/* TODO this is a key frequency and should be moved into lib_xua */
#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC)
#define PLL_SYNC_FREQ (500)
#else
#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN)
/* Choose a frequency the xcore can easily generate internally */
#define PLL_SYNC_FREQ (300)
#else
#define PLL_SYNC_FREQ (1000000)
#endif
#endif
#ifndef CS2100_REGREAD
#define CS2100_REGREAD(reg, data) {data[0] = i2c.read_reg(CS2100_I2C_DEVICE_ADDR, reg, result);}
#endif
#ifndef CS2100_REGREAD_ASSERT
#define CS2100_REGREAD_ASSERT(reg, data, expected) {data[0] = i2c.read_reg(CS2100_I2C_DEVICE_ADDR, reg, result); assert(data[0] == expected);}
#endif
#ifndef CS2100_REGWRITE
#define CS2100_REGWRITE(reg, val) {result = i2c.write_reg(CS2100_I2C_DEVICE_ADDR, reg, val);}
#endif
#ifndef UNSAFE
#define UNSAFE
#endif
/* Init of CS2100 */
void PllInit(UNSAFE client interface i2c_master_if i2c)
{
UNSAFE
{
unsigned char data[1] = {0};
i2c_regop_res_t result;
/* Enable init */
CS2100_REGWRITE(CS2100_DEVICE_CONFIG_1, 0x07);
CS2100_REGWRITE(CS2100_GLOBAL_CONFIG, 0x01);
CS2100_REGWRITE(CS2100_FUNC_CONFIG_1, 0x08);
CS2100_REGWRITE(CS2100_FUNC_CONFIG_2, 0x00); //0x10 for always gen clock even when unlocked
/* Read back and check */
CS2100_REGREAD_ASSERT(CS2100_DEVICE_CONFIG_1, data, 0x07);
CS2100_REGREAD_ASSERT(CS2100_GLOBAL_CONFIG, data, 0x01);
CS2100_REGREAD_ASSERT(CS2100_FUNC_CONFIG_1, data, 0x08);
CS2100_REGREAD_ASSERT(CS2100_FUNC_CONFIG_2, data, 0x00);
//i2c.shutdown();
}
}
/* Setup PLL multiplier */
void PllMult(unsigned output, unsigned ref, UNSAFE client interface i2c_master_if i2c)
{
UNSAFE
{
unsigned char data[1] = {0};
i2c_regop_res_t result;
/* PLL expects 12:20 format, convert output and ref to 12:20 */
/* Shift up the dividend by 12 to retain format... */
unsigned mult = (unsigned) ((((unsigned long long)output) << 32) / (((unsigned long long)ref) << 20));
CS2100_REGWRITE(CS2100_RATIO_1, (mult >> 24) & 0xFF);
CS2100_REGWRITE(CS2100_RATIO_2, (mult >> 16) & 0xFF);
CS2100_REGWRITE(CS2100_RATIO_3, (mult >> 8) & 0xFF);
CS2100_REGWRITE(CS2100_RATIO_4, (mult & 0xFF));
/* Read back and check */
CS2100_REGREAD_ASSERT(CS2100_RATIO_1, data, ((mult >> 24) & 0xFF));
CS2100_REGREAD_ASSERT(CS2100_RATIO_2, data, ((mult >> 16) & 0xFF));
CS2100_REGREAD_ASSERT(CS2100_RATIO_3, data, ((mult >> 8) & 0xFF));
CS2100_REGREAD_ASSERT(CS2100_RATIO_4, data, (mult & 0xFF));
}
}

View File

@@ -0,0 +1,15 @@
// Shared version number for all reference designs
#if defined (PHATEN_GSV2)
#ifndef BCD_DEVICE_J
#define BCD_DEVICE_J (1)
#endif
#ifndef BCD_DEVICE_M
#define BCD_DEVICE_M (1)
#endif
#ifndef BCD_DEVICE_N
#define BCD_DEVICE_N (0)
#endif
#endif