mirror of https://github.com/ARMmbed/mbed-os.git
Allow Mbed CE to be built as an Arduino core (#206)
* Various changes for Arduino core support * Apply Arduino Mbed TLS patches * Apply linker script patch, add missing CXX standard * Fix SDBlockDevice compile error, fix "no rule to make mbed-target-config.h" * Fix missing source file for RPi Pico * Fix missing licensespull/15494/head
parent
8e38d02ced
commit
699437ecf9
|
|
@ -228,8 +228,10 @@ endif()
|
||||||
|
|
||||||
# Generate target config header and include it in all files
|
# Generate target config header and include it in all files
|
||||||
if(NOT MBED_IS_NATIVE_BUILD)
|
if(NOT MBED_IS_NATIVE_BUILD)
|
||||||
mbed_write_target_config_header(${CMAKE_CURRENT_BINARY_DIR}/mbed-target-config.h MBED_TARGET_DEFINITIONS MBED_CONFIG_DEFINITIONS)
|
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/generated-headers)
|
||||||
target_compile_options(mbed-core-flags INTERFACE -include ${CMAKE_CURRENT_BINARY_DIR}/mbed-target-config.h)
|
mbed_write_target_config_header(${CMAKE_CURRENT_BINARY_DIR}/generated-headers/mbed-target-config.h MBED_TARGET_DEFINITIONS MBED_CONFIG_DEFINITIONS)
|
||||||
|
target_compile_options(mbed-core-flags INTERFACE -include ${CMAKE_CURRENT_BINARY_DIR}/generated-headers/mbed-target-config.h)
|
||||||
|
target_include_directories(mbed-core-flags INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/generated-headers)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Include mbed.h and config from generate folder
|
# Include mbed.h and config from generate folder
|
||||||
|
|
@ -283,7 +285,7 @@ if(NOT MBED_IS_NATIVE_BUILD)
|
||||||
mbed_create_distro(mbed-os ${MBED_TARGET_CMAKE_NAME} mbed-core-flags mbed-core-sources mbed-rtos-flags mbed-rtos-sources)
|
mbed_create_distro(mbed-os ${MBED_TARGET_CMAKE_NAME} mbed-core-flags mbed-core-sources mbed-rtos-flags mbed-rtos-sources)
|
||||||
|
|
||||||
# Set up the linker script and hook it up to the top-level OS targets
|
# Set up the linker script and hook it up to the top-level OS targets
|
||||||
mbed_setup_linker_script(mbed-baremetal mbed-os ${CMAKE_CURRENT_BINARY_DIR}/mbed-target-config.h)
|
mbed_setup_linker_script(mbed-baremetal mbed-os ${CMAKE_CURRENT_BINARY_DIR}/generated-headers/mbed-target-config.h)
|
||||||
|
|
||||||
# Make sure that things linking mbed-core-flags can also get the target-specific include dirs and flags.
|
# Make sure that things linking mbed-core-flags can also get the target-specific include dirs and flags.
|
||||||
mbed_extract_flags(${MBED_TARGET_CMAKE_NAME}-flags ${MBED_TARGET_CMAKE_NAME})
|
mbed_extract_flags(${MBED_TARGET_CMAKE_NAME}-flags ${MBED_TARGET_CMAKE_NAME})
|
||||||
|
|
|
||||||
|
|
@ -1204,7 +1204,7 @@
|
||||||
*
|
*
|
||||||
* Enable functions that use the filesystem.
|
* Enable functions that use the filesystem.
|
||||||
*/
|
*/
|
||||||
//#define MBEDTLS_FS_IO
|
#define MBEDTLS_FS_IO
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \def MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES
|
* \def MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES
|
||||||
|
|
@ -3227,7 +3227,7 @@
|
||||||
* on it, and considering stronger message digests instead.
|
* on it, and considering stronger message digests instead.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
//#define MBEDTLS_SHA1_C
|
#define MBEDTLS_SHA1_C
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \def MBEDTLS_SHA256_C
|
* \def MBEDTLS_SHA256_C
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"name": "mbedtls"
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"name": "mbedtls",
|
||||||
|
"config": {
|
||||||
|
"entropy-nv-seed": {
|
||||||
|
"macro_name": "MBEDTLS_ENTROPY_NV_SEED",
|
||||||
|
"help": "Set to 1 to enable Mbed TLS's Non-Volatile Storage entropy source. This source allows usage of Mbed TLS on devices which do not have a cryptographic RNG.",
|
||||||
|
"value": null,
|
||||||
|
// Note: see here for details on how to implement the seed I/O: https://os.mbed.com/docs/mbed-os/v6.16/porting/entropy-sources.html
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,6 +31,10 @@
|
||||||
* \ingroup public-crypto
|
* \ingroup public-crypto
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if CONFIG_MBEDTLS_ENTROPY_NV_SEED
|
||||||
|
#define MBEDTLS_ENTROPY_NV_SEED
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(FEATURE_EXPERIMENTAL_API) && defined(FEATURE_PSA)
|
#if defined(FEATURE_EXPERIMENTAL_API) && defined(FEATURE_PSA)
|
||||||
|
|
||||||
#if defined(MBEDTLS_ENTROPY_NV_SEED)
|
#if defined(MBEDTLS_ENTROPY_NV_SEED)
|
||||||
|
|
@ -72,9 +76,6 @@
|
||||||
#include "mbedtls_device.h"
|
#include "mbedtls_device.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Include SHA1 certificate support. Used for a lot of root CAs.
|
|
||||||
#define MBEDTLS_SHA1_C 1
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MBEDTLS_ERR_PLATFORM_HW_FAILED is deprecated and should not be used.
|
* MBEDTLS_ERR_PLATFORM_HW_FAILED is deprecated and should not be used.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,11 @@
|
||||||
#if !defined(_WIN32) || defined(EFIX64) || defined(EFI32)
|
#if !defined(_WIN32) || defined(EFIX64) || defined(EFI32)
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#if defined(__MBED__)
|
||||||
|
#include <platform/mbed_retarget.h>
|
||||||
|
#else
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#endif /* __MBED__ */
|
||||||
#endif /* !_WIN32 || EFIX64 || EFI32 */
|
#endif /* !_WIN32 || EFIX64 || EFI32 */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,20 @@
|
||||||
#
|
#
|
||||||
# This file is part of mbed TLS (https://tls.mbed.org)
|
# This file is part of mbed TLS (https://tls.mbed.org)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2015-2016, ARM Limited, All Rights Reserved
|
# Copyright (c) 2023, Arm Limited, All Rights Reserved
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the License); you may
|
||||||
|
# not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# * http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
# Purpose
|
# Purpose
|
||||||
#
|
#
|
||||||
|
|
@ -53,7 +66,6 @@ conf unset MBEDTLS_TIMING_C
|
||||||
|
|
||||||
# not supported on all targets with mbed OS, nor used by mbed Client
|
# not supported on all targets with mbed OS, nor used by mbed Client
|
||||||
conf unset MBEDTLS_HAVE_TIME_DATE
|
conf unset MBEDTLS_HAVE_TIME_DATE
|
||||||
conf unset MBEDTLS_FS_IO
|
|
||||||
conf unset MBEDTLS_PSA_ITS_FILE_C
|
conf unset MBEDTLS_PSA_ITS_FILE_C
|
||||||
conf unset MBEDTLS_PSA_CRYPTO_STORAGE_C
|
conf unset MBEDTLS_PSA_CRYPTO_STORAGE_C
|
||||||
conf set MBEDTLS_NO_PLATFORM_ENTROPY
|
conf set MBEDTLS_NO_PLATFORM_ENTROPY
|
||||||
|
|
@ -89,7 +101,6 @@ conf unset MBEDTLS_PEM_WRITE_C
|
||||||
conf unset MBEDTLS_PKCS5_C
|
conf unset MBEDTLS_PKCS5_C
|
||||||
conf unset MBEDTLS_PKCS12_C
|
conf unset MBEDTLS_PKCS12_C
|
||||||
conf unset MBEDTLS_RIPEMD160_C
|
conf unset MBEDTLS_RIPEMD160_C
|
||||||
conf unset MBEDTLS_SHA1_C
|
|
||||||
conf unset MBEDTLS_XTEA_C
|
conf unset MBEDTLS_XTEA_C
|
||||||
|
|
||||||
conf set MBEDTLS_CMAC_C
|
conf set MBEDTLS_CMAC_C
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,20 @@
|
||||||
#
|
#
|
||||||
# This file is part of mbed TLS (https://tls.mbed.org)
|
# This file is part of mbed TLS (https://tls.mbed.org)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018, ARM Limited, All Rights Reserved
|
# Copyright (c) 2018, Arm Limited, All Rights Reserved
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the License); you may
|
||||||
|
# not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# * http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
# Purpose
|
# Purpose
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -303,6 +303,12 @@ int mbedtls_x509_crt_parse(mbedtls_x509_crt *a, const unsigned char *b, size_t c
|
||||||
return mbedtls_stub.expected_int;
|
return mbedtls_stub.expected_int;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mbedtls_x509_crt_parse_path(mbedtls_x509_crt *a, const char *b)
|
||||||
|
{
|
||||||
|
// means 5 valid certificates found
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
int mbedtls_x509_crt_info(char *buf, size_t size, const char *prefix,
|
int mbedtls_x509_crt_info(char *buf, size_t size, const char *prefix,
|
||||||
const mbedtls_x509_crt *crt)
|
const mbedtls_x509_crt *crt)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,46 @@ public:
|
||||||
*/
|
*/
|
||||||
nsapi_error_t set_root_ca_cert(const char *root_ca_pem);
|
nsapi_error_t set_root_ca_cert(const char *root_ca_pem);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the Root CA certificate to a collection of files on the filesystem.
|
||||||
|
*
|
||||||
|
* All files in the supplied directory will be scanned. Note that to set up a filesystem,
|
||||||
|
* you must mount one or more block devices before calling this function.
|
||||||
|
*
|
||||||
|
* @note Must be called before calling connect()
|
||||||
|
*
|
||||||
|
* @param root_ca_path Path containing Root CA Certificate files in any Mbed TLS-supported format.
|
||||||
|
* This can point to a directory on any mounted filesystem.
|
||||||
|
* @retval NSAPI_ERROR_OK on success.
|
||||||
|
* @retval NSAPI_ERROR_NO_MEMORY in case there is not enough memory to allocate certificate.
|
||||||
|
* @retval NSAPI_ERROR_PARAMETER in case the provided root_ca parameter failed parsing.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
nsapi_error_t set_root_ca_cert_path(const char *root_ca_path);
|
||||||
|
|
||||||
|
/** Appends the certificate to an existing CA chain.
|
||||||
|
*
|
||||||
|
* @note Must be called before calling connect()
|
||||||
|
*
|
||||||
|
* @param root_ca Root CA Certificate in any Mbed TLS-supported format.
|
||||||
|
* @param len Length of certificate (including terminating 0 for PEM).
|
||||||
|
* @retval NSAPI_ERROR_OK on success.
|
||||||
|
* @retval NSAPI_ERROR_NO_MEMORY in case there is not enough memory to allocate certificate.
|
||||||
|
* @retval NSAPI_ERROR_PARAMETER in case the provided root_ca parameter failed parsing.
|
||||||
|
*/
|
||||||
|
nsapi_error_t append_root_ca_cert(const void *root_ca, size_t len);
|
||||||
|
|
||||||
|
/** Appends the certificate to an existing CA chain.
|
||||||
|
*
|
||||||
|
* @note Must be called before calling connect()
|
||||||
|
*
|
||||||
|
* @param root_ca_pem Root CA Certificate in PEM format.
|
||||||
|
* @retval NSAPI_ERROR_OK on success.
|
||||||
|
* @retval NSAPI_ERROR_NO_MEMORY in case there is not enough memory to allocate certificate.
|
||||||
|
* @retval NSAPI_ERROR_PARAMETER in case the provided root_ca parameter failed parsing.
|
||||||
|
*/
|
||||||
|
nsapi_error_t append_root_ca_cert(const char *root_ca_pem);
|
||||||
|
|
||||||
/** Sets client certificate, and client private key.
|
/** Sets client certificate, and client private key.
|
||||||
*
|
*
|
||||||
* @param client_cert Client certification in PEM or DER format.
|
* @param client_cert Client certification in PEM or DER format.
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,62 @@ nsapi_error_t TLSSocketWrapper::set_root_ca_cert(const char *root_ca_pem)
|
||||||
return set_root_ca_cert(root_ca_pem, strlen(root_ca_pem) + 1);
|
return set_root_ca_cert(root_ca_pem, strlen(root_ca_pem) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsapi_error_t TLSSocketWrapper::set_root_ca_cert_path(const char *root_ca_path)
|
||||||
|
{
|
||||||
|
#if !defined(MBEDTLS_X509_CRT_PARSE_C) || !defined(MBEDTLS_FS_IO)
|
||||||
|
return NSAPI_ERROR_UNSUPPORTED;
|
||||||
|
#else
|
||||||
|
mbedtls_x509_crt *crt;
|
||||||
|
|
||||||
|
crt = new (std::nothrow) mbedtls_x509_crt;
|
||||||
|
if (!crt) {
|
||||||
|
return NSAPI_ERROR_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
mbedtls_x509_crt_init(crt);
|
||||||
|
|
||||||
|
/* Parse CA certification */
|
||||||
|
int ret = mbedtls_x509_crt_parse_path(crt, root_ca_path);
|
||||||
|
if (ret < 0) {
|
||||||
|
print_mbedtls_error("mbedtls_x509_crt_parse", ret);
|
||||||
|
mbedtls_x509_crt_free(crt);
|
||||||
|
delete crt;
|
||||||
|
return NSAPI_ERROR_PARAMETER;
|
||||||
|
}
|
||||||
|
set_ca_chain(crt);
|
||||||
|
_cacert_allocated = true;
|
||||||
|
return NSAPI_ERROR_OK;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
nsapi_error_t TLSSocketWrapper::append_root_ca_cert(const void *root_ca, size_t len)
|
||||||
|
{
|
||||||
|
#if !defined(MBEDTLS_X509_CRT_PARSE_C)
|
||||||
|
return NSAPI_ERROR_UNSUPPORTED;
|
||||||
|
#else
|
||||||
|
mbedtls_x509_crt *crt;
|
||||||
|
|
||||||
|
crt = get_ca_chain();
|
||||||
|
if (!crt) {
|
||||||
|
return NSAPI_ERROR_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse CA certification */
|
||||||
|
int ret;
|
||||||
|
if ((ret = mbedtls_x509_crt_parse(crt, static_cast<const unsigned char *>(root_ca),
|
||||||
|
len)) != 0) {
|
||||||
|
print_mbedtls_error("mbedtls_x509_crt_parse", ret);
|
||||||
|
return NSAPI_ERROR_PARAMETER;
|
||||||
|
}
|
||||||
|
return NSAPI_ERROR_OK;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
nsapi_error_t TLSSocketWrapper::append_root_ca_cert(const char *root_ca_pem)
|
||||||
|
{
|
||||||
|
return append_root_ca_cert(root_ca_pem, strlen(root_ca_pem) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
nsapi_error_t TLSSocketWrapper::set_client_cert_key(const char *client_cert_pem, const char *client_private_key_pem)
|
nsapi_error_t TLSSocketWrapper::set_client_cert_key(const char *client_cert_pem, const char *client_private_key_pem)
|
||||||
{
|
{
|
||||||
return set_client_cert_key(client_cert_pem, strlen(client_cert_pem) + 1, client_private_key_pem, strlen(client_private_key_pem) + 1);
|
return set_client_cert_key(client_cert_pem, strlen(client_cert_pem) + 1, client_private_key_pem, strlen(client_private_key_pem) + 1);
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ list(
|
||||||
tlssocket_echotest.cpp
|
tlssocket_echotest.cpp
|
||||||
tlssocket_echotest_burst.cpp
|
tlssocket_echotest_burst.cpp
|
||||||
tlssocket_connect_invalid.cpp
|
tlssocket_connect_invalid.cpp
|
||||||
|
tlssocket_cert_in_filesystem.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(MBED_GREENTEA_TEST_BAREMETAL)
|
if(MBED_GREENTEA_TEST_BAREMETAL)
|
||||||
|
|
@ -33,6 +34,8 @@ mbed_greentea_add_test(
|
||||||
${TEST_SOURCE_LIST}
|
${TEST_SOURCE_LIST}
|
||||||
TEST_REQUIRED_LIBS
|
TEST_REQUIRED_LIBS
|
||||||
mbed-netsocket
|
mbed-netsocket
|
||||||
|
mbed-storage-blockdevice
|
||||||
|
mbed-storage-littlefs
|
||||||
TEST_SKIPPED
|
TEST_SKIPPED
|
||||||
${TEST_SKIPPED}
|
${TEST_SKIPPED}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -218,7 +218,6 @@ static void test_failure_handler(const failure_t failure)
|
||||||
|
|
||||||
|
|
||||||
Case cases[] = {
|
Case cases[] = {
|
||||||
// Disable tests temporarily till echo server is back on
|
|
||||||
Case("TLSSOCKET_ECHOTEST", TLSSOCKET_ECHOTEST),
|
Case("TLSSOCKET_ECHOTEST", TLSSOCKET_ECHOTEST),
|
||||||
Case("TLSSOCKET_ECHOTEST_NONBLOCK", TLSSOCKET_ECHOTEST_NONBLOCK),
|
Case("TLSSOCKET_ECHOTEST_NONBLOCK", TLSSOCKET_ECHOTEST_NONBLOCK),
|
||||||
Case("TLSSOCKET_CONNECT_INVALID", TLSSOCKET_CONNECT_INVALID),
|
Case("TLSSOCKET_CONNECT_INVALID", TLSSOCKET_CONNECT_INVALID),
|
||||||
|
|
@ -235,6 +234,9 @@ Case cases[] = {
|
||||||
Case("TLSSOCKET_SEND_REPEAT", TLSSOCKET_SEND_REPEAT),
|
Case("TLSSOCKET_SEND_REPEAT", TLSSOCKET_SEND_REPEAT),
|
||||||
Case("TLSSOCKET_SEND_TIMEOUT", TLSSOCKET_SEND_TIMEOUT),
|
Case("TLSSOCKET_SEND_TIMEOUT", TLSSOCKET_SEND_TIMEOUT),
|
||||||
Case("TLSSOCKET_NO_CERT", TLSSOCKET_NO_CERT),
|
Case("TLSSOCKET_NO_CERT", TLSSOCKET_NO_CERT),
|
||||||
|
#if defined(MBEDTLS_SSL_CLI_C) && defined(MBEDTLS_FS_IO)
|
||||||
|
Case("TLSSOCKET_CERT_IN_FILESYSTEM", TLSSOCKET_CERT_IN_FILESYSTEM),
|
||||||
|
#endif
|
||||||
// Temporarily removing this test, as TLS library consumes too much memory
|
// Temporarily removing this test, as TLS library consumes too much memory
|
||||||
// and we see frequent memory allocation failures on architectures with less
|
// and we see frequent memory allocation failures on architectures with less
|
||||||
// RAM such as DISCO_L475VG_IOT1A and NUCLEO_F207ZG (both have 128 kB RAM)
|
// RAM such as DISCO_L475VG_IOT1A and NUCLEO_F207ZG (both have 128 kB RAM)
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ void TLSSOCKET_SEND_REPEAT();
|
||||||
void TLSSOCKET_NO_CERT();
|
void TLSSOCKET_NO_CERT();
|
||||||
void TLSSOCKET_SIMULTANEOUS();
|
void TLSSOCKET_SIMULTANEOUS();
|
||||||
void TLSSOCKET_SEND_TIMEOUT();
|
void TLSSOCKET_SEND_TIMEOUT();
|
||||||
|
void TLSSOCKET_CERT_IN_FILESYSTEM();
|
||||||
|
|
||||||
#endif // defined(MBEDTLS_SSL_CLI_C) || defined(DOXYGEN_ONLY)
|
#endif // defined(MBEDTLS_SSL_CLI_C) || defined(DOXYGEN_ONLY)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Arduino SA, All Rights Reserved
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mbed.h"
|
||||||
|
#include "TLSSocket.h"
|
||||||
|
#include "greentea-client/test_env.h"
|
||||||
|
#include "unity/unity.h"
|
||||||
|
#include "utest.h"
|
||||||
|
#include "tls_tests.h"
|
||||||
|
#include "HeapBlockDevice.h"
|
||||||
|
#include "LittleFileSystem.h"
|
||||||
|
|
||||||
|
using namespace utest::v1;
|
||||||
|
|
||||||
|
void TLSSOCKET_CERT_IN_FILESYSTEM()
|
||||||
|
{
|
||||||
|
SKIP_IF_TCP_UNSUPPORTED();
|
||||||
|
|
||||||
|
HeapBlockDevice bd(1024 * 10);
|
||||||
|
LittleFileSystem fs("fs");
|
||||||
|
TEST_ASSERT_EQUAL(0, fs.format(&bd));
|
||||||
|
TEST_ASSERT_EQUAL(0, fs.mount(&bd));
|
||||||
|
|
||||||
|
FILE *fp = fopen("/fs/certs.pem", "wb");
|
||||||
|
int ret = fwrite(tls_global::cert, strlen(tls_global::cert), 1, fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
TLSSocket sock;
|
||||||
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance()));
|
||||||
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.set_root_ca_cert_path("/fs"));
|
||||||
|
|
||||||
|
SocketAddress a;
|
||||||
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, NetworkInterface::get_default_instance()->gethostbyname(ECHO_SERVER_ADDR, &a));
|
||||||
|
a.set_port(ECHO_SERVER_PORT_TLS);
|
||||||
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.connect(a));
|
||||||
|
}
|
||||||
|
|
@ -399,6 +399,12 @@ TEST_F(TestTLSSocketWrapper, set_root_ca_cert_invalid)
|
||||||
EXPECT_EQ(wrapper->set_root_ca_cert(cert, strlen(cert)), NSAPI_ERROR_PARAMETER);
|
EXPECT_EQ(wrapper->set_root_ca_cert(cert, strlen(cert)), NSAPI_ERROR_PARAMETER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TestTLSSocketWrapper, set_root_ca_cert_path)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(transport->open(&stack), NSAPI_ERROR_OK);
|
||||||
|
EXPECT_EQ(wrapper->set_root_ca_cert_path("/"), NSAPI_ERROR_OK);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(TestTLSSocketWrapper, set_client_cert_key)
|
TEST_F(TestTLSSocketWrapper, set_client_cert_key)
|
||||||
{
|
{
|
||||||
EXPECT_EQ(wrapper->get_own_cert(), static_cast<mbedtls_x509_crt *>(NULL));
|
EXPECT_EQ(wrapper->get_own_cert(), static_cast<mbedtls_x509_crt *>(NULL));
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,6 @@
|
||||||
#define UNITTESTS_FEATURES_NETSOCKET_TLSSOCKET_TLS_TEST_CONFIG_H_
|
#define UNITTESTS_FEATURES_NETSOCKET_TLSSOCKET_TLS_TEST_CONFIG_H_
|
||||||
|
|
||||||
#define MBEDTLS_SSL_CLI_C
|
#define MBEDTLS_SSL_CLI_C
|
||||||
|
#define MBEDTLS_FS_IO
|
||||||
|
|
||||||
#endif /* UNITTESTS_FEATURES_NETSOCKET_TLSSOCKET_TLS_TEST_CONFIG_H_ */
|
#endif /* UNITTESTS_FEATURES_NETSOCKET_TLSSOCKET_TLS_TEST_CONFIG_H_ */
|
||||||
|
|
|
||||||
|
|
@ -165,14 +165,12 @@ public:
|
||||||
*/
|
*/
|
||||||
int receive(void);
|
int receive(void);
|
||||||
|
|
||||||
/** Read specified number of bytes from an I2C master.
|
/** Read bytes transmitted to this MCU from an I2C master.
|
||||||
*
|
*
|
||||||
* @param data Pointer to the buffer to read data into.
|
* @param data Pointer to the buffer to read data into.
|
||||||
* @param length Number of bytes to read.
|
* @param length Maximum number of bytes to read.
|
||||||
*
|
*
|
||||||
* @return Result of the operation.
|
* @return The number of bytes read
|
||||||
* @retval 0 If the number of bytes read is equal to length requested.
|
|
||||||
* @retval nonzero On error or if the number of bytes read is less than requested.
|
|
||||||
*/
|
*/
|
||||||
int read(char *data, int length);
|
int read(char *data, int length);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ int I2CSlave::receive(void)
|
||||||
|
|
||||||
int I2CSlave::read(char *data, int length)
|
int I2CSlave::read(char *data, int length)
|
||||||
{
|
{
|
||||||
return i2c_slave_read(&_i2c, data, length) != length;
|
return i2c_slave_read(&_i2c, data, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
int I2CSlave::read(void)
|
int I2CSlave::read(void)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
|
|
||||||
|
if(MBED_ENABLE_TESTING)
|
||||||
|
# Build greentea only when testing is enabled
|
||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
greentea-client
|
greentea-client
|
||||||
GIT_REPOSITORY https://github.com/ARMmbed/greentea-client.git
|
GIT_REPOSITORY https://github.com/ARMmbed/greentea-client.git
|
||||||
|
|
@ -10,3 +12,4 @@ FetchContent_Declare(
|
||||||
)
|
)
|
||||||
|
|
||||||
FetchContent_MakeAvailable(greentea-client)
|
FetchContent_MakeAvailable(greentea-client)
|
||||||
|
endif()
|
||||||
5
mbed.h
5
mbed.h
|
|
@ -17,6 +17,11 @@
|
||||||
#ifndef MBED_H
|
#ifndef MBED_H
|
||||||
#define MBED_H
|
#define MBED_H
|
||||||
|
|
||||||
|
// In some cases, Mbed headers are included from source files that don't have
|
||||||
|
// mbed-target-config.h applied via "-include" flag (this is true for the Arduino Mbed core).
|
||||||
|
// In that case, we should manually include the target config header here.
|
||||||
|
#include "mbed-target-config.h"
|
||||||
|
|
||||||
#include "platform/mbed_version.h"
|
#include "platform/mbed_version.h"
|
||||||
|
|
||||||
#if MBED_CONF_RTOS_API_PRESENT
|
#if MBED_CONF_RTOS_API_PRESENT
|
||||||
|
|
|
||||||
|
|
@ -441,11 +441,13 @@ end:
|
||||||
return BD_ERROR_OK;
|
return BD_ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEVICE_SPI_ASYNCH
|
||||||
void SDBlockDevice::set_async_spi_mode(bool enabled, DMAUsage dma_usage_hint)
|
void SDBlockDevice::set_async_spi_mode(bool enabled, DMAUsage dma_usage_hint)
|
||||||
{
|
{
|
||||||
_async_spi_enabled = enabled;
|
_async_spi_enabled = enabled;
|
||||||
_spi.set_dma_usage(dma_usage_hint);
|
_spi.set_dma_usage(dma_usage_hint);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int SDBlockDevice::deinit()
|
int SDBlockDevice::deinit()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,8 @@ target_include_directories(mbed-raspberrypi
|
||||||
pico-sdk/src/rp2_common/hardware_pll/include
|
pico-sdk/src/rp2_common/hardware_pll/include
|
||||||
pico-sdk/src/rp2_common/hardware_sync/include
|
pico-sdk/src/rp2_common/hardware_sync/include
|
||||||
pico-sdk/src/rp2_common/hardware_xosc/include
|
pico-sdk/src/rp2_common/hardware_xosc/include
|
||||||
|
pico-sdk/src/rp2_common/hardware_pio/include
|
||||||
|
pico-sdk/src/rp2_common/hardware_dma/include
|
||||||
pico-sdk/src/rp2_common/pico_platform/include
|
pico-sdk/src/rp2_common/pico_platform/include
|
||||||
pico-sdk/src/rp2_common/pico_fix/rp2040_usb_device_enumeration/include/
|
pico-sdk/src/rp2_common/pico_fix/rp2040_usb_device_enumeration/include/
|
||||||
pico-sdk/src/rp2_common/pico_bootrom/include
|
pico-sdk/src/rp2_common/pico_bootrom/include
|
||||||
|
|
@ -142,6 +144,7 @@ target_include_directories(mbed-raspberrypi
|
||||||
|
|
||||||
target_sources(mbed-raspberrypi
|
target_sources(mbed-raspberrypi
|
||||||
INTERFACE
|
INTERFACE
|
||||||
|
pico-sdk/src/rp2_common/hardware_adc/adc.c
|
||||||
pico-sdk/src/rp2_common/hardware_flash/flash.c
|
pico-sdk/src/rp2_common/hardware_flash/flash.c
|
||||||
pico-sdk/src/rp2_common/hardware_uart/uart.c
|
pico-sdk/src/rp2_common/hardware_uart/uart.c
|
||||||
pico-sdk/src/rp2_common/hardware_spi/spi.c
|
pico-sdk/src/rp2_common/hardware_spi/spi.c
|
||||||
|
|
@ -157,6 +160,8 @@ target_sources(mbed-raspberrypi
|
||||||
pico-sdk/src/rp2_common/hardware_timer/timer.c
|
pico-sdk/src/rp2_common/hardware_timer/timer.c
|
||||||
pico-sdk/src/rp2_common/hardware_sync/sync.c
|
pico-sdk/src/rp2_common/hardware_sync/sync.c
|
||||||
pico-sdk/src/rp2_common/hardware_rtc/rtc.c
|
pico-sdk/src/rp2_common/hardware_rtc/rtc.c
|
||||||
|
pico-sdk/src/rp2_common/hardware_pio/pio.c
|
||||||
|
pico-sdk/src/rp2_common/hardware_dma/dma.c
|
||||||
pico-sdk/src/rp2_common/pico_bootrom/bootrom.c
|
pico-sdk/src/rp2_common/pico_bootrom/bootrom.c
|
||||||
pico-sdk/src/rp2_common/pico_platform/platform.c
|
pico-sdk/src/rp2_common/pico_platform/platform.c
|
||||||
pico-sdk/src/common/pico_time/time.c
|
pico-sdk/src/common/pico_time/time.c
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
pico-sdk/common/pico_stdlib/include/pico/*
|
|
||||||
pico-sdk/common/pico_time/include/pico/*
|
|
||||||
pico-sdk/rp2_common/pico_stdio*
|
|
||||||
pico-sdk/rp2_common/pico_printf*
|
|
||||||
pico-sdk/boards/include/boards/*
|
|
||||||
pico-sdk/common/pico_base/include/pico/*
|
|
||||||
pico-sdk/rp2_common/boot_stage2/*
|
|
||||||
pico-sdk/rp2_common/pico_malloc/*
|
|
||||||
pico-sdk/rp2_common/pico_stdlib/
|
|
||||||
pico-sdk/rp2_common/pico_mem_ops/*
|
|
||||||
pico-sdk/rp2_common/pico_double/double_aeabi.S
|
|
||||||
pico-sdk/rp2_common/pico_double/double_none.S
|
|
||||||
pico-sdk/rp2_common/pico_float/float_aeabi.S
|
|
||||||
pico-sdk/rp2_common/pico_float/float_none.S
|
|
||||||
pico-sdk/rp2_common/pico_float/include/pico/*
|
|
||||||
pico-sdk/rp2_common/pico_standard_link/new_delete.cpp
|
|
||||||
pico-sdk/rp2_common/pico_standard_link/*.ld
|
|
||||||
pico-sdk/rp2_common/pico_unique_id/*
|
|
||||||
pico-sdk/rp2_common/hardware_divider/*
|
|
||||||
pico-sdk/rp2_common/hardware_spi/include/hardware/*
|
|
||||||
pico-sdk/rp2040/hardware_structs/include/hardware/structs/*
|
|
||||||
pico-sdk/rp2040/hardware_regs/include/hardware/regs/*
|
|
||||||
pico-sdk/host/*
|
|
||||||
|
|
@ -1,3 +1,21 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2019, Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "pinmap.h"
|
#include "pinmap.h"
|
||||||
#include "objects.h"
|
#include "objects.h"
|
||||||
#include "PeripheralPins.h"
|
#include "PeripheralPins.h"
|
||||||
|
|
@ -210,10 +228,10 @@ const PinMap PinMap_I2C_SCL[] = {
|
||||||
* is the onboard temperature sensor.
|
* is the onboard temperature sensor.
|
||||||
*/
|
*/
|
||||||
const PinMap PinMap_ADC[] = {
|
const PinMap PinMap_ADC[] = {
|
||||||
{ A0, ADC0, 0},
|
{ p26, ADC0, 0},
|
||||||
{ A1, ADC0, 1},
|
{ p27, ADC0, 1},
|
||||||
{ A2, ADC0, 2},
|
{ p28, ADC0, 2},
|
||||||
{ A3, ADC0, 3},
|
{ p29, ADC0, 3},
|
||||||
{ ADC_TEMP, ADC0, 4},
|
{ ADC_TEMP, ADC0, 4},
|
||||||
{ NC, NC, 0}
|
{ NC, NC, 0}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,9 @@ SECTIONS
|
||||||
and checksummed. It is usually built by the boot_stage2 target
|
and checksummed. It is usually built by the boot_stage2 target
|
||||||
in the Raspberry Pi Pico SDK
|
in the Raspberry Pi Pico SDK
|
||||||
*/
|
*/
|
||||||
|
.second_stage_ota : {
|
||||||
|
KEEP (*(.second_stage_ota))
|
||||||
|
} > FLASH
|
||||||
|
|
||||||
.flash_begin : {
|
.flash_begin : {
|
||||||
__flash_binary_start = .;
|
__flash_binary_start = .;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,21 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2019, Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "mbed_assert.h"
|
#include "mbed_assert.h"
|
||||||
#include "analogin_api.h"
|
#include "analogin_api.h"
|
||||||
#include "hardware/adc.h"
|
#include "hardware/adc.h"
|
||||||
|
|
@ -26,7 +44,7 @@ void analogin_init(analogin_t *obj, PinName pin)
|
||||||
/* Lookup the corresponding ADC channel for a given pin. */
|
/* Lookup the corresponding ADC channel for a given pin. */
|
||||||
obj->channel = pinmap_find_function(pin, PinMap_ADC);
|
obj->channel = pinmap_find_function(pin, PinMap_ADC);
|
||||||
/* Make sure GPIO is high-impedance, no pullups etc. */
|
/* Make sure GPIO is high-impedance, no pullups etc. */
|
||||||
adc_pico_sdk_gpio_init(pin);
|
adc_gpio_init(pin);
|
||||||
/* Check if the ADC channel we just configured belongs to the
|
/* Check if the ADC channel we just configured belongs to the
|
||||||
* temperature sensor. If that's the case, enable the temperature
|
* temperature sensor. If that's the case, enable the temperature
|
||||||
* sensor.
|
* sensor.
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ void adc_init(void);
|
||||||
*
|
*
|
||||||
* \param gpio The GPIO number to use. Allowable GPIO numbers are 26 to 29 inclusive.
|
* \param gpio The GPIO number to use. Allowable GPIO numbers are 26 to 29 inclusive.
|
||||||
*/
|
*/
|
||||||
static inline void adc_pico_sdk_gpio_init(uint gpio) {
|
static inline void adc_gpio_init(uint gpio) {
|
||||||
invalid_params_if(ADC, gpio < 26 || gpio > 29);
|
invalid_params_if(ADC, gpio < 26 || gpio > 29);
|
||||||
// Select NULL function to make output driver hi-Z
|
// Select NULL function to make output driver hi-Z
|
||||||
gpio_set_function(gpio, GPIO_FUNC_NULL);
|
gpio_set_function(gpio, GPIO_FUNC_NULL);
|
||||||
|
|
|
||||||
|
|
@ -307,7 +307,7 @@ void clocks_enable_resus(resus_callback_t resus_callback) {
|
||||||
clocks_hw->resus.ctrl = CLOCKS_CLK_SYS_RESUS_CTRL_ENABLE_BITS | timeout;
|
clocks_hw->resus.ctrl = CLOCKS_CLK_SYS_RESUS_CTRL_ENABLE_BITS | timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clock_pico_sdk_gpio_init_int_frac(uint gpio, uint src, uint32_t div_int, uint8_t div_frac) {
|
void clock_gpio_init_int_frac(uint gpio, uint src, uint32_t div_int, uint8_t div_frac) {
|
||||||
// Bit messy but it's as much code to loop through a lookup
|
// Bit messy but it's as much code to loop through a lookup
|
||||||
// table. The sources for each gpout generators are the same
|
// table. The sources for each gpout generators are the same
|
||||||
// so just call with the sources from GP0
|
// so just call with the sources from GP0
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,7 @@ void clocks_enable_resus(resus_callback_t resus_callback);
|
||||||
* \param div_int The integer part of the value to divide the source clock by. This is useful to not overwhelm the GPIO pin with a fast clock. this is in range of 1..2^24-1.
|
* \param div_int The integer part of the value to divide the source clock by. This is useful to not overwhelm the GPIO pin with a fast clock. this is in range of 1..2^24-1.
|
||||||
* \param div_frac The fractional part of the value to divide the source clock by. This is in range of 0..255 (/256).
|
* \param div_frac The fractional part of the value to divide the source clock by. This is in range of 0..255 (/256).
|
||||||
*/
|
*/
|
||||||
void clock_pico_sdk_gpio_init_int_frac(uint gpio, uint src, uint32_t div_int, uint8_t div_frac);
|
void clock_gpio_init_int_frac(uint gpio, uint src, uint32_t div_int, uint8_t div_frac);
|
||||||
|
|
||||||
/*! \brief Output an optionally divided clock to the specified gpio pin.
|
/*! \brief Output an optionally divided clock to the specified gpio pin.
|
||||||
* \ingroup hardware_clocks
|
* \ingroup hardware_clocks
|
||||||
|
|
@ -248,11 +248,11 @@ void clock_pico_sdk_gpio_init_int_frac(uint gpio, uint src, uint32_t div_int, ui
|
||||||
* \param src The source clock. See the register field CLOCKS_CLK_GPOUT0_CTRL_AUXSRC for a full list. The list is the same for each GPOUT clock generator.
|
* \param src The source clock. See the register field CLOCKS_CLK_GPOUT0_CTRL_AUXSRC for a full list. The list is the same for each GPOUT clock generator.
|
||||||
* \param div The float amount to divide the source clock by. This is useful to not overwhelm the GPIO pin with a fast clock.
|
* \param div The float amount to divide the source clock by. This is useful to not overwhelm the GPIO pin with a fast clock.
|
||||||
*/
|
*/
|
||||||
static inline void clock_pico_sdk_gpio_init(uint gpio, uint src, float div)
|
static inline void clock_gpio_init(uint gpio, uint src, float div)
|
||||||
{
|
{
|
||||||
uint div_int = (uint)div;
|
uint div_int = (uint)div;
|
||||||
uint8_t frac = (uint8_t)((div - (float)div_int) * (1u << CLOCKS_CLK_GPOUT0_DIV_INT_LSB));
|
uint8_t frac = (uint8_t)((div - (float)div_int) * (1u << CLOCKS_CLK_GPOUT0_DIV_INT_LSB));
|
||||||
clock_pico_sdk_gpio_init_int_frac(gpio, src, div_int, frac);
|
clock_gpio_init_int_frac(gpio, src, div_int, frac);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief Configure a clock to come from a gpio input
|
/*! \brief Configure a clock to come from a gpio input
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "hardware/dma.h"
|
||||||
|
#include "hardware/claim.h"
|
||||||
|
|
||||||
|
#define DMA_CHAN_STRIDE (DMA_CH1_CTRL_TRIG_OFFSET - DMA_CH0_CTRL_TRIG_OFFSET)
|
||||||
|
check_hw_size(dma_channel_hw_t, DMA_CHAN_STRIDE);
|
||||||
|
check_hw_layout(dma_hw_t, abort, DMA_CHAN_ABORT_OFFSET);
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
static_assert(offsetof(dma_hw_t, ch[0].ctrl_trig) == DMA_CH0_CTRL_TRIG_OFFSET, "hw mismatch");
|
||||||
|
static_assert(offsetof(dma_hw_t, ch[1].ctrl_trig) == DMA_CH1_CTRL_TRIG_OFFSET, "hw mismatch");
|
||||||
|
|
||||||
|
static_assert(NUM_DMA_CHANNELS <= 16, "");
|
||||||
|
static uint16_t _claimed;
|
||||||
|
static uint8_t _timer_claimed;
|
||||||
|
|
||||||
|
void dma_channel_claim(uint channel) {
|
||||||
|
check_dma_channel_param(channel);
|
||||||
|
hw_claim_or_assert((uint8_t *) &_claimed, channel, "DMA channel %d is already claimed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void dma_claim_mask(uint32_t mask) {
|
||||||
|
for(uint i = 0; mask; i++, mask >>= 1u) {
|
||||||
|
if (mask & 1u) dma_channel_claim(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dma_channel_unclaim(uint channel) {
|
||||||
|
check_dma_channel_param(channel);
|
||||||
|
hw_claim_clear((uint8_t *) &_claimed, channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dma_unclaim_mask(uint32_t mask) {
|
||||||
|
for(uint i = 0; mask; i++, mask >>= 1u) {
|
||||||
|
if (mask & 1u) dma_channel_unclaim(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int dma_claim_unused_channel(bool required) {
|
||||||
|
return hw_claim_unused_from_range((uint8_t*)&_claimed, required, 0, NUM_DMA_CHANNELS-1, "No DMA channels are available");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dma_channel_is_claimed(uint channel) {
|
||||||
|
check_dma_channel_param(channel);
|
||||||
|
return hw_is_claimed((uint8_t *) &_claimed, channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dma_timer_claim(uint timer) {
|
||||||
|
check_dma_timer_param(timer);
|
||||||
|
hw_claim_or_assert(&_timer_claimed, timer, "DMA timer %d is already claimed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void dma_timer_unclaim(uint timer) {
|
||||||
|
check_dma_timer_param(timer);
|
||||||
|
hw_claim_clear(&_timer_claimed, timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int dma_claim_unused_timer(bool required) {
|
||||||
|
return hw_claim_unused_from_range(&_timer_claimed, required, 0, NUM_DMA_TIMERS-1, "No DMA timers are available");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dma_timer_is_claimed(uint timer) {
|
||||||
|
check_dma_timer_param(timer);
|
||||||
|
return hw_is_claimed(&_timer_claimed, timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dma_channel_cleanup(uint channel) {
|
||||||
|
check_dma_channel_param(channel);
|
||||||
|
// Disable CHAIN_TO, and disable channel, so that it ignores any further triggers
|
||||||
|
hw_write_masked( &dma_hw->ch[channel].al1_ctrl, (channel << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB) | (0u << DMA_CH0_CTRL_TRIG_EN_LSB), DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS | DMA_CH0_CTRL_TRIG_EN_BITS );
|
||||||
|
// disable IRQs first as abort can cause spurious IRQs
|
||||||
|
dma_channel_set_irq0_enabled(channel, false);
|
||||||
|
dma_channel_set_irq1_enabled(channel, false);
|
||||||
|
dma_channel_abort(channel);
|
||||||
|
// finally clear the IRQ status, which may have been set during abort
|
||||||
|
dma_hw->intr = 1u << channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
|
||||||
|
void print_dma_ctrl(dma_channel_hw_t *channel) {
|
||||||
|
uint32_t ctrl = channel->ctrl_trig;
|
||||||
|
int rgsz = (ctrl & DMA_CH0_CTRL_TRIG_RING_SIZE_BITS) >> DMA_CH0_CTRL_TRIG_RING_SIZE_LSB;
|
||||||
|
printf("(%08x) ber %d rer %d wer %d busy %d trq %d cto %d rgsl %d rgsz %d inw %d inr %d sz %d hip %d en %d",
|
||||||
|
(uint) ctrl,
|
||||||
|
ctrl & DMA_CH0_CTRL_TRIG_AHB_ERROR_BITS ? 1 : 0,
|
||||||
|
ctrl & DMA_CH0_CTRL_TRIG_READ_ERROR_BITS ? 1 : 0,
|
||||||
|
ctrl & DMA_CH0_CTRL_TRIG_WRITE_ERROR_BITS ? 1 : 0,
|
||||||
|
ctrl & DMA_CH0_CTRL_TRIG_BUSY_BITS ? 1 : 0,
|
||||||
|
(int) ((ctrl & DMA_CH0_CTRL_TRIG_TREQ_SEL_BITS) >> DMA_CH0_CTRL_TRIG_TREQ_SEL_LSB),
|
||||||
|
(int) ((ctrl & DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS) >> DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB),
|
||||||
|
ctrl & DMA_CH0_CTRL_TRIG_RING_SEL_BITS ? 1 : 0,
|
||||||
|
rgsz ? (1 << rgsz) : 0,
|
||||||
|
ctrl & DMA_CH0_CTRL_TRIG_INCR_WRITE_BITS ? 1 : 0,
|
||||||
|
ctrl & DMA_CH0_CTRL_TRIG_INCR_READ_BITS ? 1 : 0,
|
||||||
|
1 << ((ctrl & DMA_CH0_CTRL_TRIG_DATA_SIZE_BITS) >> DMA_CH0_CTRL_TRIG_DATA_SIZE_LSB),
|
||||||
|
ctrl & DMA_CH0_CTRL_TRIG_HIGH_PRIORITY_BITS ? 1 : 0,
|
||||||
|
ctrl & DMA_CH0_CTRL_TRIG_EN_BITS ? 1 : 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if PARAM_ASSERTIONS_ENABLED(DMA)
|
||||||
|
void check_dma_channel_param_impl(uint __unused channel) {
|
||||||
|
valid_params_if(DMA, channel < NUM_DMA_CHANNELS);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,921 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HARDWARE_DMA_H
|
||||||
|
#define _HARDWARE_DMA_H
|
||||||
|
|
||||||
|
#include "pico.h"
|
||||||
|
#include "hardware/structs/dma.h"
|
||||||
|
#include "hardware/regs/dreq.h"
|
||||||
|
#include "pico/assert.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** \file hardware/dma.h
|
||||||
|
* \defgroup hardware_dma hardware_dma
|
||||||
|
*
|
||||||
|
* DMA Controller API
|
||||||
|
*
|
||||||
|
* The RP2040 Direct Memory Access (DMA) master performs bulk data transfers on a processor’s
|
||||||
|
* behalf. This leaves processors free to attend to other tasks, or enter low-power sleep states. The
|
||||||
|
* data throughput of the DMA is also significantly higher than one of RP2040’s processors.
|
||||||
|
*
|
||||||
|
* The DMA can perform one read access and one write access, up to 32 bits in size, every clock cycle.
|
||||||
|
* There are 12 independent channels, which each supervise a sequence of bus transfers, usually in
|
||||||
|
* one of the following scenarios:
|
||||||
|
*
|
||||||
|
* * Memory to peripheral
|
||||||
|
* * Peripheral to memory
|
||||||
|
* * Memory to memory
|
||||||
|
*/
|
||||||
|
|
||||||
|
// these are not defined in generated dreq.h
|
||||||
|
#define DREQ_DMA_TIMER0 DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_TIMER0
|
||||||
|
#define DREQ_DMA_TIMER1 DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_TIMER1
|
||||||
|
#define DREQ_DMA_TIMER2 DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_TIMER2
|
||||||
|
#define DREQ_DMA_TIMER3 DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_TIMER3
|
||||||
|
#define DREQ_FORCE DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_PERMANENT
|
||||||
|
|
||||||
|
// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_DMA, Enable/disable DMA assertions, type=bool, default=0, group=hardware_dma
|
||||||
|
#ifndef PARAM_ASSERTIONS_ENABLED_DMA
|
||||||
|
#define PARAM_ASSERTIONS_ENABLED_DMA 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline void check_dma_channel_param(__unused uint channel) {
|
||||||
|
#if PARAM_ASSERTIONS_ENABLED(DMA)
|
||||||
|
// this method is used a lot by inline functions so avoid code bloat by deferring to function
|
||||||
|
extern void check_dma_channel_param_impl(uint channel);
|
||||||
|
check_dma_channel_param_impl(channel);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void check_dma_timer_param(__unused uint timer_num) {
|
||||||
|
valid_params_if(DMA, timer_num < NUM_DMA_TIMERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static dma_channel_hw_t *dma_channel_hw_addr(uint channel) {
|
||||||
|
check_dma_channel_param(channel);
|
||||||
|
return &dma_hw->ch[channel];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Mark a dma channel as used
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* Method for cooperative claiming of hardware. Will cause a panic if the channel
|
||||||
|
* is already claimed. Use of this method by libraries detects accidental
|
||||||
|
* configurations that would fail in unpredictable ways.
|
||||||
|
*
|
||||||
|
* \param channel the dma channel
|
||||||
|
*/
|
||||||
|
void dma_channel_claim(uint channel);
|
||||||
|
|
||||||
|
/*! \brief Mark multiple dma channels as used
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* Method for cooperative claiming of hardware. Will cause a panic if any of the channels
|
||||||
|
* are already claimed. Use of this method by libraries detects accidental
|
||||||
|
* configurations that would fail in unpredictable ways.
|
||||||
|
*
|
||||||
|
* \param channel_mask Bitfield of all required channels to claim (bit 0 == channel 0, bit 1 == channel 1 etc)
|
||||||
|
*/
|
||||||
|
void dma_claim_mask(uint32_t channel_mask);
|
||||||
|
|
||||||
|
/*! \brief Mark a dma channel as no longer used
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel the dma channel to release
|
||||||
|
*/
|
||||||
|
void dma_channel_unclaim(uint channel);
|
||||||
|
|
||||||
|
/*! \brief Mark multiple dma channels as no longer used
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel_mask Bitfield of all channels to unclaim (bit 0 == channel 0, bit 1 == channel 1 etc)
|
||||||
|
*/
|
||||||
|
void dma_unclaim_mask(uint32_t channel_mask);
|
||||||
|
|
||||||
|
/*! \brief Claim a free dma channel
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param required if true the function will panic if none are available
|
||||||
|
* \return the dma channel number or -1 if required was false, and none were free
|
||||||
|
*/
|
||||||
|
int dma_claim_unused_channel(bool required);
|
||||||
|
|
||||||
|
/*! \brief Determine if a dma channel is claimed
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel the dma channel
|
||||||
|
* \return true if the channel is claimed, false otherwise
|
||||||
|
* \see dma_channel_claim
|
||||||
|
* \see dma_channel_claim_mask
|
||||||
|
*/
|
||||||
|
bool dma_channel_is_claimed(uint channel);
|
||||||
|
|
||||||
|
/** \brief DMA channel configuration
|
||||||
|
* \defgroup channel_config channel_config
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* A DMA channel needs to be configured, these functions provide handy helpers to set up configuration
|
||||||
|
* structures. See \ref dma_channel_config
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \brief Enumeration of available DMA channel transfer sizes.
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* Names indicate the number of bits.
|
||||||
|
*/
|
||||||
|
enum dma_channel_transfer_size {
|
||||||
|
DMA_SIZE_8 = 0, ///< Byte transfer (8 bits)
|
||||||
|
DMA_SIZE_16 = 1, ///< Half word transfer (16 bits)
|
||||||
|
DMA_SIZE_32 = 2 ///< Word transfer (32 bits)
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t ctrl;
|
||||||
|
} dma_channel_config;
|
||||||
|
|
||||||
|
/*! \brief Set DMA channel read increment in a channel configuration object
|
||||||
|
* \ingroup channel_config
|
||||||
|
*
|
||||||
|
* \param c Pointer to channel configuration object
|
||||||
|
* \param incr True to enable read address increments, if false, each read will be from the same address
|
||||||
|
* Usually disabled for peripheral to memory transfers
|
||||||
|
*/
|
||||||
|
static inline void channel_config_set_read_increment(dma_channel_config *c, bool incr) {
|
||||||
|
c->ctrl = incr ? (c->ctrl | DMA_CH0_CTRL_TRIG_INCR_READ_BITS) : (c->ctrl & ~DMA_CH0_CTRL_TRIG_INCR_READ_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Set DMA channel write increment in a channel configuration object
|
||||||
|
* \ingroup channel_config
|
||||||
|
*
|
||||||
|
* \param c Pointer to channel configuration object
|
||||||
|
* \param incr True to enable write address increments, if false, each write will be to the same address
|
||||||
|
* Usually disabled for memory to peripheral transfers
|
||||||
|
*/
|
||||||
|
static inline void channel_config_set_write_increment(dma_channel_config *c, bool incr) {
|
||||||
|
c->ctrl = incr ? (c->ctrl | DMA_CH0_CTRL_TRIG_INCR_WRITE_BITS) : (c->ctrl & ~DMA_CH0_CTRL_TRIG_INCR_WRITE_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Select a transfer request signal in a channel configuration object
|
||||||
|
* \ingroup channel_config
|
||||||
|
*
|
||||||
|
* The channel uses the transfer request signal to pace its data transfer rate.
|
||||||
|
* Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data Request from the system).
|
||||||
|
* 0x0 to 0x3a -> select DREQ n as TREQ
|
||||||
|
* 0x3b -> Select Timer 0 as TREQ
|
||||||
|
* 0x3c -> Select Timer 1 as TREQ
|
||||||
|
* 0x3d -> Select Timer 2 as TREQ (Optional)
|
||||||
|
* 0x3e -> Select Timer 3 as TREQ (Optional)
|
||||||
|
* 0x3f -> Permanent request, for unpaced transfers.
|
||||||
|
*
|
||||||
|
* \param c Pointer to channel configuration data
|
||||||
|
* \param dreq Source (see description)
|
||||||
|
*/
|
||||||
|
static inline void channel_config_set_dreq(dma_channel_config *c, uint dreq) {
|
||||||
|
assert(dreq <= DREQ_FORCE);
|
||||||
|
c->ctrl = (c->ctrl & ~DMA_CH0_CTRL_TRIG_TREQ_SEL_BITS) | (dreq << DMA_CH0_CTRL_TRIG_TREQ_SEL_LSB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Set DMA channel chain_to channel in a channel configuration object
|
||||||
|
* \ingroup channel_config
|
||||||
|
*
|
||||||
|
* When this channel completes, it will trigger the channel indicated by chain_to. Disable by
|
||||||
|
* setting chain_to to itself (the same channel)
|
||||||
|
*
|
||||||
|
* \param c Pointer to channel configuration object
|
||||||
|
* \param chain_to Channel to trigger when this channel completes.
|
||||||
|
*/
|
||||||
|
static inline void channel_config_set_chain_to(dma_channel_config *c, uint chain_to) {
|
||||||
|
assert(chain_to <= NUM_DMA_CHANNELS);
|
||||||
|
c->ctrl = (c->ctrl & ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS) | (chain_to << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Set the size of each DMA bus transfer in a channel configuration object
|
||||||
|
* \ingroup channel_config
|
||||||
|
*
|
||||||
|
* Set the size of each bus transfer (byte/halfword/word). The read and write addresses
|
||||||
|
* advance by the specific amount (1/2/4 bytes) with each transfer.
|
||||||
|
*
|
||||||
|
* \param c Pointer to channel configuration object
|
||||||
|
* \param size See enum for possible values.
|
||||||
|
*/
|
||||||
|
static inline void channel_config_set_transfer_data_size(dma_channel_config *c, enum dma_channel_transfer_size size) {
|
||||||
|
assert(size == DMA_SIZE_8 || size == DMA_SIZE_16 || size == DMA_SIZE_32);
|
||||||
|
c->ctrl = (c->ctrl & ~DMA_CH0_CTRL_TRIG_DATA_SIZE_BITS) | (((uint)size) << DMA_CH0_CTRL_TRIG_DATA_SIZE_LSB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Set address wrapping parameters in a channel configuration object
|
||||||
|
* \ingroup channel_config
|
||||||
|
*
|
||||||
|
* Size of address wrap region. If 0, don’t wrap. For values n > 0, only the lower n bits of the address
|
||||||
|
* will change. This wraps the address on a (1 << n) byte boundary, facilitating access to naturally-aligned
|
||||||
|
* ring buffers.
|
||||||
|
* Ring sizes between 2 and 32768 bytes are possible (size_bits from 1 - 15)
|
||||||
|
*
|
||||||
|
* 0x0 -> No wrapping.
|
||||||
|
*
|
||||||
|
* \param c Pointer to channel configuration object
|
||||||
|
* \param write True to apply to write addresses, false to apply to read addresses
|
||||||
|
* \param size_bits 0 to disable wrapping. Otherwise the size in bits of the changing part of the address.
|
||||||
|
* Effectively wraps the address on a (1 << size_bits) byte boundary.
|
||||||
|
*/
|
||||||
|
static inline void channel_config_set_ring(dma_channel_config *c, bool write, uint size_bits) {
|
||||||
|
assert(size_bits < 32);
|
||||||
|
c->ctrl = (c->ctrl & ~(DMA_CH0_CTRL_TRIG_RING_SIZE_BITS | DMA_CH0_CTRL_TRIG_RING_SEL_BITS)) |
|
||||||
|
(size_bits << DMA_CH0_CTRL_TRIG_RING_SIZE_LSB) |
|
||||||
|
(write ? DMA_CH0_CTRL_TRIG_RING_SEL_BITS : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Set DMA byte swapping config in a channel configuration object
|
||||||
|
* \ingroup channel_config
|
||||||
|
*
|
||||||
|
* No effect for byte data, for halfword data, the two bytes of each halfword are
|
||||||
|
* swapped. For word data, the four bytes of each word are swapped to reverse their order.
|
||||||
|
*
|
||||||
|
* \param c Pointer to channel configuration object
|
||||||
|
* \param bswap True to enable byte swapping
|
||||||
|
*/
|
||||||
|
static inline void channel_config_set_bswap(dma_channel_config *c, bool bswap) {
|
||||||
|
c->ctrl = bswap ? (c->ctrl | DMA_CH0_CTRL_TRIG_BSWAP_BITS) : (c->ctrl & ~DMA_CH0_CTRL_TRIG_BSWAP_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Set IRQ quiet mode in a channel configuration object
|
||||||
|
* \ingroup channel_config
|
||||||
|
*
|
||||||
|
* In QUIET mode, the channel does not generate IRQs at the end of every transfer block. Instead,
|
||||||
|
* an IRQ is raised when NULL is written to a trigger register, indicating the end of a control
|
||||||
|
* block chain.
|
||||||
|
*
|
||||||
|
* \param c Pointer to channel configuration object
|
||||||
|
* \param irq_quiet True to enable quiet mode, false to disable.
|
||||||
|
*/
|
||||||
|
static inline void channel_config_set_irq_quiet(dma_channel_config *c, bool irq_quiet) {
|
||||||
|
c->ctrl = irq_quiet ? (c->ctrl | DMA_CH0_CTRL_TRIG_IRQ_QUIET_BITS) : (c->ctrl & ~DMA_CH0_CTRL_TRIG_IRQ_QUIET_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Set the channel priority in a channel configuration object
|
||||||
|
* \ingroup channel_config
|
||||||
|
*
|
||||||
|
* When true, gives a channel preferential treatment in issue scheduling: in each scheduling round,
|
||||||
|
* all high priority channels are considered first, and then only a single low
|
||||||
|
* priority channel, before returning to the high priority channels.
|
||||||
|
*
|
||||||
|
* This only affects the order in which the DMA schedules channels. The DMA's bus priority is not changed.
|
||||||
|
* If the DMA is not saturated then a low priority channel will see no loss of throughput.
|
||||||
|
*
|
||||||
|
* \param c Pointer to channel configuration object
|
||||||
|
* \param high_priority True to enable high priority
|
||||||
|
*/
|
||||||
|
static inline void channel_config_set_high_priority(dma_channel_config *c, bool high_priority) {
|
||||||
|
c->ctrl = high_priority ? (c->ctrl | DMA_CH0_CTRL_TRIG_HIGH_PRIORITY_BITS) : (c->ctrl & ~DMA_CH0_CTRL_TRIG_HIGH_PRIORITY_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Enable/Disable the DMA channel in a channel configuration object
|
||||||
|
* \ingroup channel_config
|
||||||
|
*
|
||||||
|
* When false, the channel will ignore triggers, stop issuing transfers, and pause the current transfer sequence (i.e. BUSY will
|
||||||
|
* remain high if already high)
|
||||||
|
*
|
||||||
|
* \param c Pointer to channel configuration object
|
||||||
|
* \param enable True to enable the DMA channel. When enabled, the channel will respond to triggering events, and start transferring data.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static inline void channel_config_set_enable(dma_channel_config *c, bool enable) {
|
||||||
|
c->ctrl = enable ? (c->ctrl | DMA_CH0_CTRL_TRIG_EN_BITS) : (c->ctrl & ~DMA_CH0_CTRL_TRIG_EN_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Enable access to channel by sniff hardware in a channel configuration object
|
||||||
|
* \ingroup channel_config
|
||||||
|
*
|
||||||
|
* Sniff HW must be enabled and have this channel selected.
|
||||||
|
*
|
||||||
|
* \param c Pointer to channel configuration object
|
||||||
|
* \param sniff_enable True to enable the Sniff HW access to this DMA channel.
|
||||||
|
*/
|
||||||
|
static inline void channel_config_set_sniff_enable(dma_channel_config *c, bool sniff_enable) {
|
||||||
|
c->ctrl = sniff_enable ? (c->ctrl | DMA_CH0_CTRL_TRIG_SNIFF_EN_BITS) : (c->ctrl &
|
||||||
|
~DMA_CH0_CTRL_TRIG_SNIFF_EN_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Get the default channel configuration for a given channel
|
||||||
|
* \ingroup channel_config
|
||||||
|
*
|
||||||
|
* Setting | Default
|
||||||
|
* --------|--------
|
||||||
|
* Read Increment | true
|
||||||
|
* Write Increment | false
|
||||||
|
* DReq | DREQ_FORCE
|
||||||
|
* Chain to | self
|
||||||
|
* Data size | DMA_SIZE_32
|
||||||
|
* Ring | write=false, size=0 (i.e. off)
|
||||||
|
* Byte Swap | false
|
||||||
|
* Quiet IRQs | false
|
||||||
|
* High Priority | false
|
||||||
|
* Channel Enable | true
|
||||||
|
* Sniff Enable | false
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \return the default configuration which can then be modified.
|
||||||
|
*/
|
||||||
|
static inline dma_channel_config dma_channel_get_default_config(uint channel) {
|
||||||
|
dma_channel_config c = {0};
|
||||||
|
channel_config_set_read_increment(&c, true);
|
||||||
|
channel_config_set_write_increment(&c, false);
|
||||||
|
channel_config_set_dreq(&c, DREQ_FORCE);
|
||||||
|
channel_config_set_chain_to(&c, channel);
|
||||||
|
channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
|
||||||
|
channel_config_set_ring(&c, false, 0);
|
||||||
|
channel_config_set_bswap(&c, false);
|
||||||
|
channel_config_set_irq_quiet(&c, false);
|
||||||
|
channel_config_set_enable(&c, true);
|
||||||
|
channel_config_set_sniff_enable(&c, false);
|
||||||
|
channel_config_set_high_priority( &c, false);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Get the current configuration for the specified channel.
|
||||||
|
* \ingroup channel_config
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \return The current configuration as read from the HW register (not cached)
|
||||||
|
*/
|
||||||
|
static inline dma_channel_config dma_get_channel_config(uint channel) {
|
||||||
|
dma_channel_config c;
|
||||||
|
c.ctrl = dma_channel_hw_addr(channel)->ctrl_trig;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Get the raw configuration register from a channel configuration
|
||||||
|
* \ingroup channel_config
|
||||||
|
*
|
||||||
|
* \param config Pointer to a config structure.
|
||||||
|
* \return Register content
|
||||||
|
*/
|
||||||
|
static inline uint32_t channel_config_get_ctrl_value(const dma_channel_config *config) {
|
||||||
|
return config->ctrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Set a channel configuration
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \param config Pointer to a config structure with required configuration
|
||||||
|
* \param trigger True to trigger the transfer immediately
|
||||||
|
*/
|
||||||
|
static inline void dma_channel_set_config(uint channel, const dma_channel_config *config, bool trigger) {
|
||||||
|
// Don't use CTRL_TRIG since we don't want to start a transfer
|
||||||
|
if (!trigger) {
|
||||||
|
dma_channel_hw_addr(channel)->al1_ctrl = channel_config_get_ctrl_value(config);
|
||||||
|
} else {
|
||||||
|
dma_channel_hw_addr(channel)->ctrl_trig = channel_config_get_ctrl_value(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Set the DMA initial read address.
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \param read_addr Initial read address of transfer.
|
||||||
|
* \param trigger True to start the transfer immediately
|
||||||
|
*/
|
||||||
|
static inline void dma_channel_set_read_addr(uint channel, const volatile void *read_addr, bool trigger) {
|
||||||
|
if (!trigger) {
|
||||||
|
dma_channel_hw_addr(channel)->read_addr = (uintptr_t) read_addr;
|
||||||
|
} else {
|
||||||
|
dma_channel_hw_addr(channel)->al3_read_addr_trig = (uintptr_t) read_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Set the DMA initial write address
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \param write_addr Initial write address of transfer.
|
||||||
|
* \param trigger True to start the transfer immediately
|
||||||
|
*/
|
||||||
|
static inline void dma_channel_set_write_addr(uint channel, volatile void *write_addr, bool trigger) {
|
||||||
|
if (!trigger) {
|
||||||
|
dma_channel_hw_addr(channel)->write_addr = (uintptr_t) write_addr;
|
||||||
|
} else {
|
||||||
|
dma_channel_hw_addr(channel)->al2_write_addr_trig = (uintptr_t) write_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Set the number of bus transfers the channel will do
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \param trans_count The number of transfers (not NOT bytes, see channel_config_set_transfer_data_size)
|
||||||
|
* \param trigger True to start the transfer immediately
|
||||||
|
*/
|
||||||
|
static inline void dma_channel_set_trans_count(uint channel, uint32_t trans_count, bool trigger) {
|
||||||
|
if (!trigger) {
|
||||||
|
dma_channel_hw_addr(channel)->transfer_count = trans_count;
|
||||||
|
} else {
|
||||||
|
dma_channel_hw_addr(channel)->al1_transfer_count_trig = trans_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Configure all DMA parameters and optionally start transfer
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \param config Pointer to DMA config structure
|
||||||
|
* \param write_addr Initial write address
|
||||||
|
* \param read_addr Initial read address
|
||||||
|
* \param transfer_count Number of transfers to perform
|
||||||
|
* \param trigger True to start the transfer immediately
|
||||||
|
*/
|
||||||
|
static inline void dma_channel_configure(uint channel, const dma_channel_config *config, volatile void *write_addr,
|
||||||
|
const volatile void *read_addr,
|
||||||
|
uint transfer_count, bool trigger) {
|
||||||
|
dma_channel_set_read_addr(channel, read_addr, false);
|
||||||
|
dma_channel_set_write_addr(channel, write_addr, false);
|
||||||
|
dma_channel_set_trans_count(channel, transfer_count, false);
|
||||||
|
dma_channel_set_config(channel, config, trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Start a DMA transfer from a buffer immediately
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \param read_addr Sets the initial read address
|
||||||
|
* \param transfer_count Number of transfers to make. Not bytes, but the number of transfers of channel_config_set_transfer_data_size() to be sent.
|
||||||
|
*/
|
||||||
|
inline static void __attribute__((always_inline)) dma_channel_transfer_from_buffer_now(uint channel,
|
||||||
|
const volatile void *read_addr,
|
||||||
|
uint32_t transfer_count) {
|
||||||
|
// check_dma_channel_param(channel);
|
||||||
|
dma_channel_hw_t *hw = dma_channel_hw_addr(channel);
|
||||||
|
hw->read_addr = (uintptr_t) read_addr;
|
||||||
|
hw->al1_transfer_count_trig = transfer_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Start a DMA transfer to a buffer immediately
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \param write_addr Sets the initial write address
|
||||||
|
* \param transfer_count Number of transfers to make. Not bytes, but the number of transfers of channel_config_set_transfer_data_size() to be sent.
|
||||||
|
*/
|
||||||
|
inline static void dma_channel_transfer_to_buffer_now(uint channel, volatile void *write_addr, uint32_t transfer_count) {
|
||||||
|
dma_channel_hw_t *hw = dma_channel_hw_addr(channel);
|
||||||
|
hw->write_addr = (uintptr_t) write_addr;
|
||||||
|
hw->al1_transfer_count_trig = transfer_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Start one or more channels simultaneously
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param chan_mask Bitmask of all the channels requiring starting. Channel 0 = bit 0, channel 1 = bit 1 etc.
|
||||||
|
*/
|
||||||
|
static inline void dma_start_channel_mask(uint32_t chan_mask) {
|
||||||
|
valid_params_if(DMA, chan_mask && chan_mask < (1u << NUM_DMA_CHANNELS));
|
||||||
|
dma_hw->multi_channel_trigger = chan_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Start a single DMA channel
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
*/
|
||||||
|
static inline void dma_channel_start(uint channel) {
|
||||||
|
dma_start_channel_mask(1u << channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Stop a DMA transfer
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* Function will only return once the DMA has stopped.
|
||||||
|
*
|
||||||
|
* Note that due to errata RP2040-E13, aborting a channel which has transfers
|
||||||
|
* in-flight (i.e. an individual read has taken place but the corresponding write has not), the ABORT
|
||||||
|
* status bit will clear prematurely, and subsequently the in-flight
|
||||||
|
* transfers will trigger a completion interrupt once they complete.
|
||||||
|
*
|
||||||
|
* The effect of this is that you \em may see a spurious completion interrupt
|
||||||
|
* on the channel as a result of calling this method.
|
||||||
|
*
|
||||||
|
* The calling code should be sure to ignore a completion IRQ as a result of this method. This may
|
||||||
|
* not require any additional work, as aborting a channel which may be about to complete, when you have a completion
|
||||||
|
* IRQ handler registered, is inherently race-prone, and so code is likely needed to disambiguate the two occurrences.
|
||||||
|
*
|
||||||
|
* If that is not the case, but you do have a channel completion IRQ handler registered, you can simply
|
||||||
|
* disable/re-enable the IRQ around the call to this method as shown by this code fragment (using DMA IRQ0).
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* // disable the channel on IRQ0
|
||||||
|
* dma_channel_set_irq0_enabled(channel, false);
|
||||||
|
* // abort the channel
|
||||||
|
* dma_channel_abort(channel);
|
||||||
|
* // clear the spurious IRQ (if there was one)
|
||||||
|
* dma_channel_acknowledge_irq0(channel);
|
||||||
|
* // re-enable the channel on IRQ0
|
||||||
|
* dma_channel_set_irq0_enabled(channel, true);
|
||||||
|
*\endcode
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
*/
|
||||||
|
static inline void dma_channel_abort(uint channel) {
|
||||||
|
check_dma_channel_param(channel);
|
||||||
|
dma_hw->abort = 1u << channel;
|
||||||
|
// Bit will go 0 once channel has reached safe state
|
||||||
|
// (i.e. any in-flight transfers have retired)
|
||||||
|
while (dma_hw->ch[channel].ctrl_trig & DMA_CH0_CTRL_TRIG_BUSY_BITS) tight_loop_contents();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Enable single DMA channel's interrupt via DMA_IRQ_0
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \param enabled true to enable interrupt 0 on specified channel, false to disable.
|
||||||
|
*/
|
||||||
|
static inline void dma_channel_set_irq0_enabled(uint channel, bool enabled) {
|
||||||
|
check_dma_channel_param(channel);
|
||||||
|
check_hw_layout(dma_hw_t, inte0, DMA_INTE0_OFFSET);
|
||||||
|
if (enabled)
|
||||||
|
hw_set_bits(&dma_hw->inte0, 1u << channel);
|
||||||
|
else
|
||||||
|
hw_clear_bits(&dma_hw->inte0, 1u << channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Enable multiple DMA channels' interrupts via DMA_IRQ_0
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel_mask Bitmask of all the channels to enable/disable. Channel 0 = bit 0, channel 1 = bit 1 etc.
|
||||||
|
* \param enabled true to enable all the interrupts specified in the mask, false to disable all the interrupts specified in the mask.
|
||||||
|
*/
|
||||||
|
static inline void dma_set_irq0_channel_mask_enabled(uint32_t channel_mask, bool enabled) {
|
||||||
|
if (enabled) {
|
||||||
|
hw_set_bits(&dma_hw->inte0, channel_mask);
|
||||||
|
} else {
|
||||||
|
hw_clear_bits(&dma_hw->inte0, channel_mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Enable single DMA channel's interrupt via DMA_IRQ_1
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \param enabled true to enable interrupt 1 on specified channel, false to disable.
|
||||||
|
*/
|
||||||
|
static inline void dma_channel_set_irq1_enabled(uint channel, bool enabled) {
|
||||||
|
check_dma_channel_param(channel);
|
||||||
|
check_hw_layout(dma_hw_t, inte1, DMA_INTE1_OFFSET);
|
||||||
|
if (enabled)
|
||||||
|
hw_set_bits(&dma_hw->inte1, 1u << channel);
|
||||||
|
else
|
||||||
|
hw_clear_bits(&dma_hw->inte1, 1u << channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Enable multiple DMA channels' interrupts via DMA_IRQ_1
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel_mask Bitmask of all the channels to enable/disable. Channel 0 = bit 0, channel 1 = bit 1 etc.
|
||||||
|
* \param enabled true to enable all the interrupts specified in the mask, false to disable all the interrupts specified in the mask.
|
||||||
|
*/
|
||||||
|
static inline void dma_set_irq1_channel_mask_enabled(uint32_t channel_mask, bool enabled) {
|
||||||
|
if (enabled) {
|
||||||
|
hw_set_bits(&dma_hw->inte1, channel_mask);
|
||||||
|
} else {
|
||||||
|
hw_clear_bits(&dma_hw->inte1, channel_mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Enable single DMA channel interrupt on either DMA_IRQ_0 or DMA_IRQ_1
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param irq_index the IRQ index; either 0 or 1 for DMA_IRQ_0 or DMA_IRQ_1
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \param enabled true to enable interrupt via irq_index for specified channel, false to disable.
|
||||||
|
*/
|
||||||
|
static inline void dma_irqn_set_channel_enabled(uint irq_index, uint channel, bool enabled) {
|
||||||
|
invalid_params_if(DMA, irq_index > 1);
|
||||||
|
if (irq_index) {
|
||||||
|
dma_channel_set_irq1_enabled(channel, enabled);
|
||||||
|
} else {
|
||||||
|
dma_channel_set_irq0_enabled(channel, enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Enable multiple DMA channels' interrupt via either DMA_IRQ_0 or DMA_IRQ_1
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param irq_index the IRQ index; either 0 or 1 for DMA_IRQ_0 or DMA_IRQ_1
|
||||||
|
* \param channel_mask Bitmask of all the channels to enable/disable. Channel 0 = bit 0, channel 1 = bit 1 etc.
|
||||||
|
* \param enabled true to enable all the interrupts specified in the mask, false to disable all the interrupts specified in the mask.
|
||||||
|
*/
|
||||||
|
static inline void dma_irqn_set_channel_mask_enabled(uint irq_index, uint32_t channel_mask, bool enabled) {
|
||||||
|
invalid_params_if(DMA, irq_index > 1);
|
||||||
|
if (irq_index) {
|
||||||
|
dma_set_irq1_channel_mask_enabled(channel_mask, enabled);
|
||||||
|
} else {
|
||||||
|
dma_set_irq0_channel_mask_enabled(channel_mask, enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Determine if a particular channel is a cause of DMA_IRQ_0
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \return true if the channel is a cause of DMA_IRQ_0, false otherwise
|
||||||
|
*/
|
||||||
|
static inline bool dma_channel_get_irq0_status(uint channel) {
|
||||||
|
check_dma_channel_param(channel);
|
||||||
|
return dma_hw->ints0 & (1u << channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Determine if a particular channel is a cause of DMA_IRQ_1
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \return true if the channel is a cause of DMA_IRQ_1, false otherwise
|
||||||
|
*/
|
||||||
|
static inline bool dma_channel_get_irq1_status(uint channel) {
|
||||||
|
check_dma_channel_param(channel);
|
||||||
|
return dma_hw->ints1 & (1u << channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Determine if a particular channel is a cause of DMA_IRQ_N
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param irq_index the IRQ index; either 0 or 1 for DMA_IRQ_0 or DMA_IRQ_1
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \return true if the channel is a cause of the DMA_IRQ_N, false otherwise
|
||||||
|
*/
|
||||||
|
static inline bool dma_irqn_get_channel_status(uint irq_index, uint channel) {
|
||||||
|
invalid_params_if(DMA, irq_index > 1);
|
||||||
|
check_dma_channel_param(channel);
|
||||||
|
return (irq_index ? dma_hw->ints1 : dma_hw->ints0) & (1u << channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Acknowledge a channel IRQ, resetting it as the cause of DMA_IRQ_0
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
*/
|
||||||
|
static inline void dma_channel_acknowledge_irq0(uint channel) {
|
||||||
|
check_dma_channel_param(channel);
|
||||||
|
dma_hw->ints0 = 1u << channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Acknowledge a channel IRQ, resetting it as the cause of DMA_IRQ_1
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
*/
|
||||||
|
static inline void dma_channel_acknowledge_irq1(uint channel) {
|
||||||
|
check_dma_channel_param(channel);
|
||||||
|
dma_hw->ints1 = 1u << channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Acknowledge a channel IRQ, resetting it as the cause of DMA_IRQ_N
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param irq_index the IRQ index; either 0 or 1 for DMA_IRQ_0 or DMA_IRQ_1
|
||||||
|
* \param channel DMA channel
|
||||||
|
*/
|
||||||
|
static inline void dma_irqn_acknowledge_channel(uint irq_index, uint channel) {
|
||||||
|
invalid_params_if(DMA, irq_index > 1);
|
||||||
|
check_dma_channel_param(channel);
|
||||||
|
if (irq_index)
|
||||||
|
dma_hw->ints1 = 1u << channel;
|
||||||
|
else
|
||||||
|
dma_hw->ints0 = 1u << channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Check if DMA channel is busy
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \return true if the channel is currently busy
|
||||||
|
*/
|
||||||
|
inline static bool dma_channel_is_busy(uint channel) {
|
||||||
|
check_dma_channel_param(channel);
|
||||||
|
return !!(dma_hw->ch[channel].al1_ctrl & DMA_CH0_CTRL_TRIG_BUSY_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Wait for a DMA channel transfer to complete
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
*/
|
||||||
|
inline static void dma_channel_wait_for_finish_blocking(uint channel) {
|
||||||
|
while (dma_channel_is_busy(channel)) tight_loop_contents();
|
||||||
|
// stop the compiler hoisting a non volatile buffer access above the DMA completion.
|
||||||
|
__compiler_memory_barrier();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Enable the DMA sniffing targeting the specified channel
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* The mode can be one of the following:
|
||||||
|
*
|
||||||
|
* Mode | Function
|
||||||
|
* -----|---------
|
||||||
|
* 0x0 | Calculate a CRC-32 (IEEE802.3 polynomial)
|
||||||
|
* 0x1 | Calculate a CRC-32 (IEEE802.3 polynomial) with bit reversed data
|
||||||
|
* 0x2 | Calculate a CRC-16-CCITT
|
||||||
|
* 0x3 | Calculate a CRC-16-CCITT with bit reversed data
|
||||||
|
* 0xe | XOR reduction over all data. == 1 if the total 1 population count is odd.
|
||||||
|
* 0xf | Calculate a simple 32-bit checksum (addition with a 32 bit accumulator)
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
* \param mode See description
|
||||||
|
* \param force_channel_enable Set true to also turn on sniffing in the channel configuration (this
|
||||||
|
* is usually what you want, but sometimes you might have a chain DMA with only certain segments
|
||||||
|
* of the chain sniffed, in which case you might pass false).
|
||||||
|
*/
|
||||||
|
inline static void dma_sniffer_enable(uint channel, uint mode, bool force_channel_enable) {
|
||||||
|
check_dma_channel_param(channel);
|
||||||
|
check_hw_layout(dma_hw_t, sniff_ctrl, DMA_SNIFF_CTRL_OFFSET);
|
||||||
|
if (force_channel_enable) {
|
||||||
|
hw_set_bits(&dma_hw->ch[channel].al1_ctrl, DMA_CH0_CTRL_TRIG_SNIFF_EN_BITS);
|
||||||
|
}
|
||||||
|
hw_write_masked(&dma_hw->sniff_ctrl,
|
||||||
|
(((channel << DMA_SNIFF_CTRL_DMACH_LSB) & DMA_SNIFF_CTRL_DMACH_BITS) |
|
||||||
|
((mode << DMA_SNIFF_CTRL_CALC_LSB) & DMA_SNIFF_CTRL_CALC_BITS) |
|
||||||
|
DMA_SNIFF_CTRL_EN_BITS),
|
||||||
|
(DMA_SNIFF_CTRL_DMACH_BITS |
|
||||||
|
DMA_SNIFF_CTRL_CALC_BITS |
|
||||||
|
DMA_SNIFF_CTRL_EN_BITS));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Enable the Sniffer byte swap function
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* Locally perform a byte reverse on the sniffed data, before feeding into checksum.
|
||||||
|
*
|
||||||
|
* Note that the sniff hardware is downstream of the DMA channel byteswap performed in the
|
||||||
|
* read master: if channel_config_set_bswap() and dma_sniffer_set_byte_swap_enabled() are both enabled,
|
||||||
|
* their effects cancel from the sniffer’s point of view.
|
||||||
|
*
|
||||||
|
* \param swap Set true to enable byte swapping
|
||||||
|
*/
|
||||||
|
inline static void dma_sniffer_set_byte_swap_enabled(bool swap) {
|
||||||
|
if (swap)
|
||||||
|
hw_set_bits(&dma_hw->sniff_ctrl, DMA_SNIFF_CTRL_BSWAP_BITS);
|
||||||
|
else
|
||||||
|
hw_clear_bits(&dma_hw->sniff_ctrl, DMA_SNIFF_CTRL_BSWAP_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Enable the Sniffer output invert function
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* If enabled, the sniff data result appears bit-inverted when read.
|
||||||
|
* This does not affect the way the checksum is calculated.
|
||||||
|
*
|
||||||
|
* \param invert Set true to enable output bit inversion
|
||||||
|
*/
|
||||||
|
inline static void dma_sniffer_set_output_invert_enabled(bool invert) {
|
||||||
|
if (invert)
|
||||||
|
hw_set_bits(&dma_hw->sniff_ctrl, DMA_SNIFF_CTRL_OUT_INV_BITS);
|
||||||
|
else
|
||||||
|
hw_clear_bits(&dma_hw->sniff_ctrl, DMA_SNIFF_CTRL_OUT_INV_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Enable the Sniffer output bit reversal function
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* If enabled, the sniff data result appears bit-reversed when read.
|
||||||
|
* This does not affect the way the checksum is calculated.
|
||||||
|
*
|
||||||
|
* \param reverse Set true to enable output bit reversal
|
||||||
|
*/
|
||||||
|
inline static void dma_sniffer_set_output_reverse_enabled(bool reverse) {
|
||||||
|
if (reverse)
|
||||||
|
hw_set_bits(&dma_hw->sniff_ctrl, DMA_SNIFF_CTRL_OUT_REV_BITS);
|
||||||
|
else
|
||||||
|
hw_clear_bits(&dma_hw->sniff_ctrl, DMA_SNIFF_CTRL_OUT_REV_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Disable the DMA sniffer
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
inline static void dma_sniffer_disable(void) {
|
||||||
|
dma_hw->sniff_ctrl = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Set the sniffer's data accumulator with initial value
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* Generally, CRC algorithms are used with the data accumulator initially
|
||||||
|
* seeded with 0xFFFF or 0xFFFFFFFF (for crc16 and crc32 algorithms)
|
||||||
|
*
|
||||||
|
* \param seed_value value to set data accumulator
|
||||||
|
*/
|
||||||
|
inline static void dma_sniffer_set_data_accumulator(uint32_t seed_value) {
|
||||||
|
dma_hw->sniff_data = seed_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Get the sniffer's data accumulator value
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* Read value calculated by the hardware from sniffing the DMA stream
|
||||||
|
*/
|
||||||
|
inline static uint32_t dma_sniffer_get_data_accumulator(void) {
|
||||||
|
return dma_hw->sniff_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Mark a dma timer as used
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* Method for cooperative claiming of hardware. Will cause a panic if the timer
|
||||||
|
* is already claimed. Use of this method by libraries detects accidental
|
||||||
|
* configurations that would fail in unpredictable ways.
|
||||||
|
*
|
||||||
|
* \param timer the dma timer
|
||||||
|
*/
|
||||||
|
void dma_timer_claim(uint timer);
|
||||||
|
|
||||||
|
/*! \brief Mark a dma timer as no longer used
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* Method for cooperative claiming of hardware.
|
||||||
|
*
|
||||||
|
* \param timer the dma timer to release
|
||||||
|
*/
|
||||||
|
void dma_timer_unclaim(uint timer);
|
||||||
|
|
||||||
|
/*! \brief Claim a free dma timer
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param required if true the function will panic if none are available
|
||||||
|
* \return the dma timer number or -1 if required was false, and none were free
|
||||||
|
*/
|
||||||
|
int dma_claim_unused_timer(bool required);
|
||||||
|
|
||||||
|
/*! \brief Determine if a dma timer is claimed
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param timer the dma timer
|
||||||
|
* \return true if the timer is claimed, false otherwise
|
||||||
|
* \see dma_timer_claim
|
||||||
|
*/
|
||||||
|
bool dma_timer_is_claimed(uint timer);
|
||||||
|
|
||||||
|
/*! \brief Set the divider for the given DMA timer
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* The timer will run at the system_clock_freq * numerator / denominator, so this is the speed
|
||||||
|
* that data elements will be transferred at via a DMA channel using this timer as a DREQ
|
||||||
|
*
|
||||||
|
* \param timer the dma timer
|
||||||
|
* \param numerator the fraction's numerator
|
||||||
|
* \param denominator the fraction's denominator
|
||||||
|
*/
|
||||||
|
static inline void dma_timer_set_fraction(uint timer, uint16_t numerator, uint16_t denominator) {
|
||||||
|
check_dma_timer_param(timer);
|
||||||
|
dma_hw->timer[timer] = (((uint32_t)numerator) << DMA_TIMER0_X_LSB) | (((uint32_t)denominator) << DMA_TIMER0_Y_LSB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Return the DREQ number for a given DMA timer
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* \param timer_num DMA timer number 0-3
|
||||||
|
*/
|
||||||
|
static inline uint dma_get_timer_dreq(uint timer_num) {
|
||||||
|
static_assert(DREQ_DMA_TIMER1 == DREQ_DMA_TIMER0 + 1, "");
|
||||||
|
static_assert(DREQ_DMA_TIMER2 == DREQ_DMA_TIMER0 + 2, "");
|
||||||
|
static_assert(DREQ_DMA_TIMER3 == DREQ_DMA_TIMER0 + 3, "");
|
||||||
|
check_dma_timer_param(timer_num);
|
||||||
|
return DREQ_DMA_TIMER0 + timer_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Performs DMA channel cleanup after use
|
||||||
|
* \ingroup hardware_dma
|
||||||
|
*
|
||||||
|
* This can be used to cleanup dma channels when they're no longer needed, such that they are in a clean state for reuse.
|
||||||
|
* IRQ's for the channel are disabled, any in flight-transfer is aborted and any outstanding interrupts are cleared.
|
||||||
|
* The channel is then clear to be reused for other purposes.
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* if (dma_channel >= 0) {
|
||||||
|
* dma_channel_cleanup(dma_channel);
|
||||||
|
* dma_channel_unclaim(dma_channel);
|
||||||
|
* dma_channel = -1;
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* \param channel DMA channel
|
||||||
|
*/
|
||||||
|
void dma_channel_cleanup(uint channel);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
void print_dma_ctrl(dma_channel_hw_t *channel);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,484 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HARDWARE_PIO_INSTRUCTIONS_H
|
||||||
|
#define _HARDWARE_PIO_INSTRUCTIONS_H
|
||||||
|
|
||||||
|
#include "pico.h"
|
||||||
|
|
||||||
|
/** \brief PIO instruction encoding
|
||||||
|
* \defgroup pio_instructions pio_instructions
|
||||||
|
* \ingroup hardware_pio
|
||||||
|
*
|
||||||
|
* Functions for generating PIO instruction encodings programmatically. In debug builds
|
||||||
|
*`PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` can be set to 1 to enable validation of encoding function
|
||||||
|
* parameters.
|
||||||
|
*
|
||||||
|
* For fuller descriptions of the instructions in question see the "RP2040 Datasheet"
|
||||||
|
*/
|
||||||
|
|
||||||
|
// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS, Enable/disable assertions in the PIO instructions, type=bool, default=0, group=pio_instructions
|
||||||
|
#ifndef PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS
|
||||||
|
#define PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum pio_instr_bits {
|
||||||
|
pio_instr_bits_jmp = 0x0000,
|
||||||
|
pio_instr_bits_wait = 0x2000,
|
||||||
|
pio_instr_bits_in = 0x4000,
|
||||||
|
pio_instr_bits_out = 0x6000,
|
||||||
|
pio_instr_bits_push = 0x8000,
|
||||||
|
pio_instr_bits_pull = 0x8080,
|
||||||
|
pio_instr_bits_mov = 0xa000,
|
||||||
|
pio_instr_bits_irq = 0xc000,
|
||||||
|
pio_instr_bits_set = 0xe000,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#define _PIO_INVALID_IN_SRC 0x08u
|
||||||
|
#define _PIO_INVALID_OUT_DEST 0x10u
|
||||||
|
#define _PIO_INVALID_SET_DEST 0x20u
|
||||||
|
#define _PIO_INVALID_MOV_SRC 0x40u
|
||||||
|
#define _PIO_INVALID_MOV_DEST 0x80u
|
||||||
|
#else
|
||||||
|
#define _PIO_INVALID_IN_SRC 0u
|
||||||
|
#define _PIO_INVALID_OUT_DEST 0u
|
||||||
|
#define _PIO_INVALID_SET_DEST 0u
|
||||||
|
#define _PIO_INVALID_MOV_SRC 0u
|
||||||
|
#define _PIO_INVALID_MOV_DEST 0u
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*! \brief Enumeration of values to pass for source/destination args for instruction encoding functions
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* \note Not all values are suitable for all functions. Validity is only checked in debug mode when
|
||||||
|
* `PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` is 1
|
||||||
|
*/
|
||||||
|
enum pio_src_dest {
|
||||||
|
pio_pins = 0u,
|
||||||
|
pio_x = 1u,
|
||||||
|
pio_y = 2u,
|
||||||
|
pio_null = 3u | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST,
|
||||||
|
pio_pindirs = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST,
|
||||||
|
pio_exec_mov = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC,
|
||||||
|
pio_status = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST,
|
||||||
|
pio_pc = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC,
|
||||||
|
pio_isr = 6u | _PIO_INVALID_SET_DEST,
|
||||||
|
pio_osr = 7u | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST,
|
||||||
|
pio_exec_out = 7u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline uint _pio_major_instr_bits(uint instr) {
|
||||||
|
return instr & 0xe000u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint _pio_encode_instr_and_args(enum pio_instr_bits instr_bits, uint arg1, uint arg2) {
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, arg1 <= 0x7);
|
||||||
|
#if PARAM_ASSERTIONS_ENABLED(PIO_INSTRUCTIONS)
|
||||||
|
uint32_t major = _pio_major_instr_bits(instr_bits);
|
||||||
|
if (major == pio_instr_bits_in || major == pio_instr_bits_out) {
|
||||||
|
assert(arg2 && arg2 <= 32);
|
||||||
|
} else {
|
||||||
|
assert(arg2 <= 31);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return instr_bits | (arg1 << 5u) | (arg2 & 0x1fu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint _pio_encode_instr_and_src_dest(enum pio_instr_bits instr_bits, enum pio_src_dest dest, uint value) {
|
||||||
|
return _pio_encode_instr_and_args(instr_bits, dest & 7u, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode just the delay slot bits of an instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* \note This function does not return a valid instruction encoding; instead it returns an encoding of the delay
|
||||||
|
* slot suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when
|
||||||
|
* combining the results of this function with the results of \ref pio_encode_sideset and \ref pio_encode_sideset_opt
|
||||||
|
* as they share the same bits within the instruction encoding.
|
||||||
|
*
|
||||||
|
* \param cycles the number of cycles 0-31 (or less if side set is being used)
|
||||||
|
* \return the delay slot bits to be ORed with an instruction encoding
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_delay(uint cycles) {
|
||||||
|
// note that the maximum cycles will be smaller if sideset_bit_count > 0
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, cycles <= 0x1f);
|
||||||
|
return cycles << 8u;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode just the side set bits of an instruction (in non optional side set mode)
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits
|
||||||
|
* suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when
|
||||||
|
* combining the results of this function with the results of \ref pio_encode_delay as they share the same bits
|
||||||
|
* within the instruction encoding.
|
||||||
|
*
|
||||||
|
* \param sideset_bit_count number of side set bits as would be specified via `.sideset` in pioasm
|
||||||
|
* \param value the value to sideset on the pins
|
||||||
|
* \return the side set bits to be ORed with an instruction encoding
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_sideset(uint sideset_bit_count, uint value) {
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 5);
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1));
|
||||||
|
return value << (13u - sideset_bit_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode just the side set bits of an instruction (in optional -`opt` side set mode)
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits
|
||||||
|
* suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when
|
||||||
|
* combining the results of this function with the results of \ref pio_encode_delay as they share the same bits
|
||||||
|
* within the instruction encoding.
|
||||||
|
*
|
||||||
|
* \param sideset_bit_count number of side set bits as would be specified via `.sideset <n> opt` in pioasm
|
||||||
|
* \param value the value to sideset on the pins
|
||||||
|
* \return the side set bits to be ORed with an instruction encoding
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_sideset_opt(uint sideset_bit_count, uint value) {
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 4);
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1));
|
||||||
|
return 0x1000u | value << (12u - sideset_bit_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode an unconditional JMP instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `JMP <addr>`
|
||||||
|
*
|
||||||
|
* \param addr The target address 0-31 (an absolute address within the PIO instruction memory)
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_jmp(uint addr) {
|
||||||
|
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 0, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a conditional JMP if scratch X zero instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `JMP !X <addr>`
|
||||||
|
*
|
||||||
|
* \param addr The target address 0-31 (an absolute address within the PIO instruction memory)
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_jmp_not_x(uint addr) {
|
||||||
|
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 1, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a conditional JMP if scratch X non-zero (and post-decrement X) instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `JMP X-- <addr>`
|
||||||
|
*
|
||||||
|
* \param addr The target address 0-31 (an absolute address within the PIO instruction memory)
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_jmp_x_dec(uint addr) {
|
||||||
|
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 2, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a conditional JMP if scratch Y zero instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `JMP !Y <addr>`
|
||||||
|
*
|
||||||
|
* \param addr The target address 0-31 (an absolute address within the PIO instruction memory)
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_jmp_not_y(uint addr) {
|
||||||
|
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 3, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a conditional JMP if scratch Y non-zero (and post-decrement Y) instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `JMP Y-- <addr>`
|
||||||
|
*
|
||||||
|
* \param addr The target address 0-31 (an absolute address within the PIO instruction memory)
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_jmp_y_dec(uint addr) {
|
||||||
|
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 4, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a conditional JMP if scratch X not equal scratch Y instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `JMP X!=Y <addr>`
|
||||||
|
*
|
||||||
|
* \param addr The target address 0-31 (an absolute address within the PIO instruction memory)
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_jmp_x_ne_y(uint addr) {
|
||||||
|
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 5, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a conditional JMP if input pin high instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `JMP PIN <addr>`
|
||||||
|
*
|
||||||
|
* \param addr The target address 0-31 (an absolute address within the PIO instruction memory)
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_jmp_pin(uint addr) {
|
||||||
|
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 6, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a conditional JMP if output shift register not empty instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `JMP !OSRE <addr>`
|
||||||
|
*
|
||||||
|
* \param addr The target address 0-31 (an absolute address within the PIO instruction memory)
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_jmp_not_osre(uint addr) {
|
||||||
|
return _pio_encode_instr_and_args(pio_instr_bits_jmp, 7, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint _pio_encode_irq(bool relative, uint irq) {
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, irq <= 7);
|
||||||
|
return (relative ? 0x10u : 0x0u) | irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a WAIT for GPIO pin instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `WAIT <polarity> GPIO <gpio>`
|
||||||
|
*
|
||||||
|
* \param polarity true for `WAIT 1`, false for `WAIT 0`
|
||||||
|
* \param gpio The real GPIO number 0-31
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_wait_gpio(bool polarity, uint gpio) {
|
||||||
|
return _pio_encode_instr_and_args(pio_instr_bits_wait, 0u | (polarity ? 4u : 0u), gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a WAIT for pin instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `WAIT <polarity> PIN <pin>`
|
||||||
|
*
|
||||||
|
* \param polarity true for `WAIT 1`, false for `WAIT 0`
|
||||||
|
* \param pin The pin number 0-31 relative to the executing SM's input pin mapping
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_wait_pin(bool polarity, uint pin) {
|
||||||
|
return _pio_encode_instr_and_args(pio_instr_bits_wait, 1u | (polarity ? 4u : 0u), pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a WAIT for IRQ instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `WAIT <polarity> IRQ <irq> <relative>`
|
||||||
|
*
|
||||||
|
* \param polarity true for `WAIT 1`, false for `WAIT 0`
|
||||||
|
* \param relative true for a `WAIT IRQ <irq> REL`, false for regular `WAIT IRQ <irq>`
|
||||||
|
* \param irq the irq number 0-7
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_wait_irq(bool polarity, bool relative, uint irq) {
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, irq <= 7);
|
||||||
|
return _pio_encode_instr_and_args(pio_instr_bits_wait, 2u | (polarity ? 4u : 0u), _pio_encode_irq(relative, irq));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode an IN instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `IN <src>, <count>`
|
||||||
|
*
|
||||||
|
* \param src The source to take data from
|
||||||
|
* \param count The number of bits 1-32
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_in(enum pio_src_dest src, uint count) {
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_IN_SRC));
|
||||||
|
return _pio_encode_instr_and_src_dest(pio_instr_bits_in, src, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode an OUT instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `OUT <src>, <count>`
|
||||||
|
*
|
||||||
|
* \param dest The destination to write data to
|
||||||
|
* \param count The number of bits 1-32
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_out(enum pio_src_dest dest, uint count) {
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_OUT_DEST));
|
||||||
|
return _pio_encode_instr_and_src_dest(pio_instr_bits_out, dest, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a PUSH instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `PUSH <if_full>, <block>`
|
||||||
|
*
|
||||||
|
* \param if_full true for `PUSH IF_FULL ...`, false for `PUSH ...`
|
||||||
|
* \param block true for `PUSH ... BLOCK`, false for `PUSH ...`
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_push(bool if_full, bool block) {
|
||||||
|
return _pio_encode_instr_and_args(pio_instr_bits_push, (if_full ? 2u : 0u) | (block ? 1u : 0u), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a PULL instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `PULL <if_empty>, <block>`
|
||||||
|
*
|
||||||
|
* \param if_empty true for `PULL IF_EMPTY ...`, false for `PULL ...`
|
||||||
|
* \param block true for `PULL ... BLOCK`, false for `PULL ...`
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_pull(bool if_empty, bool block) {
|
||||||
|
return _pio_encode_instr_and_args(pio_instr_bits_pull, (if_empty ? 2u : 0u) | (block ? 1u : 0u), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a MOV instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `MOV <dest>, <src>`
|
||||||
|
*
|
||||||
|
* \param dest The destination to write data to
|
||||||
|
* \param src The source to take data from
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_mov(enum pio_src_dest dest, enum pio_src_dest src) {
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST));
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC));
|
||||||
|
return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, src & 7u);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a MOV instruction with bit invert
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `MOV <dest>, ~<src>`
|
||||||
|
*
|
||||||
|
* \param dest The destination to write inverted data to
|
||||||
|
* \param src The source to take data from
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_mov_not(enum pio_src_dest dest, enum pio_src_dest src) {
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST));
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC));
|
||||||
|
return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (1u << 3u) | (src & 7u));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a MOV instruction with bit reverse
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `MOV <dest>, ::<src>`
|
||||||
|
*
|
||||||
|
* \param dest The destination to write bit reversed data to
|
||||||
|
* \param src The source to take data from
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_mov_reverse(enum pio_src_dest dest, enum pio_src_dest src) {
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST));
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC));
|
||||||
|
return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (2u << 3u) | (src & 7u));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a IRQ SET instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `IRQ SET <irq> <relative>`
|
||||||
|
*
|
||||||
|
* \param relative true for a `IRQ SET <irq> REL`, false for regular `IRQ SET <irq>`
|
||||||
|
* \param irq the irq number 0-7
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_irq_set(bool relative, uint irq) {
|
||||||
|
return _pio_encode_instr_and_args(pio_instr_bits_irq, 0, _pio_encode_irq(relative, irq));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a IRQ WAIT instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `IRQ WAIT <irq> <relative>`
|
||||||
|
*
|
||||||
|
* \param relative true for a `IRQ WAIT <irq> REL`, false for regular `IRQ WAIT <irq>`
|
||||||
|
* \param irq the irq number 0-7
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_irq_wait(bool relative, uint irq) {
|
||||||
|
return _pio_encode_instr_and_args(pio_instr_bits_irq, 1, _pio_encode_irq(relative, irq));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a IRQ CLEAR instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `IRQ CLEAR <irq> <relative>`
|
||||||
|
*
|
||||||
|
* \param relative true for a `IRQ CLEAR <irq> REL`, false for regular `IRQ CLEAR <irq>`
|
||||||
|
* \param irq the irq number 0-7
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_irq_clear(bool relative, uint irq) {
|
||||||
|
return _pio_encode_instr_and_args(pio_instr_bits_irq, 2, _pio_encode_irq(relative, irq));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a SET instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `SET <dest>, <value>`
|
||||||
|
*
|
||||||
|
* \param dest The destination to apply the value to
|
||||||
|
* \param value The value 0-31
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_set(enum pio_src_dest dest, uint value) {
|
||||||
|
valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_SET_DEST));
|
||||||
|
return _pio_encode_instr_and_src_dest(pio_instr_bits_set, dest, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Encode a NOP instruction
|
||||||
|
* \ingroup pio_instructions
|
||||||
|
*
|
||||||
|
* This is the equivalent of `NOP` which is itself encoded as `MOV y, y`
|
||||||
|
*
|
||||||
|
* \return The instruction encoding with 0 delay and no side set value
|
||||||
|
* \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt
|
||||||
|
*/
|
||||||
|
static inline uint pio_encode_nop(void) {
|
||||||
|
return pio_encode_mov(pio_y, pio_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,268 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hardware/claim.h"
|
||||||
|
#include "hardware/pio.h"
|
||||||
|
#include "hardware/pio_instructions.h"
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
check_hw_layout(pio_hw_t, sm[0].clkdiv, PIO_SM0_CLKDIV_OFFSET);
|
||||||
|
check_hw_layout(pio_hw_t, sm[1].clkdiv, PIO_SM1_CLKDIV_OFFSET);
|
||||||
|
check_hw_layout(pio_hw_t, instr_mem[0], PIO_INSTR_MEM0_OFFSET);
|
||||||
|
check_hw_layout(pio_hw_t, inte0, PIO_IRQ0_INTE_OFFSET);
|
||||||
|
check_hw_layout(pio_hw_t, txf[1], PIO_TXF1_OFFSET);
|
||||||
|
check_hw_layout(pio_hw_t, rxf[3], PIO_RXF3_OFFSET);
|
||||||
|
check_hw_layout(pio_hw_t, ints1, PIO_IRQ1_INTS_OFFSET);
|
||||||
|
|
||||||
|
static_assert(NUM_PIO_STATE_MACHINES * NUM_PIOS <= 8, "");
|
||||||
|
static uint8_t claimed;
|
||||||
|
|
||||||
|
void pio_sm_claim(PIO pio, uint sm) {
|
||||||
|
check_sm_param(sm);
|
||||||
|
uint which = pio_get_index(pio);
|
||||||
|
if (which) {
|
||||||
|
hw_claim_or_assert(&claimed, NUM_PIO_STATE_MACHINES + sm, "PIO 1 SM (%d - 4) already claimed");
|
||||||
|
} else {
|
||||||
|
hw_claim_or_assert(&claimed, sm, "PIO 0 SM %d already claimed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pio_claim_sm_mask(PIO pio, uint sm_mask) {
|
||||||
|
for(uint i = 0; sm_mask; i++, sm_mask >>= 1u) {
|
||||||
|
if (sm_mask & 1u) pio_sm_claim(pio, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pio_sm_unclaim(PIO pio, uint sm) {
|
||||||
|
check_sm_param(sm);
|
||||||
|
uint which = pio_get_index(pio);
|
||||||
|
hw_claim_clear(&claimed, which * NUM_PIO_STATE_MACHINES + sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pio_claim_unused_sm(PIO pio, bool required) {
|
||||||
|
// PIO index is 0 or 1.
|
||||||
|
uint which = pio_get_index(pio);
|
||||||
|
uint base = which * NUM_PIO_STATE_MACHINES;
|
||||||
|
int index = hw_claim_unused_from_range((uint8_t*)&claimed, required, base,
|
||||||
|
base + NUM_PIO_STATE_MACHINES - 1, "No PIO state machines are available");
|
||||||
|
return index >= (int)base ? index - (int)base : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pio_sm_is_claimed(PIO pio, uint sm) {
|
||||||
|
check_sm_param(sm);
|
||||||
|
uint which = pio_get_index(pio);
|
||||||
|
return hw_is_claimed(&claimed, which * NUM_PIO_STATE_MACHINES + sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert(PIO_INSTRUCTION_COUNT <= 32, "");
|
||||||
|
static uint32_t _used_instruction_space[2];
|
||||||
|
|
||||||
|
static int _pio_find_offset_for_program(PIO pio, const pio_program_t *program) {
|
||||||
|
assert(program->length <= PIO_INSTRUCTION_COUNT);
|
||||||
|
uint32_t used_mask = _used_instruction_space[pio_get_index(pio)];
|
||||||
|
uint32_t program_mask = (1u << program->length) - 1;
|
||||||
|
if (program->origin >= 0) {
|
||||||
|
if (program->origin > 32 - program->length) return -1;
|
||||||
|
return used_mask & (program_mask << program->origin) ? -1 : program->origin;
|
||||||
|
} else {
|
||||||
|
// work down from the top always
|
||||||
|
for (int i = 32 - program->length; i >= 0; i--) {
|
||||||
|
if (!(used_mask & (program_mask << (uint) i))) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pio_can_add_program(PIO pio, const pio_program_t *program) {
|
||||||
|
uint32_t save = hw_claim_lock();
|
||||||
|
bool rc = -1 != _pio_find_offset_for_program(pio, program);
|
||||||
|
hw_claim_unlock(save);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _pio_can_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) {
|
||||||
|
valid_params_if(PIO, offset < PIO_INSTRUCTION_COUNT);
|
||||||
|
valid_params_if(PIO, offset + program->length <= PIO_INSTRUCTION_COUNT);
|
||||||
|
if (program->origin >= 0 && (uint)program->origin != offset) return false;
|
||||||
|
uint32_t used_mask = _used_instruction_space[pio_get_index(pio)];
|
||||||
|
uint32_t program_mask = (1u << program->length) - 1;
|
||||||
|
return !(used_mask & (program_mask << offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pio_can_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) {
|
||||||
|
uint32_t save = hw_claim_lock();
|
||||||
|
bool rc = _pio_can_add_program_at_offset(pio, program, offset);
|
||||||
|
hw_claim_unlock(save);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _pio_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) {
|
||||||
|
if (!_pio_can_add_program_at_offset(pio, program, offset)) {
|
||||||
|
panic("No program space");
|
||||||
|
}
|
||||||
|
for (uint i = 0; i < program->length; ++i) {
|
||||||
|
uint16_t instr = program->instructions[i];
|
||||||
|
pio->instr_mem[offset + i] = pio_instr_bits_jmp != _pio_major_instr_bits(instr) ? instr : instr + offset;
|
||||||
|
}
|
||||||
|
uint32_t program_mask = (1u << program->length) - 1;
|
||||||
|
_used_instruction_space[pio_get_index(pio)] |= program_mask << offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// these assert if unable
|
||||||
|
uint pio_add_program(PIO pio, const pio_program_t *program) {
|
||||||
|
uint32_t save = hw_claim_lock();
|
||||||
|
int offset = _pio_find_offset_for_program(pio, program);
|
||||||
|
if (offset < 0) {
|
||||||
|
panic("No program space");
|
||||||
|
}
|
||||||
|
_pio_add_program_at_offset(pio, program, (uint)offset);
|
||||||
|
hw_claim_unlock(save);
|
||||||
|
return (uint)offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pio_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) {
|
||||||
|
uint32_t save = hw_claim_lock();
|
||||||
|
_pio_add_program_at_offset(pio, program, offset);
|
||||||
|
hw_claim_unlock(save);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pio_remove_program(PIO pio, const pio_program_t *program, uint loaded_offset) {
|
||||||
|
uint32_t program_mask = (1u << program->length) - 1;
|
||||||
|
program_mask <<= loaded_offset;
|
||||||
|
uint32_t save = hw_claim_lock();
|
||||||
|
assert(program_mask == (_used_instruction_space[pio_get_index(pio)] & program_mask));
|
||||||
|
_used_instruction_space[pio_get_index(pio)] &= ~program_mask;
|
||||||
|
hw_claim_unlock(save);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pio_clear_instruction_memory(PIO pio) {
|
||||||
|
uint32_t save = hw_claim_lock();
|
||||||
|
_used_instruction_space[pio_get_index(pio)] = 0;
|
||||||
|
for(uint i=0;i<PIO_INSTRUCTION_COUNT;i++) {
|
||||||
|
pio->instr_mem[i] = pio_encode_jmp(i);
|
||||||
|
}
|
||||||
|
hw_claim_unlock(save);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the value of all PIO pins. This is done by forcibly executing
|
||||||
|
// instructions on a "victim" state machine, sm. Ideally you should choose one
|
||||||
|
// which is not currently running a program. This is intended for one-time
|
||||||
|
// setup of initial pin states.
|
||||||
|
void pio_sm_set_pins(PIO pio, uint sm, uint32_t pins) {
|
||||||
|
check_pio_param(pio);
|
||||||
|
check_sm_param(sm);
|
||||||
|
uint32_t pinctrl_saved = pio->sm[sm].pinctrl;
|
||||||
|
uint32_t execctrl_saved = pio->sm[sm].execctrl;
|
||||||
|
hw_clear_bits(&pio->sm[sm].execctrl, 1u << PIO_SM0_EXECCTRL_OUT_STICKY_LSB);
|
||||||
|
uint remaining = 32;
|
||||||
|
uint base = 0;
|
||||||
|
while (remaining) {
|
||||||
|
uint decrement = remaining > 5 ? 5 : remaining;
|
||||||
|
pio->sm[sm].pinctrl =
|
||||||
|
(decrement << PIO_SM0_PINCTRL_SET_COUNT_LSB) |
|
||||||
|
(base << PIO_SM0_PINCTRL_SET_BASE_LSB);
|
||||||
|
pio_sm_exec(pio, sm, pio_encode_set(pio_pins, pins & 0x1fu));
|
||||||
|
remaining -= decrement;
|
||||||
|
base += decrement;
|
||||||
|
pins >>= 5;
|
||||||
|
}
|
||||||
|
pio->sm[sm].pinctrl = pinctrl_saved;
|
||||||
|
pio->sm[sm].execctrl = execctrl_saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pio_sm_set_pins_with_mask(PIO pio, uint sm, uint32_t pinvals, uint32_t pin_mask) {
|
||||||
|
check_pio_param(pio);
|
||||||
|
check_sm_param(sm);
|
||||||
|
uint32_t pinctrl_saved = pio->sm[sm].pinctrl;
|
||||||
|
uint32_t execctrl_saved = pio->sm[sm].execctrl;
|
||||||
|
hw_clear_bits(&pio->sm[sm].execctrl, 1u << PIO_SM0_EXECCTRL_OUT_STICKY_LSB);
|
||||||
|
while (pin_mask) {
|
||||||
|
uint base = (uint)__builtin_ctz(pin_mask);
|
||||||
|
pio->sm[sm].pinctrl =
|
||||||
|
(1u << PIO_SM0_PINCTRL_SET_COUNT_LSB) |
|
||||||
|
(base << PIO_SM0_PINCTRL_SET_BASE_LSB);
|
||||||
|
pio_sm_exec(pio, sm, pio_encode_set(pio_pins, (pinvals >> base) & 0x1u));
|
||||||
|
pin_mask &= pin_mask - 1;
|
||||||
|
}
|
||||||
|
pio->sm[sm].pinctrl = pinctrl_saved;
|
||||||
|
pio->sm[sm].execctrl = execctrl_saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pio_sm_set_pindirs_with_mask(PIO pio, uint sm, uint32_t pindirs, uint32_t pin_mask) {
|
||||||
|
check_pio_param(pio);
|
||||||
|
check_sm_param(sm);
|
||||||
|
uint32_t pinctrl_saved = pio->sm[sm].pinctrl;
|
||||||
|
uint32_t execctrl_saved = pio->sm[sm].execctrl;
|
||||||
|
hw_clear_bits(&pio->sm[sm].execctrl, 1u << PIO_SM0_EXECCTRL_OUT_STICKY_LSB);
|
||||||
|
while (pin_mask) {
|
||||||
|
uint base = (uint)__builtin_ctz(pin_mask);
|
||||||
|
pio->sm[sm].pinctrl =
|
||||||
|
(1u << PIO_SM0_PINCTRL_SET_COUNT_LSB) |
|
||||||
|
(base << PIO_SM0_PINCTRL_SET_BASE_LSB);
|
||||||
|
pio_sm_exec(pio, sm, pio_encode_set(pio_pindirs, (pindirs >> base) & 0x1u));
|
||||||
|
pin_mask &= pin_mask - 1;
|
||||||
|
}
|
||||||
|
pio->sm[sm].pinctrl = pinctrl_saved;
|
||||||
|
pio->sm[sm].execctrl = execctrl_saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pio_sm_set_consecutive_pindirs(PIO pio, uint sm, uint pin, uint count, bool is_out) {
|
||||||
|
check_pio_param(pio);
|
||||||
|
check_sm_param(sm);
|
||||||
|
valid_params_if(PIO, pin < 32u);
|
||||||
|
uint32_t pinctrl_saved = pio->sm[sm].pinctrl;
|
||||||
|
uint32_t execctrl_saved = pio->sm[sm].execctrl;
|
||||||
|
hw_clear_bits(&pio->sm[sm].execctrl, 1u << PIO_SM0_EXECCTRL_OUT_STICKY_LSB);
|
||||||
|
uint pindir_val = is_out ? 0x1f : 0;
|
||||||
|
while (count > 5) {
|
||||||
|
pio->sm[sm].pinctrl = (5u << PIO_SM0_PINCTRL_SET_COUNT_LSB) | (pin << PIO_SM0_PINCTRL_SET_BASE_LSB);
|
||||||
|
pio_sm_exec(pio, sm, pio_encode_set(pio_pindirs, pindir_val));
|
||||||
|
count -= 5;
|
||||||
|
pin = (pin + 5) & 0x1f;
|
||||||
|
}
|
||||||
|
pio->sm[sm].pinctrl = (count << PIO_SM0_PINCTRL_SET_COUNT_LSB) | (pin << PIO_SM0_PINCTRL_SET_BASE_LSB);
|
||||||
|
pio_sm_exec(pio, sm, pio_encode_set(pio_pindirs, pindir_val));
|
||||||
|
pio->sm[sm].pinctrl = pinctrl_saved;
|
||||||
|
pio->sm[sm].execctrl = execctrl_saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pio_sm_init(PIO pio, uint sm, uint initial_pc, const pio_sm_config *config) {
|
||||||
|
valid_params_if(PIO, initial_pc < PIO_INSTRUCTION_COUNT);
|
||||||
|
// Halt the machine, set some sensible defaults
|
||||||
|
pio_sm_set_enabled(pio, sm, false);
|
||||||
|
|
||||||
|
if (config) {
|
||||||
|
pio_sm_set_config(pio, sm, config);
|
||||||
|
} else {
|
||||||
|
pio_sm_config c = pio_get_default_sm_config();
|
||||||
|
pio_sm_set_config(pio, sm, &c);
|
||||||
|
}
|
||||||
|
|
||||||
|
pio_sm_clear_fifos(pio, sm);
|
||||||
|
|
||||||
|
// Clear FIFO debug flags
|
||||||
|
const uint32_t fdebug_sm_mask =
|
||||||
|
(1u << PIO_FDEBUG_TXOVER_LSB) |
|
||||||
|
(1u << PIO_FDEBUG_RXUNDER_LSB) |
|
||||||
|
(1u << PIO_FDEBUG_TXSTALL_LSB) |
|
||||||
|
(1u << PIO_FDEBUG_RXSTALL_LSB);
|
||||||
|
pio->fdebug = fdebug_sm_mask << sm;
|
||||||
|
|
||||||
|
// Finally, clear some internal SM state
|
||||||
|
pio_sm_restart(pio, sm);
|
||||||
|
pio_sm_clkdiv_restart(pio, sm);
|
||||||
|
pio_sm_exec(pio, sm, pio_encode_jmp(initial_pc));
|
||||||
|
}
|
||||||
|
|
||||||
|
void pio_sm_drain_tx_fifo(PIO pio, uint sm) {
|
||||||
|
uint instr = (pio->sm[sm].shiftctrl & PIO_SM0_SHIFTCTRL_AUTOPULL_BITS) ? pio_encode_out(pio_null, 32) :
|
||||||
|
pio_encode_pull(false, false);
|
||||||
|
while (!pio_sm_is_tx_fifo_empty(pio, sm)) {
|
||||||
|
pio_sm_exec(pio, sm, instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,53 +12,54 @@ Mbed ones. This script takes care of that too, and provides a one-click way to
|
||||||
import pathlib
|
import pathlib
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
import re
|
||||||
from typing import List, Tuple
|
from typing import List, Tuple
|
||||||
|
|
||||||
this_script_dir = pathlib.Path(__file__).resolve().parent
|
this_script_dir = pathlib.Path(__file__).resolve().parent
|
||||||
|
|
||||||
# List of identifiers to rename b/c they clash with Mbed symbols
|
# List of identifiers to rename b/c they clash with Mbed symbols
|
||||||
IDENTIFIERS_TO_RENAME: List[Tuple[bytes, bytes]] = [
|
IDENTIFIERS_TO_RENAME: List[Tuple[str, str]] = [
|
||||||
(b"gpio_irq_handler", b"pico_sdk_gpio_irq_handler"),
|
(r"gpio_irq_handler", r"pico_sdk_gpio_irq_handler"),
|
||||||
(b"gpio_init", b"pico_sdk_gpio_init"),
|
(r"gpio_init", r"pico_sdk_gpio_init"),
|
||||||
(b"i2c_init", b"pico_sdk_i2c_init"),
|
(r"i2c_init", r"pico_sdk_i2c_init"),
|
||||||
(b"rtc_init", b"pico_sdk_rtc_init"),
|
(r"rtc_init", r"pico_sdk_rtc_init"),
|
||||||
(b"spi_init", b"pico_sdk_spi_init"),
|
(r"spi_init", r"pico_sdk_spi_init"),
|
||||||
|
|
||||||
# Rename IRQ handlers to the CMSIS exception names.
|
# Rename IRQ handlers to the CMSIS exception names.
|
||||||
# Pico SDK does this with macros, but easier to just
|
# Pico SDK does this with macros, but easier to just
|
||||||
# do it here.
|
# do it here.
|
||||||
# Based on cmsis/include/rename_exceptions.h.
|
# Based on cmsis/include/rename_exceptions.h.
|
||||||
(b"isr_nmi", b"NMI_Handler"),
|
(r"isr_nmi", r"NMI_Handler"),
|
||||||
(b"isr_hardfault", b"HardFault_Handler"),
|
(r"isr_hardfault", r"HardFault_Handler"),
|
||||||
(b"isr_svcall", b"SVC_Handler"),
|
(r"isr_svcall", r"SVC_Handler"),
|
||||||
(b"isr_pendsv", b"PendSV_Handler"),
|
(r"isr_pendsv", r"PendSV_Handler"),
|
||||||
(b"isr_systick", b"SysTick_Handler"),
|
(r"isr_systick", r"SysTick_Handler"),
|
||||||
(b"isr_irq0", b"TIMER_IRQ_0_Handler"),
|
(r"isr_irq0", r"TIMER_IRQ_0_Handler"),
|
||||||
(b"isr_irq1", b"TIMER_IRQ_1_Handler"),
|
(r"isr_irq1", r"TIMER_IRQ_1_Handler"),
|
||||||
(b"isr_irq2", b"TIMER_IRQ_2_Handler"),
|
(r"isr_irq2", r"TIMER_IRQ_2_Handler"),
|
||||||
(b"isr_irq3", b"TIMER_IRQ_3_Handler"),
|
(r"isr_irq3", r"TIMER_IRQ_3_Handler"),
|
||||||
(b"isr_irq4", b"PWM_IRQ_WRAP_Handler"),
|
(r"isr_irq4", r"PWM_IRQ_WRAP_Handler"),
|
||||||
(b"isr_irq5", b"USBCTRL_IRQ_Handler"),
|
(r"isr_irq5", r"USBCTRL_IRQ_Handler"),
|
||||||
(b"isr_irq6", b"XIP_IRQ_Handler"),
|
(r"isr_irq6", r"XIP_IRQ_Handler"),
|
||||||
(b"isr_irq7", b"PIO0_IRQ_0_Handler"),
|
(r"isr_irq7", r"PIO0_IRQ_0_Handler"),
|
||||||
(b"isr_irq8", b"PIO0_IRQ_1_Handler"),
|
(r"isr_irq8", r"PIO0_IRQ_1_Handler"),
|
||||||
(b"isr_irq9", b"PIO1_IRQ_0_Handler"),
|
(r"isr_irq9", r"PIO1_IRQ_0_Handler"),
|
||||||
(b"isr_irq10", b"PIO1_IRQ_1_Handler"),
|
(r"isr_irq10", r"PIO1_IRQ_1_Handler"),
|
||||||
(b"isr_irq11", b"DMA_IRQ_0_Handler"),
|
(r"isr_irq11", r"DMA_IRQ_0_Handler"),
|
||||||
(b"isr_irq12", b"DMA_IRQ_1_Handler"),
|
(r"isr_irq12", r"DMA_IRQ_1_Handler"),
|
||||||
(b"isr_irq13", b"IO_IRQ_BANK0_Handler"),
|
(r"isr_irq13", r"IO_IRQ_BANK0_Handler"),
|
||||||
(b"isr_irq14", b"IO_IRQ_QSPI_Handler"),
|
(r"isr_irq14", r"IO_IRQ_QSPI_Handler"),
|
||||||
(b"isr_irq15", b"SIO_IRQ_PROC0_Handler"),
|
(r"isr_irq15", r"SIO_IRQ_PROC0_Handler"),
|
||||||
(b"isr_irq16", b"SIO_IRQ_PROC1_Handler"),
|
(r"isr_irq16", r"SIO_IRQ_PROC1_Handler"),
|
||||||
(b"isr_irq17", b"CLOCKS_IRQ_Handler"),
|
(r"isr_irq17", r"CLOCKS_IRQ_Handler"),
|
||||||
(b"isr_irq18", b"SPI0_IRQ_Handler"),
|
(r"isr_irq18", r"SPI0_IRQ_Handler"),
|
||||||
(b"isr_irq19", b"SPI1_IRQ_Handler"),
|
(r"isr_irq19", r"SPI1_IRQ_Handler"),
|
||||||
(b"isr_irq20", b"UART0_IRQ_Handler"),
|
(r"isr_irq20", r"UART0_IRQ_Handler"),
|
||||||
(b"isr_irq21", b"UART1_IRQ_Handler"),
|
(r"isr_irq21", r"UART1_IRQ_Handler"),
|
||||||
(b"isr_irq22", b"ADC_IRQ_FIFO_Handler"),
|
(r"isr_irq22", r"ADC_IRQ_FIFO_Handler"),
|
||||||
(b"isr_irq23", b"I2C0_IRQ_Handler"),
|
(r"isr_irq23", r"I2C0_IRQ_Handler"),
|
||||||
(b"isr_irq24", b"I2C1_IRQ_Handler"),
|
(r"isr_irq24", r"I2C1_IRQ_Handler"),
|
||||||
(b"isr_irq25", b"RTC_IRQ_Handler"),
|
(r"isr_irq25", r"RTC_IRQ_Handler"),
|
||||||
]
|
]
|
||||||
|
|
||||||
# List of files and directories which need to be copied into Mbed.
|
# List of files and directories which need to be copied into Mbed.
|
||||||
|
|
@ -85,6 +86,8 @@ FILES_DIRS_TO_COPY: List[pathlib.Path] = [
|
||||||
pathlib.Path("src") / "rp2_common" / "hardware_timer",
|
pathlib.Path("src") / "rp2_common" / "hardware_timer",
|
||||||
pathlib.Path("src") / "rp2_common" / "hardware_sync",
|
pathlib.Path("src") / "rp2_common" / "hardware_sync",
|
||||||
pathlib.Path("src") / "rp2_common" / "hardware_rtc",
|
pathlib.Path("src") / "rp2_common" / "hardware_rtc",
|
||||||
|
pathlib.Path("src") / "rp2_common" / "hardware_pio",
|
||||||
|
pathlib.Path("src") / "rp2_common" / "hardware_dma",
|
||||||
pathlib.Path("src") / "rp2_common" / "pico_bootrom",
|
pathlib.Path("src") / "rp2_common" / "pico_bootrom",
|
||||||
pathlib.Path("src") / "rp2_common" / "pico_platform",
|
pathlib.Path("src") / "rp2_common" / "pico_platform",
|
||||||
pathlib.Path("src") / "rp2_common" / "pico_float",
|
pathlib.Path("src") / "rp2_common" / "pico_float",
|
||||||
|
|
@ -137,11 +140,12 @@ for source_file_path in files_to_copy:
|
||||||
|
|
||||||
# Load file
|
# Load file
|
||||||
with open(source_file_path, "rb") as source_file:
|
with open(source_file_path, "rb") as source_file:
|
||||||
source_file_contents = source_file.read()
|
source_file_contents = source_file.read().decode("UTF-8")
|
||||||
|
|
||||||
# Perform replacements
|
# Perform replacements
|
||||||
for old_identifier, new_identifier in IDENTIFIERS_TO_RENAME:
|
for old_identifier, new_identifier in IDENTIFIERS_TO_RENAME:
|
||||||
source_file_contents = source_file_contents.replace(old_identifier, new_identifier)
|
# Always require one non-word character before each replacement so we can do a full word match
|
||||||
|
source_file_contents = re.sub(r"(\W)" + old_identifier, r"\1" + new_identifier, source_file_contents)
|
||||||
|
|
||||||
# Figure out new path
|
# Figure out new path
|
||||||
relative_path = source_file_path.relative_to(sdk_path)
|
relative_path = source_file_path.relative_to(sdk_path)
|
||||||
|
|
@ -150,6 +154,6 @@ for source_file_path in files_to_copy:
|
||||||
# Write new contents
|
# Write new contents
|
||||||
dest_file_path.parent.mkdir(parents=True, exist_ok=True)
|
dest_file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
with open(dest_file_path, "wb") as dest_file:
|
with open(dest_file_path, "wb") as dest_file:
|
||||||
dest_file.write(source_file_contents)
|
dest_file.write(source_file_contents.encode("UTF-8"))
|
||||||
|
|
||||||
print(f"Copied {relative_path}")
|
print(f"Copied {relative_path}")
|
||||||
|
|
@ -7205,6 +7205,8 @@
|
||||||
],
|
],
|
||||||
"macros_add": [
|
"macros_add": [
|
||||||
"CONFIG_GPIO_AS_PINRESET",
|
"CONFIG_GPIO_AS_PINRESET",
|
||||||
|
|
||||||
|
// Make room for the bootloader
|
||||||
"MBED_APP_START=0x10000",
|
"MBED_APP_START=0x10000",
|
||||||
"MBED_APP_SIZE=0xf0000"
|
"MBED_APP_SIZE=0xf0000"
|
||||||
],
|
],
|
||||||
|
|
@ -7216,20 +7218,19 @@
|
||||||
},
|
},
|
||||||
"ARDUINO_NANO33BLE_SWD": {
|
"ARDUINO_NANO33BLE_SWD": {
|
||||||
"inherits": [
|
"inherits": [
|
||||||
"MCU_NRF52840"
|
"ARDUINO_NANO33BLE"
|
||||||
],
|
],
|
||||||
"features_add": [
|
|
||||||
"STORAGE"
|
// for SWD we default to UART console
|
||||||
],
|
"overrides": {
|
||||||
"components_remove": [
|
"console-usb": false,
|
||||||
"QSPIF"
|
"console-uart": true
|
||||||
],
|
},
|
||||||
"device_has_remove": [
|
|
||||||
"QSPI",
|
// For SWD we don't want to leave space for the bootloader
|
||||||
"ITM"
|
"macros_remove": [
|
||||||
],
|
"MBED_APP_START=0x10000",
|
||||||
"macros_add": [
|
"MBED_APP_SIZE=0xf0000"
|
||||||
"CONFIG_GPIO_AS_PINRESET"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"NUMAKER_PFM_NUC472": {
|
"NUMAKER_PFM_NUC472": {
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,15 @@
|
||||||
function(mbed_write_target_config_header HEADER_PATH) # ARGN: Lists of defines to add to the file.
|
function(mbed_write_target_config_header HEADER_PATH) # ARGN: Lists of defines to add to the file.
|
||||||
|
|
||||||
set(TARGET_HEADER_CONTENTS
|
set(TARGET_HEADER_CONTENTS
|
||||||
" /*
|
"
|
||||||
|
/*
|
||||||
Mbed OS Target Define Header.
|
Mbed OS Target Define Header.
|
||||||
This contains all of the #defines specific to your target and device.
|
This contains all of the #defines specific to your target and device.
|
||||||
It is prepended to every source file using the -include compiler option.
|
It is prepended to every source file using the -include compiler option.
|
||||||
AUTOGENERATED by cmake. DO NOT EDIT!
|
AUTOGENERATED by cmake. DO NOT EDIT!
|
||||||
*/
|
*/
|
||||||
|
#ifndef MBED_TARGET_CONFIG_H
|
||||||
|
#define MBED_TARGET_CONFIG_H
|
||||||
")
|
")
|
||||||
|
|
||||||
foreach(DEFINE_LIST ${ARGN})
|
foreach(DEFINE_LIST ${ARGN})
|
||||||
|
|
@ -30,6 +33,8 @@ function(mbed_write_target_config_header HEADER_PATH) # ARGN: Lists of defines t
|
||||||
endforeach()
|
endforeach()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
string(APPEND TARGET_HEADER_CONTENTS "#endif\n")
|
||||||
|
|
||||||
# Write the file, using file(GENERATE) so that the timestamp is not updated if the contents don't change
|
# Write the file, using file(GENERATE) so that the timestamp is not updated if the contents don't change
|
||||||
file(GENERATE OUTPUT ${HEADER_PATH} CONTENT ${TARGET_HEADER_CONTENTS})
|
file(GENERATE OUTPUT ${HEADER_PATH} CONTENT ${TARGET_HEADER_CONTENTS})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,3 +90,9 @@ list_to_space_separated(CMAKE_EXE_LINKER_FLAGS_INIT ${link_options})
|
||||||
set(CMAKE_C_FLAGS_INIT "${CMAKE_C_FLAGS_INIT} ")
|
set(CMAKE_C_FLAGS_INIT "${CMAKE_C_FLAGS_INIT} ")
|
||||||
set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} ")
|
set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} ")
|
||||||
set(CMAKE_ASM_FLAGS_INIT "${CMAKE_ASM_FLAGS_INIT} ")
|
set(CMAKE_ASM_FLAGS_INIT "${CMAKE_ASM_FLAGS_INIT} ")
|
||||||
|
|
||||||
|
# Set language standards
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_C_EXTENSIONS TRUE)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS TRUE)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ function(mbed_set_profile_options target mbed_toolchain)
|
||||||
|
|
||||||
if(${mbed_toolchain} STREQUAL "GCC_ARM")
|
if(${mbed_toolchain} STREQUAL "GCC_ARM")
|
||||||
list(APPEND profile_c_compile_options
|
list(APPEND profile_c_compile_options
|
||||||
"-c"
|
|
||||||
"-Og"
|
"-Og"
|
||||||
)
|
)
|
||||||
target_compile_options(${target}
|
target_compile_options(${target}
|
||||||
|
|
@ -16,7 +15,6 @@ function(mbed_set_profile_options target mbed_toolchain)
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND profile_cxx_compile_options
|
list(APPEND profile_cxx_compile_options
|
||||||
"-c"
|
|
||||||
"-fno-rtti"
|
"-fno-rtti"
|
||||||
"-Wvla"
|
"-Wvla"
|
||||||
"-Og"
|
"-Og"
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ function(mbed_set_profile_options target mbed_toolchain)
|
||||||
|
|
||||||
if(${mbed_toolchain} STREQUAL "GCC_ARM")
|
if(${mbed_toolchain} STREQUAL "GCC_ARM")
|
||||||
list(APPEND profile_c_compile_options
|
list(APPEND profile_c_compile_options
|
||||||
"-c"
|
|
||||||
"-Os"
|
"-Os"
|
||||||
)
|
)
|
||||||
target_compile_options(${target}
|
target_compile_options(${target}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue