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
 | 
			
		||||
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)
 | 
			
		||||
    target_compile_options(mbed-core-flags INTERFACE -include ${CMAKE_CURRENT_BINARY_DIR}/mbed-target-config.h)
 | 
			
		||||
    file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/generated-headers)
 | 
			
		||||
    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()
 | 
			
		||||
 | 
			
		||||
# 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)
 | 
			
		||||
 | 
			
		||||
    # 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.
 | 
			
		||||
    mbed_extract_flags(${MBED_TARGET_CMAKE_NAME}-flags ${MBED_TARGET_CMAKE_NAME})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1204,7 +1204,7 @@
 | 
			
		|||
 *
 | 
			
		||||
 * Enable functions that use the filesystem.
 | 
			
		||||
 */
 | 
			
		||||
//#define MBEDTLS_FS_IO
 | 
			
		||||
#define MBEDTLS_FS_IO
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \def MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES
 | 
			
		||||
| 
						 | 
				
			
			@ -3227,7 +3227,7 @@
 | 
			
		|||
 *            on it, and considering stronger message digests instead.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
//#define MBEDTLS_SHA1_C
 | 
			
		||||
#define MBEDTLS_SHA1_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
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#if CONFIG_MBEDTLS_ENTROPY_NV_SEED
 | 
			
		||||
#define MBEDTLS_ENTROPY_NV_SEED
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(FEATURE_EXPERIMENTAL_API) && defined(FEATURE_PSA)
 | 
			
		||||
 | 
			
		||||
#if defined(MBEDTLS_ENTROPY_NV_SEED)
 | 
			
		||||
| 
						 | 
				
			
			@ -72,9 +76,6 @@
 | 
			
		|||
#include "mbedtls_device.h"
 | 
			
		||||
#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.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -74,7 +74,11 @@
 | 
			
		|||
#if !defined(_WIN32) || defined(EFIX64) || defined(EFI32)
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#if defined(__MBED__)
 | 
			
		||||
#include <platform/mbed_retarget.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#endif /* __MBED__ */
 | 
			
		||||
#endif /* !_WIN32 || EFIX64 || EFI32 */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,20 @@
 | 
			
		|||
#
 | 
			
		||||
# 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
 | 
			
		||||
#
 | 
			
		||||
| 
						 | 
				
			
			@ -53,7 +66,6 @@ conf unset MBEDTLS_TIMING_C
 | 
			
		|||
 | 
			
		||||
# not supported on all targets with mbed OS, nor used by mbed Client
 | 
			
		||||
conf unset MBEDTLS_HAVE_TIME_DATE
 | 
			
		||||
conf unset MBEDTLS_FS_IO
 | 
			
		||||
conf unset MBEDTLS_PSA_ITS_FILE_C
 | 
			
		||||
conf unset MBEDTLS_PSA_CRYPTO_STORAGE_C
 | 
			
		||||
conf set MBEDTLS_NO_PLATFORM_ENTROPY
 | 
			
		||||
| 
						 | 
				
			
			@ -89,7 +101,6 @@ conf unset MBEDTLS_PEM_WRITE_C
 | 
			
		|||
conf unset MBEDTLS_PKCS5_C
 | 
			
		||||
conf unset MBEDTLS_PKCS12_C
 | 
			
		||||
conf unset MBEDTLS_RIPEMD160_C
 | 
			
		||||
conf unset MBEDTLS_SHA1_C
 | 
			
		||||
conf unset MBEDTLS_XTEA_C
 | 
			
		||||
 | 
			
		||||
conf set MBEDTLS_CMAC_C
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,20 @@
 | 
			
		|||
#
 | 
			
		||||
# 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
 | 
			
		||||
#
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
                          const mbedtls_x509_crt *crt)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -116,6 +116,46 @@ public:
 | 
			
		|||
     */
 | 
			
		||||
    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.
 | 
			
		||||
     *
 | 
			
		||||
     * @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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
    return set_client_cert_key(client_cert_pem, strlen(client_cert_pem) + 1, client_private_key_pem, strlen(client_private_key_pem) + 1);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,8 @@ list(
 | 
			
		|||
            tlssocket_endpoint_close.cpp
 | 
			
		||||
            tlssocket_echotest.cpp
 | 
			
		||||
            tlssocket_echotest_burst.cpp
 | 
			
		||||
            tlssocket_connect_invalid.cpp       
 | 
			
		||||
            tlssocket_connect_invalid.cpp
 | 
			
		||||
            tlssocket_cert_in_filesystem.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
if(MBED_GREENTEA_TEST_BAREMETAL)
 | 
			
		||||
| 
						 | 
				
			
			@ -33,6 +34,8 @@ mbed_greentea_add_test(
 | 
			
		|||
        ${TEST_SOURCE_LIST}
 | 
			
		||||
    TEST_REQUIRED_LIBS
 | 
			
		||||
        mbed-netsocket
 | 
			
		||||
        mbed-storage-blockdevice
 | 
			
		||||
    mbed-storage-littlefs
 | 
			
		||||
    TEST_SKIPPED
 | 
			
		||||
        ${TEST_SKIPPED}
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -218,7 +218,6 @@ static void test_failure_handler(const failure_t failure)
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
Case cases[] = {
 | 
			
		||||
// Disable tests temporarily till echo server is back on
 | 
			
		||||
    Case("TLSSOCKET_ECHOTEST", TLSSOCKET_ECHOTEST),
 | 
			
		||||
    Case("TLSSOCKET_ECHOTEST_NONBLOCK", TLSSOCKET_ECHOTEST_NONBLOCK),
 | 
			
		||||
    Case("TLSSOCKET_CONNECT_INVALID", TLSSOCKET_CONNECT_INVALID),
 | 
			
		||||
| 
						 | 
				
			
			@ -235,6 +234,9 @@ Case cases[] = {
 | 
			
		|||
    Case("TLSSOCKET_SEND_REPEAT", TLSSOCKET_SEND_REPEAT),
 | 
			
		||||
    Case("TLSSOCKET_SEND_TIMEOUT", TLSSOCKET_SEND_TIMEOUT),
 | 
			
		||||
    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
 | 
			
		||||
//    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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -91,6 +91,7 @@ void TLSSOCKET_SEND_REPEAT();
 | 
			
		|||
void TLSSOCKET_NO_CERT();
 | 
			
		||||
void TLSSOCKET_SIMULTANEOUS();
 | 
			
		||||
void TLSSOCKET_SEND_TIMEOUT();
 | 
			
		||||
void TLSSOCKET_CERT_IN_FILESYSTEM();
 | 
			
		||||
 | 
			
		||||
#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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
    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 MBEDTLS_SSL_CLI_C
 | 
			
		||||
 | 
			
		||||
#define MBEDTLS_FS_IO
 | 
			
		||||
 | 
			
		||||
#endif /* UNITTESTS_FEATURES_NETSOCKET_TLSSOCKET_TLS_TEST_CONFIG_H_ */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -165,14 +165,12 @@ public:
 | 
			
		|||
     */
 | 
			
		||||
    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 length Number of bytes to read.
 | 
			
		||||
     *  @param length Maximum number of bytes to read.
 | 
			
		||||
     *
 | 
			
		||||
     *  @return Result of the operation.
 | 
			
		||||
     *  @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.
 | 
			
		||||
     *  @return The number of bytes read
 | 
			
		||||
     */
 | 
			
		||||
    int read(char *data, int length);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,7 +52,7 @@ int I2CSlave::receive(void)
 | 
			
		|||
 | 
			
		||||
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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,10 +3,13 @@
 | 
			
		|||
 | 
			
		||||
include(FetchContent)
 | 
			
		||||
 | 
			
		||||
FetchContent_Declare(
 | 
			
		||||
    greentea-client
 | 
			
		||||
    GIT_REPOSITORY  https://github.com/ARMmbed/greentea-client.git
 | 
			
		||||
    GIT_TAG         472aad2327fbfde827852fc44775904706847a3a
 | 
			
		||||
)
 | 
			
		||||
if(MBED_ENABLE_TESTING)
 | 
			
		||||
	# Build greentea only when testing is enabled
 | 
			
		||||
	FetchContent_Declare(
 | 
			
		||||
	    greentea-client
 | 
			
		||||
	    GIT_REPOSITORY  https://github.com/ARMmbed/greentea-client.git
 | 
			
		||||
	    GIT_TAG         472aad2327fbfde827852fc44775904706847a3a
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
FetchContent_MakeAvailable(greentea-client)
 | 
			
		||||
	FetchContent_MakeAvailable(greentea-client)
 | 
			
		||||
endif()
 | 
			
		||||
							
								
								
									
										5
									
								
								mbed.h
								
								
								
								
							
							
						
						
									
										5
									
								
								mbed.h
								
								
								
								
							| 
						 | 
				
			
			@ -17,6 +17,11 @@
 | 
			
		|||
#ifndef 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"
 | 
			
		||||
 | 
			
		||||
#if MBED_CONF_RTOS_API_PRESENT
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -441,11 +441,13 @@ end:
 | 
			
		|||
    return BD_ERROR_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if DEVICE_SPI_ASYNCH
 | 
			
		||||
void SDBlockDevice::set_async_spi_mode(bool enabled, DMAUsage dma_usage_hint)
 | 
			
		||||
{
 | 
			
		||||
    _async_spi_enabled = enabled;
 | 
			
		||||
    _spi.set_dma_usage(dma_usage_hint);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
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_sync/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_fix/rp2040_usb_device_enumeration/include/
 | 
			
		||||
		pico-sdk/src/rp2_common/pico_bootrom/include
 | 
			
		||||
| 
						 | 
				
			
			@ -142,6 +144,7 @@ target_include_directories(mbed-raspberrypi
 | 
			
		|||
 | 
			
		||||
target_sources(mbed-raspberrypi
 | 
			
		||||
    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_uart/uart.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_sync/sync.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_platform/platform.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 "objects.h"
 | 
			
		||||
#include "PeripheralPins.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -210,10 +228,10 @@ const PinMap PinMap_I2C_SCL[] = {
 | 
			
		|||
 * is the onboard temperature sensor.
 | 
			
		||||
 */
 | 
			
		||||
const PinMap PinMap_ADC[] = {
 | 
			
		||||
    { A0,       ADC0, 0},
 | 
			
		||||
    { A1,       ADC0, 1},
 | 
			
		||||
    { A2,       ADC0, 2},
 | 
			
		||||
    { A3,       ADC0, 3},
 | 
			
		||||
    { p26,       ADC0, 0},
 | 
			
		||||
    { p27,       ADC0, 1},
 | 
			
		||||
    { p28,       ADC0, 2},
 | 
			
		||||
    { p29,       ADC0, 3},
 | 
			
		||||
    { ADC_TEMP, ADC0, 4},
 | 
			
		||||
    { NC,       NC,   0}
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,6 +51,9 @@ SECTIONS
 | 
			
		|||
       and checksummed. It is usually built by the boot_stage2 target
 | 
			
		||||
       in the Raspberry Pi Pico SDK
 | 
			
		||||
    */
 | 
			
		||||
    .second_stage_ota : {
 | 
			
		||||
        KEEP (*(.second_stage_ota))
 | 
			
		||||
    } > FLASH
 | 
			
		||||
 | 
			
		||||
    .flash_begin : {
 | 
			
		||||
        __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 "analogin_api.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. */
 | 
			
		||||
    obj->channel = pinmap_find_function(pin, PinMap_ADC);
 | 
			
		||||
    /* 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
 | 
			
		||||
     * temperature sensor. If that's the case, enable the temperature
 | 
			
		||||
     * sensor.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,7 +66,7 @@ void adc_init(void);
 | 
			
		|||
 *
 | 
			
		||||
 * \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);
 | 
			
		||||
    // Select NULL function to make output driver hi-Z
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
    // table. The sources for each gpout generators are the same
 | 
			
		||||
    // 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_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.
 | 
			
		||||
 *  \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 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;
 | 
			
		||||
    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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 shutil
 | 
			
		||||
import sys
 | 
			
		||||
import re
 | 
			
		||||
from typing import List, Tuple
 | 
			
		||||
 | 
			
		||||
this_script_dir = pathlib.Path(__file__).resolve().parent
 | 
			
		||||
 | 
			
		||||
# List of identifiers to rename b/c they clash with Mbed symbols
 | 
			
		||||
IDENTIFIERS_TO_RENAME: List[Tuple[bytes, bytes]] = [
 | 
			
		||||
    (b"gpio_irq_handler", b"pico_sdk_gpio_irq_handler"),
 | 
			
		||||
    (b"gpio_init", b"pico_sdk_gpio_init"),
 | 
			
		||||
    (b"i2c_init", b"pico_sdk_i2c_init"),
 | 
			
		||||
    (b"rtc_init", b"pico_sdk_rtc_init"),
 | 
			
		||||
    (b"spi_init", b"pico_sdk_spi_init"),
 | 
			
		||||
IDENTIFIERS_TO_RENAME: List[Tuple[str, str]] = [
 | 
			
		||||
    (r"gpio_irq_handler", r"pico_sdk_gpio_irq_handler"),
 | 
			
		||||
    (r"gpio_init", r"pico_sdk_gpio_init"),
 | 
			
		||||
    (r"i2c_init", r"pico_sdk_i2c_init"),
 | 
			
		||||
    (r"rtc_init", r"pico_sdk_rtc_init"),
 | 
			
		||||
    (r"spi_init", r"pico_sdk_spi_init"),
 | 
			
		||||
 | 
			
		||||
    # Rename IRQ handlers to the CMSIS exception names.
 | 
			
		||||
    # Pico SDK does this with macros, but easier to just
 | 
			
		||||
    # do it here.
 | 
			
		||||
    # Based on cmsis/include/rename_exceptions.h.
 | 
			
		||||
    (b"isr_nmi", b"NMI_Handler"),
 | 
			
		||||
    (b"isr_hardfault", b"HardFault_Handler"),
 | 
			
		||||
    (b"isr_svcall", b"SVC_Handler"),
 | 
			
		||||
    (b"isr_pendsv", b"PendSV_Handler"),
 | 
			
		||||
    (b"isr_systick", b"SysTick_Handler"),
 | 
			
		||||
    (b"isr_irq0", b"TIMER_IRQ_0_Handler"),
 | 
			
		||||
    (b"isr_irq1", b"TIMER_IRQ_1_Handler"),
 | 
			
		||||
    (b"isr_irq2", b"TIMER_IRQ_2_Handler"),
 | 
			
		||||
    (b"isr_irq3", b"TIMER_IRQ_3_Handler"),
 | 
			
		||||
    (b"isr_irq4", b"PWM_IRQ_WRAP_Handler"),
 | 
			
		||||
    (b"isr_irq5", b"USBCTRL_IRQ_Handler"),
 | 
			
		||||
    (b"isr_irq6", b"XIP_IRQ_Handler"),
 | 
			
		||||
    (b"isr_irq7", b"PIO0_IRQ_0_Handler"),
 | 
			
		||||
    (b"isr_irq8", b"PIO0_IRQ_1_Handler"),
 | 
			
		||||
    (b"isr_irq9", b"PIO1_IRQ_0_Handler"),
 | 
			
		||||
    (b"isr_irq10", b"PIO1_IRQ_1_Handler"),
 | 
			
		||||
    (b"isr_irq11", b"DMA_IRQ_0_Handler"),
 | 
			
		||||
    (b"isr_irq12", b"DMA_IRQ_1_Handler"),
 | 
			
		||||
    (b"isr_irq13", b"IO_IRQ_BANK0_Handler"),
 | 
			
		||||
    (b"isr_irq14", b"IO_IRQ_QSPI_Handler"),
 | 
			
		||||
    (b"isr_irq15", b"SIO_IRQ_PROC0_Handler"),
 | 
			
		||||
    (b"isr_irq16", b"SIO_IRQ_PROC1_Handler"),
 | 
			
		||||
    (b"isr_irq17", b"CLOCKS_IRQ_Handler"),
 | 
			
		||||
    (b"isr_irq18", b"SPI0_IRQ_Handler"),
 | 
			
		||||
    (b"isr_irq19", b"SPI1_IRQ_Handler"),
 | 
			
		||||
    (b"isr_irq20", b"UART0_IRQ_Handler"),
 | 
			
		||||
    (b"isr_irq21", b"UART1_IRQ_Handler"),
 | 
			
		||||
    (b"isr_irq22", b"ADC_IRQ_FIFO_Handler"),
 | 
			
		||||
    (b"isr_irq23", b"I2C0_IRQ_Handler"),
 | 
			
		||||
    (b"isr_irq24", b"I2C1_IRQ_Handler"),
 | 
			
		||||
    (b"isr_irq25", b"RTC_IRQ_Handler"),
 | 
			
		||||
    (r"isr_nmi", r"NMI_Handler"),
 | 
			
		||||
    (r"isr_hardfault", r"HardFault_Handler"),
 | 
			
		||||
    (r"isr_svcall", r"SVC_Handler"),
 | 
			
		||||
    (r"isr_pendsv", r"PendSV_Handler"),
 | 
			
		||||
    (r"isr_systick", r"SysTick_Handler"),
 | 
			
		||||
    (r"isr_irq0", r"TIMER_IRQ_0_Handler"),
 | 
			
		||||
    (r"isr_irq1", r"TIMER_IRQ_1_Handler"),
 | 
			
		||||
    (r"isr_irq2", r"TIMER_IRQ_2_Handler"),
 | 
			
		||||
    (r"isr_irq3", r"TIMER_IRQ_3_Handler"),
 | 
			
		||||
    (r"isr_irq4", r"PWM_IRQ_WRAP_Handler"),
 | 
			
		||||
    (r"isr_irq5", r"USBCTRL_IRQ_Handler"),
 | 
			
		||||
    (r"isr_irq6", r"XIP_IRQ_Handler"),
 | 
			
		||||
    (r"isr_irq7", r"PIO0_IRQ_0_Handler"),
 | 
			
		||||
    (r"isr_irq8", r"PIO0_IRQ_1_Handler"),
 | 
			
		||||
    (r"isr_irq9", r"PIO1_IRQ_0_Handler"),
 | 
			
		||||
    (r"isr_irq10", r"PIO1_IRQ_1_Handler"),
 | 
			
		||||
    (r"isr_irq11", r"DMA_IRQ_0_Handler"),
 | 
			
		||||
    (r"isr_irq12", r"DMA_IRQ_1_Handler"),
 | 
			
		||||
    (r"isr_irq13", r"IO_IRQ_BANK0_Handler"),
 | 
			
		||||
    (r"isr_irq14", r"IO_IRQ_QSPI_Handler"),
 | 
			
		||||
    (r"isr_irq15", r"SIO_IRQ_PROC0_Handler"),
 | 
			
		||||
    (r"isr_irq16", r"SIO_IRQ_PROC1_Handler"),
 | 
			
		||||
    (r"isr_irq17", r"CLOCKS_IRQ_Handler"),
 | 
			
		||||
    (r"isr_irq18", r"SPI0_IRQ_Handler"),
 | 
			
		||||
    (r"isr_irq19", r"SPI1_IRQ_Handler"),
 | 
			
		||||
    (r"isr_irq20", r"UART0_IRQ_Handler"),
 | 
			
		||||
    (r"isr_irq21", r"UART1_IRQ_Handler"),
 | 
			
		||||
    (r"isr_irq22", r"ADC_IRQ_FIFO_Handler"),
 | 
			
		||||
    (r"isr_irq23", r"I2C0_IRQ_Handler"),
 | 
			
		||||
    (r"isr_irq24", r"I2C1_IRQ_Handler"),
 | 
			
		||||
    (r"isr_irq25", r"RTC_IRQ_Handler"),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
# 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_sync",
 | 
			
		||||
    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_platform",
 | 
			
		||||
    pathlib.Path("src") / "rp2_common" / "pico_float",
 | 
			
		||||
| 
						 | 
				
			
			@ -137,11 +140,12 @@ for source_file_path in files_to_copy:
 | 
			
		|||
 | 
			
		||||
    # Load 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
 | 
			
		||||
    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
 | 
			
		||||
    relative_path = source_file_path.relative_to(sdk_path)
 | 
			
		||||
| 
						 | 
				
			
			@ -150,6 +154,6 @@ for source_file_path in files_to_copy:
 | 
			
		|||
    # Write new contents
 | 
			
		||||
    dest_file_path.parent.mkdir(parents=True, exist_ok=True)
 | 
			
		||||
    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}")
 | 
			
		||||
| 
						 | 
				
			
			@ -7205,6 +7205,8 @@
 | 
			
		|||
        ],
 | 
			
		||||
        "macros_add": [
 | 
			
		||||
            "CONFIG_GPIO_AS_PINRESET",
 | 
			
		||||
 | 
			
		||||
            // Make room for the bootloader
 | 
			
		||||
            "MBED_APP_START=0x10000",
 | 
			
		||||
            "MBED_APP_SIZE=0xf0000"
 | 
			
		||||
        ],
 | 
			
		||||
| 
						 | 
				
			
			@ -7216,20 +7218,19 @@
 | 
			
		|||
    },
 | 
			
		||||
    "ARDUINO_NANO33BLE_SWD": {
 | 
			
		||||
        "inherits": [
 | 
			
		||||
            "MCU_NRF52840"
 | 
			
		||||
            "ARDUINO_NANO33BLE"
 | 
			
		||||
        ],
 | 
			
		||||
        "features_add": [
 | 
			
		||||
            "STORAGE"
 | 
			
		||||
        ],
 | 
			
		||||
        "components_remove": [
 | 
			
		||||
            "QSPIF"
 | 
			
		||||
        ],
 | 
			
		||||
        "device_has_remove": [
 | 
			
		||||
            "QSPI",
 | 
			
		||||
            "ITM"
 | 
			
		||||
        ],
 | 
			
		||||
        "macros_add": [
 | 
			
		||||
            "CONFIG_GPIO_AS_PINRESET"
 | 
			
		||||
 | 
			
		||||
        // for SWD we default to UART console
 | 
			
		||||
        "overrides": {
 | 
			
		||||
            "console-usb": false,
 | 
			
		||||
            "console-uart": true
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        // For SWD we don't want to leave space for the bootloader
 | 
			
		||||
        "macros_remove": [
 | 
			
		||||
            "MBED_APP_START=0x10000",
 | 
			
		||||
            "MBED_APP_SIZE=0xf0000"
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    "NUMAKER_PFM_NUC472": {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,16 +7,19 @@
 | 
			
		|||
function(mbed_write_target_config_header HEADER_PATH) # ARGN: Lists of defines to add to the file.
 | 
			
		||||
 | 
			
		||||
	set(TARGET_HEADER_CONTENTS
 | 
			
		||||
"    /*
 | 
			
		||||
    Mbed OS Target Define Header.
 | 
			
		||||
    This contains all of the #defines specific to your target and device.
 | 
			
		||||
    It is prepended to every source file using the -include compiler option.
 | 
			
		||||
    AUTOGENERATED by cmake.  DO NOT EDIT!
 | 
			
		||||
    */
 | 
			
		||||
"
 | 
			
		||||
/*
 | 
			
		||||
Mbed OS Target Define Header.
 | 
			
		||||
This contains all of the #defines specific to your target and device.
 | 
			
		||||
It is prepended to every source file using the -include compiler option.
 | 
			
		||||
AUTOGENERATED by cmake.  DO NOT EDIT!
 | 
			
		||||
*/
 | 
			
		||||
#ifndef MBED_TARGET_CONFIG_H
 | 
			
		||||
#define MBED_TARGET_CONFIG_H
 | 
			
		||||
")
 | 
			
		||||
 | 
			
		||||
	foreach(DEFINE_LIST ${ARGN})
 | 
			
		||||
		string(APPEND TARGET_HEADER_CONTENTS "\n    // Defines from ${DEFINE_LIST}:\n")
 | 
			
		||||
		string(APPEND TARGET_HEADER_CONTENTS "\n// Defines from ${DEFINE_LIST}:\n")
 | 
			
		||||
 | 
			
		||||
		foreach(DEFINE_COMMAND ${${DEFINE_LIST}}) # double dereference needed to get contents of list
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +33,8 @@ function(mbed_write_target_config_header HEADER_PATH) # ARGN: Lists of defines t
 | 
			
		|||
		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
 | 
			
		||||
	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_CXX_FLAGS_INIT "${CMAKE_CXX_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")
 | 
			
		||||
        list(APPEND profile_c_compile_options
 | 
			
		||||
            "-c"
 | 
			
		||||
            "-Og"
 | 
			
		||||
        )
 | 
			
		||||
        target_compile_options(${target}
 | 
			
		||||
| 
						 | 
				
			
			@ -16,7 +15,6 @@ function(mbed_set_profile_options target mbed_toolchain)
 | 
			
		|||
        )
 | 
			
		||||
 | 
			
		||||
        list(APPEND profile_cxx_compile_options
 | 
			
		||||
            "-c"
 | 
			
		||||
            "-fno-rtti"
 | 
			
		||||
            "-Wvla"
 | 
			
		||||
            "-Og"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,6 @@ function(mbed_set_profile_options target mbed_toolchain)
 | 
			
		|||
 | 
			
		||||
    if(${mbed_toolchain} STREQUAL "GCC_ARM")
 | 
			
		||||
        list(APPEND profile_c_compile_options
 | 
			
		||||
            "-c"
 | 
			
		||||
            "-Os"
 | 
			
		||||
        )
 | 
			
		||||
        target_compile_options(${target}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue