/*!
* @file     Crypto_Aes256.c
*
* @version  1.0.0
*
* @brief    This file provides high level functions to encrypt and decrypt,
 *          Input message using AES in ECB/CBC/CTR/CMAC/MPC modes.
* @attention
*
* Project Name      : AUTOSAR MCAL
*
* Platform          : Arm
*
* Autosar Standard  : Classic Platform 4.4.0
*
* Compiled Version  : G32A10xx_MCAL_1_0_0_15-Nov-25
*
* Copyright (C) 2026 Geehy Semiconductor
*
* You may not use this file except in compliance with the
* GEEHY COPYRIGHT NOTICE (Geehy Semiconductor Software License Agreement).
*
* The program is only for reference, which is distributed in the hope
* that it will be usefull and instructional for customers to develop
* their software. Unless required by applicable law or agreed to in
* writing, the program is distributed on an "AS IS" BASIS, WITHOUT
* ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
* See the Geehy Semiconductor Software License Agreement for the governing permissions
* and limitations under the License.
*/

#ifdef __cplusplus
extern "C"{
#endif
    
#include "Crypto_Aes256.h"
#include "Crypto_AesReg.h"
#include "Compiler.h"
#include "Crypto_Cse.h"
#include "Mcu_RcmReg.h"
/** @addtogroup G32A10xx_StdPeriphDriver
  @{
*/

/** @defgroup CRYPTO_Driver
  * @brief CRYPTO driver modules
  @{
*/

/** @defgroup CRYPTO_Macros
  @{
*/
/*******************************************************************************
                    VENDOR ID & AUTOSAR VERSION INFO
*******************************************************************************/
/** Vendor ID (vendorId) of the dedicated implementation of this module according to the AUTOSAR vendor list. */
#define CRYPTO_AES256_VENDOR_ID                         0x00C1U
/** Major version number of AUTOSAR release on which the appropriate implementation is based on. */
#define CRYPTO_AES256_AR_RELEASE_MAJOR_VERSION          0x04U
/** Minor version number of AUTOSAR release on which the appropriate implementation is based on. */
#define CRYPTO_AES256_AR_RELEASE_MINOR_VERSION          0x04U
/** Revision version number of AUTOSAR release on which the appropriate implementation is based on. */
#define CRYPTO_AES256_AR_RELEASE_REVISION_VERSION       0x00U
/** Major version number of the vendor specific implementation of the module. The numbering is vendor specific. */
#define CRYPTO_AES256_SW_MAJOR_VERSION                  0x01U
/** Minor version number of the vendor specific implementation of the module. The numbering is vendor specific. */
#define CRYPTO_AES256_SW_MINOR_VERSION                  0x00U
/** Patch level version number of the vendor specific implementation of the module. The numbering is vendor specific. */
#define CRYPTO_AES256_SW_PATCH_VERSION                  0x00U
/**@} end of group CRYPTO_Macros */
/*******************************************************************************
                            LOCAL VARIABLE
*******************************************************************************/
/** @defgroup CRYPTO_Variable
  @{
*/
static CONSTP2VAR(volatile uint32, AUTOMATIC, CRYPTO_APPL_DATA) s_regAddrs[4] =
{
    (volatile uint32*)AES_DATAIN_0_3_REG_ADDR,
    (volatile uint32*)AES_DATAIN_4_7_REG_ADDR,
    (volatile uint32*)AES_DATAIN_8_B_REG_ADDR,
    (volatile uint32*)AES_DATAIN_C_F_REG_ADDR
};
/**@} end of group CRYPTO_Variable */
/*******************************************************************************
                            LOCAL FUNCTIONS
*******************************************************************************/
/** @defgroup CRYPTO_Fuctions
  @{
*/

static FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Aes256_WaitForCompute( VAR(uint32, AUTOMATIC) timeOut );

/*!
* @brief     Pack four consecutive bytes into one uint32 (little-endian word layout).
*
* @param     buf:   Byte buffer.
* @param     index: Start index of the first of four bytes in buf.
*
* @retval    32-bit word; buf[index] is the least significant byte.
*/
LOCAL_INLINE FUNC(uint32, CRYPTO_CODE) Crypto_Aes256_PackBytes(P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) buf,VAR(uint32, AUTOMATIC) index)
{
    return (((uint32)buf[index]) |
           (((uint32)buf[index + 1u]) << 8u) |
           (((uint32)buf[index + 2u]) << 16u) |
           (((uint32)buf[index + 3u]) << 24u));
}

/*!
* @brief Copy one 16-byte AES DATAOUT block to memory (CPU register read).
*/
LOCAL_INLINE FUNC(void, CRYPTO_CODE) Crypto_Aes256_CopyDataOutBlock(VAR(uint32, AUTOMATIC) outputAddr, VAR(uint32, AUTOMATIC) byteOffset)
{
    VAR(uint32, AUTOMATIC) dataIdx;
    for (dataIdx = 0u; dataIdx < 4u; dataIdx++)
    {
        *((uint32*)(outputAddr + byteOffset + (dataIdx * 4u))) = REG_READ32(AES_DATAOUT_REG_ADDR(dataIdx));
    }
}

/*!
* @brief     Load 16-byte IV from byte buffer into four uint32 words for AES IV registers.
*
* @param     srcIv: Source IV (16 bytes).
* @param     dstIv: Output IV word array (4 elements) for Crypto_Aes256_ConfigIV.
*
* @retval    None
*/
LOCAL_INLINE FUNC(void, CRYPTO_CODE) Crypto_Aes256_InputIv(P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) srcIv, P2VAR(uint32, AUTOMATIC, CRYPTO_APPL_DATA) dstIv)
{
    VAR(uint32, AUTOMATIC) i = 0u;
    VAR(uint32, AUTOMATIC) j = 0u;
    VAR(uint32, AUTOMATIC) data = 0u;
    for ( j = 0u; j < 16u; j += 4u)
    {
        data = 0;
        data |= (uint32)srcIv[j];
        data |= (uint32)srcIv[j + 1u] << 8u;
        data |= (uint32)srcIv[j + 2u] << 16u;
        data |= (uint32)srcIv[j + 3u] << 24u;
        dstIv[i] = data;
        i++;
    }
}

/*!
* @brief     Load AES key material into Crypto_Aes256_KeyConfigType (word array for KEY registers).
*
* @param     key:       Key bytes (length implied by keysize).
* @param     keysize:   AES key size (128 / 192 / 256 bit).
* @param     keyConfig: Output structure holding key words aes_key[0..7].
*
* @retval    None
*/
LOCAL_INLINE FUNC(void, CRYPTO_CODE) Crypto_Aes256_InputKey(P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) key, VAR(Crypto_Aes256_KeySizeType, AUTOMATIC) keysize, 
                            P2VAR( Crypto_Aes256_KeyConfigType, AUTOMATIC, CRYPTO_APPL_DATA ) keyConfig)
{
    VAR(uint8, AUTOMATIC) totalBytes = 0;
    VAR(uint8, AUTOMATIC) i = 0;
    VAR(uint8, AUTOMATIC) j = 0;
    switch (keysize)
    {
        case AES256_KEYSIZE_128B: 
            totalBytes = 16; 
            break;
        case AES256_KEYSIZE_192B: 
            totalBytes = 24; 
            break;
        case AES256_KEYSIZE_256B: 
            totalBytes = 32; 
            break;
        default: 
            /* Nothing */
            break; 
    }

    for (j = 0u; j < totalBytes; j += 4u)
    {
        keyConfig->aes_key[i] = Crypto_Aes256_PackBytes(key, j);
        i++;
    }
}

/*!
* @brief     Process all full 16-byte blocks except the last (used by CTR sync path).
*
* @param     blockNum:   Total number of 16-byte blocks for the message.
* @param     input:      Input plaintext byte buffer.
* @param     outputAddr: Start address of output buffer (uint32 words written per block).
*
* @retval    CSE_ERC_OK if all blocks complete; CSE_ERC_ERROR on timeout or failure.
*/
LOCAL_INLINE FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Aes256_FBlockProcess(VAR(uint32, AUTOMATIC) blockNum, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) input,
                VAR(uint32, AUTOMATIC) outputAddr)
{
    VAR(uint32, AUTOMATIC) blkCnt = 0;
    VAR(uint32, AUTOMATIC) offset = 0;
    VAR(uint32, AUTOMATIC) j = 0;
    VAR(uint32, AUTOMATIC) data = 0;
    Crypto_Cse_ErrorCodeType status = CSE_ERC_OK;
    
    /* Block-by-block Data Writing */
    for (blkCnt = 0u; blkCnt < (blockNum - 1u); blkCnt++) 
    {
        offset = blkCnt * 16u;
        for (j = 0u; j < 4u; j++) 
        {
            data = ((uint32)input[offset + (j * 4u)]       |
                   ((uint32)input[offset + (j * 4u) + 1u] << 8u)  |
                   ((uint32)input[offset + (j * 4u) + 2u] << 16u) |
                   ((uint32)input[offset + (j * 4u) + 3u] << 24u));
            REG_WRITE32(AES_DATAIN_0_3_REG_ADDR + (j * sizeof(uint32)), data);
        }

        REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
        
        status = Crypto_Aes256_WaitForCompute(CRYPTO_TIMEOUT_VALUE);
        if(CSE_ERC_OK == status)
        {
            Crypto_Aes256_CopyDataOutBlock(outputAddr, offset);
        }
        else
        {
            status = CSE_ERC_ERROR;
            break;
        }
    }
    return status;
}

/*!
* @brief     Zero-initialize AES key configuration structure.
*
* @param     keyConfig: Pointer to Crypto_Aes256_KeyConfigType to clear.
*
* @retval    None
*/
static FUNC(void, CRYPTO_CODE) Crypto_Aes256_ConfigKeyStructInit( P2VAR(Crypto_Aes256_KeyConfigType, AUTOMATIC, CRYPTO_APPL_DATA) keyConfig )
{
    VAR(uint32, AUTOMATIC) i = 0;
    for ( i = 0u; i < 8u; i++)
    {   
        /* Key Initialisation */
        keyConfig->aes_key[i] = 0;
    }
}

/*!
* @brief     Configure AES Keys
*
* @param     keyConfig: pointer to a Crypto_Aes256_KeyConfigType structure.
*
* @retval    None
*/
static FUNC(void, CRYPTO_CODE) Crypto_Aes256_ConfigKey(P2CONST(Crypto_Aes256_KeyConfigType, AUTOMATIC, CRYPTO_APPL_CONST) keyConfig)
{
    VAR(uint32, AUTOMATIC) i = 0;
    for ( i = 0u; i < 8u; i++)
    {   
        /* Key config */
        REG_WRITE32(AES_KEY_REG_ADDR(i), keyConfig->aes_key[i]);
    }
}

/*!
* @brief     Write IV words to AES IV registers.
*
* @param     IVConfig: Pointer to Crypto_Aes256_IvConfigType (IV[0..3]).
*
* @retval    None
*/
static FUNC(void, CRYPTO_CODE) Crypto_Aes256_ConfigIV(P2CONST(Crypto_Aes256_IvConfigType, AUTOMATIC, CRYPTO_APPL_CONST) IVConfig)
{
    VAR(uint32, AUTOMATIC) i = 0u;
    for ( i = 0u; i < 4u; i++ )
    {   
        /* IV config */
        REG_WRITE32(AES_IV_REG_ADDR(i), IVConfig->IV[i]);
    }
}

/*!
* @brief     Write up to 16 message bytes into AES DATAIN for CMAC (per-word packing matches StdPeriph Aes256_WriteBlockCmac).
*
* @param     byteOffset: Byte offset in input where this chunk starts.
* @param     byteLength: Number of bytes to take (clamped to 16); remaining block bytes zero-padded.
* @param     input:      Source message buffer.
*
* @retval    None
*/
static FUNC(void, CRYPTO_CODE) Crypto_Aes256_WriteCmacFifoFromBytes(
    VAR(uint32, AUTOMATIC) byteOffset,
    VAR(uint32, AUTOMATIC) byteLength,
    P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) input)
{
    VAR(uint8, AUTOMATIC) block[16] = {0};
    VAR(uint32, AUTOMATIC) copySize;
    VAR(uint32, AUTOMATIC) i = 0u;
    VAR(uint32, AUTOMATIC) wi = 0u;
    VAR(uint32, AUTOMATIC) data = 0u;

    copySize = (byteLength > 16U) ? 16U : byteLength;
    for (i = 0u; i < copySize; i++)
    {
        block[i] = input[byteOffset + i];
    }
    for (wi = 0u; wi < 4u; wi++)
    {
        data = ((uint32)block[(wi << 2U) + 0U] << 24) |
               ((uint32)block[(wi << 2U) + 1U] << 16) |
               ((uint32)block[(wi << 2U) + 2U] << 8) |
               ((uint32)block[(wi << 2U) + 3U]);
        REG_WRITE32(AES_DATAIN_0_3_REG_ADDR + (wi * sizeof(uint32)), data);
    }
}

/*!
* @brief     Clear AES DATAIN registers (same role as StdPeriph Aes256_ClearInput before CMAC subkey step).
*
* @param     None
*
* @retval    None
*/
static FUNC(void, CRYPTO_CODE) Crypto_Aes256_ClearDataInFifo(void)
{
    VAR(uint32, AUTOMATIC) k = 0u;
    for (k = 0u; k < 4u; k++)
    {
        REG_WRITE32(AES_DATAIN_0_3_REG_ADDR + (k * sizeof(uint32)), 0u);
    }
}

/*!
* @brief     Write last partial 16-byte chunk to DATAIN for MPC/CTR paths (little-endian word packing per sub-word).
*
* @param     remainLen: Remaining byte length for this chunk (typically 1..16).
* @param     usedBlk:   Index of the starting 16-byte block in input (0-based).
* @param     input:     Source byte buffer.
*
* @retval    None
*/
static FUNC(void, CRYPTO_CODE) Crypto_Aes256_LastBlkSet(VAR(uint32, AUTOMATIC) remainLen,
                                  VAR(uint32, AUTOMATIC) usedBlk,P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST)input)
{
    VAR(uint32, AUTOMATIC) offset = (uint32)usedBlk * 16u;
    VAR(uint32, AUTOMATIC) data = 0;
    VAR(uint32, AUTOMATIC) bytesToRead = 0;
    VAR(uint32, AUTOMATIC) i = 0;
    VAR(uint32, AUTOMATIC) regIndex = 0;
    /* initial DATAIN_0-DATAIN_F */
    for ( i = 0u; i < 4u; i++) 
    {
        REG_WRITE32(AES_DATAIN_0_3_REG_ADDR + (i * sizeof(uint32)), 0u);
    }
    
    for ( regIndex = 0u; regIndex < 4u; regIndex++) 
    {
        if (remainLen > (regIndex * 4u)) 
        {
            data = 0;
            bytesToRead = remainLen - (regIndex * 4u);
            if (bytesToRead > 4u)
            {
                bytesToRead = 4u;
            }
            else
            {
                /* nothing */
            }
            for (i = 0u; i < bytesToRead; i++) 
            {
                data |= ((uint32)input[offset + (regIndex * 4u) + i]) << (8u * i);
            }

            switch (regIndex) 
            {
                case 0: REG_WRITE32(AES_DATAIN_0_3_REG_ADDR,data); break;
                case 1: REG_WRITE32(AES_DATAIN_4_7_REG_ADDR,data); break;
                case 2: REG_WRITE32(AES_DATAIN_8_B_REG_ADDR,data); break;
                case 3: REG_WRITE32(AES_DATAIN_C_F_REG_ADDR,data); break;
                default: 
                    /* nothing */
                    break;
            }
        }
        else
        {
            /* nothing */
        }
    }
}

/*!
* @brief     CMAC finalization: ECB+SubKey on valid length, then CMAC mode with last block data.
*
* @param     dataLengthBits: Total message length in bits.
* @param     blkCnt:         Number of 16-byte blocks (ceiling of length/16).
* @param     remain:         Bytes in the last chunk (1..16).
* @param     usedBlk:        Index of first byte of last chunk (usually blkCnt-1 in block units).
* @param     input:          Full message buffer.
*
* @retval    CSE_ERC_OK on success; CSE_ERC_ERROR on timeout or failure.
*/
static FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Aes256_CmacProcessLastBlock(VAR(uint32, AUTOMATIC) dataLengthBits, VAR(uint32, AUTOMATIC) blkCnt, 
    VAR(uint32, AUTOMATIC) remain, VAR(uint32, AUTOMATIC) usedBlk, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) input)
{
    Crypto_Cse_ErrorCodeType flag = CSE_ERC_OK;

    REG_RMW32(AES_CTRL_REG_ADDR, AES_CTRL_MODE_MASK, (uint32)AES256_ALGOMODE_AES_ECB << AES_CTRL_MODE_SHIFT);
    REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_SUBKG_MASK);
    REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_VALID_LENTH_MASK,(uint32)(dataLengthBits - ((blkCnt - 1u) * 128u)) << AES_CTRL_VALID_LENTH_SHIFT);
    REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
     
    flag = Crypto_Aes256_WaitForCompute(CRYPTO_TIMEOUT_VALUE);

    if(CSE_ERC_OK == flag)
    {
        Crypto_Aes256_WriteCmacFifoFromBytes(usedBlk * 16u, remain, input);
        REG_RMW32(AES_CTRL_REG_ADDR, AES_CTRL_MODE_MASK, (uint32)AES256_ALGOMODE_AES_CMAC << AES_CTRL_MODE_SHIFT);
        REG_BIT_CLEAR32(AES_CTRL_REG_ADDR,AES_CTRL_SUBKG_MASK);
        REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_VALID_LENTH_MASK,(uint32)(dataLengthBits - ((blkCnt - 1u) * 128u)) << AES_CTRL_VALID_LENTH_SHIFT);
        REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
        flag = Crypto_Aes256_WaitForCompute(CRYPTO_TIMEOUT_VALUE);
    }
    else
    {
        /* nothing */
    }
    return flag;
}

/*******************************************************************************
                            GLOBAL FUNCTIONS
*******************************************************************************/
/*!
* @brief     Reset AES
*
* @param     None
*
* @retval    None
*/
static FUNC(void, CRYPTO_CODE) Crypto_Aes256_Reset(void)
{
    REG_BIT_SET32(RCM_AHBRST_REG_ADDR,RCM_AHBRST_AES256RST_MASK);
    REG_BIT_CLEAR32(RCM_AHBRST_REG_ADDR,RCM_AHBRST_AES256RST_MASK);
}

/*!
* @brief     Configure AES
*
* @param     crypConfig: pointer to a Crypto_Aes256_ConfigType structure
*
* @retval    None
*/
FUNC(void, CRYPTO_CODE) Crypto_Aes256_Config(P2CONST(Crypto_Aes256_ConfigType, AUTOMATIC, CRYPTO_APPL_CONST) crypConfig)
{
    REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_OPCODE_MASK,(uint32)crypConfig->algoOpcode << AES_CTRL_OPCODE_SHIFT);
    REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_MODE_MASK,(uint32)crypConfig->algoMode << AES_CTRL_MODE_SHIFT);
    REG_BIT_CLEAR32(AES_CTRL_REG_ADDR,AES_CTRL_BIG_ENDIAN_MASK);
     
    /* select Key size */
    REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_KEY_LEN_MASK,(uint32)(crypConfig->keySize) << AES_CTRL_KEY_LEN_SHIFT);
}

/*!
* @brief     Assert AES START bit to begin the configured operation.
*
* @param     None
*
* @retval    None
*/
FUNC(void, CRYPTO_CODE) Crypto_Aes256_Start(void)
{
    REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
}

/*!
* @brief     Enable or disable AES data-complete interrupt.
*
* @param     Enstatus: TRUE to enable DATA_INT_EN; FALSE to disable.
*
* @retval    None
*/
FUNC(void, CRYPTO_CODE) Crypto_Aes256_EnableInterrupt(VAR(boolean, AUTOMATIC) Enstatus)
{
    if(TRUE == Enstatus)
    {
        REG_BIT_SET32(AES_CTRL_REG_ADDR, AES_CTRL_DATA_INT_EN_MASK);
    }
    else
    {
        REG_BIT_CLEAR32(AES_CTRL_REG_ADDR, AES_CTRL_DATA_INT_EN_MASK);
    }
}

/*!
* @brief     Enable or disable AES key-expand complete interrupt.
*
* @param     Enstatus: TRUE to enable KEY_INT_EN; FALSE to disable.
*
* @retval    None
*/
FUNC(void, CRYPTO_CODE) Crypto_Aes256_EnableKeyExpandInterrupt(VAR(boolean, AUTOMATIC) Enstatus)
{
    if(TRUE == Enstatus)
    {
        REG_BIT_SET32(AES_CTRL_REG_ADDR, AES_CTRL_KEY_INT_EN_MASK );
    }
    else
    {
        REG_BIT_CLEAR32(AES_CTRL_REG_ADDR, AES_CTRL_KEY_INT_EN_MASK );
    }
}

/*!
* @brief     Read key-expand interrupt flag from AES status register.
*
* @param     None
*
* @retval    TRUE if KEY_INT flag set; FALSE otherwise.
*/
FUNC(uint8, CRYPTO_CODE) Crypto_Aes256_ExpandInterruptStatus(void)
{
    VAR(uint8, AUTOMATIC) status = FALSE;
    if((REG_READ32(AES_STS_REG_ADDR) & AES_STS_KEY_INT_FLG_MASK) != 0u)
    {
        status = TRUE;
    }
    else
    {
        /* nothing */
    }
    return status;
}

/*!
* @brief     Set AES OPCODE field in CTRL (encrypt / decrypt / key expand).
*
* @param     modeCfg: Opcode value (Crypto_Aes256_OpcodeType).
*
* @retval    None
*/
FUNC(void, CRYPTO_CODE) Crypto_Aes256_modeSet(VAR(Crypto_Aes256_OpcodeType, AUTOMATIC) modeCfg)
{
    REG_RMW32(AES_CTRL_REG_ADDR, AES_CTRL_OPCODE_MASK, ((uint32)modeCfg << (uint32)AES_CTRL_OPCODE_SHIFT));
}


/*!
 * @brief     Clear AES status interrupt flags (key-expand and data-complete).
 *
 * @param     None
 *
 * @retval    None
 */
FUNC(void, CRYPTO_CODE) Crypto_Aes256_ClearStatus(void)
{
    REG_BIT_CLEAR32(AES_STS_REG_ADDR,(AES_STS_KEY_INT_FLG_MASK | AES_STS_DATA_INT_FLG_MASK));
}

/*!
* @brief     AES-ECB encrypt or decrypt (sync loads full message; async uses first block path).
*
* @param     mode:      AES direction / opcode (e.g. encrypt, decrypt, key expand for decrypt setup).
* @param     key:       Key material bytes.
* @param     keysize:   Key length (128 / 192 / 256 bit).
* @param     input:     Input buffer (length must be multiple of 16 for sync path).
* @param     length:    Input length in bytes.
* @param     output:    Output buffer (at least length bytes for sync).
* @param     syncmode:  CRYPTO_PROCESSING_SYNC or CRYPTO_PROCESSING_ASYNC.
*
* @retval    CSE_ERC_OK or CSE_ERC_ERROR
*/
FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Aes256_GenerateEcb( VAR(Crypto_Aes256_OpcodeType, AUTOMATIC) mode, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) key, 
                       VAR(Crypto_Aes256_KeySizeType, AUTOMATIC) keysize, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) input,
                       VAR(uint32, AUTOMATIC) length, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) output,VAR(Crypto_ProcessingType, AUTOMATIC) syncmode)
{
    VAR(Crypto_Aes256_ConfigType, AUTOMATIC) aesCrypConfig = {AES256_ALGODIR_ENCRYPT,AES256_ALGOMODE_AES_ECB,AES256_KEYSIZE_128B}; 
    VAR(Crypto_Aes256_KeyConfigType, AUTOMATIC) aesKeyConfig = {0};  
    VAR(uint32, AUTOMATIC) data[4] = {0};
    VAR(uint32, AUTOMATIC) outputAddr = (uint32)output;
    VAR(uint32, AUTOMATIC) i = 0;
    VAR(uint32, AUTOMATIC) dataIdx = 0;
    VAR(Crypto_Cse_ErrorCodeType, AUTOMATIC) status = CSE_ERC_OK;
    VAR(uint32, AUTOMATIC) blkNum = (length + 15u) / 16u;
    Crypto_Aes256_ConfigKeyStructInit(&aesKeyConfig);
    aesCrypConfig.keySize = keysize;
    Crypto_Aes256_InputKey(key, keysize, &aesKeyConfig);
    aesCrypConfig.algoOpcode  = mode;  
    aesCrypConfig.algoMode = AES256_ALGOMODE_AES_ECB; 
 
    REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_OPCODE_MASK,(uint32)aesCrypConfig.algoOpcode << AES_CTRL_OPCODE_SHIFT);
    REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_MODE_MASK,(uint32)aesCrypConfig.algoMode << AES_CTRL_MODE_SHIFT);
    REG_BIT_CLEAR32(AES_CTRL_REG_ADDR,AES_CTRL_BIG_ENDIAN_MASK);
    REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_KEY_LEN_MASK,(uint32)aesCrypConfig.keySize << AES_CTRL_KEY_LEN_SHIFT);
     
    Crypto_Aes256_ConfigKey(&aesKeyConfig);
    if(CRYPTO_PROCESSING_SYNC == syncmode)
    {
        if(mode == AES256_ALGODIR_DECRYPT)
        {      
            REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_OPCODE_MASK,(uint32)AES256_ALGODIR_KEYEXPAND << AES_CTRL_OPCODE_SHIFT);
            REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
             
            status = Crypto_Aes256_WaitForCompute(CRYPTO_TIMEOUT_VALUE);
        } 
        else
        {
            /* nothing */
        }
        if(CSE_ERC_OK == status)
        {
            REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_OPCODE_MASK,(uint32)aesCrypConfig.algoOpcode << AES_CTRL_OPCODE_SHIFT);
              
            for(i = 0u; i < length; i += 16u)
            {
                for(dataIdx = 0u; dataIdx < 4u; dataIdx++)
                {
                    data[dataIdx] = Crypto_Aes256_PackBytes(input, i + (dataIdx * 4u));
                }
                
                for(dataIdx = 0u; dataIdx < 4u; dataIdx++)
                {
                    REG_WRITE32(s_regAddrs[dataIdx], data[dataIdx]);
                }

                REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);

                status = Crypto_Aes256_WaitForCompute(CRYPTO_TIMEOUT_VALUE);
                
                if(CSE_ERC_OK == status)
                {
                    Crypto_Aes256_CopyDataOutBlock(outputAddr, i);
                }
                else
                {
                    break;
                }
            }
        }
        else
        {
            /* nothing */
        }
    }
    else
    {
        if(mode == AES256_ALGODIR_DECRYPT) 
        {      
            REG_RMW32(AES_CTRL_REG_ADDR, AES_CTRL_OPCODE_MASK, (uint32)AES256_ALGODIR_KEYEXPAND << AES_CTRL_OPCODE_SHIFT);
            Crypto_Aes256_EnableKeyExpandInterrupt(TRUE);
            REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK); 
        }
        else
        {
            Crypto_Aes256_BlockProcessAsync(blkNum, length, input);
        }
    }
   
    return status;
}

/*!
* @brief     AES-CBC encrypt or decrypt.
*
* @param     mode:      AES direction / opcode.
* @param     key:       Key material bytes.
* @param     keysize:   Key length (128 / 192 / 256 bit).
* @param     IV:        Initialization vector (16 bytes).
* @param     input:     Input buffer (length multiple of 16 for sync).
* @param     length:    Input length in bytes.
* @param     output:    Output buffer.
* @param     syncmode:  CRYPTO_PROCESSING_SYNC or CRYPTO_PROCESSING_ASYNC.
*
* @retval    CSE_ERC_OK or CSE_ERC_ERROR
*/
FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Aes256_GenerateCbc( VAR(Crypto_Aes256_OpcodeType, AUTOMATIC) mode, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) key,
                       VAR(Crypto_Aes256_KeySizeType, AUTOMATIC) keysize, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) IV, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) input,
                       VAR(uint32, AUTOMATIC) length, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) output,VAR(Crypto_ProcessingType, AUTOMATIC) syncmode)
{
    VAR(Crypto_Aes256_ConfigType, AUTOMATIC) aesCrypConfig = {AES256_ALGODIR_ENCRYPT,AES256_ALGOMODE_AES_ECB,AES256_KEYSIZE_128B}; 
    VAR(Crypto_Aes256_KeyConfigType, AUTOMATIC) aesKeyConfig = {0};  
    VAR(Crypto_Aes256_IvConfigType, AUTOMATIC)  aesIvConfig = {0}; 
    VAR(uint32, AUTOMATIC) inputWords[4];
    VAR(uint32, AUTOMATIC) outputAddr = (uint32)output;
    VAR(uint32, AUTOMATIC) i = 0;
    VAR(uint32, AUTOMATIC) dataIdx = 0;
    VAR(Crypto_Cse_ErrorCodeType, AUTOMATIC) status = CSE_ERC_OK;
    VAR(uint32, AUTOMATIC) blkNum = (length + 15u) / 16u;
    Crypto_Aes256_ConfigKeyStructInit(&aesKeyConfig);
    aesCrypConfig.keySize = keysize;
    Crypto_Aes256_InputKey(key, keysize, &aesKeyConfig);
    Crypto_Aes256_InputIv(IV,aesIvConfig.IV);

    aesCrypConfig.algoOpcode  = mode;  
    aesCrypConfig.algoMode = AES256_ALGOMODE_AES_CBC; 

    REG_RMW32(AES_CTRL_REG_ADDR, AES_CTRL_OPCODE_MASK, (uint32)aesCrypConfig.algoOpcode << AES_CTRL_OPCODE_SHIFT);
    REG_RMW32(AES_CTRL_REG_ADDR, AES_CTRL_MODE_MASK, (uint32)aesCrypConfig.algoMode << AES_CTRL_MODE_SHIFT);
    REG_BIT_CLEAR32(AES_CTRL_REG_ADDR,AES_CTRL_BIG_ENDIAN_MASK);
    REG_RMW32(AES_CTRL_REG_ADDR, AES_CTRL_KEY_LEN_MASK, (uint32)aesCrypConfig.keySize << AES_CTRL_KEY_LEN_SHIFT);
    
    Crypto_Aes256_ConfigKey(&aesKeyConfig); 
    Crypto_Aes256_ConfigIV(&aesIvConfig);
    if(CRYPTO_PROCESSING_SYNC == syncmode)
    {
        if(mode == AES256_ALGODIR_DECRYPT)
        {      
            REG_RMW32(AES_CTRL_REG_ADDR, AES_CTRL_OPCODE_MASK, (uint32)AES256_ALGODIR_KEYEXPAND << AES_CTRL_OPCODE_SHIFT);
            REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK); 
            
            status = Crypto_Aes256_WaitForCompute(CRYPTO_TIMEOUT_VALUE);
        }
        else
        {
            /* nothing */
        }
        if(CSE_ERC_OK == status)
        {
            REG_RMW32(AES_CTRL_REG_ADDR, AES_CTRL_OPCODE_MASK, (uint32)aesCrypConfig.algoOpcode << AES_CTRL_OPCODE_SHIFT);

            for(i = 0u; i < length; i += 16u)
            {
                for(dataIdx = 0u; dataIdx < 4u; dataIdx++)
                {
                    inputWords[dataIdx] = Crypto_Aes256_PackBytes(input, i + (dataIdx * 4u));
                }
                
                for(dataIdx = 0u; dataIdx < 4u; dataIdx++)
                {
                    REG_WRITE32(s_regAddrs[dataIdx], inputWords[dataIdx]);
                }

                REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);

                status = Crypto_Aes256_WaitForCompute(CRYPTO_TIMEOUT_VALUE);
                
                if(CSE_ERC_OK == status)
                {
                    Crypto_Aes256_CopyDataOutBlock(outputAddr, i);
                }
                else
                {
                    break;
                }
            }
        }
        else
        {
            /* nothing */
        }
    }
    else
    {
        if(mode == AES256_ALGODIR_DECRYPT) 
        {      
            REG_RMW32(AES_CTRL_REG_ADDR, AES_CTRL_OPCODE_MASK, (uint32)AES256_ALGODIR_KEYEXPAND << AES_CTRL_OPCODE_SHIFT);
            Crypto_Aes256_EnableKeyExpandInterrupt(TRUE);
            REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK); 
        }
        else
        {
            Crypto_Aes256_BlockProcessAsync(blkNum, length, input);
        }
    }

    return status;
}

/*!
* @brief     Start async AES data path: load first input block (or last-block helper) and assert START.
*
* @param     blockNum: Number of 16-byte blocks (ceiling of dataLen/16).
* @param     dataLen:  Total message length in bytes.
* @param     input:      Input byte buffer.
*
* @retval    None
*/
FUNC(void, CRYPTO_CODE) Crypto_Aes256_BlockProcessAsync(VAR(uint32, AUTOMATIC) blockNum, VAR(uint32, AUTOMATIC) dataLen,
                    P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) input)
{
    VAR(uint32, AUTOMATIC) j = 0;
    VAR(uint32, AUTOMATIC) data = 0;
    
    /* Block-by-block Data Writing */
    if(blockNum <= 1u)
    {
        Crypto_Aes256_LastBlkSet(dataLen, 0u, input);
    }
    else
    {
        for (j = 0u; j < 4u; j++) 
        {
            data =  (uint32)input[j * 4u]       |
                   ((uint32)input[(j * 4u) + 1u] << 8u)  |
                   ((uint32)input[(j * 4u) + 2u] << 16u) |
                   ((uint32)input[(j * 4u) + 3u] << 24u);
            REG_WRITE32(AES_DATAIN_0_3_REG_ADDR + (j * sizeof(uint32)), data);
        }
    }
    Crypto_Aes256_EnableInterrupt(TRUE);
    REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
}

/*!
* @brief     Continue async ECB/CBC/MPC: feed next 16-byte block or last partial block from ISR context.
*
* @param     curBlkCnt: Current block index being processed (driver state).
* @param     dataLen:   Total message length in bytes.
* @param     blockNum:  Total number of 16-byte blocks.
* @param     input:     Input byte buffer.
*
* @retval    CSE_ERC_OK (reserved for future error propagation).
*/
FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Aes256_BlockProcessContinueAsync(VAR(uint32, AUTOMATIC) curBlkCnt,VAR(uint32, AUTOMATIC) dataLen,
      VAR(uint32, AUTOMATIC) blockNum, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) input)
{
    VAR(uint32, AUTOMATIC) offset = 0;
    VAR(uint32, AUTOMATIC) j = 0;
    VAR(uint32, AUTOMATIC) data = 0;
    VAR(uint32, AUTOMATIC) remainLen = 0;
    Crypto_Cse_ErrorCodeType status = CSE_ERC_OK;

    /* Block-by-block Data Writing */
    if(curBlkCnt < blockNum)
    {
        offset = curBlkCnt * 16u;
        for (j = 0u; j < 4u; j++) 
        {
            data =  (uint32)input[offset + (j * 4u)]       |
                   ((uint32)input[offset + (j * 4u) + 1u] << 8u)  |
                   ((uint32)input[offset + (j * 4u) + 2u] << 16u) |
                   ((uint32)input[offset + (j * 4u) + 3u] << 24u);
            REG_WRITE32(AES_DATAIN_0_3_REG_ADDR + (j * sizeof(uint32)), data);
        }
        Crypto_Aes256_EnableInterrupt(TRUE);
        REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
    }
    else if(curBlkCnt == blockNum)
    {
        /* Last Block process */
        remainLen = dataLen - ((blockNum - 1u) * 16u); 
        if(0u != remainLen)
        {
            Crypto_Aes256_LastBlkSet(remainLen, blockNum - 1u, input);
            Crypto_Aes256_EnableInterrupt(TRUE);
            REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
        }
        else
        {
            /* nothing */
        }
    }
    else
    {
        Crypto_Aes256_EnableInterrupt(FALSE);
        Crypto_Aes256_ClearStatus();
    }
    return status;
}

/*!
* @brief     Start async CMAC: first ECB+SubKey step or first full 16-byte block.
*
* @param     blockNum: Number of 16-byte blocks.
* @param     dataLen:  Message length in bytes.
* @param     input:    Message bytes.
*
* @retval    None
*/
static FUNC(void, CRYPTO_CODE) Crypto_Aes256_BlockProcessMacAsync(VAR(uint32, AUTOMATIC) blockNum, VAR(uint32, AUTOMATIC) dataLen,
                    P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) input)
{
    VAR(uint32, AUTOMATIC) dataLengthBits = dataLen << 3;
    
    /* Block-by-block Data Writing */
    if(blockNum <= 1u)
    {
        REG_RMW32(AES_CTRL_REG_ADDR, AES_CTRL_MODE_MASK, (uint32)AES256_ALGOMODE_AES_ECB << AES_CTRL_MODE_SHIFT);
        REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_SUBKG_MASK);
        REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_VALID_LENTH_MASK,(uint32)(dataLengthBits - ((blockNum - 1u) * 128u)) << AES_CTRL_VALID_LENTH_SHIFT);
        REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
    }
    else
    {
        Crypto_Aes256_WriteCmacFifoFromBytes(0u, 16u, input);
    }
    Crypto_Aes256_EnableInterrupt(TRUE);
    REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
}

/*!
* @brief     Continue async CMAC: middle blocks, subkey phase, or final CMAC step.
*
* @param     curBlkCnt: Current block index (driver state).
* @param     dataLen:   Message length in bytes.
* @param     blockNum:  Number of 16-byte blocks.
* @param     input:     Message bytes.
*
* @retval    CSE_ERC_OK (reserved for future error propagation).
*/
FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Aes256_BlockProcessMacContinueAsync(VAR(uint32, AUTOMATIC) curBlkCnt,VAR(uint32, AUTOMATIC) dataLen,
      VAR(uint32, AUTOMATIC) blockNum, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) input)
{
    VAR(uint32, AUTOMATIC) offset = 0;
    VAR(uint32, AUTOMATIC) remainLen = 0;
    Crypto_Cse_ErrorCodeType status = CSE_ERC_OK;

    /* Block-by-block Data Writing */
    if((curBlkCnt + 1u) == blockNum)
    {
        REG_RMW32(AES_CTRL_REG_ADDR, AES_CTRL_MODE_MASK, (uint32)AES256_ALGOMODE_AES_ECB << AES_CTRL_MODE_SHIFT);
        REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_SUBKG_MASK);
        REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_VALID_LENTH_MASK,(uint32)((dataLen << 3u) - ((blockNum - 1u) * 128u)) << AES_CTRL_VALID_LENTH_SHIFT);
        REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
    }
    else if(curBlkCnt < blockNum)
    {
        offset = curBlkCnt * 16u;
        Crypto_Aes256_WriteCmacFifoFromBytes(offset, 16u, input);
        Crypto_Aes256_EnableInterrupt(TRUE);
        REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
    }
    else if(curBlkCnt == blockNum)
    {
        /* Last Block process */
        remainLen = dataLen - ((blockNum - 1u) * 16u); 
        if(0u != remainLen)
        {
            Crypto_Aes256_WriteCmacFifoFromBytes((blockNum - 1u) * 16u, remainLen, input);
            REG_RMW32(AES_CTRL_REG_ADDR, AES_CTRL_MODE_MASK, (uint32)AES256_ALGOMODE_AES_CMAC << AES_CTRL_MODE_SHIFT);
            REG_BIT_CLEAR32(AES_CTRL_REG_ADDR,AES_CTRL_SUBKG_MASK);
            REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_VALID_LENTH_MASK,(uint32)((dataLen << 3u) - ((blockNum - 1u) * 128u)) << AES_CTRL_VALID_LENTH_SHIFT);
            Crypto_Aes256_EnableInterrupt(TRUE);
            REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
        }
        else
        {
            /* nothing */
        }
    }
    else
    {
        Crypto_Aes256_EnableInterrupt(FALSE);
        Crypto_Aes256_ClearStatus();
    }
    return status;
}

/*!
* @brief     AES-CTR keystream XOR (sync: full blocks + optional last partial; async: first block path).
*
* @param     mode:      AES opcode (typically encrypt for CTR stream).
* @param     key:       Key material bytes.
* @param     keysize:   Key length (128 / 192 / 256 bit).
* @param     IV:        Initial counter block (16 bytes).
* @param     input:     Plaintext or ciphertext buffer.
* @param     length:    Byte length (any; last block may be shorter than 16).
* @param     output:    Output buffer (at least length bytes for sync).
* @param     syncmode:  CRYPTO_PROCESSING_SYNC or CRYPTO_PROCESSING_ASYNC.
*
* @retval    CSE_ERC_OK or CSE_ERC_ERROR
*/
FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Aes256_GenerateCtr( VAR(Crypto_Aes256_OpcodeType, AUTOMATIC) mode, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) key,
                       VAR(Crypto_Aes256_KeySizeType, AUTOMATIC) keysize, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) IV, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) input,
                       VAR(uint32, AUTOMATIC) length, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) output,VAR(Crypto_ProcessingType, AUTOMATIC) syncmode)
{
    VAR(Crypto_Aes256_ConfigType, AUTOMATIC) aesCrypConfig = {AES256_ALGODIR_ENCRYPT,AES256_ALGOMODE_AES_ECB,AES256_KEYSIZE_128B}; 
    VAR(Crypto_Aes256_KeyConfigType, AUTOMATIC) aesKeyConfig = {0};  
    VAR(Crypto_Aes256_IvConfigType, AUTOMATIC)  aesIvConfig = {0}; 
    VAR(uint32, AUTOMATIC) outputAddr = (uint32)output;
    VAR(Crypto_Cse_ErrorCodeType, AUTOMATIC) status = CSE_ERC_OK;
    VAR(uint32, AUTOMATIC) remainLen = 0;
    VAR(uint32, AUTOMATIC) blkNum = (length + 15u) / 16u;
    
    Crypto_Aes256_ConfigKeyStructInit(&aesKeyConfig);
    aesCrypConfig.keySize = keysize;

    Crypto_Aes256_InputKey(key, keysize, &aesKeyConfig);
    Crypto_Aes256_InputIv(IV,aesIvConfig.IV);

    
    aesCrypConfig.algoOpcode  = mode;   
    aesCrypConfig.algoMode = AES256_ALGOMODE_AES_CTR; 
 
    REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_OPCODE_MASK,(uint32)aesCrypConfig.algoOpcode << AES_CTRL_OPCODE_SHIFT);
    REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_MODE_MASK,(uint32)aesCrypConfig.algoMode << AES_CTRL_MODE_SHIFT);
    REG_BIT_CLEAR32(AES_CTRL_REG_ADDR,AES_CTRL_BIG_ENDIAN_MASK);
    REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_KEY_LEN_MASK,(uint32)aesCrypConfig.keySize << AES_CTRL_KEY_LEN_SHIFT);
     
    Crypto_Aes256_ConfigKey(&aesKeyConfig); 
    Crypto_Aes256_ConfigIV(&aesIvConfig);
    if(CRYPTO_PROCESSING_SYNC == syncmode)
    {
        /* Block-by-block Data Writing */
        status = Crypto_Aes256_FBlockProcess(blkNum, input, outputAddr);
        /* Last Block process */
        remainLen = length - ((blkNum - 1u) * 16u); 
        if(CSE_ERC_OK == status)
        {
     
            Crypto_Aes256_LastBlkSet(remainLen, blkNum - 1u, input);
            
            REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
            status = Crypto_Aes256_WaitForCompute(CRYPTO_TIMEOUT_VALUE);
            
            if(status == CSE_ERC_OK)
            {
                Crypto_Aes256_CopyDataOutBlock(outputAddr, (blkNum - 1u) * 16u);
            }
            else
            {
                /* nothing */
            }
        }
        else
        {
            /* nothing */
        }
    }
    else
    {
        /* Block-by-block Data Writing */
        Crypto_Aes256_BlockProcessAsync(blkNum, length, input);
    }
    return status;
}

/*!
* @brief     AES-CMAC over arbitrary-length message (sync or async).
*
* @param     key:       Key material bytes.
* @param     keysize:   Key length (128 / 192 / 256 bit).
* @param     IV:        CMAC IV / initial chaining value (16 bytes; use all-zero to match common profiles).
* @param     input:     Message bytes.
* @param     length:    Message length in bytes (any; padding handled internally).
* @param     output:    16-byte MAC output buffer.
* @param     syncmode:  CRYPTO_PROCESSING_SYNC or CRYPTO_PROCESSING_ASYNC.
*
* @retval    CSE_ERC_OK or CSE_ERC_ERROR
*/
FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Aes256_GenerateCmac( P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) key, VAR(Crypto_Aes256_KeySizeType, AUTOMATIC) keysize, 
                       P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) IV, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) input,
                       VAR(uint32, AUTOMATIC) length, P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) output,VAR(Crypto_ProcessingType, AUTOMATIC) syncmode)
{
    VAR(Crypto_Aes256_ConfigType, AUTOMATIC) aesCrypConfig = {AES256_ALGODIR_ENCRYPT,AES256_ALGOMODE_AES_ECB,AES256_KEYSIZE_128B}; 
    VAR(Crypto_Aes256_KeyConfigType, AUTOMATIC) aesKeyConfig = {0}; 
    VAR(Crypto_Aes256_IvConfigType, AUTOMATIC)  aesIvConfig = {0};
    VAR(uint32, AUTOMATIC) outputAddr = (uint32)output;
    VAR(uint32, AUTOMATIC) blkNum = (length + 15u) / 16u;
    VAR(uint32, AUTOMATIC) blkCnt = 1;
    VAR(uint32, AUTOMATIC) dataLengthBits = length << 3u;
    VAR(Crypto_Cse_ErrorCodeType, AUTOMATIC) status = CSE_ERC_OK;
    VAR(uint32, AUTOMATIC) remain = 0;
    VAR(uint32, AUTOMATIC) offset = 0;
    
    Crypto_Aes256_Reset();

    /* initial AES Key */
    Crypto_Aes256_ConfigKeyStructInit(&aesKeyConfig);
    aesCrypConfig.keySize = keysize;

    Crypto_Aes256_InputKey(key, keysize, &aesKeyConfig);
    Crypto_Aes256_InputIv(IV,aesIvConfig.IV);

    aesCrypConfig.algoOpcode  = AES256_ALGODIR_ENCRYPT;
    aesCrypConfig.algoMode    = AES256_ALGOMODE_AES_CMAC;
    REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_OPCODE_MASK,(uint32)aesCrypConfig.algoOpcode << AES_CTRL_OPCODE_SHIFT);
    REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_MODE_MASK,(uint32)aesCrypConfig.algoMode << AES_CTRL_MODE_SHIFT);
    REG_BIT_CLEAR32(AES_CTRL_REG_ADDR,AES_CTRL_BIG_ENDIAN_MASK);
    REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_KEY_LEN_MASK,(uint32)aesCrypConfig.keySize << AES_CTRL_KEY_LEN_SHIFT);
    Crypto_Aes256_ConfigKey(&aesKeyConfig);
    Crypto_Aes256_ConfigIV(&aesIvConfig);
    if(CRYPTO_PROCESSING_SYNC == syncmode)
    {
        /* Block-by-block Data Writing */
        for (blkCnt = 1u; blkCnt < blkNum; blkCnt++) 
        {
            offset = ((blkCnt - 1u) * 16u);
            Crypto_Aes256_WriteCmacFifoFromBytes(offset, 16u, input);

            REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
            status = Crypto_Aes256_WaitForCompute(CRYPTO_TIMEOUT_VALUE);
            if(CSE_ERC_ERROR == status)
            {
                break;
            }
            else
            {
                /* nothing */
            }
        }
        /* Periphery expects DATAIN cleared before ECB+SubKey (see Aes256_GenerateCmac -> Aes256_ClearInput). */
        if (blkNum > 1u)
        {
            Crypto_Aes256_ClearDataInFifo();
        }
        else
        {
            /* do nothing */
        }
        /* Last Block process */
        remain = length - ((blkNum - 1u) * 16u);  
        if(CSE_ERC_OK == status)
        {
            status = Crypto_Aes256_CmacProcessLastBlock(dataLengthBits, blkNum, remain, blkNum - 1u, input);
            
            if(CSE_ERC_OK == status)
            {
                Crypto_Aes256_CopyDataOutBlock(outputAddr, 0u);
            }
            else
            {
                /* do nothing */
            }
        }
        else
        {
            /* do nothing */
        }
    }
    else
    {
        Crypto_Aes256_BlockProcessMacAsync(blkNum, length, input);
    }
    return status;
}

/*!
* @brief     AES-MPC tag generation (hardware MPC mode, 256-bit key path as configured in this driver).
*
* @param     input:      Message buffer.
* @param     length:     Message length in bytes (any; last block may be partial).
* @param     output:     16-byte MPC result buffer.
* @param     syncmode:   CRYPTO_PROCESSING_SYNC or CRYPTO_PROCESSING_ASYNC.
*
* @retval    CSE_ERC_OK or CSE_ERC_ERROR
*/
FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Aes256_GenerateMpc(P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) input, VAR(uint32, AUTOMATIC) length, 
                       P2CONST(uint8, AUTOMATIC, CRYPTO_APPL_CONST) output,VAR(Crypto_ProcessingType, CSM_CONST) syncmode)
{
    VAR(Crypto_Aes256_KeyConfigType, AUTOMATIC) aesKeyConfig = {0}; 
    VAR(Crypto_Aes256_ConfigType, AUTOMATIC) aesCrypConfig = {AES256_ALGODIR_ENCRYPT,AES256_ALGOMODE_AES_ECB,AES256_KEYSIZE_128B}; 
    VAR(uint32, AUTOMATIC) data = 0;
    VAR(uint32, AUTOMATIC) outputAddr = (uint32)output;
    VAR(uint32, AUTOMATIC) blkNum = (length + 15u) / 16u;
    VAR(uint32, AUTOMATIC) blkCnt = 1;
    VAR(Crypto_Cse_ErrorCodeType, AUTOMATIC) status = CSE_ERC_OK;
    VAR(uint32, AUTOMATIC) j = 0;
    VAR(uint32, AUTOMATIC) remain = 0;
    VAR(uint32, AUTOMATIC) offset = 0;
    Crypto_Aes256_Reset();

    /* initial AES Key */
    aesCrypConfig.algoOpcode  = AES256_ALGODIR_ENCRYPT;  
    aesCrypConfig.algoMode = AES256_ALGOMODE_AES_MPC; 
    
    REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_OPCODE_MASK,(uint32)aesCrypConfig.algoOpcode << AES_CTRL_OPCODE_SHIFT);
    REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_MODE_MASK,(uint32)aesCrypConfig.algoMode << AES_CTRL_MODE_SHIFT);
    REG_BIT_CLEAR32(AES_CTRL_REG_ADDR,AES_CTRL_BIG_ENDIAN_MASK);
    REG_RMW32(AES_CTRL_REG_ADDR,AES_CTRL_KEY_LEN_MASK,(uint32)AES256_KEYSIZE_256B << AES_CTRL_KEY_LEN_SHIFT);
    
    Crypto_Aes256_ConfigKey(&aesKeyConfig);
    
    if(CRYPTO_PROCESSING_SYNC == syncmode)
    {
        /* Block-by-block Data Writing */
        for (blkCnt = 1; blkCnt < blkNum; blkCnt++) 
        {
            offset = (blkCnt - 1u) * 16u;
            for (j = 0u; j < 4u; j++) 
            {
                data = (uint32)input[offset + (j * 4u)]       |
                       ((uint32)input[offset + (j * 4u) + 1u] << 8u)  |
                       ((uint32)input[offset + (j * 4u) + 2u] << 16u) |
                       ((uint32)input[offset + (j * 4u) + 3u] << 24u);
                REG_WRITE32(AES_DATAIN_0_3_REG_ADDR + (j * sizeof(uint32)), data);
            }

            REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
            status = Crypto_Aes256_WaitForCompute(CRYPTO_TIMEOUT_VALUE);
            if(CSE_ERC_ERROR == status)
            {
                break;
            }
            else
            {
                /* nothing */
            }
        }
        if(CSE_ERC_OK == status)
        {
            /* Last Block process */
            remain = length - ((blkNum - 1u) * 16u);  
            Crypto_Aes256_LastBlkSet(remain, blkNum - 1u, input);

            REG_BIT_SET32(AES_CTRL_REG_ADDR,AES_CTRL_START_MASK);
            
            status = Crypto_Aes256_WaitForCompute(CRYPTO_TIMEOUT_VALUE);
                    
            if(CSE_ERC_OK == status)
            {
                Crypto_Aes256_CopyDataOutBlock(outputAddr, 0u);
            }
            else
            {
                /* do nothing */
            }
        }
        else
        {
            /* do nothing */
        }   
                   
    }
    else
    {
        Crypto_Aes256_BlockProcessAsync(blkNum, length, input);
    }
    return status;
}
/*!
* @brief     Poll until AES BUSY clears or iteration budget expires.
*
* @param     timeOut: Maximum poll iterations (not necessarily wall-clock time).
*
* @retval    CSE_ERC_OK if BUSY cleared before count reached zero; CSE_ERC_ERROR on timeout.
*/
static FUNC(Crypto_Cse_ErrorCodeType, CRYPTO_CODE) Crypto_Aes256_WaitForCompute( VAR(uint32, AUTOMATIC) timeOut )
{
    VAR(uint32, AUTOMATIC) timeCnt = timeOut;
    VAR(Crypto_Cse_ErrorCodeType, AUTOMATIC) reply = CSE_ERC_ERROR;
    
    while (timeCnt > 0u)
    {
        if (0u == (REG_READ32(AES_STS_REG_ADDR) & AES_STS_BUSY_MASK))
        {
            break;
        }
        else
        {
            timeCnt --;
        }
    }

    if (timeCnt > 0u)
    {
        reply = CSE_ERC_OK;
    }
    else
    {
        /* do nothing */
    }
    
    return reply;
}

/*!
* @brief     Read AES hardware BUSY status bit.
*
* @param     None
*
* @retval    TRUE if AES is busy; FALSE if idle.
*/
FUNC(boolean, CRYPTO_CODE) Crypto_Aes256_IsBusy(void)
{
    VAR(uint8, AUTOMATIC) AesStatus = (uint8)REG_BIT_GET8(AES_STS_REG_ADDR, AES_STS_BUSY_MASK);

    VAR(boolean, AUTOMATIC) isBusy = (boolean)TRUE;

    if (AES_STS_BUSY_MASK != AesStatus) 
    {
        isBusy = (boolean)FALSE;
    }
    else
    {
        /* nothing */
    }
    return isBusy;
}
/**@} end of group CRYPTO_Fuctions */
#ifdef __cplusplus
}
#endif
/**@} end of group CRYPTO_Driver */
/**@} end of group G32A10xx_StdPeriphDriver */

