SILABS AN664

AN664
P RECISION 32™ CMSIS AND HAL U SER ’ S G UIDE
1. Introduction
CMSIS is the Cortex Microcontroller Software Interface Standard, and the Hardware Access Layer (HAL) is a
defined part of this standard.
The HAL provides an access layer for the SiM3xxxx device registers. The functions and macros are non-blocking
and simple; they cannot return error codes, so they are designed to never fail. The HAL is designed to replace the
individual bit field accesses of the module with a function name that describes the action the bit is controlling.
Note: HAL functions and macros are not designed to be thread-safe. These routines do not disable interrupts during nonmonotonic register modifications.
The HAL is one layer above the hardware and is the only code that accesses the registers directly. More complex
firmware systems like a Real Time Operating System (RTOS) or code example call the HAL and CMSIS routines.
Figure 1 shows the Precision32™ firmware layer block diagram.
CODE EXAMPLES
APPLICATION
si32Library
Callback
RTOS
CMSIS CoreSupport
from ARM
CMSIS
HAL from Silicon Labs
HARDWARE
Figure 1. Firmware Layer Block Diagram
2. Relevant Documentation
Precision32 Application Notes are listed on the following website: www.silabs.com/32bit-appnotes.
AN668:
Precision32™ Software Development Kit (SDK) Code Examples Overview
Getting Started with the Silicon Labs Precision32™ AppBuilder
AN672: Precision32™ si32Library Overview
AN673: Precision32™ Software Development Kit Overview
AN675: Precision32™ Development Suite Overview
AN670:
Rev. 0.2 3/12
Copyright © 2012 by Silicon Laboratories
AN664
AN664
3. Peripheral Memory Organization
Each peripheral exists as a set of registers in memory. Most peripherals start at 0x1000 address blocks in the
peripheral memory area starting at address 0x4000_0000. The base pointer of a peripheral points to the starting
address of the peripheral, and each register is an offset from the base address. In the case of the USART0 module,
the base pointer is SI32_USART_0, and it is assigned an address of 0x4000_0000, since it’s the first peripheral in
the peripheral memory area.
The registers each take 16 bytes (0x10) of memory: a word each for the register and the SET, CLR, and MSK
addresses. These addresses are reserved for registers that do not implement them.
Figure 2 shows the USART0 registers as they appear in memory.
SiM3xxxx
Address Space
FLOWCN_CLR
FLOWCN_SET
FLOWCN
MODE_CLR
MODE_SET
MODE
CONFIG_CLR
CONFIG_SET
SI32_USART_0
CONFIG
0x4000_002C
0x4000_0028
0x4000_0024
0x4000_0020
0x4000_001C
0x4000_0018
0x4000_0014
0x4000_0010
0x4000_000C
0x4000_0008
0x4000_0004
0x4000_0000
Figure 2. USART0 Registers in Memory
2
Rev. 0.2
AN664
4. HAL Organization
The HAL is organized based on the SiM3xxxx peripheral modules. Modules of the same type and revision are
exactly the same, so these modules share the same generic description. The individual instances of the modules
then instantiate their own copies of the generic description. For example, the HAL implements a USART (module)
A (revision) type. This type is then instantiated multiple times for USART0 and USART1, which have their own
base pointers.
Each generic module has a *_Registers.h file that contains a module structure comprised of register structures and
bit fields properly aligned in memory. The *_Type.h file contains the HAL interface for the module, and the *_Type.c
contains the HAL implementation. Some modules will also have a *_Support.h file that contains helpful
enumeration definitions. Finally, a device header file named for the device (e.g., sim3u1xx.h) contains base pointer
and interrupt vector instantiations for each module on a device.
Figure 3 displays a block diagram showing the relationship of the HAL files.
sim3u1xx.h
Module
Interrupt Vectors
Base Pointers
Support.h
Registers.h
Type.h
Type.c
Enumerated Type
Definitions
Register and Bit
Definitions
Function
Prototypes and
Macros
Functions
Figure 3. HAL Block Diagram
Rev. 0.2
3
AN664
4.1. USART0 HAL Example
The sim3u1xx.h device file contains the USART0 interrupt vector (USART0_IRQn) and base pointer information
(SI32_USART_0). The SI32_USART_A_Registers.h file contains the module structure, which includes structures
of bit fields for each register. The SI32_USART_A_Type.h and Type.c files contain routines that access each of
these bits.
Figure 4 displays a block diagram showing the relationship of the HAL files for the USART0 module.
sim3u1xx.h
Interrupt Vector: USART0_IRQn
Base Pointer: SI32_USART_0
USART0
SI32_USART_A_Registers.h
CONFIG.TPARMD
#define SI32_USART_A_CONFIG_TPARMD_MASK 0x00600000
#define SI32_USART_A_CONFIG_TPARMD_SHIFT 21
// Odd Parity.
#define SI32_USART_A_CONFIG_TPARMD_ODD_VALUE 0
#define SI32_USART_A_CONFIG_TPARMD_ODD_U32 \
(SI32_USART_A_CONFIG_TPARMD_ODD_VALUE <<
SI32_USART_A_CONFIG_TPARMD_SHIFT)
...
SI32_USART_A_Type.c
void
_SI32_USART_A_select_tx_parity(
SI32_USART_A_Type * basePointer,
uint32_t parity)
{
assert(parity < 4); // parity < 2^2
//{{
basePointer->CONFIG_CLR = SI32_USART_A_CONFIG_TPARMD_MASK;
basePointer->CONFIG_SET = parity << SI32_USART_A_CONFIG_TPARMD_SHIFT;
//}}
}
SI32_USART_A_Type.h
void
_SI32_USART_A_select_tx_parity(
SI32_USART_A_Type * basePointer,
uint32_t parity);
Figure 4. Example USART HAL Block Diagram
Firmware can call the USART0 select tx parity routine using the base pointer defined in si3mu1xx.h and the
function implemented in SI32_USART_A_Type.c:
SI32_USART_A_select_tx_parity(SI32_USART_0, parity);
4
Rev. 0.2
AN664
Table 1 shows an example set of registers for the USART0 module.
CLR(+0x8)
USART0_CONFIG
Module Configuration
0x4000_0000
Y
Y
USART0_MODE
Module Mode Select
0x4000_0010
Y
Y
USART0_FLOWCN
Flow Control
0x4000_0020
Y
Y
USART0_CONTROL
Module Control
0x4000_0030
Y
Y
USART0_IPDELAY
Inter-Packet Delay
0x4000_0040
USART0_BAUDRATE
Transmit and Receive Baud Rate
0x4000_0050
USART0_FIFOCN
FIFO Control
0x4000_0060
Y
Y
USART0_DATA
FIFO Input/Output Data
0x4000_0070
Register Name
Title
Address
(ALL Access)
MSK (+0xC)
SET (+0x4)
Table 1. Example USART Registers
USART0 Registers
Figure 5 illustrates the resulting USART module structure in Registers.h.
Figure 5. Example USART Module Structure
Rev. 0.2
5
AN664
Each of the registers has a corresponding structure that defines the bit fields within that register. In addition,
registers that implement the clear and set addresses have 32-bit variables defined at the appropriate addresses.
Figure 6 shows the CONFIG register structure, which is declared as a part of the USART module structure.
Figure 6. Example USART CONFIG Register Structure
This CONFIG register structure has the TPARMD 2-bit field that controls the transmit parity. The U32 value
declared at the bottom of the structure is a union with the bit fields and is an entity that firmware can use to access
the entire register at one time.
The HAL Type.c functions and macros can access the entire USART0 CONFIG register:
SI32_USART_0->CONFIG.U32 = config;
The HAL can also read or write the TPARMD field in the USART0 CONFIG register:
parity = SI32_USART_0->CONFIG.TPARMD;
SI32_USART_0->CONFIG_SET = parity << SI32_USART_A_CONFIG_TPARMD_SHIFT;
Finally, the HAL can clear the TPARMD field:
SI32_USART_0->CONFIG_CLR = SI32_USART_A_CONFIG_TPARMD_MASK;
6
Rev. 0.2
AN664
2. Detailed HAL Overview
The HAL for each module consists of *_Registers.h, *_Type.h, and *_Type.c file. Some modules will have an
additional *_Support.h file. This section discusses the role of each of these files in detail.
Note: All definitions, functions, and macros in the Silicon Labs HAL start with a SI32_ shield to prevent any potential conflicts
with other code in the system.
2.1. Registers Header File (Registers.h)
The *_Registers.h file creates a memory map of the module’s registers and bits. In addition, this file declares
detailed definitions to manipulate each bit or field.
2.1.1. Registers
Each of the module’s registers are defined as:
struct SI32_Peripheral_R_Register_Struct
{
union
{
struct
{
volatile uint32_t bitfield0: 4;
volatile uint32_t bitfield1: 12;
volatile uint8_t bitfield2;
volatile uint32_t bitfield3: 4;
uint32_t reserved0: 4;
};
volatile uint32_t U32;
};
};
In this definition, Peripheral is the generic name of the peripheral (USART, for example), R is the revision (A, B,
etc.), and Register is the name of the register. The size of the bitfields in a register structure must add up to 32 bits.
Any 8- or 16-bit bitfields aligned on 8-bit boundaries are not defined as bitfields but as uint8_t or uint16_t. Unused
bitfield regions are defined as reserved#.
Rev. 0.2
7
AN664
FIFO registers are not defined using any bitfields. If byte accesses are not allowed, the register structure does not
include a uint8_t definition. The same applies to half-word accesses and the uint16_t definition. All FIFOs are
accessed from the least significant byte, so there is no explicit access provided for the upper three bytes or the
upper half-word.
struct SI32_Peripheral_R_Register_Struct
{
union
{
volatile uint8_t U8;
volatile uint16_t U16;
volatile uint32_t U32;
};
};
2.1.2. Bitfields
Each of the bitfields of a module has several defines in the *_Registers.h file. These defines all have the form
SI32_Module_Revision_Register_Bitfield_DefineType. The DefineType is:
MASK:
a bit mask for the position of the bit or field in the register.
SHIFT: a left-shift value for the position of the bit or field in the register.
Enumeration_VALUE or Enumeration_U32: These enumerations define every valid value of the bit or
field. The enumerations also include a U32 version that the HAL routines can write to entire register using
the defined SHIFT value.
For example, with the IPRDY bit in the EPCONTROL register (bit 0) of the USBEP module:
#define SI32_USBEP_A_EPCONTROL_IPRDYI_MASK 0x00000001
#define SI32_USBEP_A_EPCONTROL_IPRDYI_SHIFT 0
// The packet has been sent or there is an open FIFO slot.
#define SI32_USBEP_A_EPCONTROL_IPRDYI_NOT_SET_VALUE 0
#define SI32_USBEP_A_EPCONTROL_IPRDYI_NOT_SET_U32 \
(SI32_USBEP_A_EPCONTROL_IPRDYI_NOT_SET_VALUE <<
SI32_USBEP_A_EPCONTROL_IPRDYI_SHIFT)
// A packet is loaded in the FIFO.
#define SI32_USBEP_A_EPCONTROL_IPRDYI_SET_VALUE 1
#define SI32_USBEP_A_EPCONTROL_IPRDYI_SET_U32 \
(SI32_USBEP_A_EPCONTROL_IPRDYI_SET_VALUE <<
SI32_USBEP_A_EPCONTROL_IPRDYI_SHIFT)
The NOT_SET and SET definitions are the enumerations for this bit. The VALUE definitions set the value of each
valid bit value, and the U32 definitions are the appropriate write value for the bit when writing to entire register.
8
Rev. 0.2
AN664
2.1.2.1. Using the MASK Definition
The HAL uses MASK definitions to clear or set bitfields in registers that support clear and set addresses. In these
cases, the address can be set equal to the mask directly to manipulate the bitfield. For example:
void
_SI32_USART_A_disable_rx_start_bit(SI32_USART_A_Type * basePointer)
{
//{{
basePointer->CONFIG_CLR = SI32_USART_A_CONFIG_RSTRTEN_MASK;
//}}
}
2.1.2.2. Using the SHIFT Definition
The HAL uses the SHIFT definition when a field value is passed to a routine. This allows the calling code to pass in
a raw value, and the HAL can handle the actual placement of the bitfield in the register. For example:
void
_SI32_USART_A_select_rx_stop_bits(SI32_USART_A_Type * basePointer,
SI32_USART_A_STOP_BITS_Enum_Type bits)
{
assert(bits < 4);
// bits < 2^2
//{{
basePointer->CONFIG_CLR = SI32_USART_A_CONFIG_RSTPMD_MASK;
basePointer->CONFIG_SET = bits << SI32_USART_A_CONFIG_RSTPMD_SHIFT;
//}}
}
2.1.2.3. Using the Enumeration Definitions
The HAL uses the U32 enumeration definitions for the clear and set addresses or when accessing the entire
register. The HAL does not use the value definitions themselves whenever possible, since accessing a single
bitfield in a register is inefficient. For example:
void
_SI32_USART_A_enable_rx_error_interrupts(SI32_USART_A_Type *
basePointer)
{
//{{
basePointer->CONTROL_SET = SI32_USART_A_CONTROL_RERIEN_ENABLED_U32;
//}}
}
Rev. 0.2
9
AN664
2.1.3. Modules
A module structure at the end of each *_Registers.h file aggregates all of the peripherals’s registers and spaces
them from the peripheral base pointer address:
typedef struct SI32_Peripheral_R_Struct
{
struct SI32_PERIPHERAL_R_REGNAME1_Struct
Register1;
uint32
Register1_SET;
uint32
Register1_CLR;
uint32_t
reserved0;
struct SI32_Peripheral_R_Register2_Struct
Register2;
uint32_t
reserved1;
uint32_t
reserved2;
uint32_t
reserved3;
} SI32_Peripheral_R_Type;
The struct is assumed to start at offset 0x0 from the peripheral’s base pointer. In this example, Register1 is at
address 0x0 and supports clear and set addresses, so Register1_SET is located at 0x04 and Register1_CLR is
located at 0x08. There is no mask address defined for this register, so the corresponding address 0x0C is simply
marked as reserved#. Register2 has no set, clear, or mask addresses, so these offsets are also defined as
reserved#.
This struct is never allocated as a variable; it is simply used as offsets from a base pointer.
10
Rev. 0.2
AN664
2.2. Type Header (Type.h) and Code (Type.c) Files
The *_Type.h file creates the interface definition of the module. This file includes function prototypes and macro
definitions for each of the HAL routines. Including this file in a source file means that file now has access to all of
the HAL routines for a module.
The *_Type.c file is the implementation of the module HAL functions. This file includes all of the function
declarations for the prototypes in the *_Type.h file.
Each of the routines starts with the SI32_ shield and the module name. The descriptive name of the function then
follows. For example, the routine to set the IPRDYI bit in the USBEP module is:
void _SI32_USBEP_A_set_in_packet_ready(SI32_USBEP_A_Type * basePointer);
2.2.1. Function Naming
The function names for the HAL follow a set of guidelines to make them consistent and predictable across
modules:
Names
are in lower case, including acronyms: read_nss_pin.
Do not use abbreviations, with one exception being tx for transmit and rx for receive.
Separate words using underscores.
Use enable_module and disable_module for bits that control the whole module.
Use reset_module for bits that reset the whole module.
Use select for functions that are selecting an item that is mutually exclusive from other options. This is
common with settings that involve multiple bitfields.
Use enter_mode_description_mode and exit_mode_description_mode for non-mutually exclusive
items, and all enter_mode functions must have an exit_mode counterpart. For example, to enter the IrDA
mode in the USART module: enter_rx_irda_mode.
Use set, get, and clear for bits and registers. For example: set_parity_even, get_status,
clear_carry_flag.
Use numerical value for numbers. For example: set_fifo_threshold_1, set_fifo_threshold_2.
Use read and write when manipulating data or pins. For example: read_fifo, write_fifo, read_nss_pin.
Use start and stop for things that run, like oscillators and timers.
Use enable, disable, “is enabled”, “is pending”, and clear for interrupts. For example:
enable_overrun_interrupt, disable_overrun_interrupt, is_overrun_interrupt_pending, and
clear_overrun_interrupt.
Use has for events that have flags but do not cause interrupts. For example:
has_buffer_overflow_occurred.
For debug bits, use the enable_stall_in_debug_mode and disable_stall_in_debug_mode routine
names.
Functions referring to the peripheral should always use the module keyword.
Rev. 0.2
11
AN664
2.2.2. Required Functions
Each module must have the following functions:
initialize
enable_module
whenever a corresponding bit exists
disable_module whenever a corresponding bit exists
reset_module whenever a corresponding bit exists
A write_<register> function for each register
A read_<register> function for each register
enable_<interrupt>_interrupt for each interrupt
disable_<interrupt>_interrupt for each interrupt
is_<interrupt>_interrupt_enabled for each interrupt
is_<interrupt>_interrupt_pending for each interrupt
clear_<interrupt>_interrupt for each interrupt
The initialize function takes all writable registers as parameters and copies them into the registers. This function is
knowledgeable about order, whenever order matters.
The write register functions take the register value as a parameter and copies it into the register. The read register
functions return the value of the register.
2.2.3. Return and Parameter Types
Return types must be one of the following:
void
bool
uint32_t
uint16_t
uint8_t
Enumeration Type defined for the module in *_Support.h
When returning a bool value, the functions explicitly typecast the return value as a bool.
Parameters must be one of the following:
uint32_t
uint16_t
uint8_t
bool
Enumeration Type defined for the module in *_Support.h
The functions use these types for all parameters used to carry values that are smaller than 32 bits but are passed
wide for efficiency:
wide8_t:
value can be converted to signed 8-bit number without loss of precision
value can be converted to unsigned 8-bit number without loss of precision
wide16_t: value can be converted to signed 16-bit number without loss of precision
uwide_16_t: value can be converted to unsigned 16-bit number without loss of precision
These types indicate to the calling code that, while the parameter is passed wide, the actual value will be stored
narrow in the indicated storage format.
uwide8_t:
12
Rev. 0.2
AN664
2.2.4. Function Implementation Guidelines
Functions do not block or return error codes. This serves several purposes:
1. This eliminates the need for the HAL to validate parameters, beyond debug bounds assertions.
2. This eliminates the need for the calling code to validate the return codes.
If a function could not be safely implemented without polling, such as enabling an external oscillator, then it is not a
HAL function. This functionality is left to a higher level of firmware, like the si32Library or AppBuilder.
Functions are not allowed to allocate memory locally or use global variables. As a result, the functions will not be
able to maintain state separate from what is maintained in the registers themselves.
Functions are as efficient as possible. For set or clear operations, functions use the set or clear addresses, where
applicable. Read-modify-write operations are used only when absolutely necessary when clearing a field before
setting it to a new value causes an issue, like undesired pin transitions.
All functions have four or fewer parameters whenever possible, including the base pointer to the module.
2.3. Optional Support Files (Support.h)
The *_Support.h files are optional for a module and share the same basic template as *_Type.h files. These files
contain helpful enumerations for modules and any other support code necessary. The format for these
enumerations is:
typedef enum SI32_Peripheral_Struct
{
SI32_Peripheral_EnumName1 = 0,
SI32_Peripheral_EnumName2 = 1,
...
} SI32_Peripheral_Enum_Type;
The enumerations provide set values for a function parameter to eliminate the chance of using an invalid value and
to make code more readable. For example:
typedef enum SI32_RSTSRC_Struct
{
SI32_RSTSRC_PIN_RESET = 0,
SI32_RSTSRC_POWER_ON_RESET = 1,
SI32_RSTSRC_VDD_MONITOR = 2,
...
} SI32_RSTSRC_Enum_Type;
The get_last_reset_source routine can then return a value of this enumeration type:
SI32_RSTSRC_Enum_Type _SI32_RSTSRC_A_get_last_reset_source(
SI32_RSTSRC_A_Type * basePointer);
Firmware can then decode the return value of this routine using the enumeration values defined in
SI32_RSTSRC_A_Support.h to create readable and less error-prone code.
Rev. 0.2
13
AN664
2.4. HAL Startup Code
The si32Hal startup implementation differs from the CMSIS recommendation in order to support multiple tool
chains. Each individual application has explicit control over all of its memory model, including the stack, heap,
RAM, retention RAM, and EMIF. The symbols used to define the base and size of these memory areas is available
for use by the application without duplication.
The HAL uses tool chain-specific linker control files in the si32-x.y\si32Hal\device directory to specify these
ranges. These files are:
GCC/Precision32:
linker_DeviceFamily_p32.ld
ARM: linker_DeviceFamily_arm.sct
IAR: linker_DeviceFamily_iar.icf
The code examples also provide basic linker control files that are sufficient for many applications. For cases where
these linker files are not sufficient, the application developer should use application-specific linker files.
3. Performance
The HAL implements both macros and functions for most routines. This allows firmware layers that call the HAL to
choose between the faster performance of macros or the smaller footprint of functions. For the routines that do not
have an implemented macro, a macro still exists, but it just calls the function.
The functions and macros have the same parameters and names, but functions have an underscore prefix. For
example, the function for setting the USART transmit parity is:
_SI32_USART_A_select_tx_parity(SI32_USART_0, parity);
The macro for the same routine is:
SI32_USART_A_select_tx_parity(SI32_USART_0, parity);
4. Revisions
Each version of the HAL sits in a separate folder. The path of these is C:\SiLabs\32bit\si32\si32-x.y, where x is
the primary HAL version and y is the secondary HAL version. Each time a new version of the HAL is installed, it will
leave all previous versions to eliminate the chance of a new install breaking a working firmware project.
In addition, any deprecated functions will remain a part of the HAL. These functions will either remain unchanged
or call the new version to prevent the need to modify firmware when migrating to a newer version of the HAL.
5. Code Examples
Each version of the HAL includes stand-alone code examples for the device modules that use the macro routines
by default. These examples can be found in C:\SiLabs\32bit\si32-x.y\Examples after installing the Precision32
IDE.
6. The HAL and AppBuilder
The Silicon Labs AppBuilder program uses HAL macros when configuring peripherals. The AppBuilder project
options can select between different versions of the HAL.
14
Rev. 0.2
AN664
7. Detailed Documentation
The detailed Silicon Labs CMSIS documentation can be found in the si32Hal Windows help file (si32Hal.chm). The
documentation includes the Cortex-M3 Core Register Definitions, Core Function Interface, and Core Instruction
Interface, as well as the SiM3xxxx HAL.
The si32Hal file shown in Figure 7 is installed in C:\SiLabs\32bit\si32-x.y\Documentation after installing the
Precision32 software package from www.silabs.com/32bit-software.
Figure 7. Silicon Labs HAL and CMSIS Documentation
Rev. 0.2
15
AN664
DOCUMENT CHANGE LIST
Revision 0.1 to Revision 0.2
Updated
Figure 1.
the name of the HAL help file to
si32Hal.chm.
Added Section 2.
Updated
16
Rev. 0.2
AN664
NOTES:
Rev. 0.2
17
AN664
CONTACT INFORMATION
Silicon Laboratories Inc.
400 West Cesar Chavez
Austin, TX 78701
Tel: 1+(512) 416-8500
Fax: 1+(512) 416-9669
Toll Free: 1+(877) 444-3032
Please visit the Silicon Labs Technical Support web page:
https://www.silabs.com/support/pages/contacttechnicalsupport.aspx
and register to submit a technical support request.
The information in this document is believed to be accurate in all respects at the time of publication but is subject to change without notice.
Silicon Laboratories assumes no responsibility for errors and omissions, and disclaims responsibility for any consequences resulting from
the use of information included herein. Additionally, Silicon Laboratories assumes no responsibility for the functioning of undescribed features
or parameters. Silicon Laboratories reserves the right to make changes without further notice. Silicon Laboratories makes no warranty, representation or guarantee regarding the suitability of its products for any particular purpose, nor does Silicon Laboratories assume any liability
arising out of the application or use of any product or circuit, and specifically disclaims any and all liability, including without limitation consequential or incidental damages. Silicon Laboratories products are not designed, intended, or authorized for use in applications intended to
support or sustain life, or for any other application in which the failure of the Silicon Laboratories product could create a situation where personal injury or death may occur. Should Buyer purchase or use Silicon Laboratories products for any such unintended or unauthorized application, Buyer shall indemnify and hold Silicon Laboratories harmless against all claims and damages.
Silicon Laboratories and Silicon Labs are trademarks of Silicon Laboratories Inc.
Other products or brandnames mentioned herein are trademarks or registered trademarks of their respective holders.
18
Rev. 0.2