//*****************************************************************************
//
//  am_reg_macros.h
//! @file
//!
//! @brief Helper macros for using hardware registers.
//
//*****************************************************************************

//*****************************************************************************
//
// Copyright (c) 2017, Ambiq Micro
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// 
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision v1.2.10-2-gea660ad-hotfix2 of the AmbiqSuite Development Package.
//
//*****************************************************************************

#ifndef AM_REG_MACROS_H
#define AM_REG_MACROS_H

#ifdef __cplusplus
extern "C"
{
#endif

//*****************************************************************************
//
// Include the inline assembly macros.
//
//*****************************************************************************
#include "am_reg_macros_asm.h"

//*****************************************************************************
//
// High-level Helper Macros.
//
// Usage:
//
// For direct 32-bit access to a register, use AM_REGVAL:
//      AM_REGVAL(REG_VCOMP_BASEADDR + AM_VCOMP_VCMPCFG_O) |= 0xDEADBEEF;
//
// The AM_REG macro can also be used as a shorthand version of AM_REGVAL:
//      AM_REG(VCOMP, VCMPCFG) |= 0xDEADBEEF;
//
// The AM_REGn macro is used for accessing registers of peripherals with
// multiple instances, such as IOMSTR.
//      AM_REGn(IOMSTR, 1, CLKCFG) |= 0xDEADBEEF;
//
// To write to a specific bitfield within a register, use AM_BFW or AM_BFWn:
//      AM_BFW(CTIMER, 0, CTCTRL0, TMRB0FN, 0x3);
//
// To read a field, use AM_BFR or AM_BFRn:
//      ui32Timer0Fn = AM_BFR((CTIMER, 0, CTCTRL0, TMRB0FN);
//
// Note:
//
// AM_REGn, AM_BFW and AM_BFR are concatenation-based, which means that
// standalone macro definitions should not be used for the 'module', 'reg', and
// 'field' arguments.All macro names in the various peripheral header files are
// written in one of the following forms:
//      - AM_REG_##module_reg_O
//      - AM_REG_##module_reg_field_S
//      - AM_REG_##module_reg_field_M
//
// The "module", "reg" and "field" fragments may be used as valid arguments to
// the AM_REGn, AM_BFW, and AM_BFR macros, all of which are able to perform the
// necessary concatenation operations to reconstruct the full macros and look
// up the appropriate base address for the instance number given. For
// peripherals with only one instance, use instance number 0.
//
// The AM_REGVAL macro does not perform any concatenation operations, so the
// complete macro name (including any suffix) must be specified.
//
//*****************************************************************************
#define AM_REGVAL(x)               (*((volatile uint32_t *)(x)))
#define AM_REGVAL_FLOAT(x)         (*((volatile float *)(x)))

//*****************************************************************************
//
// Register access macros for single-instance modules
// AM_REG  - Write a register of a module.
// AM_BFW  - Write a value to a bitfield of a register.
// AM_BFWe - Use a defined enum value to write a value to a register bitfield.
// AM_BFR  - Read a bitfield value from a register.
// AM_BFM  - Read and mask a bitfield from a register, but leave the value in
//           its bit position. Useful for comparing with enums.
//
// AM_BFV  - Move a value to a bitfield.  This macro is used for creating a
//           value, it does not modify any register.
// AM_BFX  - Extract the value of a bitfield from a 32-bit value, such as that
//           read from a register. Does not read or modify any register.
//
//*****************************************************************************
#define AM_REG(module, reg)                                                   \
    AM_REGn(module, 0, reg)

#define AM_BFW(module, reg, field, value)                                     \
    AM_BFWn(module, 0, reg, field, value)

#define AM_BFWe(module, reg, field, enumval)                                  \
    AM_BFWen(module, 0, reg, field, enumval)

#define AM_BFR(module, reg, field)                                            \
    AM_BFRn(module, 0, reg, field)

#define AM_BFM(module, reg, field)                                            \
    AM_BFMn(module, 0, reg, field)

#define AM_BFV(module, reg, field, value)                                     \
    (((uint32_t)(value) << AM_REG_##module##_##reg##_##field##_S) &           \
     AM_REG_##module##_##reg##_##field##_M)

#define AM_BFX(module, reg, field, value)                                     \
    (((uint32_t)(value) & AM_REG_##module##_##reg##_##field##_M) >>           \
     AM_REG_##module##_##reg##_##field##_S)


//*****************************************************************************
//
// Register access macros for multi-instance modules
// AM_REGADDRn - Calc the register address inside a multiple instance module.
// AM_REGn - Write a register of a multiple instance module.
// AM_BFWn - Write a value to a bitfield of a register in a multiple instance.
// AM_BFWen - Use a defined enum value to write a value to a bitfield of a
//            register in a multiple instance.
// AM_BFRn - Read a bitfield value from a register in a multiple instance.
// AM_BFMn - Read a bitfield, but leave the value in its bitfield position.
// AM_BFMn - Read and mask a bitfield, but leave the value in its bit position.
//           (Useful for comparing with enums.)
//
//*****************************************************************************
#define AM_REGADDRn(module, instance, reg)                                    \
      (AM_REG_##module##n(instance) + AM_REG_##module##_##reg##_O)

#define AM_REGn(module, instance, reg)                                        \
    AM_REGVAL(AM_REG_##module##n(instance) + AM_REG_##module##_##reg##_O)

#define AM_BFWn(module, instance, reg, field, value)                          \
    AM_REGn(module, instance, reg) =                                          \
        (AM_BFV(module, reg, field, value) |                                  \
         (AM_REGn(module, instance, reg) &                                    \
          (~AM_REG_##module##_##reg##_##field##_M)))

#define AM_BFWen(module, instance, reg, field, enumval)                       \
    AM_REGn(module, instance, reg) =                                          \
        (AM_REG_##module##_##reg##_##field##_##enumval |                      \
         (AM_REGn(module, instance, reg) &                                    \
          (~AM_REG_##module##_##reg##_##field##_M)))

#define AM_BFRn(module, instance, reg, field)                                 \
    AM_BFX(module, reg, field, AM_REGn(module, instance, reg))

#define AM_BFMn(module, instance, reg, field)                                 \
    (AM_REGn(module, instance, reg) & AM_REG_##module##_##reg##_##field##_M)

//*****************************************************************************
//
// "Atomic" register access macros - use when a read-modify-write is required.
//
// These macros will be slower than the normal macros, but they will also
// guarantee threadsafe hardware access.
//
// These macros require a nesting-friendly critical section implementation. If
// you are using the HAL, you can use the default definitions below. If not,
// you will need to supply your own.
//
// Atomic register access macros usage:
// AM_REGa      - Write a register of a single instance module. Provide operator
//                (&,|,etc) to perform that operation on the reg using value, or
//                no operator to simply write the value atomically.
// AM_REGa_SET  - Set bits in a single instance module according to the mask.
// AM_REGa_CLR  - Clear bits in a single instance module according to the mask.
// AM_REGan     - Multiple module version of AM_REGa.
// AM_REGan_SET - Multiple instance version of AM_REGa_SET.
// AM_REGan_CLR - Multiple instance version of AM_REGa_CLR.
// AM_BFWa   - Write a value to a register bitfield.
// AM_BFWae  - Use a defined enum value to write a value to a bitfield.
// AM_BFWan  - Write a value to a bitfield of a register in a multiple instance.
// AM_BFWaen - Use a defined enum value to write a value to a bitfield of a
//             register in a multiple instance.
//
//*****************************************************************************
#ifndef AM_CRITICAL_BEGIN
#define AM_CRITICAL_BEGIN   uint32_t ui32Primask = am_hal_interrupt_master_disable()
#define AM_CRITICAL_END     am_hal_interrupt_master_set(ui32Primask)
#endif

#define AM_REGan(module, instance, reg, operator, value)                    \
    AM_CRITICAL_BEGIN_ASM                                                   \
    AM_REGn(module, instance, reg) operator##= (value);                     \
    AM_CRITICAL_END_ASM

#define AM_REGan_SET(module, instance, reg, mask)                           \
    AM_CRITICAL_BEGIN_ASM                                                   \
    AM_REGn(module, instance, reg) |= (mask);                               \
    AM_CRITICAL_END_ASM

#define AM_REGan_CLR(module, instance, reg, mask)                           \
    AM_CRITICAL_BEGIN_ASM                                                   \
    AM_REGn(module, instance, reg) &= (~mask);                              \
    AM_CRITICAL_END_ASM

#define AM_REGa(module, reg, operator, value)                               \
    AM_REGan(module, 0, reg, operator, value)

#define AM_REGa_CLR(module, reg, mask)                                      \
    AM_REGan_CLR(module, 0, reg, mask)

#define AM_REGa_SET(module, reg, mask)                                      \
    AM_REGan_SET(module, 0, reg, mask)

#define AM_BFWa(module, reg, field, value)                                  \
    AM_CRITICAL_BEGIN_ASM                                                   \
    AM_BFW(module, reg, field, value);                                      \
    AM_CRITICAL_END_ASM

#define AM_BFWae(module, reg, field, enumval)                               \
    AM_CRITICAL_BEGIN_ASM                                                   \
    AM_BFWe(module, reg, field, enumval);                                   \
    AM_CRITICAL_END_ASM

#define AM_BFWan(module, instance, reg, field, value)                       \
    AM_CRITICAL_BEGIN_ASM                                                   \
    AM_BFWn(module, instance, reg, field, enumval);                         \
    AM_CRITICAL_END_ASM

#define AM_BFWaen(module, instance, reg, field, enumval)                    \
    AM_CRITICAL_BEGIN_ASM                                                   \
    AM_BFWen(module, instance reg, field, enumval);                         \
    AM_CRITICAL_END_ASM

//*****************************************************************************
//
// Other helper Macros.
//
// Note: These macros make use of macro concatenation, so the '_S' or '_M'
// suffix on a register bitfield macro should not be supplied by the user.
// The macro will apply each suffix as needed.
//
//*****************************************************************************

//
// AM_ENUMX extracts a register bitfield enumeration to the bit 0 position,
//  which makes it possible to use enums directly with existing macros such
//  as AM_BFR() or AM_BFW().
// Brief overview: bitfield enumerations are pre-shifted such that the defined
//  value lines up with the bitfield.  This is convenient for many operations,
//  but not so when using AM_BFR() to read the value of a register bitfield
//  as AM_BFR() shifts the bitfield value to the bit 0 position.
// Note that this type of bitfield extraction is Cortex efficient via the
//  UBFX (unsigned bit field extract) instruction.
//
// Alternately, AM_BFM() can also be used.  AM_BFM() reads a register and masks
//  the bitfield value (without shifting), thereby allowing  direct comparison
//  with a defined enum.
//
// Examples:
//  if  ( AM_BFR(CLKGEN, CCTRL, CORESEL) ==
//        AM_ENUMX(CLKGEN, CCTRL, CORESEL, HFRC) )
//
//  or alternatively:
//  if  ( AM_BFM(CLKGEN, CCTRL, CORESEL) == AM_REG_CLKGEN_CCTRL_CORESEL_HFRC )
//
#define AM_ENUMX(module, reg, field, enumname)                              \
    ((AM_REG_##module##_##reg##_##field##_##enumname) >>                    \
     (AM_REG_##module##_##reg##_##field##_S))

//
// AM_WRITE_SM performs a shift/mask operation to prepare the value 'x' to be
// written to the register field 'field'.
//
// For example:
// AM_REGVAL(ui32Base + AM_VCOMP_VCMP_CFG_O) |=
//     AM_WRITE_SM(AM_VCOMP_VCMP_CFG_LVLSEL, ui32Value);
//
#define AM_WRITE_SM(field, x)      (((x) << field##_S) & field##_M)

//
// AM_READ_SM performs a shift/mask operation to make it easier to interpret
// the value of a given bitfield. This is essentially the reverse of the
// AM_WRITE_SM operation. In most cases, you will want to use the shorter
// AM_BFR macro instead of this one.
//
// For example:
// ui32Value = AM_READ_SM(AM_VCOMP_VCMP_CFG_NSEL,
//                        AM_REGVAL(ui32Base + AM_VCOMP_VCMP_CFG_O));
//
#define AM_READ_SM(field, x)       (((x) & field##_M) >> field##_S)

#ifdef __cplusplus
}
#endif

#endif // AM_REG_MACROS_H

