/*
 * Copyright 2017-2020 NXP
 * All rights reserved.
 *
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_cau3.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.cau3"
#endif

/*! Compile time sizeof() check */
#define BUILD_ASSURE(condition, msg) extern int msg[1 - 2 * (!(condition))] __attribute__((unused))

#define CAU3_SR_TKCS_INITRUN   0x00000000U
#define CAU3_SR_TKCS_RUN       0x00000100U
#define CAU3_SR_TKCS_DBGHALT   0x00000200U
#define CAU3_SR_TKCS_STOPNOERR 0x00000900U
#define CAU3_SR_TKCS_STOPERROR 0x00000A00U
#define CAU3_SR_TKCS_SECV      0x00000E00U
#define CAU3_SR_TKCS_EXTSECV   0x00000F00U

#define CAU3_DMEM_STK_BASE 0xfff003f8U
#define CAU3_DMEM_AES_RCON 0xfff00A00U

#define CAU3_TASK_SECV_INIT         0x00 * 4
#define CAU3_TASK_STOPERROR         0x01 * 4
#define CAU3_TASK_STOPNOERR         0x02 * 4
#define CAU3_TASK_NULL              0x02 * 4
#define CAU3_TASK_BLKLD_DMEM        0x03 * 4
#define CAU3_TASK_LD_KEYCTX         0x04 * 4
#define CAU3_TASK_LD_SP_KEYCTX      0x05 * 4
#define CAU3_TASK_CLR_KEYCTX        0x06 * 4
#define CAU3_TASK_LD_KEY            0x07 * 4
#define CAU3_TASK_LD_KEK            0x08 * 4
#define CAU3_TASK_LD_IV             0x09 * 4
#define CAU3_TASK_AES_KEY_SCH       0x0A * 4
#define CAU3_TASK_AES_ENCRYPT       0x0B * 4
#define CAU3_TASK_AES_DECRYPT       0x0C * 4
#define CAU3_TASK_AES128_ENCRYPT    0x0D * 4
#define CAU3_TASK_AES128_DECRYPT    0x0E * 4
#define CAU3_TASK_AES128_CMAC       0x0F * 4
#define CAU3_TASK_SHA256_INIT_STATE 0x10 * 4
#define CAU3_TASK_SHA256_UPDATE     0x11 * 4
#define CAU3_TASK_KEY_BLOB_UNWRAP   0x12 * 4
#define CAU3_TASK_SHA1_HASH         0x13 * 4
#define CAU3_TASK_SHA1_INIT_STATE   0x14 * 4
#define CAU3_TASK_SHA512_INIT_STATE 0x15 * 4
#define CAU3_TASK_SHA512_UPDATE     0x16 * 4
#define CAU3_TASK_3DES_CHECK_PARITY 0x17 * 4
#define CAU3_TASK_3DES_ENCRYPT      0x18 * 4
#define CAU3_TASK_3DES_DECRYPT      0x19 * 4
#define CAU3_TASK_CHA_POLY_ENCRYPT  0x1A * 4
#define CAU3_TASK_CHA_POLY_DECRYPT  0x1B * 4

#define CAU3_SEMA4_LOCK         0x80000000U
#define CAU3_SEMA4_RELEASE      0x00000000U
#define CAU3_SMOWNR_OWNED_BY_ME 0x00000001U
#define CAU3_SMOWNR_UNLOCKED    0x80000000U

/*! @brief keyContext structure in the CAU3's DataMemory */
typedef struct _cau3_key_context
{
    uint32_t keySize;    /*!< key size in bytes, 0 = invalid context */
    uint32_t streamSize; /*!< rfu-firmware; stream length in bytes */
    uint32_t *keySched;  /*!< rfu-firmware; ptr to expanded key schedule */
    uint32_t zeroFill;   /*!< zero (unused) to keep 0-mod-4 alignment */
    uint8_t iv[16];      /*!< initialization vector */
    uint8_t key[32];     /*!< key for 3des, aes[128,192,256] */
} cau3_key_context_t;

typedef enum _cau3_crypt
{
    kCAU3_Encrypt = 0,
    kCAU3_Decrypt = 1,
} cau3_crypt_t;

#define cau3_memcpy memcpy

/*! Internal states of the HASH creation process */
typedef enum _cau3_hash_algo_state
{
    kCAU3_StateHashInit = 1u, /*!< Init state. */
    kCAU3_StateHashUpdate,    /*!< Update state. */
} cau3_hash_algo_state_t;

/*! multiple of 64-byte block represented as byte array of 32-bit words */
typedef union _cau3_hash_block
{
    uint32_t w[CAU3_HASH_BLOCK_SIZE / 4]; /*!< array of 32-bit words */
    uint8_t b[CAU3_HASH_BLOCK_SIZE];      /*!< byte array */
} cau3_hash_block_t;

/*! internal cau3_hash context structure */
typedef struct _cau3_hash_ctx_internal
{
    cau3_hash_block_t blk;        /*!< memory buffer. only full blocks are written to CAU3 during hash/cmac updates */
    size_t blksz;                 /*!< number of valid bytes in memory buffer */
    cau3_hash_algo_t algo;        /*!< selected algorithm from the set of supported algorithms */
    cau3_hash_algo_state_t state; /*!< finite machine state of the hash software process */
    size_t fullMessageSize;       /*!< track message size during CAU3_HASH_Update(). The value is used for padding. */
    uint32_t runningHash[8];
} cau3_hash_ctx_internal_t;

/*!< SHA-1/SHA-2 digest length in bytes  */
enum _cau3_hash_digest_len
{
    kCAU3_OutLenSha1   = 20u,
    kCAU3_OutLenSha256 = 32u,
};

/*! 64-byte block represented as byte array of 16 32-bit words */
typedef union _cau3_sha_block
{
    uint32_t w[64 / 4]; /*!< array of 32-bit words */
    uint8_t b[64];      /*!< byte array */
} cau3_sha_block_t;

/*! Full word representing the actual bit values for the CAU3 1.0 mode register. */
typedef uint32_t cau3_mode_t;

#define CAU3_MDPK_ALG_PKHA (0x80U) /*!< Bit field value for CAU3_MDPK_ALG: PKHA */
#define CAU3_MD_ALG_SHIFT  16

typedef enum _cau3_algorithm
{
    kCAU3_AlgorithmPKHA = CAU3_MDPK_ALG_PKHA << CAU3_MD_ALG_SHIFT,
} cau3_algorithm_t;

/*! @brief CAU3 status flags */
enum _cau3_status_flag
{
    kCAU3_StatusPkhaBusy        = 1U << CAU3_STA_PB_SHIFT,
    kCAU3_StatusDoneIsr         = 1U << CAU3_STA_DI_SHIFT,
    kCAU3_StatusErrorIsr        = 1U << CAU3_STA_EI_SHIFT,
    kCAU3_StatusPublicKeyPrime  = 1U << CAU3_STA_PKP_SHIFT,
    kCAU3_StatusPublicKeyOpOne  = 1U << CAU3_STA_PKO_SHIFT,
    kCAU3_StatusPublicKeyOpZero = 1U << CAU3_STA_PKZ_SHIFT,
    kCAU3_StatusAll             = 0 | kCAU3_StatusDoneIsr | kCAU3_StatusErrorIsr | kCAU3_StatusPkhaBusy |
                      kCAU3_StatusPublicKeyPrime | kCAU3_StatusPublicKeyOpOne | kCAU3_StatusPublicKeyOpZero
};

/*! @brief CAU3 clear register */
typedef enum _cau3_clear_written
{
    kCAU3_ClearMode      = 1U << 0,
    kCAU3_ClearDataSize  = 1U << 2,
    kCAU3_ClearPkhaSizeA = 1U << 12,
    kCAU3_ClearPkhaSizeB = 1U << 13,
    kCAU3_ClearPkhaSizeN = 1U << 14,
    kCAU3_ClearPkhaSizeE = 1U << 15,
    kCAU3_ClearAllSize = (int)kCAU3_ClearPkhaSizeA | kCAU3_ClearPkhaSizeB | kCAU3_ClearPkhaSizeN | kCAU3_ClearPkhaSizeE,
    kCAU3_ClearAll     = (int)(kCAU3_ClearMode | kCAU3_ClearDataSize | kCAU3_ClearAllSize | 0)
} cau3_clear_written_t;

/*! @brief PKHA functions - arithmetic, copy/clear memory. */
typedef enum _cau3_pkha_func_t
{
    kCAU3_PKHA_ClearMem           = 1U,
    kCAU3_PKHA_ArithModAdd        = 2U,  /*!< (A + B) mod N */
    kCAU3_PKHA_ArithModSub1       = 3U,  /*!< (A - B) mod N */
    kCAU3_PKHA_ArithModSub2       = 4U,  /*!< (B - A) mod N */
    kCAU3_PKHA_ArithModMul        = 5U,  /*!< (A x B) mod N */
    kCAU3_PKHA_ArithModExp        = 6U,  /*!< (A^E) mod N */
    kCAU3_PKHA_ArithModRed        = 7U,  /*!< (A) mod N */
    kCAU3_PKHA_ArithModInv        = 8U,  /*!< (A^-1) mod N */
    kCAU3_PKHA_ArithEccAdd        = 9U,  /*!< (P1 + P2) */
    kCAU3_PKHA_ArithEccDouble     = 10U, /*!< (P2 + P2) */
    kCAU3_PKHA_ArithEccMul        = 11U, /*!< (E x P(A0,A1) */
    kCAU3_PKHA_ArithModR2         = 12U, /*!< (R^2 mod N) */
    kCAU3_PKHA_ArithModRR         = 13U, /*!< (RERP mod N) */
    kCAU3_PKHA_ArithGcd           = 14U, /*!< GCD (A, N) */
    kCAU3_PKHA_ArithPrimalityTest = 15U, /*!< Miller-Rabin */
    kCAU3_PKHA_CopyMemSizeN       = 16U,
    kCAU3_PKHA_CopyMemSizeSrc     = 17U,
    kCAU3_PKHA_ArithModSqrt       = 0x17U, /*!< (B0 x B0) mod N = A mod N */
    kCAU3_PKHA_ArithEcmMul        = 0x4B,  /*!< (E x P[A0]) */
    kCAU3_PKHA_ArithEctAdd        = 0x89,  /*!< (P[A0,A1] + P[B1,B2]) */
    kCAU3_PKHA_ArithEctMul        = 0x8B,  /*!< (E x P[A0,A1]) */
} cau3_pkha_func_t;

/*! @brief Register areas for PKHA clear memory operations. */
typedef enum _cau3_pkha_reg_area
{
    kCAU3_PKHA_RegA   = 8U,
    kCAU3_PKHA_RegB   = 4U,
    kCAU3_PKHA_RegE   = 2U,
    kCAU3_PKHA_RegN   = 1U,
    kCAU3_PKHA_RegAll = kCAU3_PKHA_RegA | kCAU3_PKHA_RegB | kCAU3_PKHA_RegE | kCAU3_PKHA_RegN,
} cau3_pkha_reg_area_t;

/*! @brief Quadrant areas for 2048-bit registers for PKHA copy memory
 * operations. */
typedef enum _cau3_pkha_quad_area_t
{
    kCAU3_PKHA_Quad0 = 0U,
    kCAU3_PKHA_Quad1 = 1U,
    kCAU3_PKHA_Quad2 = 2U,
    kCAU3_PKHA_Quad3 = 3U,
} cau3_pkha_quad_area_t;

/*! @brief User-supplied (R^2 mod N) input or CAU3 should calculate. */
typedef enum _cau3_pkha_r2_t
{
    kCAU3_PKHA_CalcR2  = 0U, /*!< Calculate (R^2 mod N) */
    kCAU3_PKHA_InputR2 = 1U  /*!< (R^2 mod N) supplied as input */
} cau3_pkha_r2_t;

/*! @brief CAU3 PKHA parameters */
typedef struct _cau3_pkha_mode_params_t
{
    cau3_pkha_func_t func;
    cau3_pkha_f2m_t arithType;
    cau3_pkha_montgomery_form_t montFormIn;
    cau3_pkha_montgomery_form_t montFormOut;
    cau3_pkha_reg_area_t srcReg;
    cau3_pkha_quad_area_t srcQuad;
    cau3_pkha_reg_area_t dstReg;
    cau3_pkha_quad_area_t dstQuad;
    cau3_pkha_timing_t equalTime;
    cau3_pkha_r2_t r2modn;
} cau3_pkha_mode_params_t;

/*******************************************************************************
 * Variables
 ******************************************************************************/
/*******************************************************************************
 * CAU3 Read-Only Data Constants and CryptoCore Code Image
 ******************************************************************************/

/*  in the cau3's private, local data memory, there is a section for read-only  */
/*  constants associated with AES, SHA-1, SHA-256, SHA-384 and SHA-512.  */
/*  the memory organization and layout of this section is defined as:  */
/*   */
/*  description            size dmem_base       description  */
/*  cau_dmem_aes_rcon        48  FFF00800       RO aes constants        10 x 32b  */
/*  cau_dmem_sha1_k          16  FFF00830       RO sha1 initial "k"      4 x 32b              */
/*  cau_dmem_sha1_init_h     32  FFF00840       RO sha1 initial state    5 x 32b  */
/*  cau_dmem_sha224_init_h   32  FFF00860       RO sha224 initial state  8 x 32b              */
/*  cau_dmem_sha256_init_h   32  FFF00880       RO sha256 initial state  8 x 32b              */
/*  cau_dmem_sha256_k       256  FFF008A0       RO sha256 initial "k"   64 x 32b  */
/*  cau_dmem_sha384_init_h   64  FFF009A0       RO sha384 initial state 16 x 32b  */
/*  cau_dmem_sha512_init_h   64  FFF009E0       RO sha512 initial state 16 x 32b  */
/*  cau_dmem_sha512_k       640  FFF00A20       RO sha512 initial "k"   160 x 32b  */
/*   */
/*  data size allocation are rounded up to be modulo 16 bytes as required.  */
/*   */

static const uint32_t s_cau3ReadOnlyConstants[] __attribute__((aligned(16))) = {
    /*  AES RCON[]  */
    0x01000000U, 0x02000000U, 0x04000000U, 0x08000000U, 0x10000000U, 0x20000000U, 0x40000000U, 0x80000000U, 0x1b000000U,
    0x36000000U, 0x00000000U, 0x00000000U, /*  zero fill for 0-mod-16 alignment  */

    /*  SHA1_K[]  */
    0x5a827999U, 0x6ed9eba1U, 0x8f1bbcdcU, 0xca62c1d6U,

    /*  SHA1_INIT_H[]  */
    0x67452301U, 0xefcdab89U, 0x98badcfeU, 0x10325476U, 0xc3d2e1f0U, 0x00000000U, 0x00000000U,
    0x00000000U, /*  zero fill for 0-mod-16 alignemnt  */

    /*  SHA224_INIT_H[]  */
    0xc1059ed8U, 0x367cd507U, 0x3070dd17U, 0xf70e5939U, 0xffc00b31U, 0x68581511U, 0x64f98fa7U, 0xbefa4fa4U,

    /*  SHA256_INIT_H[]  */
    /*  As described in FIPS PUB 180-4 "Secure Hash Standard", the initial hash value  */
    /*  for SHA-256 is obtained by taking the first thirty-two bits of the fractional  */
    /*  parts of the square roots of the first eight prime numbers.  */
    0x6a09e667U, 0xbb67ae85U, 0x3c6ef372U, 0xa54ff53aU, 0x510e527fU, 0x9b05688cU, 0x1f83d9abU, 0x5be0cd19U,

    /*  SHA256_K[], also used as SHA224_K[]  */
    /*  As described in FIPS PUB 180-4 "Secure Hash Standard", SHA-224 & SHA-256 use  */
    /*  the same sequence of sixty-four constant 32-bit words (K[]), where the words  */
    /*  represent the first thirty-two bits of the fractional parts of the cube roots  */
    /*  of the first sixty-four prime numbers.  */

    0x428a2f98U, 0x71374491U, 0xb5c0fbcfU, 0xe9b5dba5U, 0x3956c25bU, 0x59f111f1U, 0x923f82a4U, 0xab1c5ed5U, 0xd807aa98U,
    0x12835b01U, 0x243185beU, 0x550c7dc3U, 0x72be5d74U, 0x80deb1feU, 0x9bdc06a7U, 0xc19bf174U, 0xe49b69c1U, 0xefbe4786U,
    0x0fc19dc6U, 0x240ca1ccU, 0x2de92c6fU, 0x4a7484aaU, 0x5cb0a9dcU, 0x76f988daU, 0x983e5152U, 0xa831c66dU, 0xb00327c8U,
    0xbf597fc7U, 0xc6e00bf3U, 0xd5a79147U, 0x06ca6351U, 0x14292967U, 0x27b70a85U, 0x2e1b2138U, 0x4d2c6dfcU, 0x53380d13U,
    0x650a7354U, 0x766a0abbU, 0x81c2c92eU, 0x92722c85U, 0xa2bfe8a1U, 0xa81a664bU, 0xc24b8b70U, 0xc76c51a3U, 0xd192e819U,
    0xd6990624U, 0xf40e3585U, 0x106aa070U, 0x19a4c116U, 0x1e376c08U, 0x2748774cU, 0x34b0bcb5U, 0x391c0cb3U, 0x4ed8aa4aU,
    0x5b9cca4fU, 0x682e6ff3U, 0x748f82eeU, 0x78a5636fU, 0x84c87814U, 0x8cc70208U, 0x90befffaU, 0xa4506cebU, 0xbef9a3f7U,
    0xc67178f2U,

    /*  SHA384_INIT_H[]  */
    /*  8 x 64-bit words in little-endian format  */
    0xc1059ed8U, 0xcbbb9d5dU, 0x367cd507U, 0x629a292aU, 0x3070dd17U, 0x9159015aU, 0xf70e5939U, 0x152fecd8U, 0xffc00b31U,
    0x67332667U, 0x68581511U, 0x8eb44a87U, 0x64f98fa7U, 0xdb0c2e0dU, 0xbefa4fa4U, 0x47b5481dU,

    /*  SHA512_INIT_H[]  */
    /*  8 x 64-bit words in little-endian format  */
    0xf3bcc908U, 0x6a09e667U, 0x84caa73bU, 0xbb67ae85U, 0xfe94f82bU, 0x3c6ef372U, 0x5f1d36f1U, 0xa54ff53aU, 0xade682d1U,
    0x510e527fU, 0x2b3e6c1fU, 0x9b05688cU, 0xfb41bd6bU, 0x1f83d9abU, 0x137e2179U, 0x5be0cd19U,

    /*  SHA512_K[]  */
    /*  80 x 64-bit words in little-endian format  */
    0xd728ae22U, 0x428a2f98U, 0x23ef65cdU, 0x71374491U, 0xec4d3b2fU, 0xb5c0fbcfU, 0x8189dbbcU, 0xe9b5dba5U, 0xf348b538U,
    0x3956c25bU, 0xb605d019U, 0x59f111f1U, 0xaf194f9bU, 0x923f82a4U, 0xda6d8118U, 0xab1c5ed5U, 0xa3030242U, 0xd807aa98U,
    0x45706fbeU, 0x12835b01U, 0x4ee4b28cU, 0x243185beU, 0xd5ffb4e2U, 0x550c7dc3U, 0xf27b896fU, 0x72be5d74U, 0x3b1696b1U,
    0x80deb1feU, 0x25c71235U, 0x9bdc06a7U, 0xcf692694U, 0xc19bf174U, 0x9ef14ad2U, 0xe49b69c1U, 0x384f25e3U, 0xefbe4786U,
    0x8b8cd5b5U, 0x0fc19dc6U, 0x77ac9c65U, 0x240ca1ccU, 0x592b0275U, 0x2de92c6fU, 0x6ea6e483U, 0x4a7484aaU, 0xbd41fbd4U,
    0x5cb0a9dcU, 0x831153b5U, 0x76f988daU, 0xee66dfabU, 0x983e5152U, 0x2db43210U, 0xa831c66dU, 0x98fb213fU, 0xb00327c8U,
    0xbeef0ee4U, 0xbf597fc7U, 0x3da88fc2U, 0xc6e00bf3U, 0x930aa725U, 0xd5a79147U, 0xe003826fU, 0x06ca6351U, 0x0a0e6e70U,
    0x14292967U, 0x46d22ffcU, 0x27b70a85U, 0x5c26c926U, 0x2e1b2138U, 0x5ac42aedU, 0x4d2c6dfcU, 0x9d95b3dfU, 0x53380d13U,
    0x8baf63deU, 0x650a7354U, 0x3c77b2a8U, 0x766a0abbU, 0x47edaee6U, 0x81c2c92eU, 0x1482353bU, 0x92722c85U, 0x4cf10364U,
    0xa2bfe8a1U, 0xbc423001U, 0xa81a664bU, 0xd0f89791U, 0xc24b8b70U, 0x0654be30U, 0xc76c51a3U, 0xd6ef5218U, 0xd192e819U,
    0x5565a910U, 0xd6990624U, 0x5771202aU, 0xf40e3585U, 0x32bbd1b8U, 0x106aa070U, 0xb8d2d0c8U, 0x19a4c116U, 0x5141ab53U,
    0x1e376c08U, 0xdf8eeb99U, 0x2748774cU, 0xe19b48a8U, 0x34b0bcb5U, 0xc5c95a63U, 0x391c0cb3U, 0xe3418acbU, 0x4ed8aa4aU,
    0x7763e373U, 0x5b9cca4fU, 0xd6b2b8a3U, 0x682e6ff3U, 0x5defb2fcU, 0x748f82eeU, 0x43172f60U, 0x78a5636fU, 0xa1f0ab72U,
    0x84c87814U, 0x1a6439ecU, 0x8cc70208U, 0x23631e28U, 0x90befffaU, 0xde82bde9U, 0xa4506cebU, 0xb2c67915U, 0xbef9a3f7U,
    0xe372532bU, 0xc67178f2U, 0xea26619cU, 0xca273eceU, 0x21c0c207U, 0xd186b8c7U, 0xcde0eb1eU, 0xeada7dd6U, 0xee6ed178U,
    0xf57d4f7fU, 0x72176fbaU, 0x06f067aaU, 0xa2c898a6U, 0x0a637dc5U, 0xbef90daeU, 0x113f9804U, 0x131c471bU, 0x1b710b35U,
    0x23047d84U, 0x28db77f5U, 0x40c72493U, 0x32caab7bU, 0x15c9bebcU, 0x3c9ebe0aU, 0x9c100d4cU, 0x431d67c4U, 0xcb3e42b6U,
    0x4cc5d4beU, 0xfc657e2aU, 0x597f299cU, 0x3ad6faecU, 0x5fcb6fabU, 0x4a475817U, 0x6c44198cU,
    /*  CHACHA_K[]  */
    0x61707865U, 0x3320646eU, 0x79622d32U, 0x6b206574U};

static const uint32_t s_cau3ReadOnlyConstantsBytes = sizeof(s_cau3ReadOnlyConstants);

static const uint32_t s_cau3ImemImage[] __attribute__((aligned(16))) = {
    0x60C00000U, 0x54000040U, 0x54000020U, 0x60812760U, 0x608128C0U, 0x60812F00U, 0x60813640U, 0x608138C0U, 0x54000040U,
    0x60813C20U, 0x60813F40U, 0x60802060U, 0x60803BC0U, 0x60805780U, 0x54000040U, 0x60806800U, 0x60808AA0U, 0x60808D00U,
    0x60814120U, 0x6080C100U, 0x6080BF60U, 0x54000040U, 0x54000040U, 0x6080FB60U, 0x6080FE20U, 0x608112C0U, 0x608150C0U,
    0x60815700U, 0x54000040U, 0x54000040U, 0x54000040U, 0x54000040U, 0x86000280U, 0x86002282U, 0x08C82236U, 0x2000C011U,
    0x101FFE11U, 0x00405A31U, 0x20014015U, 0x101FFE15U, 0x00800004U, 0x00800425U, 0x00800846U, 0x00800C67U, 0xCE002220U,
    0xCE002222U, 0x3C000192U, 0x66801420U, 0x63801400U, 0x86004284U, 0x86006286U, 0xCE002224U, 0xCE002226U, 0x08D020E8U,
    0x01800108U, 0xAC0012A8U, 0x008C2000U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x01800068U,
    0x008C2084U, 0x008C10A5U, 0x008C14C6U, 0x008C18E7U, 0xCE002224U, 0xCE002226U, 0x08D020E8U, 0x01800108U, 0xAC0012A8U,
    0x008C2000U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x01800068U, 0x008C2084U, 0x008C10A5U,
    0x008C14C6U, 0x008C18E7U, 0xCE002224U, 0xCE002226U, 0x08D020E8U, 0x01800108U, 0xAC0012A8U, 0x008C2000U, 0x008C0021U,
    0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x01800068U, 0x008C2084U, 0x008C10A5U, 0x008C14C6U, 0x008C18E7U,
    0xCE002224U, 0xCE002226U, 0x08D020E8U, 0x01800108U, 0xAC0012A8U, 0x008C2000U, 0x008C0021U, 0x008C0442U, 0x008C0863U,
    0xCE002220U, 0xCE002222U, 0x01800068U, 0x008C2084U, 0x008C10A5U, 0x008C14C6U, 0x008C18E7U, 0xCE002224U, 0xCE002226U,
    0x08D020E8U, 0x01800108U, 0xAC0012A8U, 0x008C2000U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U,
    0x01800068U, 0x008C2084U, 0x008C10A5U, 0x008C14C6U, 0x008C18E7U, 0xCE002224U, 0xCE002226U, 0x08D020E8U, 0x01800108U,
    0xAC0012A8U, 0x008C2000U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x01800068U, 0x008C2084U,
    0x008C10A5U, 0x008C14C6U, 0x008C18E7U, 0xCE002224U, 0xCE002226U, 0x08D020E8U, 0x01800108U, 0xAC0012A8U, 0x008C2000U,
    0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x29FFE211U, 0x60801F80U, 0x54000040U, 0x08D02067U,
    0x018000E7U, 0xAC0012A7U, 0x008C1C00U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U,
    0x018000E7U, 0xAC0012A7U, 0x008C1C00U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U,
    0x018000E7U, 0xAC0012A7U, 0x008C1C00U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U,
    0x018000E7U, 0xAC0012A7U, 0x008C1C00U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U,
    0x018000E7U, 0xAC0012A7U, 0x008C1C00U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U,
    0x018000E7U, 0xAC0012A7U, 0x008C1C00U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U,
    0x018000E7U, 0xAC0012A7U, 0x008C1C00U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U,
    0x018000E7U, 0xAC0012A7U, 0x008C1C00U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U,
    0x018000E7U, 0xAC0012A7U, 0x008C1C00U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U,
    0x018000E7U, 0xAC0012A7U, 0x008C1C00U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x29FFEA11U,
    0x04807FFFU, 0xC4FFA291U, 0x63802000U, 0x7080001FU, 0x5C1FFFE0U, 0x581FFFE0U, 0x54000020U, 0x08800E31U, 0x08C81A20U,
    0x2000A012U, 0x101FFE12U, 0x80000252U, 0x3C000312U, 0x668021A0U, 0x63802180U, 0x08501A52U, 0x08501A52U, 0x08501A52U,
    0x08C82236U, 0x2000C011U, 0x101FFE11U, 0x00405A31U, 0x0C800E04U, 0x638029C0U, 0x3C000024U, 0x63802780U, 0x3C000044U,
    0x63802540U, 0x94FFF605U, 0x08C860A0U, 0x94000605U, 0x08CC20A6U, 0x00881800U, 0x08C860A1U, 0x94001605U, 0x08CC20A6U,
    0x00881821U, 0x08C860A2U, 0x94002605U, 0x08CC20A6U, 0x00881842U, 0x08C860A3U, 0x94003605U, 0x08CC20A6U, 0x00881863U,
    0x60802A40U, 0x94FFFA05U, 0x08C840A0U, 0x94000A05U, 0x08CC40A6U, 0x00881800U, 0x08C840A1U, 0x94001A05U, 0x08CC40A6U,
    0x00881821U, 0x08C840A2U, 0x94002A05U, 0x08CC40A6U, 0x00881842U, 0x08C840A3U, 0x94003A05U, 0x08CC40A6U, 0x00881863U,
    0x60802A40U, 0x94FFFE05U, 0x08C820A0U, 0x94000E05U, 0x08CC60A6U, 0x00881800U, 0x08C820A1U, 0x94001E05U, 0x08CC60A6U,
    0x00881821U, 0x08C820A2U, 0x94002E05U, 0x08CC60A6U, 0x00881842U, 0x08C820A3U, 0x94003E05U, 0x08CC60A6U, 0x00881863U,
    0x60802A40U, 0x94000200U, 0x94001201U, 0x94002202U, 0x94003203U, 0xAC001220U, 0xAC001221U, 0xAC001222U, 0xAC001223U,
    0x08500A48U, 0x08CC0508U, 0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0x8E002224U, 0x8E002226U,
    0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U, 0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U,
    0x8E002224U, 0x8E002226U, 0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U, 0x60002B08U, 0x01800000U, 0x01800021U,
    0x01800042U, 0x01800063U, 0x01900000U, 0x8E002224U, 0x8E002226U, 0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U,
    0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0xAC001220U, 0xAC001221U, 0xAC001222U, 0xAC001223U,
    0x0C800E64U, 0x63803A80U, 0x3C000024U, 0x63803760U, 0x3C000044U, 0x63803440U, 0x94FFF664U, 0x08CC2084U, 0x08C82084U,
    0x08CC6005U, 0x008810A5U, 0xD4FFF665U, 0x08C82005U, 0x08CC6024U, 0x008810A5U, 0xD4000665U, 0x08C82025U, 0x08CC6044U,
    0x008810A5U, 0xD4001665U, 0x08C82045U, 0x08CC6064U, 0x008810A5U, 0xD4002665U, 0x94003664U, 0x08C86084U, 0x08CC6084U,
    0x08C82065U, 0x008810A5U, 0xD4003665U, 0x60803B00U, 0x94FFFA64U, 0x08CC4084U, 0x08C84084U, 0x08CC4005U, 0x008810A5U,
    0xD4FFFA65U, 0x08C84005U, 0x08CC4024U, 0x008810A5U, 0xD4000A65U, 0x08C84025U, 0x08CC4044U, 0x008810A5U, 0xD4001A65U,
    0x08C84045U, 0x08CC4064U, 0x008810A5U, 0xD4002A65U, 0x94003A64U, 0x08C84084U, 0x08CC4084U, 0x08C84065U, 0x008810A5U,
    0xD4003A65U, 0x60803B00U, 0x94FFFE64U, 0x08CC6084U, 0x08C86084U, 0x08CC2005U, 0x008810A5U, 0xD4FFFE65U, 0x08C86005U,
    0x08CC2024U, 0x008810A5U, 0xD4000E65U, 0x08C86025U, 0x08CC2044U, 0x008810A5U, 0xD4001E65U, 0x08C86045U, 0x08CC2064U,
    0x008810A5U, 0xD4002E65U, 0x94003E64U, 0x08C82084U, 0x08CC2084U, 0x08C86065U, 0x008810A5U, 0xD4003E65U, 0x60803B00U,
    0xD4000260U, 0xD4001261U, 0xD4002262U, 0xD4003263U, 0x04807FFFU, 0x63803B60U, 0x7080001FU, 0x5C1FFFE0U, 0x581FFFE0U,
    0x54000020U, 0x08800E31U, 0x08C81A20U, 0x2000A012U, 0x101FFE12U, 0x80000252U, 0x3C000312U, 0x66803D00U, 0x63803CE0U,
    0x08501A52U, 0x08501A52U, 0x08501A52U, 0x08C82236U, 0x2000C011U, 0x101FFE11U, 0x00405A31U, 0x0C800E04U, 0x63804520U,
    0x3C000024U, 0x638042E0U, 0x3C000044U, 0x638040A0U, 0x94FFF605U, 0x08C860A0U, 0x94000605U, 0x08CC20A6U, 0x00881800U,
    0x08C860A1U, 0x94001605U, 0x08CC20A6U, 0x00881821U, 0x08C860A2U, 0x94002605U, 0x08CC20A6U, 0x00881842U, 0x08C860A3U,
    0x94003605U, 0x08CC20A6U, 0x00881863U, 0x608045A0U, 0x94FFFA05U, 0x08C840A0U, 0x94000A05U, 0x08CC40A6U, 0x00881800U,
    0x08C840A1U, 0x94001A05U, 0x08CC40A6U, 0x00881821U, 0x08C840A2U, 0x94002A05U, 0x08CC40A6U, 0x00881842U, 0x08C840A3U,
    0x94003A05U, 0x08CC40A6U, 0x00881863U, 0x608045A0U, 0x94FFFE05U, 0x08C820A0U, 0x94000E05U, 0x08CC60A6U, 0x00881800U,
    0x08C820A1U, 0x94001E05U, 0x08CC60A6U, 0x00881821U, 0x08C820A2U, 0x94002E05U, 0x08CC60A6U, 0x00881842U, 0x08C820A3U,
    0x94003E05U, 0x08CC60A6U, 0x00881863U, 0x608045A0U, 0x94000200U, 0x94001201U, 0x94002202U, 0x94003203U, 0x08C81259U,
    0x00406631U, 0xA8003223U, 0xA8FFF222U, 0xA8FFF221U, 0xA8FFF220U, 0x08500A59U, 0x08CC0739U, 0x01940000U, 0x01840063U,
    0x01840042U, 0x01840021U, 0x01840000U, 0x8AFFE23CU, 0x8AFFE23AU, 0x018C7463U, 0x018C7042U, 0x018C6C21U, 0x018C6800U,
    0x01940000U, 0x01840063U, 0x01840042U, 0x01840021U, 0x01840000U, 0x8AFFE23CU, 0x8AFFE23AU, 0x018C7463U, 0x018C7042U,
    0x018C6C21U, 0x018C6800U, 0x600046B9U, 0x01940000U, 0x01840063U, 0x01840042U, 0x01840021U, 0x01840000U, 0x8AFFE23CU,
    0x8AFFE23AU, 0x018C7463U, 0x018C7042U, 0x018C6C21U, 0x018C6800U, 0x01940000U, 0x01840063U, 0x01840042U, 0x01840021U,
    0x01840000U, 0xA8FFF223U, 0xA8FFF222U, 0xA8FFF221U, 0xA8FFF220U, 0x0C800E79U, 0x63805620U, 0x3C000039U, 0x63805300U,
    0x3C000059U, 0x63804FE0U, 0x94FFF664U, 0x08CC2084U, 0x08C82084U, 0x08CC6005U, 0x008810A5U, 0xD4FFF665U, 0x08C82005U,
    0x08CC6024U, 0x008810A5U, 0xD4000665U, 0x08C82025U, 0x08CC6044U, 0x008810A5U, 0xD4001665U, 0x08C82045U, 0x08CC6064U,
    0x008810A5U, 0xD4002665U, 0x94003664U, 0x08C86084U, 0x08CC6084U, 0x08C82065U, 0x008810A5U, 0xD4003665U, 0x608056A0U,
    0x94FFFA64U, 0x08CC4084U, 0x08C84084U, 0x08CC4005U, 0x008810A5U, 0xD4FFFA65U, 0x08C84005U, 0x08CC4024U, 0x008810A5U,
    0xD4000A65U, 0x08C84025U, 0x08CC4044U, 0x008810A5U, 0xD4001A65U, 0x08C84045U, 0x08CC4064U, 0x008810A5U, 0xD4002A65U,
    0x94003A64U, 0x08C84084U, 0x08CC4084U, 0x08C84065U, 0x008810A5U, 0xD4003A65U, 0x608056A0U, 0x94FFFE64U, 0x08CC6084U,
    0x08C86084U, 0x08CC2005U, 0x008810A5U, 0xD4FFFE65U, 0x08C86005U, 0x08CC2024U, 0x008810A5U, 0xD4000E65U, 0x08C86025U,
    0x08CC2044U, 0x008810A5U, 0xD4001E65U, 0x08C86045U, 0x08CC2064U, 0x008810A5U, 0xD4002E65U, 0x94003E64U, 0x08C82084U,
    0x08CC2084U, 0x08C86065U, 0x008810A5U, 0xD4003E65U, 0x608056A0U, 0xD4003263U, 0xD4002262U, 0xD4001261U, 0xD4000260U,
    0x04807FFFU, 0x63805700U, 0x7080001FU, 0x5C1FFFE0U, 0x581FFFE0U, 0x54000020U, 0x7080001FU, 0x08800E31U, 0x08C82236U,
    0x2000C011U, 0x101FFE11U, 0x00405A31U, 0x94000200U, 0x94001201U, 0x94002202U, 0x94003203U, 0xAC001220U, 0xAC001221U,
    0xAC001222U, 0xAC001223U, 0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0x8E002224U, 0x8E002226U,
    0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U, 0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U,
    0x8E002224U, 0x8E002226U, 0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U, 0x01800000U, 0x01800021U, 0x01800042U,
    0x01800063U, 0x01900000U, 0x8E002224U, 0x8E002226U, 0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U, 0x01800000U,
    0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0x8E002224U, 0x8E002226U, 0x01881000U, 0x01881421U, 0x01881842U,
    0x01881C63U, 0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0x8E002224U, 0x8E002226U, 0x01881000U,
    0x01881421U, 0x01881842U, 0x01881C63U, 0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0x8E002224U,
    0x8E002226U, 0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U, 0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U,
    0x01900000U, 0x8E002224U, 0x8E002226U, 0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U, 0x01800000U, 0x01800021U,
    0x01800042U, 0x01800063U, 0x01900000U, 0x8E002224U, 0x8E002226U, 0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U,
    0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0x8E002224U, 0x8E002226U, 0x01881000U, 0x01881421U,
    0x01881842U, 0x01881C63U, 0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0xAC001220U, 0xAC001221U,
    0xAC001222U, 0xAC001223U, 0xD4000260U, 0xD4001261U, 0xD4002262U, 0xD4003263U, 0x04807FFFU, 0x29FFEA11U, 0x638067A0U,
    0x7080001FU, 0x5C1FFFE0U, 0x581FFFE0U, 0x54000020U, 0xC8FF03DEU, 0xC40013DFU, 0x2000A114U, 0x101FFE14U, 0x08C81A31U,
    0x00405231U, 0x84000231U, 0x04804631U, 0x63006940U, 0x54000040U, 0x5C0001E0U, 0x700058BFU, 0x08CC7C04U, 0x04800000U,
    0x04980063U, 0x04980042U, 0x04980021U, 0x04980000U, 0x60006A84U, 0x088E1C63U, 0xC40043C0U, 0xC40053C1U, 0xC40063C2U,
    0xC40073C3U, 0x08CC7C04U, 0x04800000U, 0x04980063U, 0x04980042U, 0x04980021U, 0x04980000U, 0x60006C04U, 0x088E1C63U,
    0xC40083C0U, 0xC40093C1U, 0xC400A3C2U, 0xC400B3C3U, 0x0C800E09U, 0x63806D20U, 0x08C80D2AU, 0x088C7D4BU, 0x2800002BU,
    0x08403E4DU, 0x0CCC11ADU, 0x63006DE0U, 0x2000002DU, 0x2000000EU, 0x60806E80U, 0x0C803E4FU, 0x63806E60U, 0x2000000EU,
    0x60806E80U, 0x2000002EU, 0x085005ADU, 0x08C811ACU, 0x0040418CU, 0x600072CEU, 0x0C800E09U, 0x638071A0U, 0x0050258CU,
    0x9C001189U, 0x00C82920U, 0x9C001189U, 0x00CC2D39U, 0x00886400U, 0x00C82921U, 0x9C001189U, 0x00CC2D39U, 0x00886421U,
    0x00C82922U, 0x9C001189U, 0x00CC2D39U, 0x00886442U, 0x00C82923U, 0x9C001189U, 0x00CC2D39U, 0x00886463U, 0x60807220U,
    0x94000180U, 0x94001181U, 0x94002182U, 0x94003183U, 0xA40043C0U, 0xA40053C1U, 0xA40063C2U, 0xA40073C3U, 0x60808340U,
    0x5C003E00U, 0x04803DEFU, 0x638077C0U, 0x0C800E09U, 0x63807680U, 0x0050258CU, 0x9C001189U, 0x00C82924U, 0x9C001189U,
    0x00CC2D39U, 0x00886484U, 0x0C5015F8U, 0x658077C0U, 0x00C82925U, 0x9C001189U, 0x00CC2D39U, 0x008864A5U, 0x0C501318U,
    0x658077C0U, 0x00C82926U, 0x9C001189U, 0x00CC2D39U, 0x008864C6U, 0x0C501318U, 0x658077C0U, 0x00C82927U, 0x9C001189U,
    0x00CC2D39U, 0x008864E7U, 0x608077C0U, 0x94000184U, 0x0C5015E9U, 0x658077C0U, 0x94001185U, 0x0C501129U, 0x658077C0U,
    0x94002186U, 0x0C501129U, 0x658077C0U, 0x94003187U, 0x2001E194U, 0x10000014U, 0x08C809EFU, 0x00403E9FU, 0x7080001FU,
    0x60807A60U, 0x60807AC0U, 0x60807B60U, 0x60807C00U, 0x60807C80U, 0x60807CE0U, 0x60807D80U, 0x60807E20U, 0x60807EA0U,
    0x60807F00U, 0x60807FA0U, 0x60808040U, 0x608080C0U, 0x60808120U, 0x608081C0U, 0x60808260U, 0x10100008U, 0x00882084U,
    0x608082C0U, 0x101FE008U, 0x00802084U, 0x10001008U, 0x00882084U, 0x608082C0U, 0x101FFFE8U, 0x00802084U, 0x30100008U,
    0x00882084U, 0x608082C0U, 0x31FFE008U, 0x00802084U, 0x088A0084U, 0x608082C0U, 0x10100008U, 0x008820A5U, 0x608082C0U,
    0x101FE008U, 0x008020A5U, 0x10001008U, 0x008820A5U, 0x608082C0U, 0x101FFFE8U, 0x008020A5U, 0x30100008U, 0x008820A5U,
    0x608082C0U, 0x31FFE008U, 0x008020A5U, 0x088A00A5U, 0x608082C0U, 0x10100008U, 0x008820C6U, 0x608082C0U, 0x101FE008U,
    0x008020C6U, 0x10001008U, 0x008820C6U, 0x608082C0U, 0x101FFFE8U, 0x008020C6U, 0x30100008U, 0x008820C6U, 0x608082C0U,
    0x31FFE008U, 0x008020C6U, 0x088A00C6U, 0x608082C0U, 0x10100008U, 0x008820E7U, 0x608082C0U, 0x101FE008U, 0x008020E7U,
    0x10001008U, 0x008820E7U, 0x608082C0U, 0x101FFFE8U, 0x008020E7U, 0x30100008U, 0x008820E7U, 0x608082C0U, 0x31FFE008U,
    0x008020E7U, 0x088A00E7U, 0x008C1000U, 0x008C1421U, 0x008C1842U, 0x008C1C63U, 0xC400C3C0U, 0xC400D3C1U, 0xC400E3C2U,
    0xC400F3C3U, 0x5C0001E0U, 0x048035ADU, 0x63808880U, 0x0C800E09U, 0x638087A0U, 0x00502617U, 0x9C0012E9U, 0x00C82938U,
    0x9C0012E9U, 0x00CC2D39U, 0x00886718U, 0x008C6000U, 0x00C82938U, 0x9C0012E9U, 0x00CC2D39U, 0x00886718U, 0x008C6021U,
    0x00C82938U, 0x9C0012E9U, 0x00CC2D39U, 0x00886718U, 0x008C6042U, 0x00C82938U, 0x9C0012E9U, 0x00CC2D39U, 0x00886718U,
    0x008C6063U, 0x700058BFU, 0x28000210U, 0x600084ADU, 0x60808880U, 0xB4000200U, 0xB4001201U, 0xB4002202U, 0xB4003203U,
    0x700058BFU, 0x28000210U, 0x600087ADU, 0xA400C3C0U, 0xA400D3C1U, 0xA400E3C2U, 0xA400F3C3U, 0x700058BFU, 0xD4000260U,
    0xD4001261U, 0xD4002262U, 0xD4003263U, 0x840013DFU, 0x840003DEU, 0x04807FFFU, 0x63808A40U, 0x7080001FU, 0x5C1FFFE0U,
    0x581FFFE0U, 0x54000020U, 0x20015000U, 0x101FFE00U, 0x84000001U, 0x84001002U, 0x84002003U, 0x84003004U, 0x84004005U,
    0x84005006U, 0x84006007U, 0x84007008U, 0xD40003A1U, 0xD40013A2U, 0xD40023A3U, 0xD40033A4U, 0xD40043A5U, 0xD40053A6U,
    0xD40063A7U, 0xD40073A8U, 0x54000020U, 0xC8FF93DEU, 0xC40013DFU, 0x940003A1U, 0x940013A2U, 0x940023A3U, 0x940033A4U,
    0x940043A5U, 0x940053A6U, 0x940063A7U, 0x940073A8U, 0xC40043DBU, 0x0C800F7AU, 0x63808F80U, 0x00506B7BU, 0x2800009BU,
    0x08C80F5AU, 0xC40053DAU, 0x088C7F5AU, 0x2800003AU, 0xC40063DAU, 0x2001541AU, 0x101FFE1AU, 0xC40033DDU, 0xC40023DCU,
    0x840043DFU, 0x0C800FFFU, 0x6380A8A0U, 0x94FFF37FU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU,
    0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U,
    0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U,
    0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U,
    0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU,
    0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U,
    0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U,
    0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U,
    0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU,
    0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U,
    0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U,
    0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U,
    0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU,
    0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U,
    0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U,
    0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U,
    0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU,
    0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U,
    0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U,
    0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U,
    0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU,
    0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x6080B6A0U, 0x9C001369U,
    0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U,
    0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U,
    0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU,
    0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U,
    0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U,
    0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U,
    0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U,
    0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U,
    0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U,
    0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU,
    0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U,
    0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U,
    0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x2000019FU, 0x00406609U, 0x01E06169U, 0x8C001340U, 0x01E80120U, 0x01E41500U,
    0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x00406609U, 0x01E06169U, 0x8C001340U, 0x01E80120U, 0x01E41500U, 0x00400084U,
    0x01EC0020U, 0x41FFFEFFU, 0x00406609U, 0x01E06169U, 0x8C001340U, 0x01E80120U, 0x01E41500U, 0x00400084U, 0x01EC0020U,
    0x41FFFEFFU, 0x00406609U, 0x01E06169U, 0x8C001340U, 0x01E80120U, 0x01E41500U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU,
    0x6000B6DFU, 0x840033DDU, 0x940003A0U, 0x00400021U, 0x940013A0U, 0x00400042U, 0x940023A0U, 0x00400063U, 0x940033A0U,
    0x00400084U, 0x940043A0U, 0x004000A5U, 0x940053A0U, 0x004000C6U, 0x940063A0U, 0x004000E7U, 0x940073A0U, 0x00400108U,
    0xD40003A1U, 0xD40013A2U, 0xD40023A3U, 0xD40033A4U, 0xD40043A5U, 0xD40053A6U, 0xD40063A7U, 0xD40073A8U, 0x29FFE01AU,
    0x840023DCU, 0x60008FDCU, 0x840013DFU, 0x840003DEU, 0x04807FFFU, 0x6380BF00U, 0x7080001FU, 0x581FFFE0U, 0x5C1FFFE0U,
    0x54000020U, 0x20014800U, 0x101FFE00U, 0x84000001U, 0x84001002U, 0x84002003U, 0x84003004U, 0x84004005U, 0xD40003A1U,
    0xD40013A2U, 0xD40023A3U, 0xD40033A4U, 0xD40043A5U, 0x54000020U, 0xC8FF43DEU, 0xC40013DFU, 0xC40033DDU, 0x940003A1U,
    0x940013A2U, 0x940023A3U, 0x940033A4U, 0x940043A5U, 0xC40043C1U, 0xC40053C2U, 0xC40063C3U, 0xC40073C4U, 0xC40083C5U,
    0xC40093DBU, 0x0C800F7DU, 0x6380C3E0U, 0x0050777BU, 0x08C80FBFU, 0xC400A3DFU, 0x088C7FE7U, 0x28000027U, 0xC400B3C7U,
    0x9C00137DU, 0xC40023DCU, 0x20014608U, 0x101FFE08U, 0x00800420U, 0x08D01400U, 0x84000108U, 0x840093DCU, 0x0C800F9CU,
    0x6380DD60U, 0x8400A3DFU, 0x8400B3C7U, 0x01D41400U, 0x00402000U, 0x00C87FA9U, 0x9C00137DU, 0x00CC1FBCU, 0x00887129U,
    0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U, 0x00C87FA9U,
    0x9C00137DU, 0x00CC1FBCU, 0x00887129U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U,
    0x01D41400U, 0x00402000U, 0x00C87FA9U, 0x9C00137DU, 0x00CC1FBCU, 0x00887129U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU,
    0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U, 0x00C87FA9U, 0x9C00137DU, 0x00CC1FBCU, 0x00887129U,
    0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U, 0x00C87FA9U,
    0x9C00137DU, 0x00CC1FBCU, 0x00887129U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U,
    0x01D41400U, 0x00402000U, 0x00C87FA9U, 0x9C00137DU, 0x00CC1FBCU, 0x00887129U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU,
    0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U, 0x00C87FA9U, 0x9C00137DU, 0x00CC1FBCU, 0x00887129U,
    0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U, 0x00C87FA9U,
    0x9C00137DU, 0x00CC1FBCU, 0x00887129U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U,
    0x01D41400U, 0x00402000U, 0x00C87FA9U, 0x9C00137DU, 0x00CC1FBCU, 0x00887129U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU,
    0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U, 0x00C87FA9U, 0x9C00137DU, 0x00CC1FBCU, 0x00887129U,
    0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U, 0x00C87FA9U,
    0x9C00137DU, 0x00CC1FBCU, 0x00887129U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U,
    0x01D41400U, 0x00402000U, 0x00C87FA9U, 0x9C00137DU, 0x00CC1FBCU, 0x00887129U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU,
    0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U, 0x00C87FA9U, 0x9C00137DU, 0x00CC1FBCU, 0x00887129U,
    0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U, 0x00C87FA9U,
    0x9C00137DU, 0x00CC1FBCU, 0x00887129U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U,
    0x01D41400U, 0x00402000U, 0x00C87FA9U, 0x9C00137DU, 0x00CC1FBCU, 0x00887129U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU,
    0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U, 0x00C87FA9U, 0x9C00137DU, 0x00CC1FBCU, 0x00887129U,
    0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x6080EF60U, 0x01D41400U, 0x00402000U,
    0x9C001369U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U,
    0x9C001369U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U,
    0x9C001369U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U,
    0x9C001369U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U,
    0x9C001369U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U,
    0x9C001369U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U,
    0x9C001369U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U,
    0x9C001369U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U,
    0x9C001369U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U,
    0x9C001369U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U,
    0x9C001369U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U,
    0x9C001369U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U,
    0x9C001369U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U,
    0x9C001369U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U,
    0x9C001369U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x01D41400U, 0x00402000U,
    0x9C001369U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x2000009FU, 0x01D41400U,
    0x00402000U, 0x00803189U, 0x008C4529U, 0x008C5D29U, 0x008C6529U, 0x08D00529U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU,
    0x4000001BU, 0x00806B43U, 0x08D01400U, 0x6000EF9FU, 0x2000029FU, 0x20014687U, 0x101FFE07U, 0x8C0010E8U, 0x01D01400U,
    0x00402000U, 0x00803189U, 0x008C4529U, 0x008C5D29U, 0x008C6529U, 0x08D00529U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU,
    0x4000001BU, 0x00806B43U, 0x08D01400U, 0x6000F1DFU, 0x2000029FU, 0x8C0010E8U, 0x01D81400U, 0x00402000U, 0x00803189U,
    0x008C4529U, 0x008C5D29U, 0x008C6529U, 0x08D00529U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U,
    0x08D01400U, 0x6000F3DFU, 0x2000029FU, 0x840000E8U, 0x01D01400U, 0x00402000U, 0x00803189U, 0x008C4529U, 0x008C5D29U,
    0x008C6529U, 0x08D00529U, 0x00402400U, 0x41FFFE00U, 0x08D0785AU, 0x4000001BU, 0x00806B43U, 0x08D01400U, 0x6000F5DFU,
    0x840043C0U, 0x00400021U, 0xC40043C1U, 0x840053C0U, 0x00400042U, 0xC40053C2U, 0x840063C0U, 0x00400063U, 0xC40063C3U,
    0x840073C0U, 0x00400084U, 0xC40073C4U, 0x840083C0U, 0x004000A5U, 0xC40083C5U, 0x840023DCU, 0x6000C3FCU, 0x840033DDU,
    0xDC0013A1U, 0xDC0013A2U, 0xDC0013A3U, 0xDC0013A4U, 0xDC0013A5U, 0x840013DFU, 0x840003DEU, 0x04807FFFU, 0x6380FB00U,
    0x7080001FU, 0x5C1FFFE0U, 0x581FFFE0U, 0x54000020U, 0x08800E31U, 0x08C81A20U, 0x2000A412U, 0x101FFE12U, 0x00400251U,
    0x84000220U, 0x84001221U, 0x01A42012U, 0x04804A52U, 0x6300FE00U, 0x84002220U, 0x84003221U, 0x01A42012U, 0x04804A52U,
    0x6300FE00U, 0x84004220U, 0x84005221U, 0x01A42012U, 0x04804A52U, 0x6300FE00U, 0x54000020U, 0x54000040U, 0x08800E31U,
    0x08C81A20U, 0x2000A412U, 0x101FFE12U, 0x00400251U, 0x84000220U, 0x84001221U, 0x0C800E07U, 0x63810380U, 0x3C000027U,
    0x63810240U, 0x3C000047U, 0x63810100U, 0x94FFF607U, 0x08C860E2U, 0x94000607U, 0x08CC20E8U, 0x00882042U, 0x08C860E3U,
    0x94001607U, 0x08CC20E8U, 0x00882063U, 0x608103C0U, 0x94FFFA07U, 0x08C840E2U, 0x94000A07U, 0x08CC40E8U, 0x00882042U,
    0x08C840E3U, 0x94001A07U, 0x08CC40E8U, 0x00882063U, 0x608103C0U, 0x94FFFE07U, 0x08C820E2U, 0x94000E07U, 0x08CC60E8U,
    0x00882042U, 0x08C820E3U, 0x94001E07U, 0x08CC60E8U, 0x00882063U, 0x608103C0U, 0x9C001202U, 0x94000203U, 0x01A40000U,
    0x01A02000U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00000U, 0x01A00400U,
    0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00000U, 0x01A01000U, 0x84002220U, 0x84003221U,
    0x01A41000U, 0x01A02800U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00800U,
    0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00800U, 0x01A01000U, 0x84004220U,
    0x84005221U, 0x01A40000U, 0x01A02000U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00400U,
    0x01A00000U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00000U, 0x01A01000U,
    0x0C800E67U, 0x638111C0U, 0x3C000027U, 0x63810FA0U, 0x3C000047U, 0x63810D80U, 0x94FFF668U, 0x08CC2108U, 0x08C82108U,
    0x08CC6047U, 0x008820E7U, 0xD4FFF667U, 0x08C82047U, 0x08CC6062U, 0x00881C42U, 0xD4000662U, 0x94001668U, 0x08C86108U,
    0x08CC6108U, 0x08C82067U, 0x008820E7U, 0xD4001667U, 0x60811200U, 0x94FFFA68U, 0x08CC4108U, 0x08C84108U, 0x08CC4047U,
    0x008820E7U, 0xD4FFFA67U, 0x08C84047U, 0x08CC4062U, 0x00881C42U, 0xD4000A62U, 0x94001A68U, 0x08C84108U, 0x08CC4108U,
    0x08C84067U, 0x008820E7U, 0xD4001A67U, 0x60811200U, 0x94FFFE68U, 0x08CC6108U, 0x08C86108U, 0x08CC2047U, 0x008820E7U,
    0xD4FFFE67U, 0x08C86047U, 0x08CC2062U, 0x00881C42U, 0xD4000E62U, 0x94001E68U, 0x08C82108U, 0x08CC2108U, 0x08C86067U,
    0x008820E7U, 0xD4001E67U, 0x60811200U, 0xDC001262U, 0xD4000263U, 0x04807FFFU, 0x63811260U, 0x7080001FU, 0x5C1FFFE0U,
    0x581FFFE0U, 0x54000020U, 0x08800E31U, 0x08C81A20U, 0x2000A412U, 0x101FFE12U, 0x00400251U, 0x84004220U, 0x84005221U,
    0x0C800E07U, 0x63811820U, 0x3C000027U, 0x638116E0U, 0x3C000047U, 0x638115A0U, 0x94FFF607U, 0x08C860E2U, 0x94000607U,
    0x08CC20E8U, 0x00882042U, 0x08C860E3U, 0x94001607U, 0x08CC20E8U, 0x00882063U, 0x60811860U, 0x94FFFA07U, 0x08C840E2U,
    0x94000A07U, 0x08CC40E8U, 0x00882042U, 0x08C840E3U, 0x94001A07U, 0x08CC40E8U, 0x00882063U, 0x60811860U, 0x94FFFE07U,
    0x08C820E2U, 0x94000E07U, 0x08CC60E8U, 0x00882042U, 0x08C820E3U, 0x94001E07U, 0x08CC60E8U, 0x00882063U, 0x60811860U,
    0x9C001202U, 0x94000203U, 0x01A41000U, 0x01A02800U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U,
    0x01A00C00U, 0x01A00800U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00800U,
    0x01A01000U, 0x84002220U, 0x84003221U, 0x01A40000U, 0x01A02000U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00400U,
    0x01A00400U, 0x01A00400U, 0x01A00000U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00400U, 0x01A00400U,
    0x01A00000U, 0x01A01000U, 0x84000220U, 0x84001221U, 0x01A41000U, 0x01A02800U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U,
    0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00800U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U, 0x01A00C00U,
    0x01A00C00U, 0x01A00800U, 0x01A01000U, 0x0C800E67U, 0x63812660U, 0x3C000027U, 0x63812440U, 0x3C000047U, 0x63812220U,
    0x94FFF668U, 0x08CC2108U, 0x08C82108U, 0x08CC6047U, 0x008820E7U, 0xD4FFF667U, 0x08C82047U, 0x08CC6062U, 0x00881C42U,
    0xD4000662U, 0x94001668U, 0x08C86108U, 0x08CC6108U, 0x08C82067U, 0x008820E7U, 0xD4001667U, 0x608126A0U, 0x94FFFA68U,
    0x08CC4108U, 0x08C84108U, 0x08CC4047U, 0x008820E7U, 0xD4FFFA67U, 0x08C84047U, 0x08CC4062U, 0x00881C42U, 0xD4000A62U,
    0x94001A68U, 0x08C84108U, 0x08CC4108U, 0x08C84067U, 0x008820E7U, 0xD4001A67U, 0x608126A0U, 0x94FFFE68U, 0x08CC6108U,
    0x08C86108U, 0x08CC2047U, 0x008820E7U, 0xD4FFFE67U, 0x08C86047U, 0x08CC2062U, 0x00881C42U, 0xD4000E62U, 0x94001E68U,
    0x08C82108U, 0x08CC2108U, 0x08C86067U, 0x008820E7U, 0xD4001E67U, 0x608126A0U, 0xDC001262U, 0xD4000263U, 0x04807FFFU,
    0x63812700U, 0x7080001FU, 0x5C1FFFE0U, 0x581FFFE0U, 0x54000020U, 0xC8FFE3DEU, 0x08CC1234U, 0x8C001200U, 0x8C001201U,
    0x8C001202U, 0x8C001203U, 0xCE002240U, 0xCE002242U, 0x600127B4U, 0x840003DEU, 0x54000020U, 0xC8FFE3DEU, 0x5C001FE0U,
    0x08800E31U, 0x0CC81A31U, 0x2000A012U, 0x101FFE12U, 0x00404652U, 0x8C004200U, 0x9C001204U, 0x9C001205U, 0x9C001206U,
    0x9C001207U, 0xCE002240U, 0xCE002242U, 0xCE002244U, 0xCE002246U, 0x9C001200U, 0x9C001201U, 0x9C001202U, 0x9C001203U,
    0x9C001204U, 0x9C001205U, 0x9C001206U, 0x9C001207U, 0xCE002240U, 0xCE002242U, 0xCE002244U, 0xCE002246U, 0x08510252U,
    0x84000240U, 0x0880E000U, 0x3C000400U, 0x67812D00U, 0x20000400U, 0xC4000240U, 0x3C000100U, 0x08408254U, 0x00800012U,
    0x08CC1A31U, 0x67812EA0U, 0x3C000300U, 0x66812E60U, 0x63812E40U, 0x08501A52U, 0x08501A52U, 0x08501A52U, 0x7000041FU,
    0x5C1FFFE0U, 0x5801FFE0U, 0x54000020U, 0xC8FFE3DEU, 0x5C001FE0U, 0x08800E31U, 0x0CC81A31U, 0x2000A012U, 0x101FFE12U,
    0x00404652U, 0x00804200U, 0xCE002240U, 0xCE002242U, 0x20D4D4C4U, 0x1014D4C4U, 0x20D4D4C5U, 0x1014D4C5U, 0xCE002244U,
    0xCE002246U, 0x24000100U, 0x24000121U, 0x24000142U, 0x24000163U, 0x24000184U, 0x240001A5U, 0x240001C6U, 0x240001E7U,
    0x00040000U, 0x00040021U, 0x00040042U, 0x00040063U, 0x00040084U, 0x000400A5U, 0x000400C6U, 0x000400E7U, 0xCE002240U,
    0xCE002242U, 0xCE002244U, 0xCE002246U, 0x08510252U, 0x84000240U, 0x0880E000U, 0x3C000400U, 0x67813440U, 0x20000400U,
    0xC4000240U, 0x3C000100U, 0x08408254U, 0x00800012U, 0x08CC1A31U, 0x678135E0U, 0x3C000300U, 0x668135A0U, 0x63813580U,
    0x08501A52U, 0x08501A52U, 0x08501A52U, 0x7000041FU, 0x5C1FFFE0U, 0x5801FFE0U, 0x54000020U, 0x08800E31U, 0x08C81A31U,
    0x2000A012U, 0x101FFE12U, 0x00404652U, 0x5C1FFFE0U, 0xCE002240U, 0xCE002242U, 0xCE002244U, 0xCE002246U, 0xCE002240U,
    0xCE002242U, 0xCE002244U, 0xCE002246U, 0x5C1FFFE0U, 0x5801FFE0U, 0x04807FFFU, 0x638138A0U, 0x7080001FU, 0x54000020U,
    0x0CCC7E48U, 0x63813940U, 0x088C7E52U, 0x28000032U, 0x08800E52U, 0x08C81A52U, 0x2000A013U, 0x101FFE13U, 0x00404A73U,
    0xC4000271U, 0x28000413U, 0x0C800E21U, 0x08CC0A31U, 0x63813AA0U, 0x28000031U, 0x3C000008U, 0x63813B60U, 0x8C001201U,
    0xCC001261U, 0x60013AF1U, 0x60813BC0U, 0x8C001201U, 0xDC001261U, 0x60013B71U, 0x5C1FFFE0U, 0x581FFFE0U, 0x54000020U,
    0x0CCC7E28U, 0x63813CA0U, 0x088C7E31U, 0x28000031U, 0x08800E31U, 0x08C81A31U, 0x2000A212U, 0x101FFE12U, 0x00404652U,
    0x8C001204U, 0x8C001205U, 0x8C001206U, 0x8C001207U, 0x3C000008U, 0x63813E60U, 0xCE002244U, 0xCE002246U, 0x60813EE0U,
    0xDC001244U, 0xDC001245U, 0xDC001246U, 0xDC001247U, 0x5C1FFFE0U, 0x5801FFE0U, 0x54000020U, 0xC8FFE3DEU, 0x08800E31U,
    0x08C81A20U, 0x2000A014U, 0x101FFE14U, 0x00400294U, 0x84000292U, 0x3C000312U, 0x668140C0U, 0x638140A0U, 0x08501A52U,
    0x08501A52U, 0x08501A52U, 0x28000414U, 0x60800400U, 0x0C8013C6U, 0x638141C0U, 0xC8FEB3DEU, 0xC40013DFU, 0x60814200U,
    0xC8FEC3DEU, 0xC40013DFU, 0x00804A55U, 0x00804636U, 0x00804E77U, 0x084006F4U, 0x3C000094U, 0x630142E0U, 0x20000034U,
    0x3C000055U, 0x63814400U, 0x3C000095U, 0x63814400U, 0x3C000115U, 0x63814400U, 0x3C000095U, 0x67014B00U, 0x60814A80U,
    0x08800EC6U, 0x08C818C6U, 0x2000A00CU, 0x101FFE0CU, 0x0040198CU, 0x8C004198U, 0x08800E31U, 0x08C82226U, 0x2000C011U,
    0x101FFE11U, 0x00401A31U, 0x3C000318U, 0x66814600U, 0x63814A40U, 0x200001D2U, 0x60814620U, 0x20000152U, 0x2001D613U,
    0x101FFE13U, 0x9C001200U, 0x9C001201U, 0x20000106U, 0x004078C6U, 0x008056AAU, 0x9C00120EU, 0x9C00120FU, 0xCE0020CEU,
    0x6001470AU, 0x200000C9U, 0x008056AAU, 0x08500527U, 0x01001EA7U, 0x004028E6U, 0x008C1821U, 0x08C80D48U, 0x800023C2U,
    0x08401108U, 0x800023C3U, 0x700045BFU, 0x08501108U, 0xC00023C2U, 0x08401108U, 0xC00023C3U, 0x6001480AU, 0x600147A9U,
    0x8600018EU, 0x008C01CBU, 0x008C05E6U, 0x0488196BU, 0x63814BE0U, 0x3C000115U, 0x63814B00U, 0x00805EF1U, 0x7001365FU,
    0x581FFFE0U, 0x54000040U, 0x00805EF1U, 0x0080529DU, 0x7001365FU, 0x008077B1U, 0x7001365FU, 0x581FFFE0U, 0x54000040U,
    0x00805EE7U, 0x08800EF7U, 0x08C81AE6U, 0x2000A017U, 0x101FFE17U, 0x00401AF7U, 0x3C000055U, 0x63814D60U, 0x3C000095U,
    0x63814D60U, 0x20000407U, 0x60814D80U, 0x08C80EA7U, 0xCC0082E7U, 0x20000106U, 0x004078C6U, 0x008056AAU, 0x3C000115U,
    0x63814EC0U, 0x8E0020C0U, 0xCE0022E0U, 0x60014E4AU, 0x608150A0U, 0x0850114AU, 0x8E0020C0U, 0xCE0022E0U, 0x60014EEAU,
    0x08800E94U, 0x08C81A87U, 0x2000A014U, 0x101FFE14U, 0x00401E94U, 0x2000008AU, 0x20000407U, 0xCC008287U, 0x8E0020C0U,
    0xCE002280U, 0x6001504AU, 0x54000020U, 0xC4FFF3DFU, 0xC8FFE3DEU, 0x2001D600U, 0x101FFE00U, 0x700162BFU, 0x3C000017U,
    0x63815600U, 0x0C800EFDU, 0x63015280U, 0xC40002E2U, 0xC40012E3U, 0xC40022E4U, 0xC40032E5U, 0x60815600U, 0x005076F7U,
    0x08C80FBDU, 0x088C7FBCU, 0x2800003CU, 0x840002FAU, 0x00C8735FU, 0x00CC73FFU, 0x00C8745AU, 0x00887F5AU, 0xC40002FAU,
    0x00CC705FU, 0x00C8747AU, 0x00887F5AU, 0xC40012FAU, 0x00CC707FU, 0x00C8749AU, 0x00887F5AU, 0xC40022FAU, 0x00CC709FU,
    0x00C874BAU, 0x00887F5AU, 0xC40032FAU, 0x00CC70BFU, 0x840042FAU, 0x00CC775AU, 0x00C8775AU, 0x00887F5AU, 0xC40042FAU,
    0x840013DFU, 0x04807FFFU, 0x638156A0U, 0x840003DEU, 0x7080001FU, 0x5C1FFFE0U, 0x581FFFE0U, 0x54000020U, 0xC4FFF3D5U,
    0xC4FFE3D6U, 0xC4FFD3DFU, 0xC8FFC3DEU, 0x2001DE00U, 0x101FFE00U, 0x700162BFU, 0x84FFD3DFU, 0x20000033U, 0x3C000017U,
    0x638161A0U, 0x0C800EFDU, 0x63015940U, 0x840002E6U, 0x840012E7U, 0x840022E8U, 0x840032E9U, 0x60815BE0U, 0x005076F7U,
    0x08C80FBDU, 0x088C7FBCU, 0x2800003CU, 0x840002FAU, 0x00CC775FU, 0x840012FAU, 0x00C87346U, 0x00887CC6U, 0x00CC775FU,
    0x840022FAU, 0x00C87347U, 0x00887CE7U, 0x00CC775FU, 0x840032FAU, 0x00C87348U, 0x00887D08U, 0x00CC775FU, 0x840042FAU,
    0x00C87349U, 0x00887D29U, 0x008C1857U, 0x008C1C7AU, 0x00886AF7U, 0x008C209AU, 0x00886AF7U, 0x008C24BAU, 0x04886AF7U,
    0x638161A0U, 0x20000053U, 0x840033D5U, 0x3C000015U, 0x638161A0U, 0x840023D6U, 0x0C800EDAU, 0x63816020U, 0x00506AD6U,
    0x00406AB5U, 0x0C5012B5U, 0x2000001CU, 0x66015F20U, 0x840002DCU, 0x28000095U, 0x08C80EB5U, 0x00CC579CU, 0x00C8579CU,
    0x20000015U, 0x08C80F5AU, 0x088C7F5AU, 0x2800003AU, 0x840002DBU, 0x00C86B7BU, 0x00CC6B7BU, 0x0088737BU, 0xCC0012DBU,
    0x0CCC0ABBU, 0x638160C0U, 0x2000001AU, 0xCC0012DAU, 0x6001609BU, 0x0C800EBAU, 0x638161A0U, 0x08C80F5AU, 0x840002DBU,
    0x00CC6B7BU, 0x00C86B7BU, 0xCC0012DBU, 0x840013DFU, 0x04807FFFU, 0x63816240U, 0x840003DEU, 0x7080001FU, 0x5C1FFFE0U,
    0x581FFEE0U, 0x50000013U, 0x0C8013DAU, 0x63816320U, 0xCAFD13DEU, 0x60816340U, 0xCAFD23DEU, 0xC402D3C0U, 0xC402A3D3U,
    0xC402B3D5U, 0x3C000011U, 0x0080463AU, 0x660165C0U, 0x20000015U, 0x20000011U, 0x088C0F5AU, 0x2800003AU, 0x08800F5AU,
    0x08C81B5AU, 0x2000A41BU, 0x101FFE1BU, 0x00406B7AU, 0x8E002340U, 0x8E002342U, 0x8E002344U, 0x8E002346U, 0x60816840U,
    0x08800F5AU, 0x08C81B5AU, 0x2000A011U, 0x101FFE11U, 0x00406A31U, 0x0C800E9CU, 0x00507294U, 0x08C80F9CU, 0x63816740U,
    0x8C001290U, 0x00CC7210U, 0xC40263D0U, 0x08800EDDU, 0x005076D6U, 0x08C80FBDU, 0xC60243DCU, 0x20000019U, 0x3C000017U,
    0x63817A80U, 0x7001B07FU, 0xC60023C0U, 0xC60043C2U, 0xC60063C4U, 0xC60083C6U, 0x08C81808U, 0x08CC1908U, 0x08CC6009U,
    0x08C8203AU, 0x00886929U, 0x08CC0929U, 0x21FFE07AU, 0x10007FFAU, 0x00806929U, 0x08CC402AU, 0x08C8405AU, 0x0088694AU,
    0x08CC114AU, 0x21F81FFAU, 0x10007FFAU, 0x0080694AU, 0x08CC204BU, 0x08C8607AU, 0x0088696BU, 0x08CC196BU, 0x2007FFFAU,
    0x10007E1AU, 0x0080696BU, 0x08CC206CU, 0x08C8318CU, 0x08CC318CU, 0xC60103C8U, 0xC60123CAU, 0xC40143CCU, 0x860103C6U,
    0x860123C8U, 0x840143CBU, 0x08C808ECU, 0x00401D8CU, 0x08C8090DU, 0x004021ADU, 0x08C8092EU, 0x004025CEU, 0x08C8096FU,
    0x00402DEFU, 0xC60203CCU, 0xC60223CEU, 0x2000001AU, 0x2000001BU, 0xC600A3DAU, 0xC600C3DAU, 0xC400E3DAU, 0x3C000013U,
    0x63817A80U, 0x00804A5CU, 0x0C800E5AU, 0x63017220U, 0x8402D3C8U, 0x08CC0A7BU, 0x0C800E7DU, 0x63816FE0U, 0x2800003BU,
    0x3C000813U, 0x67817060U, 0x2000081BU, 0x08CC0B7BU, 0x8C001240U, 0xCC001100U, 0x6001707BU, 0x3C000813U, 0x67817800U,
    0xC40153D3U, 0x20000813U, 0xC402C3D2U, 0x7001C1DFU, 0x840153D3U, 0x8402C3D2U, 0x2000081DU, 0x00507673U, 0x60816F40U,
    0x20000038U, 0x08800E4AU, 0x08C80D4AU, 0x088C7D49U, 0x28000029U, 0x08800E7CU, 0x00406B9CU, 0x3C00009CU, 0x67817360U,
    0x28000038U, 0x8402D3C8U, 0x00506A5AU, 0x08CC0A7BU, 0x0C800E7DU, 0x63817420U, 0x2800003BU, 0x3C000813U, 0x678174A0U,
    0x2000081BU, 0x08CC0B7BU, 0x00806F7DU, 0x0040637BU, 0x8C001340U, 0x00CC2801U, 0x0850077BU, 0x00506FBDU, 0x8C001340U,
    0x00C82402U, 0x00880442U, 0x00CC2801U, 0xCC001102U, 0x6001757BU, 0x3C00001DU, 0x63817680U, 0xCC001101U, 0x3C000813U,
    0x67817800U, 0xC40153D3U, 0x20000813U, 0xC402C3D2U, 0x7001C1DFU, 0x840153D3U, 0x8402C3D2U, 0x2000081DU, 0x00507673U,
    0x00407652U, 0x60816EE0U, 0x0C803E7DU, 0x63817A20U, 0x3C000011U, 0x638179E0U, 0x00507672U, 0x8402D3DDU, 0x00407652U,
    0xC4FFF3DFU, 0xC8FFE3DEU, 0x7001F3DFU, 0x840013DFU, 0x840003DEU, 0x08CC1273U, 0x08C81273U, 0x28000213U, 0x7001C1DFU,
    0x60817A80U, 0x3C000013U, 0x63817A80U, 0x7001C1DFU, 0x3C000015U, 0x6381AD40U, 0x860243DCU, 0x840263D0U, 0x840002DFU,
    0x20000039U, 0x088C7FBBU, 0x2800003BU, 0x00C86FFFU, 0x00CC6FFFU, 0xC40273DFU, 0x7001B07FU, 0x840273DFU, 0x2001D61AU,
    0x101FFE1AU, 0xC6000340U, 0xC6002342U, 0xC6004344U, 0xC6006346U, 0xC6008348U, 0xC600A34AU, 0xC600C34CU, 0xC600E34EU,
    0x0C5102B5U, 0x860243DCU, 0x66819BC0U, 0x0C807F9CU, 0x63018020U, 0x8C001280U, 0x8C001281U, 0x8C001282U, 0x8C001283U,
    0x8C001284U, 0x8C001285U, 0x8C001286U, 0x8C001287U, 0x8C001288U, 0x8C001289U, 0x8C00128AU, 0x8C00128BU, 0x8C00128CU,
    0x8C00128DU, 0x8C00128EU, 0x8C00128FU, 0x60818880U, 0x088C7F9DU, 0x2800003DU, 0x8C00129BU, 0x00C8777AU, 0x00884340U,
    0x00CC7370U, 0x8C00129BU, 0x00C8777AU, 0x00884341U, 0x00CC7370U, 0x8C00129BU, 0x00C8777AU, 0x00884342U, 0x00CC7370U,
    0x8C00129BU, 0x00C8777AU, 0x00884343U, 0x00CC7370U, 0x8C00129BU, 0x00C8777AU, 0x00884344U, 0x00CC7370U, 0x8C00129BU,
    0x00C8777AU, 0x00884345U, 0x00CC7370U, 0x8C00129BU, 0x00C8777AU, 0x00884346U, 0x00CC7370U, 0x8C00129BU, 0x00C8777AU,
    0x00884347U, 0x00CC7370U, 0x8C00129BU, 0x00C8777AU, 0x00884348U, 0x00CC7370U, 0x8C00129BU, 0x00C8777AU, 0x00884349U,
    0x00CC7370U, 0x8C00129BU, 0x00C8777AU, 0x0088434AU, 0x00CC7370U, 0x8C00129BU, 0x00C8777AU, 0x0088434BU, 0x00CC7370U,
    0x8C00129BU, 0x00C8777AU, 0x0088434CU, 0x00CC7370U, 0x8C00129BU, 0x00C8777AU, 0x0088434DU, 0x00CC7370U, 0x8C00129BU,
    0x00C8777AU, 0x0088434EU, 0x00CC7370U, 0x8C00129BU, 0x00C8777AU, 0x0088434FU, 0x00CC7370U, 0xC40263D0U, 0x3C000017U,
    0x63818A00U, 0x2001DE12U, 0x101FFE12U, 0xC6000240U, 0xC6002242U, 0xC6004244U, 0xC6006246U, 0xC6008248U, 0xC600A24AU,
    0xC600C24CU, 0xC600E24EU, 0x2001D61AU, 0x101FFE1AU, 0xA4000340U, 0xA4001341U, 0xA4002342U, 0xA4003343U, 0xA4004344U,
    0xA4005345U, 0xA4006346U, 0xA4007347U, 0xA4008348U, 0xA4009349U, 0xA400A34AU, 0xA400B34BU, 0xA400C34CU, 0xA400D34DU,
    0xA400E34EU, 0xA400F34FU, 0x63818F20U, 0xC6000340U, 0xC6002342U, 0xC6004344U, 0xC6006346U, 0xC6008348U, 0xC600A34AU,
    0xC600C34CU, 0xC600E34EU, 0xC402C3DFU, 0x20000813U, 0x7001C1DFU, 0x8402C3DFU, 0x2001D61AU, 0x101FFE1AU, 0x86000340U,
    0x86002342U, 0x86004344U, 0x86006346U, 0x86008348U, 0x8600A34AU, 0x8600C34CU, 0x8600E34EU, 0x860243DCU, 0x0C807FBDU,
    0x63019200U, 0xCC0012C0U, 0xCC0012C1U, 0xCC0012C2U, 0xCC0012C3U, 0xCC0012C4U, 0xCC0012C5U, 0xCC0012C6U, 0xCC0012C7U,
    0xCC0012C8U, 0xCC0012C9U, 0xCC0012CAU, 0xCC0012CBU, 0xCC0012CCU, 0xCC0012CDU, 0xCC0012CEU, 0xCC0012CFU, 0x3C000015U,
    0x6381AD20U, 0x67017BC0U, 0x54000040U, 0x860243DCU, 0x088C7FBCU, 0x2800003CU, 0x00C8741BU, 0x00887F7BU, 0xCC0012DBU,
    0x00CC701FU, 0x00C8743BU, 0x00887F7BU, 0xCC0012DBU, 0x00CC703FU, 0x00C8745BU, 0x00887F7BU, 0xCC0012DBU, 0x00CC705FU,
    0x00C8747BU, 0x00887F7BU, 0xCC0012DBU, 0x00CC707FU, 0x00C8749BU, 0x00887F7BU, 0xCC0012DBU, 0x00CC709FU, 0x00C874BBU,
    0x00887F7BU, 0xCC0012DBU, 0x00CC70BFU, 0x00C874DBU, 0x00887F7BU, 0xCC0012DBU, 0x00CC70DFU, 0x00C874FBU, 0x00887F7BU,
    0xCC0012DBU, 0x00CC70FFU, 0x00C8751BU, 0x00887F7BU, 0xCC0012DBU, 0x00CC711FU, 0x00C8753BU, 0x00887F7BU, 0xCC0012DBU,
    0x00CC713FU, 0x00C8755BU, 0x00887F7BU, 0xCC0012DBU, 0x00CC715FU, 0x00C8757BU, 0x00887F7BU, 0xCC0012DBU, 0x00CC717FU,
    0x00C8759BU, 0x00887F7BU, 0xCC0012DBU, 0x00CC719FU, 0x00C875BBU, 0x00887F7BU, 0xCC0012DBU, 0x00CC71BFU, 0x00C875DBU,
    0x00887F7BU, 0xCC0012DBU, 0x00CC71DFU, 0x00C875FBU, 0x00887F7BU, 0xCC0012DBU, 0x00CC71FFU, 0xC40273DFU, 0x3C000015U,
    0x67017BC0U, 0x63819B00U, 0x54000040U, 0x840002DBU, 0x00CC777BU, 0x00C8777BU, 0x00887F7BU, 0xC40002DBU, 0x6081AD20U,
    0x28000815U, 0x860243DCU, 0x2001D69DU, 0x101FFE1DU, 0x2001DE0DU, 0x101FFE0DU, 0x0C800EBCU, 0x08CC0ABBU, 0x63819D00U,
    0x2800003BU, 0x840243DCU, 0x0C807F9CU, 0x63019E40U, 0x8C00129AU, 0xCC0011BAU, 0x008C6800U, 0xC4FFF3A0U, 0x8C0013A0U,
    0x60019D7BU, 0x60819FA0U, 0x088C7F8BU, 0x2800002BU, 0x8C001281U, 0x00C82C3AU, 0x0088435AU, 0xCC0011BAU, 0x008C6800U,
    0x00CC7030U, 0xC4FFF3A0U, 0x8C0013A0U, 0x60019E9BU, 0x3C000017U, 0x6381A180U, 0xC402C3DFU, 0x008056B3U, 0x0C803E7AU,
    0x6381A140U, 0x00506A72U, 0x8402D3DAU, 0x00406A52U, 0x7001F3DFU, 0x08803E7AU, 0x00506A73U, 0x28000213U, 0x7001C1DFU,
    0x8402C3DFU, 0x2001D61AU, 0x101FFE1AU, 0x860243DCU, 0x0C807FBDU, 0x6301A4C0U, 0x0CCC0ABBU, 0x6381A2C0U, 0x8C001340U,
    0xCC0012C0U, 0x6001A27BU, 0x0C800EBBU, 0x6381AD20U, 0x8C000340U, 0x840002DCU, 0x08800EBDU, 0x08C80FBDU, 0x088C7FBBU,
    0x2800003BU, 0x00C86C00U, 0x00CC6C00U, 0x00CC779CU, 0x00C8779CU, 0xC4001340U, 0x00887000U, 0xC40002C0U, 0x6081AD20U,
    0x0CCC0AB0U, 0x6381A720U, 0x08CC0FBCU, 0x08800EABU, 0x0040716BU, 0x3C00008BU, 0x6781A5E0U, 0x28000030U, 0x0C800D6BU,
    0x840273DFU, 0x088C7FBCU, 0x2800003CU, 0x8C001340U, 0x00C8741BU, 0x00887F7BU, 0xCC0012DBU, 0x00CC701FU, 0x6001A650U,
    0x6301AB40U, 0x860243DCU, 0x088C7FBBU, 0x2800003BU, 0x8C00135CU, 0x00C87780U, 0x00887C00U, 0x00CC6F9FU, 0x840002DCU,
    0x08CC0FABU, 0x08800EAAU, 0x00402D4AU, 0x3C00008AU, 0x6681A9A0U, 0x6381AB00U, 0xCC0012C0U, 0x840002DCU, 0x8C001340U,
    0x00C87400U, 0x00887C00U, 0x08800D4AU, 0x08C80D4AU, 0x088C7D5BU, 0x2800003BU, 0x00C86C00U, 0x00CC6C00U, 0xC4001140U,
    0x00CC2B9CU, 0x00C82B9CU, 0x00887000U, 0xC40002C0U, 0x6081AD20U, 0xC40002C0U, 0x6081AD20U, 0x08C80D6BU, 0x088C7D7CU,
    0x2800003CU, 0x84000340U, 0x00C87400U, 0x00887C00U, 0x00C87000U, 0x00CC7000U, 0x00CC740AU, 0xC400134AU, 0x840002DBU,
    0x00CC2F7BU, 0x00C82F7BU, 0x0088037BU, 0xC40002DBU, 0x008056B3U, 0x3C000017U, 0x6381AFE0U, 0x3C000011U, 0x6381AF60U,
    0x2001DE12U, 0x101FFE12U, 0x8402A3DAU, 0x2000001BU, 0x8402B3DCU, 0x2000001DU, 0xC600025AU, 0xC600225CU, 0x20000213U,
    0x7001C1FFU, 0x20000013U, 0x7001C1DFU, 0x6081AFE0U, 0x0C803E73U, 0x6301AFE0U, 0x20000013U, 0x7001C1DFU, 0x840013DFU,
    0x04807FFFU, 0x840003DEU, 0x7080001FU, 0x2001D41AU, 0x101FFE1AU, 0x86000340U, 0x86002342U, 0x86008224U, 0x8600A226U,
    0x8600C228U, 0x8600E22AU, 0x0080672CU, 0x8400422DU, 0x8400522EU, 0x8400622FU, 0x20000158U, 0x00401000U, 0x008C018CU,
    0x08D0418CU, 0x00403108U, 0x008C2084U, 0x08D03084U, 0x00401000U, 0x008C018CU, 0x08D0218CU, 0x00403108U, 0x008C2084U,
    0x08D01C84U, 0x00401421U, 0x008C05ADU, 0x08D041ADU, 0x00403529U, 0x008C24A5U, 0x08D030A5U, 0x00401421U, 0x008C05ADU,
    0x08D021ADU, 0x00403529U, 0x008C24A5U, 0x08D01CA5U, 0x00401842U, 0x008C09CEU, 0x08D041CEU, 0x0040394AU, 0x008C28C6U,
    0x08D030C6U, 0x00401842U, 0x008C09CEU, 0x08D021CEU, 0x0040394AU, 0x008C28C6U, 0x08D01CC6U, 0x00401C63U, 0x008C0DEFU,
    0x08D041EFU, 0x00403D6BU, 0x008C2CE7U, 0x08D030E7U, 0x00401C63U, 0x008C0DEFU, 0x08D021EFU, 0x00403D6BU, 0x008C2CE7U,
    0x08D01CE7U, 0x00401400U, 0x008C01EFU, 0x08D041EFU, 0x00403D4AU, 0x008C28A5U, 0x08D030A5U, 0x00401400U, 0x008C01EFU,
    0x08D021EFU, 0x00403D4AU, 0x008C28A5U, 0x08D01CA5U, 0x00401821U, 0x008C058CU, 0x08D0418CU, 0x0040316BU, 0x008C2CC6U,
    0x08D030C6U, 0x00401821U, 0x008C058CU, 0x08D0218CU, 0x0040316BU, 0x008C2CC6U, 0x08D01CC6U, 0x00401C42U, 0x008C09ADU,
    0x08D041ADU, 0x00403508U, 0x008C20E7U, 0x08D030E7U, 0x00401C42U, 0x008C09ADU, 0x08D021ADU, 0x00403508U, 0x008C20E7U,
    0x08D01CE7U, 0x00401063U, 0x008C0DCEU, 0x08D041CEU, 0x00403929U, 0x008C2484U, 0x08D03084U, 0x00401063U, 0x008C0DCEU,
    0x08D021CEU, 0x00403929U, 0x008C2484U, 0x08D01C84U, 0x6001B218U, 0x2001D41AU, 0x101FFE1AU, 0x8600035CU, 0x00407000U,
    0x00407421U, 0x8600235CU, 0x00407042U, 0x00407463U, 0x8600823AU, 0x8600A23CU, 0x00406884U, 0x00406CA5U, 0x004070C6U,
    0x004074E7U, 0x8600C23AU, 0x8600E23CU, 0x00406908U, 0x00406D29U, 0x0040714AU, 0x0040756BU, 0x0040658CU, 0x8400423AU,
    0x004069ADU, 0x8400523AU, 0x004069CEU, 0x8400623AU, 0x004069EFU, 0x28000039U, 0x7080001FU, 0x8402D3D2U, 0xC40263D0U,
    0xC60283C0U, 0x8600A3C2U, 0x8600C3C4U, 0x8400E3CAU, 0x3C000013U, 0x6381E8A0U, 0x20000010U, 0x10002010U, 0x860103C6U,
    0x860123C8U, 0x840143CBU, 0x860203CCU, 0x860223CEU, 0x0CCC1278U, 0x6381E7A0U, 0x8C00125DU, 0x08C81BBAU, 0x08CC1B5AU,
    0x00406842U, 0x08CC63BAU, 0x8C00125DU, 0x08C823BBU, 0x00886F5AU, 0x08CC0B5AU, 0x08C81B5AU, 0x08CC1B5AU, 0x00406863U,
    0x08CC43BAU, 0x8C00125DU, 0x08C843BBU, 0x00886F5AU, 0x08CC135AU, 0x08C81B5AU, 0x08CC1B5AU, 0x00406884U, 0x08CC23BAU,
    0x8C00125DU, 0x08C863BBU, 0x00886F5AU, 0x08CC1B5AU, 0x08C81B5AU, 0x08CC1B5AU, 0x004068A5U, 0x08CC23BAU, 0x0088435AU,
    0x0040694AU, 0x008C1840U, 0x08C47C00U, 0x008C0041U, 0x08CC7C00U, 0x00400021U, 0x03041820U, 0x0080001AU, 0x0080043BU,
    0x008C3C60U, 0x08C47C00U, 0x008C0061U, 0x08CC7C00U, 0x00400021U, 0x03043C20U, 0x0440035AU, 0x0044077BU, 0x008C3880U,
    0x08C47C00U, 0x008C0081U, 0x08CC7C00U, 0x00400021U, 0x03043820U, 0x0440035AU, 0x0044077BU, 0x008C34A0U, 0x08C47C00U,
    0x008C00A1U, 0x08CC7C00U, 0x00400021U, 0x03043420U, 0x0440035AU, 0x0044077BU, 0x008C3140U, 0x08C47C00U, 0x008C0141U,
    0x08CC7C00U, 0x00400021U, 0x03043020U, 0x0440035AU, 0x0044077BU, 0xC60163DAU, 0x008C1C40U, 0x08C47C00U, 0x008C0041U,
    0x08CC7C00U, 0x00400021U, 0x03041C20U, 0x0080001AU, 0x0080043BU, 0x008C1860U, 0x08C47C00U, 0x008C0061U, 0x08CC7C00U,
    0x00400021U, 0x03041820U, 0x0440035AU, 0x0044077BU, 0x008C3C80U, 0x08C47C00U, 0x008C0081U, 0x08CC7C00U, 0x00400021U,
    0x03043C20U, 0x0440035AU, 0x0044077BU, 0x008C38A0U, 0x08C47C00U, 0x008C00A1U, 0x08CC7C00U, 0x00400021U, 0x03043820U,
    0x0440035AU, 0x0044077BU, 0x008C3540U, 0x08C47C00U, 0x008C0141U, 0x08CC7C00U, 0x00400021U, 0x03043420U, 0x0440035AU,
    0x0044077BU, 0xC60183DAU, 0x008C2040U, 0x08C47C00U, 0x008C0041U, 0x08CC7C00U, 0x00400021U, 0x03042020U, 0x0080001AU,
    0x0080043BU, 0x008C1C60U, 0x08C47C00U, 0x008C0061U, 0x08CC7C00U, 0x00400021U, 0x03041C20U, 0x0440035AU, 0x0044077BU,
    0x008C1880U, 0x08C47C00U, 0x008C0081U, 0x08CC7C00U, 0x00400021U, 0x03041820U, 0x0440035AU, 0x0044077BU, 0x008C3CA0U,
    0x08C47C00U, 0x008C00A1U, 0x08CC7C00U, 0x00400021U, 0x03043C20U, 0x0440035AU, 0x0044077BU, 0x008C3940U, 0x08C47C00U,
    0x008C0141U, 0x08CC7C00U, 0x00400021U, 0x03043820U, 0x0440035AU, 0x0044077BU, 0xC601A3DAU, 0x008C2440U, 0x08C47C00U,
    0x008C0041U, 0x08CC7C00U, 0x00400021U, 0x03042420U, 0x0080001AU, 0x0080043BU, 0x008C2060U, 0x08C47C00U, 0x008C0061U,
    0x08CC7C00U, 0x00400021U, 0x03042020U, 0x0440035AU, 0x0044077BU, 0x008C1C80U, 0x08C47C00U, 0x008C0081U, 0x08CC7C00U,
    0x00400021U, 0x03041C20U, 0x0440035AU, 0x0044077BU, 0x008C18A0U, 0x08C47C00U, 0x008C00A1U, 0x08CC7C00U, 0x00400021U,
    0x03041820U, 0x0440035AU, 0x0044077BU, 0x008C3D40U, 0x08C47C00U, 0x008C0141U, 0x08CC7C00U, 0x00400021U, 0x03043C20U,
    0x0440035AU, 0x0044077BU, 0xC601C3DAU, 0x008C2C40U, 0x08C47C00U, 0x008C0041U, 0x08CC7C00U, 0x00400021U, 0x03042C20U,
    0x0080001AU, 0x0080043BU, 0x008C2460U, 0x08C47C00U, 0x008C0061U, 0x08CC7C00U, 0x00400021U, 0x03042420U, 0x0440035AU,
    0x0044077BU, 0x008C2080U, 0x08C47C00U, 0x008C0081U, 0x08CC7C00U, 0x00400021U, 0x03042020U, 0x0440035AU, 0x0044077BU,
    0x008C1CA0U, 0x08C47C00U, 0x008C00A1U, 0x08CC7C00U, 0x00400021U, 0x03041C20U, 0x0440035AU, 0x0044077BU, 0x008C1940U,
    0x08C47C00U, 0x008C0141U, 0x08CC7C00U, 0x00400021U, 0x03041820U, 0x0440035AU, 0x0044077BU, 0xC601E3DAU, 0x860163DAU,
    0x08CC6B40U, 0x08C81B62U, 0x00880800U, 0x08C81B42U, 0x08CC1842U, 0x860183DAU, 0x0440035AU, 0x0844037BU, 0x08CC6B40U,
    0x08C81B63U, 0x00880C00U, 0x08C81B43U, 0x08CC1863U, 0x8601A3DAU, 0x0440035AU, 0x0844037BU, 0x08CC6B40U, 0x08C81B64U,
    0x00881000U, 0x08C81B44U, 0x08CC1884U, 0x8601C3DAU, 0x0440035AU, 0x0844037BU, 0x08CC6B40U, 0x08C81B65U, 0x00881400U,
    0x08C81B45U, 0x08CC18A5U, 0x8601E3DAU, 0x0440035AU, 0x0844037BU, 0x08CC6B40U, 0x08C81B6AU, 0x00882800U, 0x08C81B4AU,
    0x08CC194AU, 0x08C8081AU, 0x00406842U, 0x00400042U, 0x08CC6840U, 0x08C81842U, 0x08CC1842U, 0x00400063U, 0x6001C3F8U,
    0x3C000010U, 0x6381E8A0U, 0x0C803E73U, 0x6381F2A0U, 0xC4FFF3DFU, 0xC8FFE3DEU, 0x7001F37FU, 0x840013DFU, 0x840003DEU,
    0x20000010U, 0x20000038U, 0x6081C3E0U, 0x08CC6860U, 0x08C81863U, 0x08CC1863U, 0x00400084U, 0x08CC6880U, 0x08C81884U,
    0x08CC1884U, 0x004000A5U, 0x08CC68A0U, 0x08C818A5U, 0x08CC18A5U, 0x0040014AU, 0x08CC6940U, 0x08C8194AU, 0x08CC194AU,
    0x00400042U, 0x08C8081AU, 0x00406842U, 0x08CC6840U, 0x08C81842U, 0x08CC1842U, 0x00400063U, 0x00800846U, 0x280000A6U,
    0x08CC68C0U, 0x08C818C6U, 0x08CC18C6U, 0x00400067U, 0x08CC68E0U, 0x08C818E7U, 0x08CC18E7U, 0x00400088U, 0x08CC6900U,
    0x08C81908U, 0x08CC1908U, 0x004000A9U, 0x08CC6920U, 0x08C81929U, 0x08CC1929U, 0x0040014BU, 0x2000003AU, 0x08C86B5AU,
    0x0050696BU, 0x08CC7D7AU, 0x0850075AU, 0x008068C6U, 0x008068E7U, 0x00806908U, 0x00806929U, 0x0080696BU, 0x2000001BU,
    0x00506B7AU, 0x0850075AU, 0x00806842U, 0x00881842U, 0x00806863U, 0x00881C63U, 0x00806884U, 0x00882084U, 0x008068A5U,
    0x008824A5U, 0x0080694AU, 0x00882D4AU, 0x08C8687BU, 0x00886C42U, 0x08CC1863U, 0x08C8509BU, 0x00886C63U, 0x08CC3084U,
    0x08C838BBU, 0x00886C84U, 0x08CC48A5U, 0x08C8215BU, 0x00886CA5U, 0x860063DAU, 0x860083DCU, 0x04406842U, 0x04446C63U,
    0x04447084U, 0x004474A5U, 0xC600A3C2U, 0xC600C3C4U, 0xC400E3CAU, 0x840263D0U, 0x860283C0U, 0x7080001FU, 0x20000020U,
    0x0CCC0400U, 0x6081F3E0U, 0x04500000U, 0x8E00225AU, 0x8EFFE25CU, 0x08803E61U, 0x20000000U, 0x00500400U, 0x08C47C01U,
    0x0080075AU, 0x28000080U, 0x08C47C01U, 0x0080077BU, 0x28000080U, 0x08C47C01U, 0x0080079CU, 0x28000080U, 0x08C47C01U,
    0x008007BDU, 0xCE00225AU, 0xCEFFE25CU, 0x08800E7AU, 0x08C80F5AU, 0x20000000U, 0x08440000U, 0x00C86800U, 0x2000001CU,
    0x00506B9AU, 0x00806B5CU, 0x08C47F9CU, 0x2800041AU, 0x00CC6B9CU, 0x0880327BU, 0x00404B7BU, 0x8400037DU, 0x008073BDU,
    0x008803BDU, 0xC400037DU, 0x7080001FU, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U,
    0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U,
    0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U,
    0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U,
    0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U,
    0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U,
    0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U,
    0x00000000U};

static const uint32_t s_cau3ImemBytes = sizeof(s_cau3ImemImage);

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

static status_t cau3_initialize_inst_memory(CAU3_Type *base, const uint32_t *cau3ImemImage, size_t cau3ImemBytes);
static status_t cau3_initialize_data_memory(CAU3_Type *base, cau3_task_done_t taskDone);
static status_t cau3_initialize_read_only_data_memory(CAU3_Type *base,
                                                      const uint32_t *cau3ReadOnlyConstants,
                                                      size_t cau3ReadOnlyConstantsBytes,
                                                      cau3_task_done_t taskDone);
static status_t cau3_load_key_context(CAU3_Type *base,
                                      cau3_key_context_t *cauKeyContext,
                                      cau3_key_slot_t keySlot,
                                      cau3_task_done_t taskDone);
static status_t cau3_load_key(
    CAU3_Type *base, const uint8_t *key, size_t keySize, uint32_t keySlot, cau3_task_done_t taskDone);
static status_t cau3_pkha_clear_regabne(CAU3_Type *base, bool A, bool B, bool N, bool E);

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
static status_t cau3_execute_null_task(CAU3_Type *base, cau3_task_done_t taskDone);
static status_t cau3_lock_semaphore(CAU3_Type *base);
static void cau3_release_semaphore(CAU3_Type *base);
#endif

/*******************************************************************************
 * Code
 ******************************************************************************/

static status_t cau3_process_task_completion(CAU3_Type *base, cau3_task_done_t taskDone)
{
    status_t taskCompletionStatus;
    uint32_t tkcs;

    taskCompletionStatus = kStatus_Fail; /* assume an error completion status */

    switch (((uint32_t)taskDone >> 16U) & 7U)
    {
        case 0: /* poll the cau3 status register */
            tkcs = base->SR & CAU3_SR_TKCS_MASK;
            while (tkcs == CAU3_SR_TKCS_RUN)
            {
                tkcs = base->SR & CAU3_SR_TKCS_MASK;
            };

            /* check the task completion status*/
            if (tkcs == CAU3_SR_TKCS_STOPNOERR)
            {
                taskCompletionStatus = kStatus_Success; /* signal error-free completion status */
            }

            break;

        case 2: /* task completion signaled by event_done */
            do
            {
                __WFE(); /* cpu is waiting for cau3 event_done */
                tkcs = base->SR & CAU3_SR_TKCS_MASK;
            } while (tkcs == CAU3_SR_TKCS_RUN);

            /* check the task completion status */
            if (tkcs == CAU3_SR_TKCS_STOPNOERR)
            {
                taskCompletionStatus = kStatus_Success; /* signal error-free completion status */
            }
            break;

        case 1: /* task completion signaled by irq */
        /* TEMP FIX - for boot ROM with IRQ task completion, simply return */
        case 4: /* task completion signaled by dma_req */
            /* processing here is complete */
            taskCompletionStatus = kStatus_Success; /* signal error-free completion status */
            break;

        default: /* undefined taskDone specifier defaults to kStatus_Fail */
            break;
    } /* end - switch (taskDone & 7U) */

    return (taskCompletionStatus);
}

/*!
 * @brief   Initialize the CAU3's Instruction Memory
 *
 * Initializes the CAU3, including configuring it to enable the execution of
 * crypto tasks, loading the CryptoCore's firmware image into the CAU3's
 * instruction memory, and then performing a simple read-verify of its contents.
 * NOTE: All the operations for this function are executed on the host processor.
 *
 * cau3_initialize_inst_memory
 * @param   cau3ImemImage - binary firmware image for CryptoCore
 * @param   cau3ImemBytes - size of the firmware image in bytes
 *
 * @retval  status from the readVerify check: CAU_[OK (=0), ERROR (!0)]
 *          if an error is signaled, the retval is 0xbad10000UU + i, where i is
 *          the first miscompare word index location
 */
static status_t cau3_initialize_inst_memory(CAU3_Type *base, const uint32_t *cau3ImemImage, size_t cau3ImemBytes)
{
    uint32_t i;

    /* enable the cau3 */
    base->CR = 0U;

    /* poll if/while the cau3 is running initialization */
    while ((base->SR & CAU3_SR_TKCS_MASK) == CAU3_SR_TKCS_INITRUN)
    {
    };

    /* check for error-free stop state */
    if ((base->SR & CAU3_SR_TKCS_MASK) != CAU3_SR_TKCS_STOPNOERR)
    {
        return (int32_t)(uint32_t)(0xbad00000U + (base->SR & CAU3_SR_TKCS_MASK)); /* exit with error */
    }

    base->SR = CAU3_SR_TCIRQ_MASK; /* clear the TCIRQ interrupt flag */

    /* write the code hex image into the cau3's imem
     * initialize the memory cmd and address registers */
    base->DBGMCMD = 0xac000000U; /* wt=1, ia=1, imem=0 */
    base->DBGMADR = 0U;          /* imem starting address */
    for (i = 0; i < cau3ImemBytes / 4U; i++)
    {
        base->DBGMDR = cau3ImemImage[i]; /* indirect write into cau3Imem */
    }

    /* read-verify the cau3 imem code image
     * initialize the memory cmd and address registers */
    base->DBGMCMD = 0x8c000000U; /* wt=0, ia=1, imem=0 */
    base->DBGMADR = 0U;          /* imem starting address */
    for (i = 0; i < cau3ImemBytes / 4U; i++)
    {
        if (base->DBGMDR != cau3ImemImage[i]) /* indirect read from cau3Imem */
        {
            return (int32_t)(uint32_t)(0xbad10000U + i); /* exit on miscompare */
        }
    }

    /* this function does *not* disable reads/writes of the cau3 local memories
     * but, this operation is needed to "secure" (i.e., make private) the cau3
     * local memories */

    return 0;
}

/*!
 * @brief   Initializes the CAU3's entire private Data Memory
 *
 * Initialize the CAU3's data memory, and then perform a read-verify versus a
 * precalculated "pseudo-hash" value.
 *
 * cau3_initialize_data_memory
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @retval  status from the readVerify check: CAU_[OK, ERROR]
 */
static status_t cau3_initialize_data_memory(CAU3_Type *base, cau3_task_done_t taskDone)
{
    status_t completionStatus;

    /* execute the cau3 "security violation + data initialization" task */
    base->CC_R30 = CAU3_DMEM_STK_BASE;  /* initialize stack pointer (sp) */
    base->CC_R31 = 0U;                  /* set LR = 0 to signal a host task */
    base->CC_PC  = CAU3_TASK_SECV_INIT; /* call cau_secv_init() */
    base->CC_CMD = (uint32_t)taskDone;  /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
    return (completionStatus);
}

/*!
 * @brief   Copies read-only constants from sysMemory to CAU3's DataMemory
 *
 * Initialize the read-only constants in the CAU3's data memory. This includes
 * the AES constants (RCON) and most of the constants used in the hash functions.
 * The constants associated with SHA-512 are NOT included and must be loaded
 * separately.
 *
 * cau3_initialize_read_only_data_memory
 * @param   cauReadOnlyConstants - sysMemory table of constants needed by CAU3
 * @param   cauReadOnlyConstantsSize - size of read-only constants in bytes
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @retval  status check from task completion: CAU_[OK, ERROR]
 */
static status_t cau3_initialize_read_only_data_memory(CAU3_Type *base,
                                                      const uint32_t *cau3ReadOnlyConstants,
                                                      size_t cau3ReadOnlyConstantsBytes,
                                                      cau3_task_done_t taskDone)
{
    status_t completionStatus;

    /* execute the cau3 "initialize dmem read-only constants" task */
    base->CC_R[16] = (uint32_t)s_cau3ReadOnlyConstants; /* pReadOnlyConstants */
    base->CC_R[17] = s_cau3ReadOnlyConstantsBytes;      /* byte count (0-mod-16) */
    base->CC_R[18] = CAU3_DMEM_AES_RCON;                /* pDMEM_AES_RCON constants base */
    base->CC_R30   = CAU3_DMEM_STK_BASE;                /* initialize stack pointer (sp) */
    base->CC_R31   = 0U;                                /* set LR = 0 to signal a host task */
    base->CC_PC    = CAU3_TASK_BLKLD_DMEM;              /* call cau_block_load_dmem task */
    base->CC_CMD   = (uint32_t)taskDone;                /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
    return (completionStatus);
}

/*!
 * brief   Make the CAU3's local memories private
 *
 * Modify the CAU3's internal configuration so the local memories are private
 * and only accessible to the CAU3. This operation is typically performed after
 * the CAU_InitializeInstMemory(),
 *     CAU_InitializeDataMemory(), and
 *     CAU_InitializeReadOnlyDataMemory() functions have been performed.
 *
 * This configuration remains in effect until the next hardware reset.
 *
 * param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * retval  status check from task completion: CAU_[OK, ERROR]
 */
status_t CAU3_MakeMemsPrivate(CAU3_Type *base, cau3_task_done_t taskDone)
{
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    uint32_t completionStatus;
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif /* FSL_CAU3_USE_HW_SEMA */

    /* making the xMEMs private involves setting DBGCSR[DDBGMC] = 1 */
    base->DBGCSR = CAU3_DBGCSR_DDBGMC_MASK; /* set DBGCSR[DDBGMC] */

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return kStatus_Success;
}

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
/*!
 * @brief   Execute a CAU3 null task to "establish ownership" by host processor
 *
 * Execute a null task to claim ownership of the CAU3 by the host processor.
 * This is required for correct IRQ, EVT and DMA_REQ signaling by subsequent
 * PKHA operations. The CryptoCore task executes one instruction - a "stop".
 *
 * cau3_execute_null_task
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @retval  status check from task completion: CAU_[OK, ERROR]
 */
static status_t cau3_execute_null_task(CAU3_Type *base, cau3_task_done_t taskDone)
{
    status_t completionStatus;

    /* execute the cau3 null task */
    base->CC_R30 = CAU3_DMEM_STK_BASE; /* initialize stack pointer (sp) */
    base->CC_R31 = 0;                  /* set LR = 0 to signal a host task */
    base->CC_PC  = CAU3_TASK_NULL;     /* call cau_null() */
    base->CC_CMD = taskDone;           /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
    return (completionStatus);
}
#endif /* FSL_CAU3_USE_HW_SEMA */

/*!
 * @brief   Load a key into a key context
 *
 * Loads up to 32-byte key into the specified key slot.
 * There is support for a maximum of 4 key slots.
 * This does not do AES key expansion (as in cau3_load_key_context() case) so we use this one for loading TDES keys.
 *
 * @param   key is the key pointer, ALIGNED ON A 0-MOD-4 ADDRESS
 * @param   keySize is the size in bytes of the key
 * @param   keySlot is the destination key context
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @retval  status check from task completion: CAU_[OK, ERROR]
 */
static status_t cau3_load_key(
    CAU3_Type *base, const uint8_t *key, size_t keySize, uint32_t keySlot, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "load initialization vector into key context" task  */
    base->CC_R[16] = (uintptr_t)key;     /*  pKey  */
    base->CC_R[17] = keySize;            /*  IV size  */
    base->CC_R[18] = keySlot;            /*  keySlot  */
    base->CC_R30   = CAU3_DMEM_STK_BASE; /*  initialize stack pointer (sp)  */
    base->CC_R31   = 0U;                 /*  set LR = 0 to signal a host task  */
    base->CC_PC    = CAU3_TASK_LD_KEY;   /*  call cau_load_key()  */
    base->CC_CMD   = (uint32_t)taskDone; /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * brief Encrypts AES on one 128-bit block.
 *
 * Encrypts AES.
 * The source plaintext and destination ciphertext can overlap in system memory.
 *
 * param base CAU3 peripheral base address
 * param handle Handle used for this request.
 * param plaintext Input plain text to encrypt
 * param[out] ciphertext Output cipher text
 * return Status from encrypt operation
 */
status_t CAU3_AES_Encrypt(CAU3_Type *base, cau3_handle_t *handle, const uint8_t plaintext[16], uint8_t ciphertext[16])
{
    status_t completionStatus;
    cau3_task_done_t taskDone;

    taskDone = handle->taskDone;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /* execute the cau3 "aes_encrypt_ecb" task */
    base->CC_R[16] = (uint32_t)plaintext;       /* pPlainText */
    base->CC_R[17] = (uint32_t)handle->keySlot; /* keySlot */
    base->CC_R[19] = (uint32_t)ciphertext;      /* pCipherText */
    base->CC_R30   = CAU3_DMEM_STK_BASE;        /* initialize stack pointer (sp) */
    base->CC_R31   = 0U;                        /* set LR = 0 to signal a host task */
    base->CC_PC    = CAU3_TASK_AES_ENCRYPT;     /* call cau_aes_encrypt() */
    base->CC_CMD   = (uint32_t)taskDone;        /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * brief Decrypts AES on one 128-bit block.
 *
 * Decrypts AES.
 * The source ciphertext and destination plaintext can overlap in system memory.
 *
 * param base CAU3 peripheral base address
 * param handle Handle used for this request.
 * param ciphertext Input plain text to encrypt
 * param[out] plaintext Output cipher text
 * return Status from decrypt operation
 */
status_t CAU3_AES_Decrypt(CAU3_Type *base, cau3_handle_t *handle, const uint8_t ciphertext[16], uint8_t plaintext[16])
{
    status_t completionStatus;
    cau3_task_done_t taskDone;

    taskDone = handle->taskDone;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /* execute the cau3 "aes_decrypt_ecb" task */
    base->CC_R[16] = (uint32_t)ciphertext;      /* pCipherText */
    base->CC_R[17] = (uint32_t)handle->keySlot; /* keySlot */
    base->CC_R[19] = (uint32_t)plaintext;       /* pPlainText */
    base->CC_R30   = CAU3_DMEM_STK_BASE;        /* initialize stack pointer (sp) */
    base->CC_R31   = 0U;                        /* set LR = 0 to signal a host task */
    base->CC_PC    = CAU3_TASK_AES_DECRYPT;     /* call cau_aes_decrypt() */
    base->CC_CMD   = (uint32_t)taskDone;        /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * brief   Enables clock for CAU3 and loads image to memory
 *
 * Enable CAU3 clock and loads image to CryptoCore.
 *
 * param base CAU3 base address
 */
void CAU3_Init(CAU3_Type *base)
{
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
    /* ungate clock */
    CLOCK_EnableClock(kCLOCK_Cau3);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    CLOCK_EnableClock(FSL_CAU3_SEMA42_CLOCK_NAME);
#endif /* FSL_CAU3_USE_HW_SEMA */
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */

    base->CR = CAU3_CR_RSTSM4(1);
    base->CR = CAU3_CR_RSTSM4(2);
    (void)cau3_initialize_inst_memory(base, s_cau3ImemImage, s_cau3ImemBytes);
    (void)cau3_initialize_data_memory(base, kCAU3_TaskDonePoll);
    (void)cau3_initialize_read_only_data_memory(base, s_cau3ReadOnlyConstants, s_cau3ReadOnlyConstantsBytes,
                                                kCAU3_TaskDonePoll);
}

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
static status_t cau3_lock_semaphore(CAU3_Type *base)
{
    uint32_t processorNumber = 0;

/* cm4 will be 1, cm0+ will be 2 */
/* This concept supports dual assymetric cores on K32W0 (dula core m4 and m0+) */
/* For next SoC, the processor number shall be defined in the SoC header file */
#if __CORTEX_M == 0U
    processorNumber++;
#endif
    processorNumber++;

    while (processorNumber != SEMA42_GATEn(FSL_CAU3_SEMA42_BASE, 1))
    {
        /* Wait for unlocked status. */
        while (SEMA42_GATEn(FSL_CAU3_SEMA42_BASE, FSL_CAU3_SEMA42_GATE))
        {
        }

        /* Lock the gate. */
        SEMA42_GATEn(FSL_CAU3_SEMA42_BASE, FSL_CAU3_SEMA42_GATE) = processorNumber;
    }

    return cau3_execute_null_task(base, kCAU3_TaskDonePoll);
}
#endif /* FSL_CAU3_USE_HW_SEMA */

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
static void cau3_release_semaphore(CAU3_Type *base)
{
    /* unlock the semaphore */
    SEMA42_GATEn(FSL_CAU3_SEMA42_BASE, FSL_CAU3_SEMA42_GATE) = 0;
}
#endif /* FSL_CAU3_USE_HW_SEMA */

/*!
 * @brief   Load a 64-byte "key context" into the CAU3's private data memory
 *
 * Load the key context into the private DMEM. This includes size and config
 * information, a 16-byte initialization vector and a key of size [8,16,24,32]
 * bytes (for DES or AES-[128,192,256]). There is support for 4 "key slots" with
 * slot 0 typically used for the system key encryption key (KEK).
 *
 * See the GENERAL COMMENTS for more information on the keyContext structure.
 *
 * NOTE: This function also performs an AES key expansion if a keySize > 8
 * is specified.
 *
 * cau3_load_key_context
 * @param   cauKeyContext is pointer to key structure in sysMemory
 * @param   keySlot is the destination key slot number [0-3]
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @return  status check from task completion: CAU_[OK, ERROR]
 */
static status_t cau3_load_key_context(CAU3_Type *base,
                                      cau3_key_context_t *cauKeyContext,
                                      cau3_key_slot_t keySlot,
                                      cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /* execute the cau3 "load key context" task */
    base->CC_R[16] = (uint32_t)cauKeyContext; /* pKeyContext */
    base->CC_R[17] = (uint32_t)keySlot;       /* keySlot */
    base->CC_R30   = CAU3_DMEM_STK_BASE;      /* initialize stack pointer (sp) */
    base->CC_R31   = 0U;                      /* set LR = 0 to signal a host task */
    base->CC_PC    = CAU3_TASK_LD_KEYCTX;     /* call cau_load_key_context() */
    base->CC_CMD   = (uint32_t)taskDone;      /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * brief   Execute a CAU3 null task to signal error termination
 *
 * Execute a null task to signal error termination.
 * The CryptoCore task executes one instruction - a "stop with error".
 *
 * param base CAU3 base address
 * param taskDone indicates completion signal
 *
 * return status check from task completion
 */
status_t CAU3_ForceError(CAU3_Type *base, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 null task  */
    base->CC_R30 = CAU3_DMEM_STK_BASE; /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0;                  /*  set LR = 0 to signal a host task  */
    base->CC_PC  = CAU3_TASK_STOPERROR;
    base->CC_CMD = (uint32_t)taskDone; /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * brief   Load special hardware "key context" into the CAU3's data memory
 *
 * Load the special hardware key context into the private DMEM. This only
 * includes the complete 256-bit key which is then specified with a size of
 * [8,16,24,32] bytes (for DES or AES-[128,256]).  It also loads the
 * default IV value specified in NIST/RFC2294 IV=0xa6a6a6a6a6a6a6a6. This operation typically
 * loads keySlot 0, which, by convention, is used for the system key encryption
 * key.
 *
 * See the GENERAL COMMENTS for more information on the keyContext structure.
 *
 * NOTE: This function also performs an AES key expansion if a keySize > 8
 * is specified.
 *
 * param base CAU3 base address
 * param   keySize is the logical key size in bytes [8,16,24,32]
 * param   keySlot is the destination key slot number [0-3]
 * param   taskDone indicates completion signal.
 *
 * return  status check from task completion
 */
status_t CAU3_LoadSpecialKeyContext(CAU3_Type *base, size_t keySize, cau3_key_slot_t keySlot, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "load special key context" task  */
    base->CC_R[16] = keySize;                /*  keySize [8,16,24,32]  */
    base->CC_R[17] = (uint32_t)keySlot;      /*  keySlot  */
    base->CC_R30   = CAU3_DMEM_STK_BASE;     /*  initialize stack pointer (sp)  */
    base->CC_R31   = 0U;                     /*  set LR = 0 to signal a host task  */
    base->CC_PC    = CAU3_TASK_LD_SP_KEYCTX; /*  call cau_load_special_key_context()  */
    base->CC_CMD   = (uint32_t)taskDone;     /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * brief   Invalidate a 64-byte "key context" in the CAU3's private data memory
 *
 * Clears the key context in the private DMEM. There is support for four "key
 * slots" with slot 0 typically used for the system key encryption key.
 *
 * param base CAU3 base address
 * param   keySlot is the key slot number [0-3] to invalidate
 * param   taskDone indicates completion signal *
 * return  status check from task completion
 */
status_t CAU3_ClearKeyContext(CAU3_Type *base, cau3_key_slot_t keySlot, cau3_task_done_t taskDone)
{
    uint32_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "clear key context" task  */
    base->CC_R[17] = (uint32_t)keySlot;    /*  keySlot  */
    base->CC_R30   = CAU3_DMEM_STK_BASE;   /*  initialize stack pointer (sp)  */
    base->CC_R31   = 0U;                   /*  set LR = 0 to signal a host task  */
    base->CC_PC    = CAU3_TASK_CLR_KEYCTX; /*  call cau_clear_key_context()  */
    base->CC_CMD   = (uint32_t)taskDone;   /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = (uint32_t)cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return ((int32_t)completionStatus);
}

/*!
 * brief   Load an initialization vector into a key context
 *
 * Loads a 16-byte initialization vector (iv) into the specified key slot.
 * There is support for a maximum of 4 key slots.
 * The function is used internally for loading AEAD_CHACHA20_POY1305 nonce.
 * It can be also used for Alternative Initial Values for A[0] in RFC 3394.
 *
 * param base CAU3 base address
 * param iv The initialization vector, ALIGNED ON A 0-MOD-4 ADDRESS.
 * param keySlot is the destination key context
 * param taskDone indicates completion signal
 *
 * return  status check from task completion
 */
status_t CAU3_LoadKeyInitVector(CAU3_Type *base, const uint8_t *iv, cau3_key_slot_t keySlot, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "load initialization vector into key context" task  */
    base->CC_R[16] = (uintptr_t)iv;      /*  pIv  */
    base->CC_R[17] = (uint32_t)keySlot;  /*  keySlot  */
    base->CC_R30   = CAU3_DMEM_STK_BASE; /*  initialize stack pointer (sp)  */
    base->CC_R31   = 0U;                 /*  set LR = 0 to signal a host task  */
    base->CC_PC    = CAU3_TASK_LD_IV;    /*  call cau_load_iv()  */
    base->CC_CMD   = (uint32_t)taskDone; /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * brief   Perform an AES key expansion for specified key slot
 *
 * Performs an AES key expansion (aka schedule) on the specified key slot. It
 * uses the keySize information in the context to determine whether the key
 * expansion applies to a 128- or 256-bit AES key.
 * This function is primarily intended to be called after
 * key blob has been unwrapped by ref CAU3_KeyBlobUnwrap to destination key slot, so that the unwrapped key
 * can be used for AES encryption.
 *
 * param base CAU3 base address
 * param keySlot is the key context
 * param taskDone indicates completion signal
 * return status check from task completion
 */
status_t CAU3_AES_KeyExpansion(CAU3_Type *base, cau3_key_slot_t keySlot, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /* execute the cau3 "aes_key_expansion" task */
    base->CC_R[17] = (uint32_t)keySlot;     /* keySlot */
    base->CC_R30   = CAU3_DMEM_STK_BASE;    /* initialize stack pointer (sp) */
    base->CC_R31   = 0U;                    /* set LR = 0 to signal a host task */
    base->CC_PC    = CAU3_TASK_AES_KEY_SCH; /* call cau_aes_key_sched() */
    base->CC_CMD   = (uint32_t)taskDone;    /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * brief Load AES key into CAU3 key slot.
 *
 * Load the key context into the private DMEM. This function also performs an AES key expansion.
 * For CAU3 AES encryption/decryption/cmac, users only need to call one of ref CAU3_AES_SetKey and ref
 * CAU3_LoadSpecialKeyContext.
 *
 * CAU3_AES_SetKey
 * param   base CAU3 peripheral base address.
 * param   handle Handle used for the request.
 * param   key 0-mod-4 aligned pointer to AES key.
 * param   keySize AES key size in bytes. Shall equal 16 or 32.
 * return  status from set key operation
 */
status_t CAU3_AES_SetKey(CAU3_Type *base, cau3_handle_t *handle, const uint8_t *key, size_t keySize)
{
    cau3_key_context_t cau3KeyCtx = {0};

    /* only work with aligned key[] */
    if (0U != (0x3U & (uintptr_t)key))
    {
        return kStatus_InvalidArgument;
    }

    /* keySize must be 16 or 32. initial CAU3 firmware doesn't support 24 bytes. */
    if ((keySize != 16U) && (keySize != 32U))
    {
        return kStatus_InvalidArgument;
    }

    cau3KeyCtx.keySize = keySize;

    /* move the key by 32-bit words */
    int i = 0;
    while (keySize != 0U)
    {
        keySize -= sizeof(uint32_t);
        ((uint32_t *)((uintptr_t)cau3KeyCtx.key))[i] = ((uint32_t *)(uintptr_t)key)[i];
        i++;
    }

    return cau3_load_key_context(base, &cau3KeyCtx, handle->keySlot, handle->taskDone);
}

/*!
 * brief   Perform an AES-128 cipher-based authentication code (CMAC)
 *
 * Performs an AES-128 cipher-based authentication code (CMAC) on a
 * message. RFC 4493.
 *
 * param base CAU3 peripheral base address
 * param handle Handle used for this request.
 * param message is source uint8_t array of data bytes, any alignment
 * param size Number of bytes in the message.
 * param mac is the output 16 bytes MAC, must be a 0-mod-4 aligned address
 * return  status check from task completion
 */
status_t CAU3_AES_Cmac(CAU3_Type *base, cau3_handle_t *handle, const uint8_t *message, size_t size, uint8_t *mac)
{
    status_t completionStatus;

    /* mac must be 0-mod-4 aligned */
    if (0U != (0x3U & (uintptr_t)mac))
    {
        return kStatus_InvalidArgument;
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "aes_cmac" task  */
    base->CC_R[16] = (uintptr_t)message;         /*  pMessage  */
    base->CC_R[17] = (uint32_t)handle->keySlot;  /*  keySlot  */
    base->CC_R[18] = size;                       /*  messageSize  */
    base->CC_R[19] = (uintptr_t)mac;             /*  pMac  */
    base->CC_R30   = CAU3_DMEM_STK_BASE;         /*  initialize stack pointer (sp)  */
    base->CC_R31   = 0U;                         /*  set LR = 0 to signal a host task  */
    base->CC_PC    = CAU3_TASK_AES128_CMAC;      /*  call cau_aes128_cmac()  */
    base->CC_CMD   = (uint32_t)handle->taskDone; /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, handle->taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * @brief Check validity of algoritm.
 *
 * This function checks the validity of input argument.
 *
 * @param algo Tested algorithm value.
 * @return kStatus_Success if valid, kStatus_InvalidArgument otherwise.
 */
static status_t cau3_hash_check_input_alg(cau3_hash_algo_t algo)
{
    if ((algo != kCAU3_Sha256) && (algo != kCAU3_Sha1))
    {
        return kStatus_InvalidArgument;
    }
    return kStatus_Success;
}

/*!
 * @brief Check validity of input arguments.
 *
 * This function checks the validity of input arguments.
 *
 * @param base CAU3 peripheral base address.
 * @param ctx Memory buffer given by user application where the CAU3_HASH_Init/CAU3_HASH_Update/CAU3_HASH_Finish store
 * context.
 * @param algo Tested algorithm value.
 * @return kStatus_Success if valid, kStatus_InvalidArgument otherwise.
 */
static status_t cau3_hash_check_input_args(CAU3_Type *base, cau3_hash_ctx_t *ctx, cau3_hash_algo_t algo)
{
    /* Check validity of input algorithm */
    if (kStatus_Success != cau3_hash_check_input_alg(algo))
    {
        return kStatus_InvalidArgument;
    }

    if ((NULL == ctx) || (NULL == base))
    {
        return kStatus_InvalidArgument;
    }

    return kStatus_Success;
}

/*!
 * @brief Check validity of internal software context.
 *
 * This function checks if the internal context structure looks correct.
 *
 * @param ctxInternal Internal context.
 * @param message Input message address.
 * @return kStatus_Success if valid, kStatus_InvalidArgument otherwise.
 */
static status_t cau3_hash_check_context(cau3_hash_ctx_internal_t *ctxInternal, const uint8_t *message)
{
    if ((NULL == message) || (NULL == ctxInternal) || (kStatus_Success != cau3_hash_check_input_alg(ctxInternal->algo)))
    {
        return kStatus_InvalidArgument;
    }
    return kStatus_Success;
}

/*!
 * @brief   Initialize message digest output state for SHA-1 hash
 *
 * Initializes the message digest output state for a SHA-1 hash.
 *
 * @param   sha1State is message digest output in sysMemory in BE format
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @retval  status check from task completion: CAU_[OK, ERROR]
 */

static status_t CAU3_Sha1InitializeOutput(CAU3_Type *base, uint32_t *sha1State, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "sha1_init_output" task  */
    base->CC_R[29] = (uintptr_t)sha1State;      /*  pSha1State  */
    base->CC_R30   = CAU3_DMEM_STK_BASE;        /*  initialize stack pointer (sp)  */
    base->CC_R31   = 0U;                        /*  set LR = 0 to signal a host task  */
    base->CC_PC    = CAU3_TASK_SHA1_INIT_STATE; /*  call cau_sha1_init_state()  */
    base->CC_CMD   = (uint32_t)taskDone;        /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * @brief   Perform a SHA-1 hash function over a message of "n" 64-byte blocks
 *
 * Perform a SHA-1 hash function over a message of "n" 64-byte data blocks,
 * returning an 8-word message digest (aka "state"). The input message must
 * be padded appropriately as defined by the SHA-1 algorithm.
 *
 * @param   message is the uint8_t input message, any alignment
 * @param   numberOfBlocks is the message length as multiple of 64-byte blocks
 * @param   sha1State is uint32_t message digest output (state) in BE format
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @retval  status check from task completion: CAU_[OK, ERROR]
 */

static status_t CAU3_Sha1Update(
    CAU3_Type *base, const uint8_t *message, uint32_t numberOfBlocks, uint32_t *sha1State, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "sha1_update" task  */
    base->CC_R[27] = (uintptr_t)message;   /*  pMessage  */
    base->CC_R[28] = numberOfBlocks;       /*  n blocks  */
    base->CC_R[29] = (uintptr_t)sha1State; /*  output  */
    base->CC_R30   = CAU3_DMEM_STK_BASE;   /*  initialize stack pointer (sp)  */
    base->CC_R31   = 0U;                   /*  set LR = 0 to signal a host task  */
    base->CC_PC    = CAU3_TASK_SHA1_HASH;  /*  call cau_sha1_hash_n()  */
    base->CC_CMD   = (uint32_t)taskDone;   /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * @brief   Initialize message digest output state for SHA-256 hash
 *
 * Initializes the message digest output state for a SHA-256 hash.
 *
 * CAU3_Sha256InitializeOutput
 * @param   sha256State is message digest output in sysMemory in BE format
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @retval  status check from task completion: CAU_[OK, ERROR]
 */

static status_t CAU3_Sha256InitializeOutput(CAU3_Type *base, uint32_t *sha256State, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /* execute the cau3 "sha256_init_output" task */
    base->CC_R[29] = (uint32_t)sha256State;       /* pSha256State */
    base->CC_R30   = CAU3_DMEM_STK_BASE;          /* initialize stack pointer (sp) */
    base->CC_R31   = 0U;                          /* set LR = 0 to signal a host task */
    base->CC_PC    = CAU3_TASK_SHA256_INIT_STATE; /* call cau_sha256_init_state() */
    base->CC_CMD   = (uint32_t)taskDone;          /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * @brief   Perform a SHA-256 hash function over a message of "n" 64-byte blocks
 *
 * Perform a SHA-256 hash function over a message of "n" 64-byte data blocks,
 * returning an 8-word message digest (aka "state"). The input message must
 * be padded appropriately as defined by the SHA-256 algorithm.
 *
 * CAU_Sha256Update
 * @param   message is the uint8_t input message, LE, ANY ALIGNMENT
 * @param   numberOfBlocks is the message length as multiple of 64-byte blocks
 * @param   sha256State is uint32_t message digest output (state) in BE format
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @retval  status check from task completion: CAU_[OK, ERROR]
 */

static status_t CAU3_Sha256Update(
    CAU3_Type *base, const uint8_t *message, uint32_t numberOfBlocks, uint32_t *sha256State, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /* execute the cau3 "sha256_update" task */
    base->CC_R[27] = (uint32_t)message;       /* pMessage */
    base->CC_R[28] = numberOfBlocks;          /* = (64*numberOfBlocks) bytes */
    base->CC_R[29] = (uint32_t)sha256State;   /* pSha256State */
    base->CC_R30   = CAU3_DMEM_STK_BASE;      /* initialize stack pointer (sp) */
    base->CC_R31   = 0U;                      /* set LR = 0 to signal a host task */
    base->CC_PC    = CAU3_TASK_SHA256_UPDATE; /* call cau_sha256_update() */
    base->CC_CMD   = (uint32_t)taskDone;      /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * @brief Initialize the SHA engine for new hash.
 *
 * This function sets NEW and MODE fields in SHA Control register to start new hash.
 *
 * @param base SHA peripheral base address.
 * @param ctxInternal Internal context.
 */
static status_t cau3_hash_engine_init(CAU3_Type *base, cau3_hash_ctx_internal_t *ctxInternal)
{
    status_t status;

    status = kStatus_InvalidArgument;

    if (kCAU3_Sha256 == ctxInternal->algo)
    {
        status = CAU3_Sha256InitializeOutput(base, ctxInternal->runningHash, kCAU3_TaskDonePoll);
    }

    if (kCAU3_Sha1 == ctxInternal->algo)
    {
        status = CAU3_Sha1InitializeOutput(base, ctxInternal->runningHash, kCAU3_TaskDonePoll);
    }

    return status;
}

/*!
 * @brief Adds message to current hash.
 *
 * This function merges the message to fill the internal buffer, empties the internal buffer if
 * it becomes full, then process all remaining message data.
 *
 *
 * @param base CAU3 peripheral base address.
 * @param ctxInternal Internal context.
 * @param message Input message.
 * @param messageSize Size of input message in bytes.
 * @return kStatus_Success.
 */
static status_t cau3_hash_process_message_data(CAU3_Type *base,
                                               cau3_hash_ctx_internal_t *ctxInternal,
                                               const uint8_t *message,
                                               size_t messageSize)
{
    status_t status;
    status_t (*funcUpdate)(CAU3_Type * cau3base, const uint8_t *msg, uint32_t numberOfBlocks, uint32_t *shaState,
                           cau3_task_done_t taskDone);

    /* first fill the internal buffer to full block */
    size_t toCopy = CAU3_HASH_BLOCK_SIZE - ctxInternal->blksz;
    (void)cau3_memcpy(&ctxInternal->blk.b[ctxInternal->blksz], message, toCopy);
    message += toCopy;
    messageSize -= toCopy;

    status     = kStatus_InvalidArgument;
    funcUpdate = NULL;

    switch (ctxInternal->algo)
    {
        case kCAU3_Sha256:
            funcUpdate = CAU3_Sha256Update;
            break;

        case kCAU3_Sha1:
            funcUpdate = CAU3_Sha1Update;
            break;

        default:
            /* All the cases have been listed above, the default clause should not be reached. */
            break;
    }

    if (NULL != funcUpdate)
    {
        /* process full internal block */
        status = funcUpdate(base, &ctxInternal->blk.b[0], CAU3_HASH_BLOCK_SIZE / 64u, ctxInternal->runningHash,
                            kCAU3_TaskDonePoll);
        if (kStatus_Success != status)
        {
            return status;
        }

        /* process all full blocks in message[] */
        while (messageSize >= CAU3_HASH_BLOCK_SIZE)
        {
            status =
                funcUpdate(base, message, CAU3_HASH_BLOCK_SIZE / 64u, ctxInternal->runningHash, kCAU3_TaskDonePoll);
            if (kStatus_Success != status)
            {
                return status;
            }
            message += CAU3_HASH_BLOCK_SIZE;
            messageSize -= CAU3_HASH_BLOCK_SIZE;
        }

        /* copy last incomplete message bytes into internal block */
        (void)cau3_memcpy(&ctxInternal->blk.b[0], message, messageSize);
        ctxInternal->blksz = messageSize;
    }

    return status;
}

/*!
 * @brief Finalize the running hash to make digest.
 *
 * This function empties the internal buffer, adds padding bits, and generates final digest.
 *
 * @param base SHA peripheral base address.
 * @param ctxInternal Internal context.
 * @return kStatus_Success.
 */
static status_t cau3_hash_finalize(CAU3_Type *base, cau3_hash_ctx_internal_t *ctxInternal)
{
    cau3_sha_block_t lastBlock;
    status_t status;
    status_t (*funcUpdate)(CAU3_Type * cau3base, const uint8_t *msg, uint32_t numberOfBlocks, uint32_t *shaState,
                           cau3_task_done_t taskDone);

    funcUpdate = NULL;

    switch (ctxInternal->algo)
    {
        case kCAU3_Sha256:
            funcUpdate = CAU3_Sha256Update;
            break;

        case kCAU3_Sha1:
            funcUpdate = CAU3_Sha1Update;
            break;

        default:
            /* All the cases have been listed above, the default clause should not be reached. */
            break;
    }

    if (NULL == funcUpdate)
    {
        return kStatus_InvalidArgument;
    }

    (void)memset(&lastBlock, 0, sizeof(cau3_sha_block_t));
    status = kStatus_Success;

    while (ctxInternal->blksz >= 64u)
    {
        status = funcUpdate(base, &ctxInternal->blk.b[0], 1, ctxInternal->runningHash, kCAU3_TaskDonePoll);
        if (kStatus_Success != status)
        {
            return status;
        }
        ctxInternal->blksz -= 64u;
        (void)memmove(&ctxInternal->blk.b[0], &ctxInternal->blk.b[64], ctxInternal->blksz);
    }

    /* this is last call, so need to flush buffered message bytes along with padding */
    if (ctxInternal->blksz <= 55u)
    {
        /* last data is 440 bits or less. */
        (void)cau3_memcpy(&lastBlock.b[0], &ctxInternal->blk.b[0], ctxInternal->blksz);
        lastBlock.b[ctxInternal->blksz] = (uint8_t)0x80U;
        lastBlock.w[15]                 = __REV(8u * ctxInternal->fullMessageSize);
        status = funcUpdate(base, &lastBlock.b[0], 1, ctxInternal->runningHash, kCAU3_TaskDonePoll);
        if (kStatus_Success != status)
        {
            return status;
        }
    }
    else
    {
        if (ctxInternal->blksz < 64u)
        {
            ctxInternal->blk.b[ctxInternal->blksz] = (uint8_t)0x80U;
            for (uint32_t i = ctxInternal->blksz + 1u; i < 64u; i++)
            {
                ctxInternal->blk.b[i] = 0;
            }
        }
        else
        {
            lastBlock.b[0] = (uint8_t)0x80U;
        }

        status = funcUpdate(base, &ctxInternal->blk.b[0], 1, ctxInternal->runningHash, kCAU3_TaskDonePoll);
        if (kStatus_Success != status)
        {
            return status;
        }
        lastBlock.w[15] = __REV(8u * ctxInternal->fullMessageSize);
        status          = funcUpdate(base, &lastBlock.b[0], 1, ctxInternal->runningHash, kCAU3_TaskDonePoll);
        if (kStatus_Success != status)
        {
            return status;
        }
    }
    return status;
}

/*!
 * brief Initialize HASH context
 *
 * This function initializes the HASH.
 *
 * For blocking CAU3 HASH API, the HASH context contains all information required for context switch,
 * such as running hash.
 *
 * param base CAU3 peripheral base address
 * param[out] ctx Output hash context
 * param algo Underlaying algorithm to use for hash computation.
 * return Status of initialization
 */
status_t CAU3_HASH_Init(CAU3_Type *base, cau3_hash_ctx_t *ctx, cau3_hash_algo_t algo)
{
    status_t status;

    cau3_hash_ctx_internal_t *ctxInternal;
    /* compile time check for the correct structure size */
    BUILD_ASSURE(sizeof(cau3_hash_ctx_t) >= sizeof(cau3_hash_ctx_internal_t), cau3_hash_ctx_t_size);
    uint32_t i, j;

    status = cau3_hash_check_input_args(base, ctx, algo);
    if (status != kStatus_Success)
    {
        return status;
    }

    /* set algorithm in context struct for later use */
    ctxInternal        = (cau3_hash_ctx_internal_t *)(uint32_t)ctx;
    ctxInternal->algo  = algo;
    ctxInternal->blksz = 0u;
    j                  = sizeof(ctxInternal->blk.w) / sizeof(ctxInternal->blk.w[0]);
    for (i = 0; i < j; i++)
    {
        ctxInternal->blk.w[0] = 0u;
    }
    ctxInternal->state           = kCAU3_StateHashInit;
    ctxInternal->fullMessageSize = 0;

    return status;
}

/*!
 * brief Add data to current HASH
 *
 * Add data to current HASH. This can be called repeatedly with an arbitrary amount of data to be
 * hashed. The functions blocks. If it returns kStatus_Success, the running hash or mac
 * has been updated (CAU3 has processed the input data), so the memory at ref input pointer
 * can be released back to system. The context is updated with the running hash or mac
 * and with all necessary information to support possible context switch.
 *
 * param base CAU3 peripheral base address
 * param[in,out] ctx HASH context
 * param input Input data
 * param inputSize Size of input data in bytes
 * return Status of the hash update operation
 */
status_t CAU3_HASH_Update(CAU3_Type *base, cau3_hash_ctx_t *ctx, const uint8_t *input, size_t inputSize)
{
    bool isUpdateState;
    status_t status;
    cau3_hash_ctx_internal_t *ctxInternal;
    size_t blockSize;

    if (inputSize == 0U)
    {
        return kStatus_Success;
    }

    ctxInternal = (cau3_hash_ctx_internal_t *)(uint32_t)ctx;
    status      = cau3_hash_check_context(ctxInternal, input);
    if (kStatus_Success != status)
    {
        return status;
    }

    ctxInternal->fullMessageSize += inputSize;
    blockSize = CAU3_HASH_BLOCK_SIZE;
    /* if we are still less than CAU3_HASH_BLOCK_SIZE bytes, keep only in context */
    if ((ctxInternal->blksz + inputSize) <= blockSize)
    {
        (void)cau3_memcpy((&ctxInternal->blk.b[0]) + ctxInternal->blksz, input, inputSize);
        ctxInternal->blksz += inputSize;
        return status;
    }
    else
    {
        isUpdateState = ctxInternal->state == kCAU3_StateHashUpdate;
        if (!isUpdateState)
        {
            /* start NEW hash */
            status = cau3_hash_engine_init(base, ctxInternal);
            if (status != kStatus_Success)
            {
                return status;
            }
            ctxInternal->state = kCAU3_StateHashUpdate;
        }
    }

    /* process input data */
    status = cau3_hash_process_message_data(base, ctxInternal, input, inputSize);
    return status;
}

/*!
 * brief Finalize hashing
 *
 * Outputs the final hash (computed by CAU3_HASH_Update()) and erases the context.
 *
 * param[in,out] ctx Input hash context
 * param[out] output Output hash data
 * param[out] outputSize Output parameter storing the size of the output hash in bytes
 * return Status of the hash finish operation
 */
status_t CAU3_HASH_Finish(CAU3_Type *base, cau3_hash_ctx_t *ctx, uint8_t *output, size_t *outputSize)
{
    size_t algOutSize = 0;
    status_t status;
    cau3_hash_ctx_internal_t *ctxInternal;

    ctxInternal = (cau3_hash_ctx_internal_t *)(uint32_t)ctx;
    status      = cau3_hash_check_context(ctxInternal, output);
    if (kStatus_Success != status)
    {
        return status;
    }

    if (ctxInternal->state == kCAU3_StateHashInit)
    {
        status = cau3_hash_engine_init(base, ctxInternal);
        if (status != kStatus_Success)
        {
            return status;
        }
    }

    size_t outSize = 0u;

    /* compute algorithm output length */
    switch (ctxInternal->algo)
    {
        case kCAU3_Sha256:
            outSize = (uint32_t)kCAU3_OutLenSha256;
            break;
        case kCAU3_Sha1:
            outSize = (uint32_t)kCAU3_OutLenSha1;
            break;
        default:
            /* All the cases have been listed above, the default clause should not be reached. */
            break;
    }
    algOutSize = outSize;

    /* flush message last incomplete block, if there is any, and add padding bits */
    status = cau3_hash_finalize(base, ctxInternal);

    if (outputSize != NULL)
    {
        if (algOutSize < *outputSize)
        {
            *outputSize = algOutSize;
        }
        else
        {
            algOutSize = *outputSize;
        }
    }

    (void)cau3_memcpy(&output[0], (const uint8_t *)ctxInternal->runningHash, algOutSize);

    (void)memset(ctx, 0, sizeof(cau3_hash_ctx_t));
    return status;
}

/*!
 * brief Create HASH on given data
 *
 * Perform the full SHA in one function call. The function is blocking.
 *
 * param base CAU3 peripheral base address
 * param algo Underlaying algorithm to use for hash computation.
 * param input Input data
 * param inputSize Size of input data in bytes
 * param[out] output Output hash data
 * param[out] outputSize Output parameter storing the size of the output hash in bytes
 * return Status of the one call hash operation.
 */
status_t CAU3_HASH(
    CAU3_Type *base, cau3_hash_algo_t algo, const uint8_t *input, size_t inputSize, uint8_t *output, size_t *outputSize)
{
    cau3_hash_ctx_t hashCtx;
    status_t status;

    status = CAU3_HASH_Init(base, &hashCtx, algo);
    if (status != kStatus_Success)
    {
        return status;
    }

    status = CAU3_HASH_Update(base, &hashCtx, input, inputSize);
    if (status != kStatus_Success)
    {
        return status;
    }

    status = CAU3_HASH_Finish(base, &hashCtx, output, outputSize);

    return status;
}

/*! @brief CAU3 driver wait mechanism. */
static status_t cau3_wait(CAU3_Type *base)
{
    status_t status;

    bool error = false;
    bool done  = false;

    /* Wait for 'done' or 'error' flag. */
    while ((!error) && (!done))
    {
        uint32_t temp32 = base->STA;
        error           = (bool)(temp32 & (uint32_t)kCAU3_StatusErrorIsr);
        done            = (bool)(temp32 & (uint32_t)kCAU3_StatusDoneIsr);
    }

    if (error)
    {
        base->COM = CAU3_COM_ALL_MASK; /* Reset all engine to clear the error flag */
        status    = kStatus_Fail;
    }
    else /* 'done' */
    {
        status = kStatus_Success;

        base->CW = (uint32_t)kCAU3_ClearDataSize;
        /* Clear 'done' interrupt status.  This also clears the mode register. */
        base->STA = (uint32_t)kCAU3_StatusDoneIsr;
    }

    return status;
}

/*!
 * @brief Clears the CAU3 module.
 * This function can be used to clear all sensitive data from theCAU3 module, such as private keys. It is called
 * internally by the CAU3 driver in case of an error or operation complete.
 * @param base CAU3 peripheral base address
 * @param pkha Include CAU3 PKHA register clear. If there is no PKHA, the argument is ignored.
 */
static void cau3_clear_all(CAU3_Type *base, bool addPKHA)
{
    base->CW = (uint32_t)kCAU3_ClearAll;
    if (addPKHA)
    {
        (void)cau3_pkha_clear_regabne(base, true, true, true, true);
    }
}

/*!
 * @brief Reads an unaligned word.
 *
 * This function creates a 32-bit word from an input array of four bytes.
 *
 * @param src Input array of four bytes. The array can start at any address in memory.
 * @return 32-bit unsigned int created from the input byte array.
 */
static inline uint32_t cau3_get_word_from_unaligned(const uint8_t *srcAddr)
{
#if (!(defined(__CORTEX_M)) || (defined(__CORTEX_M) && (__CORTEX_M == 0)))
    register const uint8_t *src = srcAddr;
    /* Cortex M0 does not support misaligned loads */
    if (0U != ((uint32_t)src & 0x3u))
    {
        union _align_bytes_t
        {
            uint32_t word;
            uint8_t byte[sizeof(uint32_t)];
        } my_bytes;

        my_bytes.byte[0] = *src;
        my_bytes.byte[1] = *(src + 1);
        my_bytes.byte[2] = *(src + 2);
        my_bytes.byte[3] = *(src + 3);
        return my_bytes.word;
    }
    else
    {
        /* addr aligned to 0-modulo-4 so it is safe to type cast */
        return *((const uint32_t *)(uintptr_t)src);
    }
#elif defined(__CC_ARM) || defined(__ARMCC_VERSION)
    /* -O3 optimization in Keil Compiler 5 uses LDM instruction here (LDM r4!, {r0})
     *    which is wrong, because srcAddr might be unaligned.
     *    LDM on unaligned address causes hard-fault. so use memcpy() */
    uint32_t ret;
    memcpy(&ret, srcAddr, sizeof(uint32_t));
    return ret;
#else
    return *((const uint32_t *)(uintptr_t)srcAddr);
#endif
}

/*******************************************************************************
 * PKHA Code static
 ******************************************************************************/

static status_t cau3_pkha_clear_regabne(CAU3_Type *base, bool A, bool B, bool N, bool E)
{
    cau3_mode_t mode;

    /* Set the PKHA algorithm and the appropriate function. */
    mode = (uint32_t)kCAU3_AlgorithmPKHA | 1U;

    /* Set ram area to clear. Clear all. */
    if (A)
    {
        mode |= ((uint32_t)1U << 19U);
    }
    if (B)
    {
        mode |= ((uint32_t)1U << 18U);
    }
    if (N)
    {
        mode |= ((uint32_t)1U << 16U);
    }
    if (E)
    {
        mode |= ((uint32_t)1U << 17U);
    }

    /* Write the mode register to the hardware.
     * NOTE: This will begin the operation. */
    base->MDPK = mode;

    /* Wait for 'done' */
    return cau3_wait(base);
}

static void cau3_pkha_default_parms(cau3_pkha_mode_params_t *params)
{
    params->func        = (cau3_pkha_func_t)0;
    params->arithType   = kCAU3_PKHA_IntegerArith;
    params->montFormIn  = kCAU3_PKHA_NormalValue;
    params->montFormOut = kCAU3_PKHA_NormalValue;
    params->srcReg      = kCAU3_PKHA_RegAll;
    params->srcQuad     = kCAU3_PKHA_Quad0;
    params->dstReg      = kCAU3_PKHA_RegAll;
    params->dstQuad     = kCAU3_PKHA_Quad0;
    params->equalTime   = kCAU3_PKHA_NoTimingEqualized;
    params->r2modn      = kCAU3_PKHA_CalcR2;
}

static void cau3_pkha_write_word(CAU3_Type *base, cau3_pkha_reg_area_t reg, uint8_t index, uint32_t data)
{
    __IO uint32_t *pka = base->PKA0;
    __IO uint32_t *pkb = base->PKB0;
    __IO uint32_t *pkn = base->PKN0;

    switch (reg)
    {
        case kCAU3_PKHA_RegA:
            pka[index] = data;
            break;

        case kCAU3_PKHA_RegB:
            pkb[index] = data;
            break;

        case kCAU3_PKHA_RegN:
            pkn[index] = data;
            break;

        case kCAU3_PKHA_RegE:
            base->PKE[index] = data;
            break;

        default:
            /* All the cases have been listed above, the default clause should not be reached. */
            break;
    }
}

static uint32_t cau3_pkha_read_word(CAU3_Type *base, cau3_pkha_reg_area_t reg, uint8_t index)
{
    uint32_t retval;
    __IO uint32_t *pka = base->PKA0;
    __IO uint32_t *pkb = base->PKB0;
    __IO uint32_t *pkn = base->PKN0;

    switch (reg)
    {
        case kCAU3_PKHA_RegA:
            retval = pka[index];
            break;

        case kCAU3_PKHA_RegB:
            retval = pkb[index];
            break;

        case kCAU3_PKHA_RegN:
            retval = pkn[index];
            break;

        default:
            retval = 0;
            break;
    }
    return retval;
}

static status_t cau3_pkha_write_reg(
    CAU3_Type *base, cau3_pkha_reg_area_t reg, uint8_t quad, const uint8_t *data, size_t dataSize)
{
    /* Select the word-based start index for each quadrant of 128 bytes. */
    uint8_t startIndex = (quad * 32u);
    uint32_t outWord;

    while (dataSize > 0U)
    {
        if (dataSize >= sizeof(uint32_t))
        {
            cau3_pkha_write_word(base, reg, startIndex++, cau3_get_word_from_unaligned(data));
            dataSize -= sizeof(uint32_t);
            data += sizeof(uint32_t);
        }
        else /* (dataSize > 0) && (dataSize < 4) */
        {
            outWord = 0;
            (void)cau3_memcpy(&outWord, (const uint32_t *)(uintptr_t)data, dataSize);
            cau3_pkha_write_word(base, reg, startIndex, outWord);
            dataSize = 0;
        }
    }

    return kStatus_Success;
}

static void cau3_pkha_read_reg(CAU3_Type *base, cau3_pkha_reg_area_t reg, uint8_t quad, uint8_t *data, size_t dataSize)
{
    /* Select the word-based start index for each quadrant of 128 bytes. */
    uint8_t startIndex = (quad * 32u);
    size_t calcSize;
    uint32_t word;

    while (dataSize > 0U)
    {
        word = cau3_pkha_read_word(base, reg, startIndex++);

        calcSize = (dataSize >= sizeof(uint32_t)) ? sizeof(uint32_t) : dataSize;
        (void)cau3_memcpy(data, (const uint8_t *)&word, calcSize);

        data += calcSize;
        dataSize -= calcSize;
    }
}

static void cau3_pkha_init_data(CAU3_Type *base,
                                const uint8_t *A,
                                size_t sizeA,
                                const uint8_t *B,
                                size_t sizeB,
                                const uint8_t *N,
                                size_t sizeN,
                                const uint8_t *E,
                                size_t sizeE)
{
    uint32_t clearMask = (uint32_t)kCAU3_ClearMode; /* clear Mode Register */

    /* Clear internal register states. */
    if (sizeA != 0U)
    {
        clearMask |= (uint32_t)kCAU3_ClearPkhaSizeA;
    }
    if (sizeB != 0U)
    {
        clearMask |= (uint32_t)kCAU3_ClearPkhaSizeB;
    }
    if (sizeN != 0U)
    {
        clearMask |= (uint32_t)kCAU3_ClearPkhaSizeN;
    }
    if (sizeE != 0U)
    {
        clearMask |= (uint32_t)kCAU3_ClearPkhaSizeE;
    }

    base->CW  = clearMask;
    base->STA = (uint32_t)kCAU3_StatusDoneIsr;
    (void)cau3_pkha_clear_regabne(base, (bool)(uintptr_t)A, (bool)(uintptr_t)B, (bool)(uintptr_t)N, (bool)(uintptr_t)E);

    /* Write register sizes. */
    /* Write modulus (N) and A and B register arguments. */
    if (sizeN != 0U)
    {
        base->PKNSZ = sizeN;
        if (N != NULL)
        {
            (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegN, 0, N, sizeN);
        }
    }

    if (sizeA != 0U)
    {
        base->PKASZ = sizeA;
        if (A != NULL)
        {
            (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 0, A, sizeA);
        }
    }

    if (sizeB != 0U)
    {
        base->PKBSZ = sizeB;
        if (B != NULL)
        {
            (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 0, B, sizeB);
        }
    }

    if (sizeE != 0U)
    {
        base->PKESZ = sizeE;
        if (E != NULL)
        {
            (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegE, 0, E, sizeE);
        }
    }
}

static void cau3_pkha_mode_set_src_reg_copy(cau3_mode_t *outMode, cau3_pkha_reg_area_t reg)
{
    int i = 0;

    do
    {
        reg = (cau3_pkha_reg_area_t)(uint32_t)((uint32_t)reg >> 1u);
        i++;
    } while ((uint32_t)reg != 0U);

    i = 4 - i;
    /* Source register must not be E. */
    if (i != 2)
    {
        *outMode |= ((uint32_t)i << 17u);
    }
}

static void cau3_pkha_mode_set_dst_reg_copy(cau3_mode_t *outMode, cau3_pkha_reg_area_t reg)
{
    int i = 0;

    do
    {
        reg = (cau3_pkha_reg_area_t)(uint32_t)(((uint32_t)reg) >> 1u);
        i++;
    } while ((uint32_t)reg != 0U);

    i = 4 - i;
    *outMode |= ((uint32_t)i << 10u);
}

static void cau3_pkha_mode_set_src_seg_copy(cau3_mode_t *outMode, const cau3_pkha_quad_area_t quad)
{
    *outMode |= ((uint32_t)quad << 8u);
}

static void cau3_pkha_mode_set_dst_seg_copy(cau3_mode_t *outMode, const cau3_pkha_quad_area_t quad)
{
    *outMode |= ((uint32_t)quad << 6u);
}

/*!
 * @brief Starts the PKHA operation.
 *
 * This function starts an operation configured by the params parameter.
 *
 * @param base CAU3 peripheral base address
 * @param params Configuration structure containing all settings required for PKHA operation.
 */
static status_t cau3_pkha_init_mode(CAU3_Type *base, const cau3_pkha_mode_params_t *params)
{
    cau3_mode_t modeReg;
    status_t retval;

    /* Set the PKHA algorithm and the appropriate function. */
    modeReg = (uint32_t)kCAU3_AlgorithmPKHA;
    modeReg |= (uint32_t)params->func;

    if ((params->func == kCAU3_PKHA_CopyMemSizeN) || (params->func == kCAU3_PKHA_CopyMemSizeSrc))
    {
        /* Set source and destination registers and quads. */
        cau3_pkha_mode_set_src_reg_copy(&modeReg, params->srcReg);
        cau3_pkha_mode_set_dst_reg_copy(&modeReg, params->dstReg);
        cau3_pkha_mode_set_src_seg_copy(&modeReg, params->srcQuad);
        cau3_pkha_mode_set_dst_seg_copy(&modeReg, params->dstQuad);
    }
    else
    {
        /* Set the arithmetic type - integer or binary polynomial (F2m). */
        modeReg |= ((uint32_t)params->arithType << 17u);

        /* Set to use Montgomery form of inputs and/or outputs. */
        modeReg |= ((uint32_t)params->montFormIn << 19u);
        modeReg |= ((uint32_t)params->montFormOut << 18u);

        /* Set to use pre-computed R2modN */
        modeReg |= ((uint32_t)params->r2modn << 16u);
    }

    modeReg |= ((uint32_t)params->equalTime << 10u);

    /* Write the mode register to the hardware.
     * NOTE: This will begin the operation. */
    base->MDPK = modeReg;

    retval = cau3_wait(base);
    return (retval);
}

static status_t cau3_pkha_modR2(
    CAU3_Type *base, const uint8_t *N, size_t sizeN, uint8_t *result, size_t *resultSize, cau3_pkha_f2m_t arithType)
{
    status_t status;
    cau3_pkha_mode_params_t params;

    cau3_pkha_default_parms(&params);
    params.func      = kCAU3_PKHA_ArithModR2;
    params.arithType = arithType;

    cau3_pkha_init_data(base, NULL, 0, NULL, 0, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if ((resultSize != NULL) && (result != NULL))
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    return status;
}

static status_t cau3_pkha_modmul(CAU3_Type *base,
                                 const uint8_t *A,
                                 size_t sizeA,
                                 const uint8_t *B,
                                 size_t sizeB,
                                 const uint8_t *N,
                                 size_t sizeN,
                                 uint8_t *result,
                                 size_t *resultSize,
                                 cau3_pkha_f2m_t arithType,
                                 cau3_pkha_montgomery_form_t montIn,
                                 cau3_pkha_montgomery_form_t montOut,
                                 cau3_pkha_timing_t equalTime)
{
    cau3_pkha_mode_params_t params;
    status_t status;

    if (arithType == kCAU3_PKHA_IntegerArith)
    {
        if (CAU3_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0)
        {
            return (kStatus_InvalidArgument);
        }

        if (CAU3_PKHA_CompareBigNum(B, sizeB, N, sizeN) >= 0)
        {
            return (kStatus_InvalidArgument);
        }
    }

    cau3_pkha_default_parms(&params);
    params.func        = kCAU3_PKHA_ArithModMul;
    params.arithType   = arithType;
    params.montFormIn  = montIn;
    params.montFormOut = montOut;
    params.equalTime   = equalTime;

    cau3_pkha_init_data(base, A, sizeA, B, sizeB, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if ((resultSize != NULL) && (result != NULL))
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    return status;
}

/*******************************************************************************
 * PKHA Code public
 ******************************************************************************/
/*!
 * addtogroup cau3_driver_pkha
 * {
 */

int CAU3_PKHA_CompareBigNum(const uint8_t *a, size_t sizeA, const uint8_t *b, size_t sizeB)
{
    int retval = 0;

    /* skip zero msbytes - integer a */
    while ((sizeA != 0U) && (0u == a[sizeA - 1U]))
    {
        sizeA--;
    }

    /* skip zero msbytes - integer b */
    while ((sizeB != 0U) && (0u == b[sizeB - 1U]))
    {
        sizeB--;
    }

    if (sizeA > sizeB)
    {
        retval = 1;
    } /* int a has more non-zero bytes, thus it is bigger than b */
    else if (sizeA < sizeB)
    {
        retval = -1;
    } /* int b has more non-zero bytes, thus it is bigger than a */
    else if (sizeA == 0U)
    {
        retval = 0;
    } /* sizeA = sizeB = 0 */
    else
    {
        int n;
        int i;
        int val;
        uint32_t equal;

        n     = (int32_t)((int32_t)sizeA - 1);
        i     = 0;
        equal = 0;

        while (n >= 0)
        {
            uint32_t chXor = ((uint32_t)a[i] ^ (uint32_t)b[i]);

            equal |= chXor;
            val = (int)chXor * ((int32_t)a[i] - (int32_t)b[i]);

            if (val < 0)
            {
                retval = -1;
            }

            if (val > 0)
            {
                retval = 1;
            }

            if (val == 0)
            {
                val = 1;
            }

            if (0 != val)
            {
                i++;
                n--;
            }
        }

        if (0U == equal)
        {
            retval = 0;
        }
    }
    return (retval);
}

/*!
 * brief Converts from integer to Montgomery format.
 *
 * This function computes R2 mod N and optionally converts A or B into Montgomery format of A or B.
 *
 * param base CAU3 peripheral base address
 * param N modulus
 * param sizeN size of N in bytes
 * param[in,out] A The first input in non-Montgomery format. Output Montgomery format of the first input.
 * param[in,out] sizeA pointer to size variable. On input it holds size of input A in bytes. On output it holds size of
 *                Montgomery format of A in bytes.
 * param[in,out] B Second input in non-Montgomery format. Output Montgomery format of the second input.
 * param[in,out] sizeB pointer to size variable. On input it holds size of input B in bytes. On output it holds size of
 *                Montgomery format of B in bytes.
 * param[out] R2 Output Montgomery factor R2 mod N.
 * param[out] sizeR2 pointer to size variable. On output it holds size of Montgomery factor R2 mod N in bytes.
 * param equalTime Run the function time equalized or no timing equalization.
 * param arithType Type of arithmetic to perform (integer or F2m)
 * return Operation status.
 */
status_t CAU3_PKHA_NormalToMontgomery(CAU3_Type *base,
                                      const uint8_t *N,
                                      size_t sizeN,
                                      uint8_t *A,
                                      size_t *sizeA,
                                      uint8_t *B,
                                      size_t *sizeB,
                                      uint8_t *R2,
                                      size_t *sizeR2,
                                      cau3_pkha_timing_t equalTime,
                                      cau3_pkha_f2m_t arithType)
{
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    /* need to convert our Integer inputs into Montgomery format */
    if ((N != NULL) && (sizeN != 0U) && (R2 != NULL) && (sizeR2 != NULL))
    {
        /* 1. R2 = MOD_R2(N) */
        status = cau3_pkha_modR2(base, N, sizeN, R2, sizeR2, arithType);
        if (status != kStatus_Success)
        {
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
            cau3_release_semaphore(base);
#endif
            return status;
        }

        /* 2. A(Montgomery) = MOD_MUL_IM_OM(A, R2, N) */
        if ((A != NULL) && (sizeA != NULL))
        {
            status = cau3_pkha_modmul(base, A, *sizeA, R2, *sizeR2, N, sizeN, A, sizeA, arithType,
                                      kCAU3_PKHA_MontgomeryFormat, kCAU3_PKHA_MontgomeryFormat, equalTime);
            if (status != kStatus_Success)
            {
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
                cau3_release_semaphore(base);
#endif
                return status;
            }
        }

        /* 2. B(Montgomery) = MOD_MUL_IM_OM(B, R2, N) */
        if ((B != NULL) && (sizeB != NULL))
        {
            status = cau3_pkha_modmul(base, B, *sizeB, R2, *sizeR2, N, sizeN, B, sizeB, arithType,
                                      kCAU3_PKHA_MontgomeryFormat, kCAU3_PKHA_MontgomeryFormat, equalTime);
            if (status != kStatus_Success)
            {
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
                cau3_release_semaphore(base);
#endif
                return status;
            }
        }

        cau3_clear_all(base, true);
    }
    else
    {
        status = kStatus_InvalidArgument;
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Converts from Montgomery format to int.
 *
 * This function converts Montgomery format of A or B into int A or B.
 *
 * param base CAU3 peripheral base address
 * param N modulus.
 * param sizeN size of N modulus in bytes.
 * param[in,out] A Input first number in Montgomery format. Output is non-Montgomery format.
 * param[in,out] sizeA pointer to size variable. On input it holds size of the input A in bytes. On output it holds
 * size of non-Montgomery A in bytes.
 * param[in,out] B Input first number in Montgomery format. Output is non-Montgomery format.
 * param[in,out] sizeB pointer to size variable. On input it holds size of the input B in bytes. On output it holds
 * size of non-Montgomery B in bytes.
 * param equalTime Run the function time equalized or no timing equalization.
 * param arithType Type of arithmetic to perform (integer or F2m)
 * return Operation status.
 */
status_t CAU3_PKHA_MontgomeryToNormal(CAU3_Type *base,
                                      const uint8_t *N,
                                      size_t sizeN,
                                      uint8_t *A,
                                      size_t *sizeA,
                                      uint8_t *B,
                                      size_t *sizeB,
                                      cau3_pkha_timing_t equalTime,
                                      cau3_pkha_f2m_t arithType)
{
    uint8_t one     = 1;
    status_t status = kStatus_InvalidArgument;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    /* A = MOD_MUL_IM_OM(A(Montgomery), 1, N) */
    if ((A != NULL) && (sizeA != NULL))
    {
        status = cau3_pkha_modmul(base, A, *sizeA, &one, sizeof(one), N, sizeN, A, sizeA, arithType,
                                  kCAU3_PKHA_MontgomeryFormat, kCAU3_PKHA_MontgomeryFormat, equalTime);
        if (kStatus_Success != status)
        {
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
            cau3_release_semaphore(base);
#endif
            return status;
        }
    }

    /* B = MOD_MUL_IM_OM(B(Montgomery), 1, N) */
    if ((B != NULL) && (sizeB != NULL))
    {
        status = cau3_pkha_modmul(base, B, *sizeB, &one, sizeof(one), N, sizeN, B, sizeB, arithType,
                                  kCAU3_PKHA_MontgomeryFormat, kCAU3_PKHA_MontgomeryFormat, equalTime);
        if (kStatus_Success != status)
        {
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
            cau3_release_semaphore(base);
#endif
            return status;
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Performs modular addition - (A + B) mod N.
 *
 * This function performs modular addition of (A + B) mod N, with either
 * integer or binary polynomial (F2m) inputs.  In the F2m form, this function is
 * equivalent to a bitwise XOR and it is functionally the same as subtraction.
 *
 * param base CAU3 peripheral base address
 * param A first addend (integer or binary polynomial)
 * param sizeA Size of A in bytes
 * param B second addend (integer or binary polynomial)
 * param sizeB Size of B in bytes
 * param N modulus.
 * param sizeN Size of N in bytes.
 * param[out] result Output array to store result of operation
 * param[out] resultSize Output size of operation in bytes
 * param arithType Type of arithmetic to perform (integer or F2m)
 * return Operation status.
 */
status_t CAU3_PKHA_ModAdd(CAU3_Type *base,
                          const uint8_t *A,
                          size_t sizeA,
                          const uint8_t *B,
                          size_t sizeB,
                          const uint8_t *N,
                          size_t sizeN,
                          uint8_t *result,
                          size_t *resultSize,
                          cau3_pkha_f2m_t arithType)
{
    cau3_pkha_mode_params_t params;
    status_t status;

    if (arithType == kCAU3_PKHA_IntegerArith)
    {
        if (CAU3_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0)
        {
            return (kStatus_InvalidArgument);
        }

        if (CAU3_PKHA_CompareBigNum(B, sizeB, N, sizeN) >= 0)
        {
            return (kStatus_InvalidArgument);
        }
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func      = kCAU3_PKHA_ArithModAdd;
    params.arithType = arithType;

    cau3_pkha_init_data(base, A, sizeA, B, sizeB, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if ((resultSize != NULL) && (result != NULL))
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Performs modular subtraction - (A - B) mod N.
 *
 * This function performs modular subtraction of (A - B) mod N with
 * integer inputs.
 *
 * param base CAU3 peripheral base address
 * param A first addend (integer or binary polynomial)
 * param sizeA Size of A in bytes
 * param B second addend (integer or binary polynomial)
 * param sizeB Size of B in bytes
 * param N modulus
 * param sizeN Size of N in bytes
 * param[out] result Output array to store result of operation
 * param[out] resultSize Output size of operation in bytes
 * return Operation status.
 */
status_t CAU3_PKHA_ModSub1(CAU3_Type *base,
                           const uint8_t *A,
                           size_t sizeA,
                           const uint8_t *B,
                           size_t sizeB,
                           const uint8_t *N,
                           size_t sizeN,
                           uint8_t *result,
                           size_t *resultSize)
{
    cau3_pkha_mode_params_t params;
    status_t status;

    if (CAU3_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0)
    {
        return (kStatus_InvalidArgument);
    }

    if (CAU3_PKHA_CompareBigNum(B, sizeB, N, sizeN) >= 0)
    {
        return (kStatus_InvalidArgument);
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithModSub1;
    cau3_pkha_init_data(base, A, sizeA, B, sizeB, N, sizeN, NULL, 0);

    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if ((resultSize != NULL) && (result != NULL))
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Performs modular subtraction - (B - A) mod N.
 *
 * This function performs modular subtraction of (B - A) mod N,
 * with integer inputs.
 *
 * param base CAU3 peripheral base address
 * param A first addend (integer or binary polynomial)
 * param sizeA Size of A in bytes
 * param B second addend (integer or binary polynomial)
 * param sizeB Size of B in bytes
 * param N modulus
 * param sizeN Size of N in bytes
 * param[out] result Output array to store result of operation
 * param[out] resultSize Output size of operation in bytes
 * return Operation status.
 */
status_t CAU3_PKHA_ModSub2(CAU3_Type *base,
                           const uint8_t *A,
                           size_t sizeA,
                           const uint8_t *B,
                           size_t sizeB,
                           const uint8_t *N,
                           size_t sizeN,
                           uint8_t *result,
                           size_t *resultSize)
{
    cau3_pkha_mode_params_t params;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithModSub2;

    cau3_pkha_init_data(base, A, sizeA, B, sizeB, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if ((resultSize != NULL) && (result != NULL))
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Performs modular multiplication - (A x B) mod N.
 *
 * This function performs modular multiplication with either integer or
 * binary polynomial (F2m) inputs.  It can optionally specify whether inputs
 * and/or outputs will be in Montgomery form or not.
 *
 * param base CAU3 peripheral base address
 * param A first addend (integer or binary polynomial)
 * param sizeA Size of A in bytes
 * param B second addend (integer or binary polynomial)
 * param sizeB Size of B in bytes
 * param N modulus.
 * param sizeN Size of N in bytes
 * param[out] result Output array to store result of operation
 * param[out] resultSize Output size of operation in bytes
 * param arithType Type of arithmetic to perform (integer or F2m)
 * param montIn Format of inputs
 * param montOut Format of output
 * param equalTime Run the function time equalized or no timing equalization. This argument is ignored for F2m modular
 * multiplication.
 * return Operation status.
 */
status_t CAU3_PKHA_ModMul(CAU3_Type *base,
                          const uint8_t *A,
                          size_t sizeA,
                          const uint8_t *B,
                          size_t sizeB,
                          const uint8_t *N,
                          size_t sizeN,
                          uint8_t *result,
                          size_t *resultSize,
                          cau3_pkha_f2m_t arithType,
                          cau3_pkha_montgomery_form_t montIn,
                          cau3_pkha_montgomery_form_t montOut,
                          cau3_pkha_timing_t equalTime)
{
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    status =
        cau3_pkha_modmul(base, A, sizeA, B, sizeB, N, sizeN, result, resultSize, arithType, montIn, montOut, equalTime);

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Performs modular exponentiation - (A^E) mod N.
 *
 * This function performs modular exponentiation with either integer or
 * binary polynomial (F2m) inputs.
 *
 * param base CAU3 peripheral base address
 * param A first addend (integer or binary polynomial)
 * param sizeA Size of A in bytes
 * param N modulus
 * param sizeN Size of N in bytes
 * param E exponent
 * param sizeE Size of E in bytes
 * param[out] result Output array to store result of operation
 * param[out] resultSize Output size of operation in bytes
 * param montIn Format of A input (normal or Montgomery)
 * param arithType Type of arithmetic to perform (integer or F2m)
 * param equalTime Run the function time equalized or no timing equalization.
 * return Operation status.
 */
status_t CAU3_PKHA_ModExp(CAU3_Type *base,
                          const uint8_t *A,
                          size_t sizeA,
                          const uint8_t *N,
                          size_t sizeN,
                          const uint8_t *E,
                          size_t sizeE,
                          uint8_t *result,
                          size_t *resultSize,
                          cau3_pkha_f2m_t arithType,
                          cau3_pkha_montgomery_form_t montIn,
                          cau3_pkha_timing_t equalTime)
{
    cau3_pkha_mode_params_t params;
    status_t status;

    if (arithType == kCAU3_PKHA_IntegerArith)
    {
        if (CAU3_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0)
        {
            return (kStatus_InvalidArgument);
        }
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func       = kCAU3_PKHA_ArithModExp;
    params.arithType  = arithType;
    params.montFormIn = montIn;
    params.equalTime  = equalTime;

    cau3_pkha_init_data(base, A, sizeA, NULL, 0, N, sizeN, E, sizeE);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if ((resultSize != NULL) && (result != NULL))
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Performs Modular Square Root.
 *
 * This function performs modular square root with integer inputs.
 * The modular square root function computes output result B, such that ( B x B ) mod N = input A.
 * If no such B result exists, the result will be set to 0 and the PKHA "prime" flag
 * will be set. Input values A and B are limited to a maximum size of 128 bytes. Note that
 * two such square root values may exist. This algorithm will find either one of them, if any
 * exist. The second possible square root (B') can be found by calculating B' = N - B.
 *
 * param base CAU3 peripheral base address
 * param A input value, for which a square root is to be calculated
 * param sizeA Size of A in bytes
 * param N modulus
 * param sizeN Size of N in bytes
 * param[out] result Output array to store result of operation
 * param[out] resultSize Output size of operation in bytes
 * return Operation status.
 */
status_t CAU3_PKHA_ModSqrt(CAU3_Type *base,
                           const uint8_t *A,
                           size_t sizeA,
                           const uint8_t *N,
                           size_t sizeN,
                           uint8_t *result,
                           size_t *resultSize)
{
    cau3_pkha_mode_params_t params;
    status_t status;

    /* A < N */
    if (CAU3_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0)
    {
        return (kStatus_InvalidArgument);
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithModSqrt;

    cau3_pkha_init_data(base, A, sizeA, NULL, 0, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if ((resultSize != NULL) && (result != NULL))
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Performs modular reduction - (A) mod N.
 *
 * This function performs modular reduction with either integer or
 * binary polynomial (F2m) inputs.
 *
 * param base CAU3 peripheral base address
 * param A first addend (integer or binary polynomial)
 * param sizeA Size of A in bytes
 * param N modulus
 * param sizeN Size of N in bytes
 * param[out] result Output array to store result of operation
 * param[out] resultSize Output size of operation in bytes
 * param arithType Type of arithmetic to perform (integer or F2m)
 * return Operation status.
 */
status_t CAU3_PKHA_ModRed(CAU3_Type *base,
                          const uint8_t *A,
                          size_t sizeA,
                          const uint8_t *N,
                          size_t sizeN,
                          uint8_t *result,
                          size_t *resultSize,
                          cau3_pkha_f2m_t arithType)
{
    cau3_pkha_mode_params_t params;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func      = kCAU3_PKHA_ArithModRed;
    params.arithType = arithType;

    cau3_pkha_init_data(base, A, sizeA, NULL, 0, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if ((resultSize != NULL) && (result != NULL))
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Performs modular inversion - (A^-1) mod N.
 *
 * This function performs modular inversion with either integer or
 * binary polynomial (F2m) inputs.
 *
 * param base CAU3 peripheral base address
 * param A first addend (integer or binary polynomial)
 * param sizeA Size of A in bytes
 * param N modulus
 * param sizeN Size of N in bytes
 * param[out] result Output array to store result of operation
 * param[out] resultSize Output size of operation in bytes
 * param arithType Type of arithmetic to perform (integer or F2m)
 * return Operation status.
 */
status_t CAU3_PKHA_ModInv(CAU3_Type *base,
                          const uint8_t *A,
                          size_t sizeA,
                          const uint8_t *N,
                          size_t sizeN,
                          uint8_t *result,
                          size_t *resultSize,
                          cau3_pkha_f2m_t arithType)
{
    cau3_pkha_mode_params_t params;
    status_t status;

    /* A must be less than N -> CAU3_PKHA_CompareBigNum() must return -1 */
    if (arithType == kCAU3_PKHA_IntegerArith)
    {
        if (CAU3_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0)
        {
            return (kStatus_InvalidArgument);
        }
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func      = kCAU3_PKHA_ArithModInv;
    params.arithType = arithType;

    cau3_pkha_init_data(base, A, sizeA, NULL, 0, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if ((resultSize != NULL) && (result != NULL))
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Computes integer Montgomery factor R^2 mod N.
 *
 * This function computes a constant to assist in converting operands
 * into the Montgomery residue system representation.
 *
 * param base CAU3 peripheral base address
 * param N modulus
 * param sizeN Size of N in bytes
 * param[out] result Output array to store result of operation
 * param[out] resultSize Output size of operation in bytes
 * param arithType Type of arithmetic to perform (integer or F2m)
 * return Operation status.
 */
status_t CAU3_PKHA_ModR2(
    CAU3_Type *base, const uint8_t *N, size_t sizeN, uint8_t *result, size_t *resultSize, cau3_pkha_f2m_t arithType)
{
    status_t status;
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif
    status = cau3_pkha_modR2(base, N, sizeN, result, resultSize, arithType);
    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Performs Integer RERP mod P.
 *
 * This function is used to compute a constant to assist in converting operands into the
 * Montgomery residue system representation specifically for Chinese Remainder Theorem
 * while performing RSA with a CRT implementation where a modulus E=P x Q, and P and
 * Q are prime numbers. Although labeled RERP mod P, this routine (function) can also
 * compute RERQ mod Q.
 *
 * param base CAU3 peripheral base address
 * param P modulus P or Q of CRT, an odd integer
 * param sizeP Size of P in bytes
 * param sizeE Number of bytes of E = P x Q (this size must be given, though content of E itself is not used).
 * param[out] result Output array to store result of operation
 * param[out] resultSize Output size of operation in bytes
 * return Operation status.
 */
status_t CAU3_PKHA_ModRR(
    CAU3_Type *base, const uint8_t *P, size_t sizeP, size_t sizeE, uint8_t *result, size_t *resultSize)
{
    status_t status;
    cau3_pkha_mode_params_t params;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithModRR;

    cau3_pkha_init_data(base, NULL, 0, NULL, 0, P, sizeP, NULL, sizeE);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if ((resultSize != NULL) && (result != NULL))
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Calculates the greatest common divisor - GCD (A, N).
 *
 * This function calculates the greatest common divisor of two inputs with
 * either integer or binary polynomial (F2m) inputs.
 *
 * param base CAU3 peripheral base address
 * param A first value (must be smaller than or equal to N)
 * param sizeA Size of A in bytes
 * param N second value (must be non-zero)
 * param sizeN Size of N in bytes
 * param[out] result Output array to store result of operation
 * param[out] resultSize Output size of operation in bytes
 * param arithType Type of arithmetic to perform (integer or F2m)
 * return Operation status.
 */
status_t CAU3_PKHA_ModGcd(CAU3_Type *base,
                          const uint8_t *A,
                          size_t sizeA,
                          const uint8_t *N,
                          size_t sizeN,
                          uint8_t *result,
                          size_t *resultSize,
                          cau3_pkha_f2m_t arithType)
{
    cau3_pkha_mode_params_t params;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func      = kCAU3_PKHA_ArithGcd;
    params.arithType = arithType;

    cau3_pkha_init_data(base, A, sizeA, NULL, 0, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if ((resultSize != NULL) && (result != NULL))
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Executes Miller-Rabin primality test.
 *
 * This function calculates whether or not a candidate prime number is likely
 * to be a prime.
 *
 * param base CAU3 peripheral base address
 * param A initial random seed
 * param sizeA Size of A in bytes
 * param B number of trial runs
 * param sizeB Size of B in bytes
 * param N candidate prime integer
 * param sizeN Size of N in bytes
 * param[out] res True if the value is likely prime or false otherwise
 * return Operation status.
 */
status_t CAU3_PKHA_PrimalityTest(CAU3_Type *base,
                                 const uint8_t *A,
                                 size_t sizeA,
                                 const uint8_t *B,
                                 size_t sizeB,
                                 const uint8_t *N,
                                 size_t sizeN,
                                 bool *res)
{
    uint8_t result;
    cau3_pkha_mode_params_t params;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithPrimalityTest;
    cau3_pkha_init_data(base, A, sizeA, B, sizeB, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the data from the result register into place. */
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, &result, 1);

        *res = (bool)result;
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Adds elliptic curve points - A + B.
 *
 * This function performs ECC point addition over a prime field (Fp) or binary field (F2m) using
 * affine coordinates.
 *
 * param base CAU3 peripheral base address
 * param A Left-hand point
 * param B Right-hand point
 * param N Prime modulus of the field
 * param R2modN NULL (the function computes R2modN internally) or pointer to pre-computed R2modN (obtained from
 *               CAU3_PKHA_ModR2() function).
 * param aCurveParam A parameter from curve equation
 * param bCurveParam B parameter from curve equation (constant)
 * param size Size in bytes of curve points and parameters
 * param arithType Type of arithmetic to perform (integer or F2m)
 * param[out] result Result point
 * return Operation status.
 */
status_t CAU3_PKHA_ECC_PointAdd(CAU3_Type *base,
                                const cau3_pkha_ecc_point_t *A,
                                const cau3_pkha_ecc_point_t *B,
                                const uint8_t *N,
                                const uint8_t *R2modN,
                                const uint8_t *aCurveParam,
                                const uint8_t *bCurveParam,
                                size_t size,
                                cau3_pkha_f2m_t arithType,
                                cau3_pkha_ecc_point_t *result)
{
    cau3_pkha_mode_params_t params;
    uint32_t clearMask;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func      = kCAU3_PKHA_ArithEccAdd;
    params.arithType = arithType;
    params.r2modn    = (R2modN != NULL) ? kCAU3_PKHA_InputR2 : kCAU3_PKHA_CalcR2;

    clearMask = (uint32_t)kCAU3_ClearMode;

    /* Clear internal register states. */
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeA;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeB;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeN;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeE;

    base->CW  = clearMask;
    base->STA = (uint32_t)kCAU3_StatusDoneIsr;
    (void)cau3_pkha_clear_regabne(base, true, true, true, false);

    /* sizeN should be less than 64 bytes. */
    base->PKNSZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegN, 0, N, size);

    base->PKASZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 0, A->X, size);
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 1, A->Y, size);
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 3, aCurveParam, size);

    base->PKBSZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 0, bCurveParam, size);
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 1, B->X, size);
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 2, B->Y, size);
    if (R2modN != NULL)
    {
        (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 3, R2modN, size);
    }

    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the data from the result register into place. */
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 1, result->X, size);
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 2, result->Y, size);
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Doubles elliptic curve points - B + B.
 *
 * This function performs ECC point doubling over a prime field (Fp) or binary field (F2m) using
 * affine coordinates.
 *
 * param base CAU3 peripheral base address
 * param B Point to double
 * param N Prime modulus of the field
 * param aCurveParam A parameter from curve equation
 * param bCurveParam B parameter from curve equation (constant)
 * param size Size in bytes of curve points and parameters
 * param arithType Type of arithmetic to perform (integer or F2m)
 * param[out] result Result point
 * return Operation status.
 */
status_t CAU3_PKHA_ECC_PointDouble(CAU3_Type *base,
                                   const cau3_pkha_ecc_point_t *B,
                                   const uint8_t *N,
                                   const uint8_t *aCurveParam,
                                   const uint8_t *bCurveParam,
                                   size_t size,
                                   cau3_pkha_f2m_t arithType,
                                   cau3_pkha_ecc_point_t *result)
{
    cau3_pkha_mode_params_t params;
    uint32_t clearMask;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func      = kCAU3_PKHA_ArithEccDouble;
    params.arithType = arithType;

    clearMask = (uint32_t)kCAU3_ClearMode;

    /* Clear internal register states. */
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeA;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeB;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeN;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeE;

    base->CW  = clearMask;
    base->STA = (uint32_t)kCAU3_StatusDoneIsr;
    (void)cau3_pkha_clear_regabne(base, true, true, true, false);

    /* sizeN should be less than 64 bytes. */
    base->PKNSZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegN, 0, N, size);

    base->PKASZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 3, aCurveParam, size);

    base->PKBSZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 0, bCurveParam, size);
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 1, B->X, size);
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 2, B->Y, size);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the data from the result register into place. */
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 1, result->X, size);
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 2, result->Y, size);
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Multiplies an elliptic curve point by a scalar - E x (A0, A1).
 *
 * This function performs ECC point multiplication to multiply an ECC point by
 * a scalar integer multiplier over a prime field (Fp) or a binary field (F2m).
 *
 * param base CAU3 peripheral base address
 * param A Point as multiplicand
 * param E Scalar multiple
 * param sizeE The size of E, in bytes
 * param N Modulus, a prime number for the Fp field or Irreducible polynomial for F2m field.
 * param R2modN NULL (the function computes R2modN internally) or pointer to pre-computed R2modN (obtained from
 *        CAU3_PKHA_ModR2() function).
 * param aCurveParam A parameter from curve equation
 * param bCurveParam B parameter from curve equation (C parameter for operation over F2m).
 * param size Size in bytes of curve points and parameters
 * param equalTime Run the function time equalized or no timing equalization.
 * param arithType Type of arithmetic to perform (integer or F2m)
 * param[out] result Result point
 * return Operation status.
 */
status_t CAU3_PKHA_ECC_PointMul(CAU3_Type *base,
                                const cau3_pkha_ecc_point_t *A,
                                const uint8_t *E,
                                size_t sizeE,
                                const uint8_t *N,
                                const uint8_t *R2modN,
                                const uint8_t *aCurveParam,
                                const uint8_t *bCurveParam,
                                size_t size,
                                cau3_pkha_timing_t equalTime,
                                cau3_pkha_f2m_t arithType,
                                cau3_pkha_ecc_point_t *result)
{
    cau3_pkha_mode_params_t params;
    uint32_t clearMask;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func      = kCAU3_PKHA_ArithEccMul;
    params.equalTime = equalTime;
    params.arithType = arithType;
    params.r2modn    = (R2modN != NULL) ? kCAU3_PKHA_InputR2 : kCAU3_PKHA_CalcR2;

    clearMask = (uint32_t)kCAU3_ClearMode;

    /* Clear internal register states. */
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeA;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeB;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeN;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeE;

    base->CW  = clearMask;
    base->STA = (uint32_t)kCAU3_StatusDoneIsr;
    (void)cau3_pkha_clear_regabne(base, true, true, true, true);

    /* sizeN should be less than 64 bytes. */
    base->PKNSZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegN, 0, N, size);

    base->PKESZ = sizeE;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegE, 0, E, sizeE);

    base->PKASZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 0, A->X, size);
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 1, A->Y, size);
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 3, aCurveParam, size);

    base->PKBSZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 0, bCurveParam, size);
    if (R2modN != NULL)
    {
        (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 1, R2modN, size);
    }

    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the data from the result register into place. */
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 1, result->X, size);
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 2, result->Y, size);
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Computes scalar multiplication of a point on an elliptic curve in Montgomery form.
 *
 * This function computes the scalar multiplication of a point on an elliptic curve in
 * Montgomery form. The input and output are just the x coordinates of the points.
 * The points on a curve are defined by the equation E: B*y^2 = x^3 + A*x^2 + x mod p
 * This function computes a point multiplication on a Montgomery curve, using
 * Montgomery values, by means of a Montgomery ladder. At the end of the ladder, P2 = P3 + P1,
 * where P1 is the input and P3 is the result.
 *
 * param base CAU3 peripheral base address
 * param E Scalar multiplier, any integer
 * param sizeE The size of E, in bytes
 * param inputCoordinate Point as multiplicand, an input point's affine x coordinate
 * param A24 elliptic curve a24 parameter, that is, (A+2)/4
 * param N Modulus, a prime number.
 * param R2modN NULL (the function computes R2modN internally) or pointer to pre-computed R2modN (obtained from
 *        ref CAU3_PKHA_ModR2() function).
 * param size Size in bytes of curve points and parameters
 * param equalTime Run the function time equalized or no timing equalization.
 * param[out] outputCoordinate Resulting poin's x affine coordinate.
 * return Operation status.
 */
status_t CAU3_PKHA_ECM_PointMul(CAU3_Type *base,
                                const uint8_t *E,
                                size_t sizeE,
                                const uint8_t *inputCoordinate,
                                const uint8_t *A24,
                                const uint8_t *N,
                                const uint8_t *R2modN,
                                size_t size,
                                cau3_pkha_timing_t equalTime,
                                uint8_t *outputCoordinate)
{
    cau3_pkha_mode_params_t params;
    uint32_t clearMask;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func      = kCAU3_PKHA_ArithEcmMul;
    params.equalTime = equalTime;
    params.r2modn    = (R2modN != NULL) ? kCAU3_PKHA_InputR2 : kCAU3_PKHA_CalcR2;

    clearMask = (uint32_t)kCAU3_ClearMode;

    /* Clear internal register states. */
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeA;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeB;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeN;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeE;

    base->CW  = clearMask;
    base->STA = (uint32_t)kCAU3_StatusDoneIsr;
    (void)cau3_pkha_clear_regabne(base, true, true, true, true);

    base->PKNSZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegN, 0, N, size);

    base->PKESZ = sizeE;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegE, 0, E, sizeE);

    base->PKASZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 0, inputCoordinate, size);
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 3, A24, size);

    if (R2modN != NULL)
    {
        base->PKBSZ = size;
        (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 1, R2modN, size);
    }

    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the data from the result register into place. */
        (void)cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 1, outputCoordinate, size);
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Multiplies an Edwards-form elliptic curve point by a scalar - E x (A0, A1).
 *
 * This function performs scalar multiplication of an Edwards-form elliptic curve point
 * in affine coordinates.
 * The points on a curve are defined by the equation E: a*X^2 + d^2 = 1 + D^2*X^2*Y^2 mod N
 *
 * param base CAU3 peripheral base address
 * param A Point as multiplicand
 * param E Scalar multiple
 * param sizeE The size of E, in bytes
 * param N Modulus, a prime number for the Fp field.
 * param R2modN NULL (the function computes R2modN internally) or pointer to pre-computed R2modN (obtained from
 *        ref CAU3_PKHA_ModR2() function).
 * param aCurveParam A parameter from curve equation
 * param dCurveParam D parameter from curve equation.
 * param size Size in bytes of curve points and parameters
 * param equalTime Run the function time equalized or no timing equalization.
 * param[out] result Result point
 * return Operation status.
 */
status_t CAU3_PKHA_ECT_PointMul(CAU3_Type *base,
                                const cau3_pkha_ecc_point_t *A,
                                const uint8_t *E,
                                size_t sizeE,
                                const uint8_t *N,
                                const uint8_t *R2modN,
                                const uint8_t *aCurveParam,
                                const uint8_t *dCurveParam,
                                size_t size,
                                cau3_pkha_timing_t equalTime,
                                cau3_pkha_ecc_point_t *result)
{
    cau3_pkha_mode_params_t params;
    uint32_t clearMask;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func      = kCAU3_PKHA_ArithEctMul;
    params.equalTime = equalTime;
    params.r2modn    = (R2modN != NULL) ? kCAU3_PKHA_InputR2 : kCAU3_PKHA_CalcR2;

    clearMask = (uint32_t)kCAU3_ClearMode;

    /* Clear internal register states. */
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeA;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeB;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeN;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeE;

    base->CW  = clearMask;
    base->STA = (uint32_t)kCAU3_StatusDoneIsr;
    (void)cau3_pkha_clear_regabne(base, true, true, true, true);

    base->PKNSZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegN, 0, N, size);

    base->PKESZ = sizeE;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegE, 0, E, sizeE);

    base->PKASZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 0, A->X, size);
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 1, A->Y, size);
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 3, aCurveParam, size);

    base->PKBSZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 0, dCurveParam, size);
    if (R2modN != NULL)
    {
        (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 1, R2modN, size);
    }

    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the data from the result register into place. */
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 1, result->X, size);
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 2, result->Y, size);
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief Adds an Edwards-form elliptic curve points - A + B.
 *
 * This function performs Edwards-form elliptic curve point addition over a prime field (Fp) using affine coordinates.
 * The points on a curve are defined by the equation E: a*X^2 + Y^2 = 1 + d^2*X^2*Y^2 mod N
 *
 * param base CAU3 peripheral base address
 * param A Left-hand point
 * param B Right-hand point
 * param N Prime modulus of the field
 * param R2modN NULL (the function computes R2modN internally) or pointer to pre-computed R2modN (obtained from
 *               ref CAU3_PKHA_ModR2() function).
 * param aCurveParam A parameter from curve equation
 * param dCurveParam D parameter from curve equation
 * param size Size in bytes of curve points and parameters
 * param[out] result Result point
 * return Operation status.
 */
status_t CAU3_PKHA_ECT_PointAdd(CAU3_Type *base,
                                const cau3_pkha_ecc_point_t *A,
                                const cau3_pkha_ecc_point_t *B,
                                const uint8_t *N,
                                const uint8_t *R2modN,
                                const uint8_t *aCurveParam,
                                const uint8_t *dCurveParam,
                                size_t size,
                                cau3_pkha_ecc_point_t *result)
{
    cau3_pkha_mode_params_t params;
    uint32_t clearMask;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func   = kCAU3_PKHA_ArithEctAdd;
    params.r2modn = (R2modN != NULL) ? kCAU3_PKHA_InputR2 : kCAU3_PKHA_CalcR2;

    clearMask = (uint32_t)kCAU3_ClearMode;

    /* Clear internal register states. */
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeA;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeB;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeN;
    clearMask |= (uint32_t)kCAU3_ClearPkhaSizeE;

    base->CW  = clearMask;
    base->STA = (uint32_t)kCAU3_StatusDoneIsr;
    (void)cau3_pkha_clear_regabne(base, true, true, true, false);

    /* sizeN should be less than 64 bytes. */
    base->PKNSZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegN, 0, N, size);

    base->PKASZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 0, A->X, size);
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 1, A->Y, size);
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 3, aCurveParam, size);

    base->PKBSZ = size;
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 0, dCurveParam, size);
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 1, B->X, size);
    (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 2, B->Y, size);
    if (R2modN != NULL)
    {
        (void)cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 3, R2modN, size);
    }

    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the data from the result register into place. */
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 1, result->X, size);
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 2, result->Y, size);
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

/*!
 * brief   Perform a 3DES key parity check
 *
 * Performs a 3DES key parity check on three 8-byte keys.
 * The function is blocking.
 *
 * param base CAU3 peripheral base address
 * param   keySlot defines the key context to be used in the parity check
 *
 * return  status check from task completion
 */
status_t CAU3_TDES_CheckParity(CAU3_Type *base, cau3_key_slot_t keySlot)
{
    status_t completionStatus;
    cau3_task_done_t taskDone;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    taskDone = kCAU3_TaskDonePoll;

    /*  execute the cau3 "3des_check_parity" task  */
    base->CC_R[17] = (uint32_t)keySlot;           /*  keySlot  */
    base->CC_R30   = CAU3_DMEM_STK_BASE;          /*  initialize stack pointer (sp)  */
    base->CC_R31   = 0U;                          /*  set LR = 0 to signal a host task  */
    base->CC_PC    = CAU3_TASK_3DES_CHECK_PARITY; /*  call cau_3des_chk_parity()  */
    base->CC_CMD   = (uint32_t)taskDone;          /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * brief Load DES key into CAU3 key slot.
 *
 * Load the key context into the private DMEM.
 *
 * param   base CAU3 peripheral base address.
 * param   handle Handle used for the request.
 * param   key 0-mod-4 aligned pointer to 3DES key.
 * param   keySize 3DES key size in bytes. Shall equal 24.
 * return  status from set key operation
 */
status_t CAU3_TDES_SetKey(CAU3_Type *base, cau3_handle_t *handle, const uint8_t *key, size_t keySize)
{
    /* only work with aligned key[] */
    if (0U != (0x3U & (uintptr_t)key))
    {
        return kStatus_InvalidArgument;
    }

    /* keySize must be 24. */
    if (keySize != 24U)
    {
        return kStatus_InvalidArgument;
    }

    return cau3_load_key(base, key, keySize, (uint32_t)handle->keySlot, handle->taskDone);
}

/*!
 * brief   Perform a 3DES encryption
 *
 * Performs a 3DES "electronic code book" encryption on one 8-byte data block.
 * The source plaintext and destination ciphertext can overlap in system memory.
 * Supports both blocking and non-blocking task completion.
 *
 * param   base CAU3 peripheral base address.
 * param handle Handle used for this request.
 * param   plaintext is source uint8_t array of data bytes, any alignment
 * param   ciphertext is destination uint8_t array of data byte, any alignment
 *
 * return  status check from task completion
 */
status_t CAU3_TDES_Encrypt(CAU3_Type *base, cau3_handle_t *handle, const uint8_t *plaintext, uint8_t *ciphertext)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "3des_encrypt_ecb" task  */
    base->CC_R[16] = (uintptr_t)plaintext;       /*  pPlainText  */
    base->CC_R[17] = (uint32_t)handle->keySlot;  /*  keySlot  */
    base->CC_R[19] = (uintptr_t)ciphertext;      /*  pCipherText  */
    base->CC_R30   = CAU3_DMEM_STK_BASE;         /*  initialize stack pointer (sp)  */
    base->CC_R31   = 0U;                         /*  set LR = 0 to signal a host task  */
    base->CC_PC    = CAU3_TASK_3DES_ENCRYPT;     /*  call cau_3des_encrypt()  */
    base->CC_CMD   = (uint32_t)handle->taskDone; /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, handle->taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * brief   Perform a 3DES decryption
 *
 * Performs a 3DES "electronic code book" decryption on one 8-byte data block.
 * The source ciphertext and destination plaintext can overlap in sysMemory.
 * Supports both blocking and non-blocking task completion.
 *
 * param   base CAU3 peripheral base address.
 * param handle Handle used for this request.
 * param   ciphertext is destination uint8_t array of data byte, any alignment
 * param   plaintext is source uint8_t array of data bytes, any alignment
 * return  status check from task completion
 */
status_t CAU3_TDES_Decrypt(CAU3_Type *base, cau3_handle_t *handle, const uint8_t *ciphertext, uint8_t *plaintext)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "3des_decrypt_ecb" task  */
    base->CC_R[16] = (uintptr_t)ciphertext;      /*  pCipherText  */
    base->CC_R[17] = (uint32_t)handle->keySlot;  /*  keySlot  */
    base->CC_R[19] = (uintptr_t)plaintext;       /*  pPlainText  */
    base->CC_R30   = CAU3_DMEM_STK_BASE;         /*  initialize stack pointer (sp)  */
    base->CC_R31   = 0U;                         /*  set LR = 0 to signal a host task  */
    base->CC_PC    = CAU3_TASK_3DES_DECRYPT;     /*  call cau_3des_decrypt()  */
    base->CC_CMD   = (uint32_t)handle->taskDone; /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, handle->taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * brief Load 256-bit key into CAU3 key context (in key slot).
 *
 * Load the key context into the private DMEM for CHACHA20_POLY1305 AEAD.
 *
 * param   base CAU3 peripheral base address.
 * param   handle Handle used for the request.
 * param   key 0-mod-4 aligned pointer to CHACHA20_POLY1305 256-bit key.
 * param   keySize Size of the key in bytes. Shall be 32.
 * return  status from set key operation
 */
status_t CAU3_CHACHA20_POLY1305_SetKey(CAU3_Type *base, cau3_handle_t *handle, const uint8_t *key, size_t keySize)
{
    /* only work with aligned key[] */
    if (0U != (0x3U & (uintptr_t)key))
    {
        return kStatus_InvalidArgument;
    }

    /* keySize must be 32. */
    if (keySize != 32U)
    {
        return kStatus_InvalidArgument;
    }

    union
    {
        uint8_t b[32];
        uint32_t w[8];
    } tempKey;

    for (uint32_t i = 0; i < ARRAY_SIZE(tempKey.w); i++)
    {
        tempKey.w[i] = __REV(((const uint32_t *)(uintptr_t)key)[i]);
    }

    return cau3_load_key(base, tempKey.b, keySize, (uint32_t)handle->keySlot, handle->taskDone);
}

static status_t cau3_load_nonce(CAU3_Type *base, const uint8_t *nonce, cau3_key_slot_t keySlot)
{
    union
    {
        uint8_t b[16];
        uint32_t w[4];
    } tempIv;

    (void)memset(&tempIv, 0, sizeof(tempIv));

    /* set nonce to keySlot */
    (void)memcpy(tempIv.b, nonce, 12);
    /* swap bytes */
    tempIv.w[0] = __REV(tempIv.w[0]);
    tempIv.w[1] = __REV(tempIv.w[1]);
    tempIv.w[2] = __REV(tempIv.w[2]);

    return CAU3_LoadKeyInitVector(base, tempIv.b, keySlot, kCAU3_TaskDonePoll);
}

/*!
 * brief   Perform ChaCha-Poly encryption/authentication
 *
 * Perform ChaCha encryption over a message of "n" bytes, and authentication
 * over the encrypted data plus an additional authenticated data,
 * returning encrypted data + a message digest.
 *
 * param base CAU3 peripheral base address
 * param handle Handle used for this request. The keySlot member specifies key context with key and IV.
 * param plaintext The uint8_t source message to be encrypted, any alignment
 * param[out] ciphertext is a pointer to the output encrypted message, any aligment
 * param size The length of the plaintext and ciphertext in bytes
 * param aad A pointer to the additional authenticated data, any alignment
 * param aadLen Length of additional authenticated data in bytes
 * param nonce 0-mod-4 aligned pointer to CHACHA20_POLY1305 96-bit nonce.
 * param[out] tag A pointer to the 128-bit message digest output, any alignment
 *
 * return  status check from task completion
 */
status_t CAU3_CHACHA20_POLY1305_Encrypt(CAU3_Type *base,
                                        cau3_handle_t *handle,
                                        const uint8_t *plaintext,
                                        uint8_t *ciphertext,
                                        size_t size,
                                        const uint8_t *aad,
                                        size_t aadLen,
                                        const uint8_t *nonce,
                                        uint8_t *tag)
{
    status_t completionStatus;

    completionStatus = cau3_load_nonce(base, nonce, handle->keySlot);
    if (kStatus_Success != completionStatus)
    {
        return completionStatus;
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    base->CC_R[17] = (uint32_t)handle->keySlot;  /*  key/iv slot  */
    base->CC_R[18] = (uintptr_t)aad;             /*  AAD pointer  */
    base->CC_R[19] = aadLen;                     /*  AAD length (bytes)  */
    base->CC_R[20] = (uintptr_t)plaintext;       /*  Plaintext pointer  */
    base->CC_R[21] = size;                       /*  Plaintext length  */
    base->CC_R[22] = (uintptr_t)ciphertext;      /*  Ciphertext pointer  */
    base->CC_R[23] = (uintptr_t)tag;             /*  Tag pointer  */
    base->CC_R30   = CAU3_DMEM_STK_BASE;         /*  initialize stack pointer (sp)  */
    base->CC_R31   = 0U;                         /*  set LR = 0 to signal a host task  */
    base->CC_PC    = CAU3_TASK_CHA_POLY_ENCRYPT; /*  ChaChaPoly encrypt vector  */
    base->CC_CMD   = (uint32_t)handle->taskDone; /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, handle->taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * brief   Perform ChaCha-Poly decryption/authentication check
 *
 * Perform ChaCha decryption over a message of "n" bytes, and checks
 * authentication over the encrypted data plus an additional authenticated data,
 * returning decrypted data. IF the tag authentication fails, the task terminates with error and
 * the output is forced to zero.
 *
 * param base CAU3 peripheral base address
 * param handle Handle used for this request. The keySlot member specifies key context with key and IV.
 * param ciphertext The uint8_t source msg to be decrypted, any alignment
 * param[out] plaintext A pointer to the output decrypted message, any alignment
 * param size Length of the plaintext and ciphertext in bytes
 * param aad A pointer to the additional authenticated data, any alignment
 * param aadLen Length of additional authenticated data in bytes
 * param nonce 0-mod-4 aligned pointer to CHACHA20_POLY1305 96-bit nonce.
 * param tag A pointer to the 128-bit msg digest input to be checked, any alignment
 *
 * return  status check from task completion
 *
 */
status_t CAU3_CHACHA20_POLY1305_Decrypt(CAU3_Type *base,
                                        cau3_handle_t *handle,
                                        const uint8_t *ciphertext,
                                        uint8_t *plaintext,
                                        size_t size,
                                        const uint8_t *aad,
                                        size_t aadLen,
                                        const uint8_t *nonce,
                                        const uint8_t *tag)
{
    status_t completionStatus;

    completionStatus = cau3_load_nonce(base, nonce, handle->keySlot);
    if (kStatus_Success != completionStatus)
    {
        return completionStatus;
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    base->CC_R[17] = (uint32_t)handle->keySlot;  /*  key/iv slot  */
    base->CC_R[18] = (uintptr_t)aad;             /*  AAD pointer  */
    base->CC_R[19] = aadLen;                     /*  AAD length (bytes)  */
    base->CC_R[20] = (uintptr_t)ciphertext;      /*  Ciphertext pointer  */
    base->CC_R[21] = size;                       /*  Cyphertext length  */
    base->CC_R[22] = (uintptr_t)plaintext;       /*  Plaintext pointer  */
    base->CC_R[23] = (uintptr_t)tag;             /*  Tag pointer  */
    base->CC_R30   = CAU3_DMEM_STK_BASE;         /*  initialize stack pointer (sp)  */
    base->CC_R31   = 0U;                         /*  set LR = 0 to signal a host task  */
    base->CC_PC    = CAU3_TASK_CHA_POLY_DECRYPT; /*  ChaChaPoly decrypt vector  */
    base->CC_CMD   = (uint32_t)handle->taskDone; /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, handle->taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * brief   Perform an RFC3394 key blob unwrap
 *
 * Perform an RFC3394 unwrap of an AES encrypted key blob. The unwrapped
 * key blob is loaded into the specified key slot [1-3]. The initial
 * special hardware KEK contained in key slot 0 is typically used for the
 * unwrapping operation. The destination context number must be different than
 * the keySlot used for unwrapping.
 * Implements the algorithm at RFC 3394 to AES key unwrap. The
 * current implementation allows to unwrap up to 512 bits,
 * with the restriction of nblocks=2 or =4 or n=8(means it
 * unwraps only 128bits, 256bits or two 256 bits keys (512)). It
 * is allowed input key of 128 and 256bits only (passed using
 * the keyslot). The function also assumes the
 * ref CAU3_LoadSpecialKeyContext was called before.
 * It returns error and clear the destination context in case
 * parameters are not inside aceptable values.
 * In case n>4 && n!=8 it clears both destination contexts (the
 * dstContext and the adjacent/next context)
 * In case of n=8, the first unwraped key will be stored in the
 * dstContext slot, and the second key will be saved in the next
 * context (E.g: if dstContext=1, then first key goes to slot 1
 * and second key to slot 2. If dstContext=3 then first key goes
 * to slot 3 and second key goes to slot 1).
 * Examples of n usage.
 * E.g.: n = 2 means a unwraped key of 128 bits (2 * 64)
 * E.g.: n = 4 means a unwraped key of 256 bits (4 * 64)
 * E.g.: n = 8 means two unwraped keys of 256 bits (8 * 64)
 *
 * The function is blocking, it uses the polling task done signaling.
 *
 * param base CAU3 peripheral base address
 * param   keySlot is the key used to unwrap the key blob [0-3]
 * param   keyBlob 0-mod-4 aligned pointer is the RFC3394 wrapped key blob.
 * param   numberOfBlocks is the unwrapped keyBlob length as multiple of 64-bit blocks
 * param   dstContext is the destination key context for unwrapped blob [0-3]
 * retval  status check from task completion
 */
status_t CAU3_KeyBlobUnwrap(CAU3_Type *base,
                            cau3_key_slot_t keySlot,
                            const uint8_t *keyBlob,
                            uint32_t numberOfBlocks,
                            cau3_key_slot_t dstContext)
{
    status_t completionStatus;
    cau3_task_done_t taskDone;

    if (0U != (0x3U & (uintptr_t)keyBlob))
    {
        return kStatus_InvalidArgument;
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    taskDone = kCAU3_TaskDonePoll;

    /*  execute the cau3 "key blob unwrap" task  */
    base->CC_R[16] = (uintptr_t)keyBlob;        /*  pKeyBlob  */
    base->CC_R[17] = (uint32_t)keySlot;         /*  keySlot  */
    base->CC_R[18] = numberOfBlocks;            /*  numberOfBlocks  */
    base->CC_R[19] = (uint32_t)dstContext;      /*  destination key context  */
    base->CC_R30   = CAU3_DMEM_STK_BASE;        /*  initialize stack pointer (sp)  */
    base->CC_R31   = 0U;                        /*  set LR = 0 to signal a host task  */
    base->CC_PC    = CAU3_TASK_KEY_BLOB_UNWRAP; /*  call cau_key_blob_unwrap()  */
    base->CC_CMD   = (uint32_t)taskDone;        /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}
