Skocz do zawartości

ARM Kompilacja z funkcjami eksportowanymi do RAM


virtualny

Pomocna odpowiedź

Z powodów sobie znanych mógłby ktoś chcieć uruchomić część kodu w RAM i nie jest to niemożliwe, a dzięki uprzejmości supportowi STM32 MCD Application Team zostało to zaimplementowane do prostego użycia.

Programiści MCD Application Team dodali takie dwie ciekawe linie w sekcji ".data" skryptu linkera:

    *(.RamFunc)        /* .RamFunc sections */
    *(.RamFunc*)       /* .RamFunc* sections */

To oznacza, że napisane funkcje z atrybutem (section(".RamFunc")) będą przez kompilator i linker traktowane jako dane do przepisania z flash'a do RAM. 

 

Sporządziłem mały programik dla BLUEPILL'a do blinkania diodą, żeby użyć tego ficzeru. Program jest bardzo mały,  stworzony w STM32CubeIDE. W zasadzie składa się z 3 plików:

1. Rozbiegówki w assemblerze (to co się dzieje po resecie)

2. Pliku main.c w którym są zamieszczone 2 funkcje. Funkcja main(), oraz blink(). Funkcja blink jest funkcją przepisywaną do RAM.

3. Skryptu linkera.

Wszystko jest okrojone do minimum, pousuwane zbędne includy, nieużywane opcje itd.

Widok projektu:

prog_blink.thumb.jpg.dc07b1a42608fff2c4c47701603cca01.jpg

Funkcja blink (to uruchamiana w RAM) ma nadane 3 atrybuty:

__attribute__ ((section(".RamFunc"), long_call, naked )) void blink(void)

1. Sekcji, wydzielającej ją do RAM

2. Atrybutu naked usuwającego zarządzanie stosem i zapisywaniem rejestrów (w tym konkretnym wypadku nie było to potrzebne) dla skrócenia programu

3. long_call pozwala skrócić wywoływanie procedury z RAM i zapobiega tworzeniu przez linker dodatkowej funkcji pośredniczącej w wywołaniu funkcji umieszczonej w RAM. 

Z podanych atrybutów obowiązkowy jest tylko pierwszy, pozostałe są użyte przeze mnie, ale nie są niezbędne do prawidłowego działania.

Funcja blink() została napisana w assemblerze (raptem 11 linii) i robi dokładnie to, co 2 zakomentowane linie w C, oraz wykonuje powrót do miejsca wywołania z funkcji main() (bx lr).

//	GPIOB_ODR_REG ^= GPIOB_ODR_ODR2;
//	for(unsigned long int delay = 500000 ; delay != 0 ; delay--){}

 

Na "rozbieg" po resecie wykonywany jest plik startupowy, również okrojony przeze mnie do niezbędnego minimum - oto jego zawartość:


  .syntax unified
  .cpu cortex-m3
  .fpu softvfp
  .thumb

.global g_pfnVectors
.global .isr_vector

.word _sidata
.word _sdata
.word _edata
/******************************************************************************/
  .section .text.Reset_Handler
  .weak Reset_Handler
  .type Reset_Handler, %function
Reset_Handler:

/* Copy the data segment initializers from flash to SRAM */
  ldr r0, =_sdata
  ldr r1, =_edata
  ldr r2, =_sidata
  movs r3, #0

CopyDataInit:
  ldr r4, [r2, r3]
  str r4, [r0, r3]
  adds r3, r3, #4

LoopCopyDataInit:
  adds r4, r0, r3
  cmp r4, r1
  bcc.n CopyDataInit
  
/* Call the application's entry point.*/
  b.n main

.size Reset_Handler, .-Reset_Handler
/******************************************************************************/
  .section .isr_vector,"a",%progbits
  .type g_pfnVectors, %object
  .size g_pfnVectors, .-g_pfnVectors
/******************************************************************************/
g_pfnVectors:

  .word _estack
  .word Reset_Handler
/******************************************************************************/

 

Program z tego pliku przepisuje funkcę blink() z pamięci FLAH do RAM i następnie wykonuje skok do funkcji main()

Funkcje main() i blink() nie są skomplikowane, a całość razem z definicjami i klamrami zamknęła się w około 40 liniach kodu... (...kodu?...)

#define RCC_APB2ENR            (*((unsigned long int *)0x40021018))
#define RCC_APB2ENR_IOPBEN     (unsigned long int) 0x08
#define GPIOB_BASE             (unsigned long int) 0x40010C00
#define GPIOB_CRL_REG          (*((unsigned long int *) GPIOB_BASE))
#define GPIOB_CRL_VALUE        (unsigned long int) 0x44484244

//#define GPIOB_ODR_REG          (*((unsigned long int *) (GPIOB_BASE + 12)))
//#define GPIOB_ODR_ODR2         (unsigned long int) 04
//-----------------------
__attribute__ ((section(".RamFunc"), long_call, naked )) void blink(void)
{
//	GPIOB_ODR_REG ^= GPIOB_ODR_ODR2;

	__asm ("ldr r0, gpiob_odr");
	__asm ("ldr r1, [r0]");
	__asm ("eor r1, #4");
	__asm ("str r1, [r0]");

//	for(unsigned long int delay = 500000 ; delay != 0 ; delay--){}
	__asm ("ldr r0, del_limit");

	__asm ("calc: subs r0, #1");
	__asm ("bne calc");
	__asm ("bx lr");

	__asm ("gpiob_odr: .word	0x40010C0C");
	__asm ("del_limit: .word	500000");
}
//----------------------
__attribute__ ((naked)) int main(void)
{
 		RCC_APB2ENR    |=  RCC_APB2ENR_IOPBEN;
 		GPIOB_CRL_REG   =  GPIOB_CRL_VALUE;
	while (1)
	{
		blink();
	}
}

 

Całość jest linkowana według skryptu konsolidatora, który również okroiłem do minimum.

/* Entry Point */
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */

_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Memories definition */
MEMORY
{
  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 20K
  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 64K
}

/* Sections */
SECTIONS
{
  /* The startup code into "FLASH" Rom type memory */
  .isr_vector :
  {

    KEEP(*(.isr_vector)) /* Startup code */

  } >FLASH

  /* The program code and other data into "FLASH" Rom type memory */
  .text :
  {

    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */

    _etext = .;        /* define a global symbols at end of code */
  } >FLASH



  /* Used by the startup to initialize data */
  _sidata = LOADADDR(.data);

  /* Initialized data sections into "RAM" Ram type memory */
  .data :
  {

    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */
    *(.RamFunc)        /* .RamFunc sections */
    *(.RamFunc*)       /* .RamFunc* sections */

    _edata = .;        /* define a global symbol at data end */

  } >RAM AT> FLASH

  /* Uninitialized data section into "RAM" Ram type memory */

  .bss :
  {
    /* This is used by the startup in order to initialize the .bss section */

    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

	
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM
}

 

Cały skompilowany oraz zdeassemblowany program to około 80 linii w assemblerze:


Disassembly of section .text:

08000008 <main>:
}
//----------------------
__attribute__ ((naked)) int main(void)
{
 		RCC_APB2ENR    |=  RCC_APB2ENR_IOPBEN;
 8000008:	4b05      	ldr	r3, [pc, #20]	; (8000020 <main+0x18>)
 800000a:	681b      	ldr	r3, [r3, #0]
 800000c:	4a04      	ldr	r2, [pc, #16]	; (8000020 <main+0x18>)
 800000e:	f043 0308 	orr.w	r3, r3, #8
 8000012:	6013      	str	r3, [r2, #0]
 		GPIOB_CRL_REG   =  GPIOB_CRL_VALUE;
 8000014:	4b03      	ldr	r3, [pc, #12]	; (8000024 <main+0x1c>)
 8000016:	4a04      	ldr	r2, [pc, #16]	; (8000028 <main+0x20>)
 8000018:	601a      	str	r2, [r3, #0]
	while (1)
	{
		blink();
 800001a:	4b04      	ldr	r3, [pc, #16]	; (800002c <main+0x24>)
 800001c:	4798      	blx	r3
 800001e:	e7fc      	b.n	800001a <main+0x12>
 8000020:	40021018 	.word	0x40021018
 8000024:	40010c00 	.word	0x40010c00
 8000028:	44484244 	.word	0x44484244
 800002c:	20000001 	.word	0x20000001

08000030 <Reset_Handler>:
  .weak Reset_Handler
  .type Reset_Handler, %function
Reset_Handler:

/* Copy the data segment initializers from flash to SRAM */

 8000030:	4805      	ldr	r0, [pc, #20]	; (8000048 <LoopCopyDataInit+0xa>)   ldr r0, =_sdata
 8000032:	4906      	ldr	r1, [pc, #24]	; (800004c <LoopCopyDataInit+0xe>)   ldr r1, =_edata
 8000034:	4a06      	ldr	r2, [pc, #24]	; (8000050 <LoopCopyDataInit+0x12>)   ldr r2, =_sidata
 8000036:	2300      	movs	r3, #0


CopyDataInit:
 8000038:	58d4      	ldr	r4, [r2, r3]
 800003a:	50c4      	str	r4, [r0, r3]
 800003c:	3304      	adds	r3, #4


 800003e:	18c4      	adds	r4, r0, r3
 8000040:	428c      	cmp	r4, r1
 8000042:	d3f9      	bcc.n	8000038 <CopyDataInit>
  
/* Call the application's entry point.*/

 8000044:	e7e0      	b.n	8000008 <main>
 8000046:	0000      	.short	0x0000

 8000048:	20000000 	.word	0x20000000   _sdata
 800004c:	2000001e 	.word	0x2000001e   _edata
 8000050:	08000054 	.word	0x08000054   _sidata

Disassembly of section .data:

20000000 <blink>:

20000000:	f8df 0010 	ldr.w	r0, [pc, #16]	; 20000014 <gpiob_odr> "ldr r0, gpiob_odr"
20000004:	6801      	ldr	r1, [r0]
20000006:	f081 0104 	eor.w	r1, r1, #4
2000000a:	6001      	str	r1, [r0, #0]
 
2000000c:	4802      	ldr	r0, [pc, #8]	; (20000018 <del_limit>) "ldr r0, del_limit"

2000000e <calc>:
2000000e:	3801      	subs	r0, #1
20000010:	d1fd      	bne.n	2000000e <calc>

20000012:	4770      	bx	lr

20000014 <gpiob_odr>:
20000014:	40010c0c 	.word	0x40010c0c
20000018 <del_limit>:
20000018:	0007a120 	.word	0x0007a120
}

Gwoli ścisłości dodam, że program testuję na BlePillPLUS, który jest ulepszoną wersją pierwotnego BluePilla. Posiada 128KB wewnętrznej pamięci flash, jest lepiej zaprojektowane PCB i ma możliwość wlutowania szeregowej pamięci, do SPI1 procesora tj. PA5, PA6, PA7 z CS/ na PA4. Jest także drobna różnica, że LED nie jest jak w wersji poprzedniej na PC13, tylko jest zamontowane na PB2. 

01.thumb.jpg.3423e633fa50bc86c0258cae3acbef35.jpg

 

02.thumb.jpg.8e9e3d7311eba8d7b5e8695f60831fc9.jpg

03.thumb.jpg.3f1e35045808f7577e34f0e5fc7ff8d5.jpg

04.thumb.jpg.9c5a1a46ec82db0d25ac8df1c8885fc7.jpg

05.thumb.jpg.70eb84a213222a2dae1cfd4ff9d8788e.jpg

 

Schemat płytki:

BluePillPlus_V10_SchDoc.thumb.gif.1443549e5370e22e22ee66cef2b6db8e.gif

 

Porównanie z poprzednią wersją:

COMPARISION.thumb.jpg.78c6668afb02f832f8081761dd344506.jpg

 

 

W załączniku omawiany program wyprodukowany w STM32CubeIDE BLUEPILL_BLINK.ZIP

 

  • Lubię! 1
Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.