2013年11月10日 星期日

作業系統之前的程式 for stm32f4discovery (2.5) - c++ 篇

遲來的 c++ 篇, 之前忙著搞懂 arm cortex m3 這顆 mcu, 無法分心在 c++ 上。

語言戰文總是引人注目, 這裡有篇年代久遠的精彩 c/c++ 論戰 (中文的相信好看多了): Linux之父炮轰C++:糟糕程序员的垃圾语言

雖然我很尊敬 Linus Torvalds, 不過我可不會照單全收他的想法, 我很喜歡 c++, 來個 c++ 版本的作業系統之前的程式, 不過我想用的部份的確是和 c 比較接近的部份。不用 class/object 的話, 還有什麼 c++ 特性可以用呢? 呃 ... 多的很

  • namespace
  • function overloaded
  • function default argument
  • template function
  • reference
  • inline function
  • 更嚴格的型別檢查
  • c++11 的部份特性

當然我會用上 class/struct 的宣告, 可以省下 typedef 這個字。

這裡用上 function overloaded。

mycpp.cpp
 1 #include "stm32.h"

 2 
 3 int print(int i)
 4 {
 5   i+=1;
 6 }
 7 
 8 int print(char c)
 9 {
10  c-=1;
11 }
12 
13 
14 void mymain(void)
15 {
16   print(35);
17   print('A');
18   while(1);
19 }
20 

stm32.ld
 1 /*
 2 MEMORY
 3 {
 4   FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 128K
 5   SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
 6 }
 7 */
 8 
 9 /* Specify the memory areas */
10 /* form: STM32F4-Discovery_FW_V1.1.0/Project/Peripheral_Examples/IO_Toggle/TrueSTUDIO/IO_Toggle/stm32_flash.ld */
11 MEMORY
12 {
13   FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 1024K
14   SRAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 192K
15   MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
16 }
17 
18 
19 SECTIONS
20 {
21   .text :
22   {
23     KEEP(*(.isr_vector .isr_vector.*))
24     *(.text .text.*)
25     *(.rodata .rodata*)
26     _etext = .;
27   } > FLASH
28 
29   /* .ARM.exidx is sorted, so has to go in its own output section.  
30         __exidx_start = .;
31         .ARM.exidx :
32         {
33                 *(.ARM.exidx* .gnu.linkonce.armexidx.*)
34         } >FLASH
35         __exidx_end = .;
36      if no -fno-exceptions -fno-rtti, g++ will procude the sections
37   */
38 
39   .data : AT (_etext)
40   {
41     _data = .;
42     *(.data .data.*)
43     _edata = .;
44   } > SRAM
45   .bss(NOLOAD) :
46   {
47     _bss = .;
48     *(.bss .bss.*)
49     *(COMMON)
50     . = ALIGN(4);
51     _ebss = .;
52   } > SRAM
53 
54 
55 
56 
57   .stackarea(NOLOAD) :
58   {
59     . = ALIGN(8);
60     *(.stackarea .stackares.*)
61     . = ALIGN(8);
62   } > SRAM
63 
64   . = ALIGN(4);
65   _end = .;
66 }

stm32.h
 1 #ifndef STM32_H
 2 #define STM32_H
 3 
 4 #define STACK_SIZE 64
 5 extern unsigned long _etext;
 6 extern unsigned long _data;
 7 extern unsigned long _edata;
 8 extern unsigned long _bss;
 9 extern unsigned long _ebss;
10 
11 extern "C"
12 {
13   void mymain(void);
14 }
15 
16 void ResetISR(void)
17 {
18   unsigned long *pulSrc, *pulDest;
19 
20   pulSrc = &_etext;
21   for (pulDest = &_data; pulDest < &_edata;)
22     *pulDest++ = *pulSrc++;
23   for (pulDest = &_bss; pulDest < &_ebss;)
24     *pulDest++ = 0;
25 
26 
27   mymain();
28 }
29 
30 void pendsv_isr(void)
31 {
32   int i=5;
33 
34   ++i;
35 }
36 
37 void svc_isr(void)
38 {
39 }
40 
41 void systick_isr(void)
42 {
43 }
44 void int_isr(void)
45 {
46 }
47 
48 typedef void (*pfnISR)(void);
49 __attribute__((section(".stackares")))
50 static unsigned long pulStack[STACK_SIZE];
51 
52 __attribute__((section(".isr_vector")))
53 pfnISR VectorTable[]=
54 {
55   (pfnISR)((unsigned long)pulStack+sizeof(pulStack)),
56   ResetISR, // 1
57   int_isr,
58   int_isr,
59   int_isr,
60   int_isr,
61   int_isr,
62   int_isr,
63   int_isr,
64   int_isr,
65   int_isr,
66   svc_isr,    // 11
67   int_isr,
68   int_isr,
69   pendsv_isr, // 14
70   systick_isr // 15
71 };
72 
73 #endif

附上重要的編譯參數:
arm-none-eabi-g++ -fno-common -O0 -g -mcpu=cortex-m3 -mthumb -fno-exceptions -fno-rtti -c mycpp.cpp 
arm-none-eabi-g++ -Wl,-T./stm32.ld -nostartfiles -o mycpp.elf mycpp.o
arm-none-eabi-objcopy -O binary mycpp.elf mycpp.bin

-fno-exceptions -fno-rtti 用來關閉 rtti 和 exception handle, 這需要額外的程式碼才能達成, 也就是 c++ runtime, 這部份我沒研究, 所以乾脆禁止使用這個功能, 這會損失不少效率, 所以不用也沒關係。

以下是一些有關開發 os kernel 相關的 gcc option
-nostartfiles
  Do not use the standard system startup files when linking.  The standard system libraries are used normally,
  unless -nostdlib or -nodefaultlibs is used.

-nodefaultlibs
  Do not use the standard system libraries when linking.  Only the libraries you specify will be passed to the
  linker, options specifying linkage of the system libraries, such as "-static-libgcc" or "-shared-libgcc",
  will be ignored.  The standard startup files are used normally, unless -nostartfiles is used.  The compiler
  may generate calls to "memcmp", "memset", "memcpy" and "memmove".  These entries are usually resolved by
  entries in libc.  These entry points should be supplied through some other mechanism when this option is specified.

-nostdlib
  Do not use the standard system startup files or libraries when linking.  No startup files and only the
  libraries you specify will be passed to the linker, options specifying linkage of the system libraries, such
  as "-static-libgcc" or "-shared-libgcc", will be ignored.  The compiler may generate calls to "memcmp",
  "memset", "memcpy" and "memmove".  These entries are usually resolved by entries in libc.  These entry points
  should be supplied through some other mechanism when this option is specified.

  One of the standard libraries bypassed by -nostdlib and -nodefaultlibs is libgcc.a,
  a library of internal subroutines that GCC uses to overcome shortcomings of particular machines,
  or special needs for some languages.

  In most cases, you need libgcc.a even when you want to avoid other standard libraries.  
  In other words, when you specify -nostdlib or -nodefaultlibs you should usually 
  specify -lgcc as well.  This ensures that you have no unresolved references to 
  internal GCC library subroutines.  (For example, __main, used to ensure C++
  constructors will be called.)

-ffreestanding
  Assert that compilation takes place in a freestanding environment.  This implies
  -fno-builtin.  A freestanding environment is one in which the standard library may not
  exist, and program startup may not necessarily be at "main".  The most obvious example is
  an OS kernel.  This is equivalent to -fno-hosted.

作業系統之前的程式 for stm32f4discovery (2) - 點亮 led, c version 這篇裡頭, 我不小心使用了
arm-none-eabi-objcopy -R .data -O binary $< $@

如果你知道這代表什麼意思, 相信你對執行檔的樣子很了解, 簡單說: 這會讓全域變數的初始值通通不見。

mycpp.cpp L13 i=5, i 不會是 5, 會是一個隨機值。但程式裡頭還是可以使用 i,
ex:
i=1;
i=9; 

所以這次我把 -R .data 去掉, 就沒問題了, 有好必有壞, 這樣的方便性是犧牲檔案大小得來的 (因為 .data 的內容都不會包含在執行檔裡頭), 但是若需要使用 i=1; 來初始化, 就不一定是犧牲了, i=1 會多 12 byte。透過 gdb single step 這程式, c++ 程式執行狀況良好。

source code: 老地方
git commit 21f49647dc48da5b3c8d2fcf1544be25d67bb45a

c++11 多了很多新的特性, 我們來試試看。

mycpp11.cpp
 1 #include "stm32.h"
 2 
 3 
 4 int print(int i)
 5 {
 6   i+=1;
 7 }
 8 
 9 int print(char c)
10 {
11  c-=1;
12 }
13 
14 int lambda_test(int girls = 3, int boys = 4)
15 {
16   auto totalChild = [](int x, int y) ->int{return x+y;};
17   return totalChild(girls, boys);
18 }
19 
20 
21 void mymain(void)
22 {
23   constexpr int d=1;
24   int x=3;
25   int y=9;
26 
27   char path[]=R"(/usr/local/aaa)";
28   char wpath[]=R"("\usr\local\aaa")";
29 
30   int total = lambda_test(x, y);
31   print(35);
32   print('A');
33   int arr[]={1,2,3,4,5};
34   for (int &e : arr)
35   {
36     print(e);
37   }
38   while(1);
39 }
40 

arm-none-eabi-g++ -fno-common -O0 -g -mcpu=cortex-m3 -mthumb  -fno-exceptions -fno-rtti -std=c++11  -c mycpp11.cpp 
arm-none-eabi-g++ -Wl,-T./stm32.ld -nostartfiles -fno-common -mcpu=cortex-m3 -mthumb -o mycpp11.elf mycpp11.o
arm-none-eabi-objcopy -O binary mycpp11.elf mycpp11.bin

descent@descent-u:cpp$ arm-none-eabi-g++ --version
arm-none-eabi-g++ (32-bit ARM EABI Toolchain JBS-2013.05-23-v2013.05-1-gd66a29f) 4.7.3
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

可以耶, 並不需要特別補上什麼 c++ runtime library, 這裡用上 c++11 lambda, constexpr, raw string literal, c++98 函式預設參數。威力有沒有很強大呢? 我不知道, 別問我。我是蠻喜歡 raw string literal。呃 ... 還有 L34 那個 for loop。

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。