bare metal for stm32f4 - c++ std::vector

作者: descent (「雄辯是銀,沉默是金」)   2015-07-07 09:07:06
在克服 exception handling 的編譯問題後, 可以試試看呼叫 std::vector。mycpp.cpp
L150 ~ L153 是熟悉的 vector 用法, 若能在 bare metal 上用上這個, 多美好阿! 老是
用固定大小的 array, 彈性不夠, 覺得很不爽。
在使用了 vector 之後, 會有很多 undefined symbol, mycpp.cpp L22 ~ L84 把他們一
一補上, 你說 vector 為什麼會和 _write 有關係? 我也不知道, 反正補上就是了。
由於在 bare metal 環境並沒有類似 malloc 的記憶體配置 function (你應該不會預期
有這樣的 function 可以用), 而這種會自己長大的容器一定是需要這種操作的, 那該怎
麼辦呢?
從 toolchain library 補上 malloc/free, new/delete 這些東西, 這個可能工程大了點
, 我改用另外一個途徑, 使用自己的 allocator, 所以要先寫一個 allocator。
看來好像也是一個大工程, 不過有現成的可以抄, 我就抄過 (就是常常被詬病的
copy/paste) 來了 (mycpp.cpp L88 ~ 140)。再把原本的 malloc 換成我自己的
mymalloc 就成了。
一開始的 mymalloc 只是回傳一個固定的記憶體位址, L86 的 memarea, 看起來很簡單也
很可疑, 不過的確可以正常運作。
void *mymalloc(u32 size)
{
return memarea;
}
mycpp.cpp
1 #include "stm32.h"
2
3 #include "mem.h"
4
5 #include <vector>
6
7 int print(int i)
8 {
9 i+=1;
10 }
11
12 int print(char c)
13 {
14 c-=1;
15 }
16
17 int print(const char *str)
18 {
19 }
20
21
22 void *dso_handle_;
23 void *__dso_handle;
24
25 extern "C" void _exit()
26 {
27 }
28
29 char brk_area[10240];
30
31 extern "C" char *_sbrk(char *increment)
32 {
33 char *ptr = brk_area;
34 return ptr;
35 }
36
37 extern "C"
38 int _kill(int a, int b)
39 {
40 return a;
41 }
42
43 extern "C"
44 int _getpid()
45 {
46 int i;
47 return i;
48 }
49
50 extern "C"
51 int _write(int fd, const void *buf, int count)
52 {
53 }
54
55 extern "C"
56 int open(const char *pathname, int flags, int mode)
57 {
58 }
59
60 extern "C"
61 int _isatty(int fd)
62 {
63 }
64
65
66 extern "C"
67 int _close(int fd)
68 {
69 }
70
71 extern "C"
72 int _fstat(int fd, struct stat *buf)
73 {
74 }
75
76 extern "C"
77 int _read(int fd, void *buf, int count)
78 {
79 }
80
81 extern "C"
82 int _lseek(int fd, int offset, int whence)
83 {
84 }
85
86 static char memarea[10240];
87
88 template <class T>
89 class my_allocator
90 {
91 public:
92 typedef int size_type;
93 typedef int difference_type;
94 typedef T* pointer;
95 typedef const T* const_pointer;
96 typedef T& reference;
97 typedef const T& const_reference;
98 typedef T value_type;
99
100 my_allocator() {}
101 my_allocator(const my_allocator&) {}
102
103
104
105 pointer allocate(size_type n, const void * = 0) {
106 T* t = (T*) mymalloc(n * sizeof(T));
107 print("used my_allocator to allocate at address ");
108 print((int)t);
109 print("\r\n");
110
111 return t;
112 }
113
114 void deallocate(void* p, size_type) {
115 if (p) {
116 myfree(p);
117 print("used my_allocator to deallocate at address ");
118 print((int)p);
119 print("\r\n");
120 }
121 }
122
123 pointer address(reference x) const { return &x; }
124 const_pointer address(const_reference x) const { return &x; }
125 my_allocator<T>& operator=(const my_allocator&) { return *this; }
126 void construct(pointer p, const T& val)
127 { new ((T*) p) T(val); }
128 void destroy(pointer p) { p->~T(); }
129
130 size_type max_size() const { return int(-1); }
131
132 template <class U>
133 struct rebind { typedef my_allocator<U> other; };
134
135 template <class U>
136 my_allocator(const my_allocator<U>&) {}
137
138 template <class U>
139 my_allocator& operator=(const my_allocator<U>&) { return *this; }
140 };
141
142 void mymain(void)
143 {
144 #if 1
145 char a, b, c , d, e, f;
146 //std::vector<int, __gnu_cxx::new_allocator<int> > vec;
147 std::vector<char, my_allocator<char> > vec;
148 print(35);
149 print('A');
150 for (int i=0 ; i < 5 ; ++i)
151 {
152 vec.push_back(i);
153 }
154 a = vec[0];
155 b = vec[1];
156 c = vec[2];
157 d = vec[3];
158 e = vec[4];
159
160 char z=a+b+c+d;
161
162 while(1);
163 #endif
164 }
165
當然這個實在是太蠢了, 也會有一些問題, vector 在記憶體不夠的情況下, 會以 1, 2,
4, 8 的大小去配置需要的記憶體 (和書上寫的一樣耶!)。若是 vector<int> 就是 1*4,
2*4, 4*4, 8*4 去配置記憶體, 所以在某個情況下, 那個寫死的大小就會不夠用了。
所以我開發了一個相對不那麼蠢的, 對, 其實還是很蠢, 不過簡單, 又具有實用性, 還可
以驗證如果配置不到需要的記憶體, 會怎麼辦? 你一定很少看到 vector 要不到記憶體的
情況吧?
mem.cpp
1 #include <stdio.h>
2
3 const int PAGE = 64;
4 const int HEAP_SIZE = PAGE*1024;
5 char heap[HEAP_SIZE];
6 unsigned char mem_area[PAGE];
7 int free_index = 0;
8
9 void print_memarea()
10 {
11 printf("free_index: %d\n", free_index);
12 for (int i = 0 ; i < PAGE ; ++i)
13 {
14 if (i % 8 == 0)
15 printf("\n");
16 printf("%02d ", mem_area[i]);
17 }
18 printf("\n");
19 }
20
21 // size: 1 means 1K, size max is 64
22 void *mymalloc(unsigned char size)
23 {
24 if (free_index + size > PAGE)
25 {
26 return 0;
27 }
28 char * ptr = heap + free_index * 1024;
29 #if 0
30 printf("xx free_index: %d\n", free_index);
31 printf("xx heap: %p\n", heap);
32 printf("xx ptr: %p\n", ptr);
33 #endif
34 *(mem_area + free_index) = size;
35
36 for (int i = 1 ; i < size ; ++i)
37 *(mem_area + free_index + i) = 1;
38
39 free_index += size;
40 return (void*)ptr;
41 }
42
43 void myfree(void *ptr)
44 {
45 int index = ((char *)ptr - heap) / 1024;
46 unsigned char size = *mem_area;
47 for (int i=0 ; i < size ; ++i)
48 *(mem_area + i) = 0;
49 }
mem.cpp 從 64k 的 heap 分配記憶體, 很蠢的只有分配, 無法再度使用 myfree 回收的
記憶體。不過實作就容易多了。
RESULT
1
2 used my_allocator to allocate at address 0x804c6e0n: 1 size: 4 (+)
3 used my_allocator to allocate at address 0x804d6e0n: 2 size: 8 (+)
4 used my_allocator to deallocate at address 0x804c6e0 (-)
5 used my_allocator to allocate at address 0x804f6e0n: 4 size: 16 (+)
6 used my_allocator to deallocate at address 0x804d6e0 (-)
7 used my_allocator to allocate at address 0x80536e0n: 8 size: 32 (+)
8 used my_allocator to deallocate at address 0x804f6e0 (-)
9 cannot alloc memory
10 terminate called after throwing an instance of 'std::bad_alloc'
11 what(): std::bad_alloc
12 Aborted
以目前的記憶體管理設計, 當 vector 要以 32 去要記憶體時, 就會不夠用, 這時候
allocate 就會丟出 std::bad_alloc() (RESULT L11)。
RESULT 是在 linux 平台下的測試結果, 再搬到 stm32f4-discovery 上測試, 可以正常
的使用
vec.push_back(0)
vec.push_back(1)
vec.push_back(2)
vec.push_back(3)
vec.push_back(4)
不過由於在 stm32f4-discovery 不能丟出 std::bad_alloc(), 我得想個辦法處理要不到
記憶體的情況, 否則, vector 就不威了, 處理方式也很簡單, 印出訊息然後無窮回圈。
stm32f4-discovery sram 只有 192k, 很小的。
目前 github 的版本已經稍做改良, 可以用更細緻的方式配置記憶體, myfree 也會去回
收用過的記憶體, 當然還是比不上一般作業系統的的精緻度。
這是 gcc malloc_allocator 的實作, L98 也是使用 malloc, 並丟出 bad_alloc
exception。
gcc-4.9.2/libstdc++-v3/include/ext/malloc_allocator.h
1 // Allocator that wraps "C" malloc -*- C++ -*-
2
3 // Copyright (C) 2001-2014 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
9 // any later version.
10
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
19
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 // <http://www.gnu.org/licenses/>.
24
25 /** @file ext/malloc_allocator.h
26 * This file is a GNU extension to the Standard C++ Library.
27 */
28
29 #ifndef _MALLOC_ALLOCATOR_H
30 #define _MALLOC_ALLOCATOR_H 1
31
32 #include <cstdlib>
33 #include <new>
34 #include <bits/functexcept.h>
35 #include <bits/move.h>
36 #if __cplusplus >= 201103L
37 #include <type_traits>
38 #endif
39
40 namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
41 {
42 _GLIBCXX_BEGIN_NAMESPACE_VERSION
43
44 using std::size_t;
45 using std::ptrdiff_t;
46
47 /**
48 * @brief An allocator that uses malloc.
49 * @ingroup allocators
50 *
51 * This is precisely the allocator defined in the C++ Standard.
52 * - all allocation calls malloc
53 * - all deallocation calls free
54 */
55 template<typename _Tp>
56 class malloc_allocator
57 {
95 // NB: __n is permitted to be 0. The C++ standard says nothing
96 // about what the return value is when __n == 0.
97 pointer
98 allocate(size_type __n, const void* = 0)
99 {
100 if (__n > this->max_size())
101 std::__throw_bad_alloc();
102
103 pointer __ret = static_cast<_Tp*>(std::malloc(__n * sizeof(_Tp)));
104 if (!__ret)
105 std::__throw_bad_alloc();
106 return __ret;
107 }
148
149 _GLIBCXX_END_NAMESPACE_VERSION
150 } // namespace
151
152 #endif
以下是在 stm32 模擬器的完整範例:
https://github.com/descent/progs/tree/master/mem_alloc/mem.h
https://github.com/descent/progs/tree/master/mem_alloc/mem.cpp
https://github.com/descent/stm32_p103_demos/tree/master/demos/uart_echo/myvec
commit 317bde9c68f38eab73969a9526247dfb6bd1a870
output
1 descent@debianlinux:myvec$ /media/2/qemu_stm32/arm-softmmu/qemu-system-arm
-display sdl -M stm32-p103 -kernel myvec.bin -nographic
2 STM32_UART: UART1 clock is set to 0 Hz.
3 STM32_UART: UART1 BRR set to 0.
4 STM32_UART: UART1 Baud is set to 0 bits per sec.
5 STM32_UART: UART2 clock is set to 0 Hz.
6 STM32_UART: UART2 BRR set to 0.
7 STM32_UART: UART2 Baud is set to 0 bits per sec.
8 STM32_UART: UART3 clock is set to 0 Hz.
9 STM32_UART: UART3 BRR set to 0.
10 STM32_UART: UART3 Baud is set to 0 bits per sec.
11 STM32_UART: UART4 clock is set to 0 Hz.
12 STM32_UART: UART4 BRR set to 0.
13 STM32_UART: UART4 Baud is set to 0 bits per sec.
14 STM32_UART: UART5 clock is set to 0 Hz.
15 STM32_UART: UART5 BRR set to 0.
16 STM32_UART: UART5 Baud is set to 0 bits per sec.
17 STM32_UART: UART5 clock is set to 0 Hz.
18 STM32_UART: UART5 BRR set to 0.
19 STM32_UART: UART5 Baud is set to 0 bits per sec.
20 STM32_UART: UART4 clock is set to 0 Hz.
21 STM32_UART: UART4 BRR set to 0.
22 STM32_UART: UART4 Baud is set to 0 bits per sec.
23 STM32_UART: UART3 clock is set to 0 Hz.
24 STM32_UART: UART3 BRR set to 0.
25 STM32_UART: UART3 Baud is set to 0 bits per sec.
26 STM32_UART: UART2 clock is set to 0 Hz.
27 STM32_UART: UART2 BRR set to 0.
28 STM32_UART: UART2 Baud is set to 0 bits per sec.
29 STM32_UART: UART1 clock is set to 0 Hz.
30 STM32_UART: UART1 BRR set to 0.
31 STM32_UART: UART1 Baud is set to 0 bits per sec.
32 LED Off
33 CLKTREE: HSI Output Change (SrcClk:None InFreq:8000000 OutFreq:8000000
Mul:1 Div:1 Enabled:1)
34 CLKTREE: HSI/2 Output Change (SrcClk:HSI InFreq:8000000 OutFreq:4000000
Mul:1 Div:2 Enabled:1)
35 CLKTREE: SYSCLK Output Change (SrcClk:HSI InFreq:8000000 OutFreq:8000000
Mul:1 Div:1 Enabled:1)
36 CLKTREE: HCLK Output Change (SrcClk:SYSCLK InFreq:8000000 OutFreq:8000000
Mul:1 Div:1 Enabled:1)
37 STM32_RCC: Cortex SYSTICK frequency set to 8000000 Hz (scale set to 125).
38 STM32_RCC: Cortex SYSTICK ext ref frequency set to 1000000 Hz (scale set
to 1000).
39 CLKTREE: PCLK1 Output Change (SrcClk:HCLK InFreq:8000000 OutFreq:8000000
Mul:1 Div:1 Enabled:1)
40 CLKTREE: PCLK2 Output Change (SrcClk:HCLK InFreq:8000000 OutFreq:8000000
Mul:1 Div:1 Enabled:1)
41 CLKTREE: HSE Output Change (SrcClk:None InFreq:8000000 OutFreq:8000000
Mul:1 Div:1 Enabled:1)
42 CLKTREE: HSE/2 Output Change (SrcClk:HSE InFreq:8000000 OutFreq:4000000
Mul:1 Div:2 Enabled:1)
43 CLKTREE: PLLXTPRE Output Change (SrcClk:HSE InFreq:8000000 OutFreq:8000000
Mul:1 Div:1 Enabled:1)
44 CLKTREE: PCLK1 Output Change (SrcClk:HCLK InFreq:8000000 OutFreq:4000000
Mul:1 Div:2 Enabled:1)
45 CLKTREE: PLLCLK Output Change (SrcClk:PLLXTPRE InFreq:8000000
OutFreq:72000000 Mul:9 Div:1 Enabled:1)
46 CLKTREE: SYSCLK Output Change (SrcClk:PLLCLK InFreq:72000000
OutFreq:72000000 Mul:1 Div:1 Enabled:1)
47 CLKTREE: HCLK Output Change (SrcClk:SYSCLK InFreq:72000000
OutFreq:72000000 Mul:1 Div:1 Enabled:1)
48 STM32_RCC: Cortex SYSTICK frequency set to 72000000 Hz (scale set to 13).
49 STM32_RCC: Cortex SYSTICK ext ref frequency set to 9000000 Hz (scale set
to 111).
50 CLKTREE: PCLK1 Output Change (SrcClk:HCLK InFreq:72000000 OutFreq:36000000
Mul:1 Div:2 Enabled:1)
51 CLKTREE: PCLK2 Output Change (SrcClk:HCLK InFreq:72000000 OutFreq:72000000
Mul:1 Div:1 Enabled:1)
52 CLKTREE: GPIOA Output Change (SrcClk:PCLK2 InFreq:72000000
OutFreq:72000000 Mul:1 Div:1 Enabled:1)
53 CLKTREE: AFIO Output Change (SrcClk:PCLK2 InFreq:72000000 OutFreq:72000000
Mul:1 Div:1 Enabled:1)
54 CLKTREE: UART2 Output Change (SrcClk:PCLK1 InFreq:36000000
OutFreq:36000000 Mul:1 Div:1 Enabled:1)
55 STM32_UART: UART2 clock is set to 36000000 Hz.
56 STM32_UART: UART2 BRR set to 0.
57 STM32_UART: UART2 Baud is set to 0 bits per sec.
58 STM32_UART: UART2 clock is set to 36000000 Hz.
59 STM32_UART: UART2 BRR set to 3750.
60 STM32_UART: UART2 Baud is set to 9600 bits per sec.
61 mymalloc: 1 byte(s)
62 used my_allocator to allocate at address 20002374 n: 1 sizeof(T): 1
63 mymalloc: 2 byte(s)
64 used my_allocator to allocate at address 20002774 n: 2 sizeof(T): 1
65 myfree: p
66 index: 0
67 size: 1
68 used my_allocator to free address 20002374
69 mymalloc: 4 byte(s)
70 used my_allocator to allocate at address 20002B74 n: 4 sizeof(T): 1
71 myfree: p
72 index: 1
73 size: 1
74 used my_allocator to free address 20002774
75 mymalloc: 8 byte(s)
76 used my_allocator to allocate at address 20002F74 n: 8 sizeof(T): 1
77 myfree: p
78 index: 2
79 size: 1
80 used my_allocator to free address 20002B74
81 0
82 1
83 2
84 3
85 4
86
87
作者: loveme00835 (髮箍)   2015-07-09 22:45:00
這篇是在..?
作者: descent (「雄辯是銀,沉默是金」)   2015-07-10 23:26:00
在 bare-metal 環境下, 如何使用 vector

Links booklink

Contact Us: admin [ a t ] ucptt.com