EmbDev.net

Forum: ARM programming with GCC/GNU tools General question: how does ARM core updates its registers?


von Andy V. (Company: Self) (newembuser)


Rate this post
useful
not useful
Hi,
A bit new to embedded programming.  I am trying to trace down where all 
the registers (such as pll setting, IO, ...) get updated.

For example, for the STM32F0 processor, below are the code to set system 
clock but the codes only have the registers that get modified, but how 
the register get updated by the processor?  My guess is there is some 
type of interrupt mechanism that detects if there is a change in the 
internal registers, the interrupt will trigger a subroutine that will 
update the registers.

My other question is what part of the linker that associate each of the 
internal register to the actual register of the hardware?  For example 
if you look at the STM32F0 driver file, there are a lot of "structs" 
defined for the IO but how are these basically purely C variables get 
associated with the actual register?

If you look at the SetSysClock() function below, the "RCC" type is only 
a C variables, where does this variable get assign to a hard register in 
the processor?
1
/**
2
  * @brief  Configures the System clock frequency, AHB/APBx prescalers and Flash
3
  *         settings.
4
  * @note   This function should be called only once the RCC clock configuration
5
  *         is reset to the default reset state (done in SystemInit() function).
6
  * @param  None
7
  * @retval None
8
  */
9
static void SetSysClock(void)
10
{
11
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
12
  
13
  /* SYSCLK, HCLK, PCLK configuration ----------------------------------------*/
14
  /* Enable HSE */    
15
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
16
 
17
  /* Wait till HSE is ready and if Time out is reached exit */
18
  do
19
  {
20
    HSEStatus = RCC->CR & RCC_CR_HSERDY;
21
    StartUpCounter++;  
22
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
23
24
  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
25
  {
26
    HSEStatus = (uint32_t)0x01;
27
  }
28
  else
29
  {
30
    HSEStatus = (uint32_t)0x00;
31
  }  
32
33
  if (HSEStatus == (uint32_t)0x01)
34
  {
35
    /* Enable Prefetch Buffer and set Flash Latency */
36
    FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;
37
 
38
    /* HCLK = SYSCLK */
39
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
40
      
41
    /* PCLK = HCLK */
42
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV1;
43
44
    /* PLL configuration = HSE * 6 = 48 MHz */
45
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
46
    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLMULL6);
47
            
48
    /* Enable PLL */
49
    RCC->CR |= RCC_CR_PLLON;
50
51
    /* Wait till PLL is ready */
52
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
53
    {
54
    }
55
56
    /* Select PLL as system clock source */
57
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
58
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    
59
60
    /* Wait till PLL is used as system clock source */
61
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)RCC_CFGR_SWS_PLL)
62
    {
63
    }
64
  }
65
  else
66
  { /* If HSE fails to start-up, the application will have wrong clock 
67
         configuration. User can add here some code to deal with this error */
68
  }  
69
}

: Edited by Moderator
von Jörg W. (dl8dtl) (Moderator)


Rate this post
useful
not useful
It is much less complicated than you might think.

All these names like RCC are simply pointers to structures (usually
hidden as preprocessing macros), and the actual elements of these
structures simply are the (IO) registers.  All ARM IO registers
are memory-mapped, i.e. they appear to be a part of the normal
address space, but when the processor decodes the respective address,
it does not access SRAM or Flash but triggers a read or write operation
to an IO register.

von Klaus (Guest)


Rate this post
useful
not useful
As a sidenote, it may be worth tracing down the actual declaration and 
definition of RCC and other I/O likewise.

The word "trigger" may cause some doubt, whether the controller behaves 
in a way which deviates only slightly from what your, Andy, hypothesis 
was. (Sorry, Jörg. I say that with all respect). But "trigger" does not 
mean the causing of a (sub-)process.
It may be reworded as, "that a certain address, which belongs to an 
I/O-register, directs whatever shall be read or written to that 
register".

Hope, that this remark is usefull.

von Andy V. (Company: Self) (newembuser)


Rate this post
useful
not useful
Jörg W. wrote:
> It is much less complicated than you might think.
>
> All these names like RCC are simply pointers to structures (usually
> hidden as preprocessing macros), and the actual elements of these
> structures simply are the (IO) registers.  All ARM IO registers
> are memory-mapped, i.e. they appear to be a part of the normal
> address space, but when the processor decodes the respective address,
> it does not access SRAM or Flash but triggers a read or write operation
> to an IO register.

Hi Jorg,
Thanks for the reply.

"(usually hidden as preprocessing macros)"
I am using Eclipse CDT.  Where can I look into these preprocessing 
macros?
For example, here is an excerpt of the IO data structures but these are 
only the data structures.  How do the macros map these data structures 
to the actual registers or memory location?
There has to be some prior knowledge between the compiler and the 
STM32F0 IO structure, because these are purely C declare variables.  I 
assume as you mentioned, the preprocessing macros has some prior 
knowledge of these data structure before hand and map these into the 
memory accordingly.

Could you point me to where I can read about the preprocessing macros?

Thanks, -Andy


file :stm32f0xx.h
/*********************************************************************** 
*******/
/* 
*/
/*                       General Purpose IOs (GPIO) 
*/
/* 
*/
/*********************************************************************** 
*******/
/*******************  Bit definition for GPIO_MODER register 
*****************/
#define GPIO_MODER_MODER0          ((uint32_t)0x00000003)
#define GPIO_MODER_MODER0_0        ((uint32_t)0x00000001)
#define GPIO_MODER_MODER0_1        ((uint32_t)0x00000002)
#define GPIO_MODER_MODER1          ((uint32_t)0x0000000C)
#define GPIO_MODER_MODER1_0        ((uint32_t)0x00000004)
#define GPIO_MODER_MODER1_1        ((uint32_t)0x00000008)
#define GPIO_MODER_MODER2          ((uint32_t)0x00000030)
#define GPIO_MODER_MODER2_0        ((uint32_t)0x00000010)
#define GPIO_MODER_MODER2_1        ((uint32_t)0x00000020)
#define GPIO_MODER_MODER3          ((uint32_t)0x000000C0)

Here are the RCC typedefs.  These are basically generic C declarations. 
There are really nothing unique about them.

typedef struct
{
  __IO uint32_t CR;         /*!< RCC clock control register, 
Address offset: 0x00 */
  __IO uint32_t CFGR;       /*!< RCC clock configuration register, 
Address offset: 0x04 */
  __IO uint32_t CIR;        /*!< RCC clock interrupt register, 
Address offset: 0x08 */
  __IO uint32_t APB2RSTR;   /*!< RCC APB2 peripheral reset register, 
Address offset: 0x0C */
  __IO uint32_t APB1RSTR;   /*!< RCC APB1 peripheral reset register, 
Address offset: 0x10 */
  __IO uint32_t AHBENR;     /*!< RCC AHB peripheral clock register, 
Address offset: 0x14 */
  __IO uint32_t APB2ENR;    /*!< RCC APB2 peripheral clock enable 
register,                   Address offset: 0x18 */
  __IO uint32_t APB1ENR;    /*!< RCC APB1 peripheral clock enable 
register,                   Address offset: 0x1C */
  __IO uint32_t BDCR;       /*!< RCC Backup domain control register, 
Address offset: 0x20 */
  __IO uint32_t CSR;        /*!< RCC clock control & status register, 
Address offset: 0x24 */
  __IO uint32_t AHBRSTR;    /*!< RCC AHB peripheral reset register, 
Address offset: 0x28 */
  __IO uint32_t CFGR2;      /*!< RCC clock configuration register 2, 
Address offset: 0x2C */
  __IO uint32_t CFGR3;      /*!< RCC clock configuration register 3, 
Address offset: 0x30 */
  __IO uint32_t CR2;        /*!< RCC clock control register 2, 
Address offset: 0x34 */
} RCC_TypeDef;

: Edited by User
von Jörg W. (dl8dtl) (Moderator)


Rate this post
useful
not useful
Andy V. wrote:
> Where can I look into these preprocessing macros?

Sorry, no idea.  I've been using some Atmel ARMs so far, and there's
a rather scattered filesystem subtree under a directory name CMSIS
where these macros come from.

von Andy V. (Company: Self) (newembuser)


Rate this post
useful
not useful
Jörg W. wrote:
> Andy V. wrote:
>> Where can I look into these preprocessing macros?
>
> Sorry, no idea.  I've been using some Atmel ARMs so far, and there's
> a rather scattered filesystem subtree under a directory name CMSIS
> where these macros come from.

Thanks.  I'll try to look into this, but it seems you're right in 
general.  There seems to be a convention for these hardware related 
registers that have to be declared in certain way.

SMSIS stands for: Cortex Microcontroller Software Interface Standard
So it has to do with the compiler, preprocessor parsing these files and 
compiler these specifically for the hardware.

Here's a link to CMSIS:
file:///C:/LinuxEmbSrc/STM32F0-Discovery_FW_V1.0.0/Libraries/CMSIS/Docum 
entation/CMSIS_Core.htm

: Edited by User
von Jörg W. (dl8dtl) (Moderator)


Rate this post
useful
not useful
Andy V. wrote:
> SMSIS stands for: Cortex Microcontroller Software Interface Standard

Yep, but Atmel also deliveres their per-CPU definitions along with
the official CMSIS stuff (which is standardized by ARM), just in a
different directory tree.

von Andy V. (Company: Self) (newembuser)


Rate this post
useful
not useful
Jörg W. wrote:
> Andy V. wrote:
>> SMSIS stands for: Cortex Microcontroller Software Interface Standard
>
> Yep, but Atmel also deliveres their per-CPU definitions along with
> the official CMSIS stuff (which is standardized by ARM), just in a
> different directory tree.

OK, I mean STM32F0 also has its own "drivers" but these drivers 
basically calls the functions from CMSIS.  By the way, do you happen to 
know where to download the Atmel drivers?  (I can search ...)

A more complete definition of CMSIS is from Keil website
http://www.keil.com/pack/doc/CMSIS/Driver/html/index.html

I guess all these hardware related "C" variables have to be standardized 
otherwise how would the compiler know which is ARM core specifics and 
which are user app specific.

The part I am still trying to figure out is when you compile your 
program, explicitly where exactly in the GNU_GCC that read in these core 
drivers?  I mean it has to be somewhere int he GNU_GCC just that I am 
not able to find it yet.

: Edited by User
von Jörg W. (dl8dtl) (Moderator)


Rate this post
useful
not useful
Andy V. wrote:
> By the way, do you happen to know where to download the Atmel drivers?

Unfortunately, they ship these header (and C) files only as part of
their (several 100 MB fat) Atmel Studio.

Note that I explicitly don't mean anything like a "driver" (that would
be Atmel's ASF) but really simply a processor (rather: MCU) definition.

Example (PIO port):
1
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
2
typedef struct {
3
  __IO PORT_DIR_Type             DIR;         /**< \brief Offset: 0x00 (R/W 32) Data Direction */
4
  __IO PORT_DIRCLR_Type          DIRCLR;      /**< \brief Offset: 0x04 (R/W 32) Data Direction Clear */
5
  __IO PORT_DIRSET_Type          DIRSET;      /**< \brief Offset: 0x08 (R/W 32) Data Direction Set */
6
  __IO PORT_DIRTGL_Type          DIRTGL;      /**< \brief Offset: 0x0C (R/W 32) Data Direction Toggle */
7
  __IO PORT_OUT_Type             OUT;         /**< \brief Offset: 0x10 (R/W 32) Data Output Value */
8
  __IO PORT_OUTCLR_Type          OUTCLR;      /**< \brief Offset: 0x14 (R/W 32) Data Output Value Clear */
9
  __IO PORT_OUTSET_Type          OUTSET;      /**< \brief Offset: 0x18 (R/W 32) Data Output Value Set */
10
  __IO PORT_OUTTGL_Type          OUTTGL;      /**< \brief Offset: 0x1C (R/W 32) Data Output Value Toggle */
11
  __I  PORT_IN_Type              IN;          /**< \brief Offset: 0x20 (R/  32) Data Input Value */
12
  __IO PORT_CTRL_Type            CTRL;        /**< \brief Offset: 0x24 (R/W 32) Control */
13
  __O  PORT_WRCONFIG_Type        WRCONFIG;    /**< \brief Offset: 0x28 ( /W 32) Write Configuration */
14
       RoReg8                    Reserved1[0x4];
15
  __IO PORT_PMUX_Type            PMUX[16];    /**< \brief Offset: 0x30 (R/W  8) Peripheral Multiplexing n */
16
  __IO PORT_PINCFG_Type          PINCFG[32];  /**< \brief Offset: 0x40 (R/W  8) Pin Configuration n */
17
       RoReg8                    Reserved2[0x20];
18
} PortGroup;
19
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
20
21
/** \brief PORT APB hardware registers */
22
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
23
typedef struct {
24
       PortGroup                 Group[2];    /**< \brief Offset: 0x00 PortGroup groups [GROUPS] */
25
} Port;

just pick the CTRL subregister:
1
typedef union {
2
  struct {
3
    uint32_t SAMPLING:32;      /*!< bit:  0..31  Input Sampling Mode                */
4
  } bit;                       /*!< Structure used for bit  access                  */
5
  uint32_t reg;                /*!< Type      used for register access              */
6
} PORT_CTRL_Type;

and finally:
1
#define PORT              ((Port     *)0x41004400U) /**< \brief (PORT) APB Base Address */

Please log in before posting. Registration is free and takes only a minute.
Existing account
Do you have a Google/GoogleMail account? No registration required!
Log in with Google account
No account? Register here.