[分享] 打造 c++ 標準程式庫 for stm32f407

看板C_and_CPP作者 (「雄辯是銀,沉默是金」)時間8年前 (2016/02/07 22:51), 8年前編輯推噓12(1203)
留言15則, 12人參與, 最新討論串1/2 (看更多)
接續的是 simple c++ 標準程式庫。我從來沒想過我會去寫標準程式庫, 這是無心插柳之 作。 標準程式庫是一個演算法和系統知識的結合, 和之前偏重在系統知識的程式不同, 比較偏 軟體, 我的演算法和資料結構能力太弱, 順道練習不是壞事。 不知道你有沒有和我一樣的感覺, 教科書上的演算法資料結構, 似乎在平凡人的工作上功 效不大, 總是有基本程式庫打造好這些, 我們只要去用就好了, 這也造成我們很難去練習 這樣的程式, 畢竟有了 std::list, 有誰能忍住誘惑不去用呢? 你說用 c 的怎麼辦, 有 發現在這樣環境下的工程師大部份都用 array 嗎? 很少用上 list, map 這些資料結構。 simple os 一開始也都是用 array, simple scheme 也是。 現在我迷上 bare-metal 程式, 剛好有個機會來練習這些基本功, 不是壞事, 只是能在學 生時代就練習到是比較好的。 在寫 simple os 時, 就已經寫了 (抄了) 不少相對應的 c function, 而在完成 simple scheme bare-metal 的版本時, 又多了一些資料結構, 再來實作 malloc 之後, 我終於有 了動態配置記憶體的能力, 可以寫 c++ 的容器 (list, vector, map ...), 好用不少, 再加上實作出 exception handle, 記憶體的管理方便多了, 又在回頭改善了 c++ 容器, 讓她更像標準程式庫的行為。 這是一個累積的成果, 把之前的努力集合起來, 再加上一些程式碼, 就是 c++ 標準程式 庫了。成果在這裡: https://github.com/descent/simple_stdcpplib ( https://goo.gl/K5Khpe ) 目前支援 3 個平台, p103 模擬器, stm32f4discovery board, raspberry pi2 (無法藉 由 uart 輸入), 所以也提供了 3 個 linker script。 要移植這個 library 有兩部份: 輸出/輸入 - 在這三個平台都是 uart c runtime - 如何從組合語言切換到 c/c++ c runtime 每個硬體平台都不同, 這便是從組合語言到 c 這段所需要最辛苦的過程, 從 這裡開始就可以擺脫組合語言, 用 c 了。當然如果你喜歡用組合語言來開發程式, 就沒 有這個需求。 在 p103/stm32f407 要做: bss init data section init rpi2 要做: bss init stack register 的設定 x86 要做: bss init stack register 的設定 使用保護模式的 Flat memory model 還要把 %ss, %ds, %cs 設到同一個 selector c runtime 除了和硬體相關之外還可能和你使用的 c compiler 有相關性, 我只有研究 gcc 這個 c compiler。 而 c++ runtime 則是 c runtime 做的事情, c++ 都要做, 而 c++ 有很多特性, 有些要 在 c++ runtime 支援, ex: global object static object rtti exception handle Pure virtual functions http://wiki.osdev.org/C%2b%2b#Pure_virtual_functions 未知 目前只支援 global object, 本來不想做的, 因為覺得過於麻煩 (這個可得從 linker script 開始著手) 也怕整個 code 太大, 不過 cout 是 global object, 就做吧! 反正 c++ 就是大。 feature list: c runtime c++ runtime global object ctor/dtor exception handle (implement by setjmp/longjmp) printf (support float) malloc/free new/delete vector string map list 想試試的朋友可以從 stm32 p103 模擬器開始: https://github.com/beckus/qemu_stm32.git ( https://goo.gl/5Sqynm ) 編譯出支援的模擬器之後, 再編譯本 library, 會有一個 mymain.bin, 執行 typescript L2 的 command, ctrl-A+X 離開 qemu。 typescript 1 Script started on Tue 12 Jan 2016 09:38:47 AM CST 2 descent@debian64: ~/git/qemu_stm32/arm-softmmu/qemu-system-arm -M stm32-p103 -kernel mymain.bin -nographic 3 4 (process:25512): GLib-WARNING **: /build/glib2.0-VKSJTv/glib2.0-2.46.1/./glib/gmem.c:482: custom memory allocation vtable not supported 5 STM32_UART: UART1 clock is set to 0 Hz. 6 STM32_UART: UART1 BRR set to 0. 7 STM32_UART: UART1 Baud is set to 0 bits per sec. 8 STM32_UART: UART2 clock is set to 0 Hz. 9 STM32_UART: UART2 BRR set to 0. 10 STM32_UART: UART2 Baud is set to 0 bits per sec. 11 STM32_UART: UART3 clock is set to 0 Hz. 12 STM32_UART: UART3 BRR set to 0. 13 STM32_UART: UART3 Baud is set to 0 bits per sec. 14 STM32_UART: UART4 clock is set to 0 Hz. 15 STM32_UART: UART4 BRR set to 0. 16 STM32_UART: UART4 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: UART5 clock is set to 0 Hz. 21 STM32_UART: UART5 BRR set to 0. 22 STM32_UART: UART5 Baud is set to 0 bits per sec. 23 STM32_UART: UART4 clock is set to 0 Hz. 24 STM32_UART: UART4 BRR set to 0. 25 STM32_UART: UART4 Baud is set to 0 bits per sec. 26 STM32_UART: UART3 clock is set to 0 Hz. 27 STM32_UART: UART3 BRR set to 0. 28 STM32_UART: UART3 Baud is set to 0 bits per sec. 29 STM32_UART: UART2 clock is set to 0 Hz. 30 STM32_UART: UART2 BRR set to 0. 31 STM32_UART: UART2 Baud is set to 0 bits per sec. 32 STM32_UART: UART1 clock is set to 0 Hz. 33 STM32_UART: UART1 BRR set to 0. 34 STM32_UART: UART1 Baud is set to 0 bits per sec. 35 LED Off 36 CLKTREE: HSI Output Change (SrcClk:None InFreq:8000000 OutFreq:8000000 Mul:1 Div:1 Enabled:1) 37 CLKTREE: HSI/2 Output Change (SrcClk:HSI InFreq:8000000 OutFreq:4000000 Mul:1 Div:2 Enabled:1) 38 CLKTREE: SYSCLK Output Change (SrcClk:HSI InFreq:8000000 OutFreq:8000000 Mul:1 Div:1 Enabled:1) 39 CLKTREE: HCLK Output Change (SrcClk:SYSCLK InFreq:8000000 OutFreq:8000000 Mul:1 Div:1 Enabled:1) 40 STM32_RCC: Cortex SYSTICK frequency set to 8000000 Hz (scale set to 125). 41 STM32_RCC: Cortex SYSTICK ext ref frequency set to 1000000 Hz (scale set to 1000). 42 CLKTREE: PCLK1 Output Change (SrcClk:HCLK InFreq:8000000 OutFreq:8000000 Mul:1 Div:1 Enabled:1) 43 CLKTREE: PCLK2 Output Change (SrcClk:HCLK InFreq:8000000 OutFreq:8000000 Mul:1 Div:1 Enabled:1) 44 CLKTREE: GPIOA Output Change (SrcClk:PCLK2 InFreq:8000000 OutFreq:8000000 Mul:1 Div:1 Enabled:1) 45 CLKTREE: AFIO Output Change (SrcClk:PCLK2 InFreq:8000000 OutFreq:8000000 Mul:1 Div:1 Enabled:1) 46 CLKTREE: UART2 Output Change (SrcClk:PCLK1 InFreq:8000000 OutFreq:8000000 Mul:1 Div:1 Enabled:1) 47 STM32_UART: UART2 clock is set to 8000000 Hz. 48 STM32_UART: UART2 BRR set to 0. 49 STM32_UART: UART2 Baud is set to 0 bits per sec. 50 STM32_UART: UART2 clock is set to 8000000 Hz. 51 STM32_UART: UART2 BRR set to 833. 52 STM32_UART: UART2 Baud is set to 9603 bits per sec. 53 i am cout ctor: 0 54 ---------------- 55 free_index: 0 56 57 0 0 0 0 0 0 0 0 58 0 0 0 0 0 0 0 0 59 0 0 0 0 0 0 0 0 60 0 0 0 0 0 0 0 0 61 0 0 0 0 0 0 0 0 62 0 0 0 0 0 0 0 0 63 0 0 0 0 0 0 0 0 64 0 0 0 0 0 0 0 0 65 ================ 66 fill ctor data: obj_count: 1, arg:537002040 67 i am cout ctor: 1 68 ---------------- 69 free_index: 0 70 71 0 0 0 0 0 0 0 0 72 0 0 0 0 0 0 0 0 73 0 0 0 0 0 0 0 0 74 0 0 0 0 0 0 0 0 75 0 0 0 0 0 0 0 0 76 0 0 0 0 0 0 0 0 77 0 0 0 0 0 0 0 0 78 0 0 0 0 0 0 0 0 79 ================ 80 fill ctor data: obj_count: 2, arg:537002048 81 i am cout ctor: 2 82 ---------------- 83 free_index: 0 84 85 0 0 0 0 0 0 0 0 86 0 0 0 0 0 0 0 0 87 0 0 0 0 0 0 0 0 88 0 0 0 0 0 0 0 0 89 0 0 0 0 0 0 0 0 90 0 0 0 0 0 0 0 0 91 0 0 0 0 0 0 0 0 92 0 0 0 0 0 0 0 0 93 ================ 94 fill ctor data: obj_count: 3, arg:537002056 95 vector ctor 96 abc 97 xyz 98 123 99 abcdefghijk!@#$%^&*() 100 test vec iterator 101 abc 102 xyz 103 123 104 abcdefghijk!@#$%^&*() 105 vector dtor 106 vector ctor 107 map ctor 108 (111,99) 109 (abc,51) 110 (abcdefghqq,151) 111 (zxcv,5) 112 map dtor 113 vector dtor 114 i: 0 115 i: 1 116 i: 2 117 i: 3 118 i: 4 119 i: 5 120 i: 6 121 i: 7 122 i: 8 123 i: 9 124 i: 10 125 i: 11 126 i: 12 127 i: 13 128 i: 14 129 i: 15 130 i: 16 131 i: 17 132 i: 18 133 i: 19 134 i: 20 135 i: 21 136 i: 22 137 i: 23 138 i: 24 139 i: 25 140 i: 26 141 i: 27 142 i: 28 143 i: 29 144 i: 30 145 i: 31 146 i: 32 147 i: 33 148 i: 34 149 i: 35 150 i: 36 151 i: 37 152 i: 38 153 i: 39 154 i: 40 155 i: 41 156 i: 42 157 i: 43 158 i: 44 159 i: 45 160 i: 46 161 i: 47 162 i: 48 163 i: 49 164 i: 50 165 i: 51 166 i: 52 167 i: 53 168 i: 54 169 i: 55 170 i: 56 171 i: 57 172 i: 58 173 i: 59 174 i: 60 175 i: 61 176 i: 62 177 i: 63 178 no mem 179 i am cout dtor: 0 180 i am cout dtor: 1 181 i am cout dtor: 2 182 QEMU: Terminated 183 ]0;descent@debian64: /media/work/git/jserv-course/simple_stdcpplib descent@debian64:simple_stdcpplib$ exit 184 185 Script done on Tue 12 Jan 2016 09:38:55 AM CST mymain.cpp 是一個 bare-metal 程式, 已經和在 os 下的 c++ 程式沒什麼分別了。測試 了 vector, map, exception handle, 我的記憶體分配區塊只有 64 塊, 所以 i 在分配 到 63 之後, 就沒有足夠的記憶體了, 會引發 exception 然後跑到 L178 的 no mem, 這 就是不用每次去 new 記憶體時都要檢查是不是有正確要到記憶體的祕密, 在一般 pc 的 環境下, 你可能根本沒看過 new 要不到記憶體的情形吧! 而也是因為有了 malloc/free 加上 exception handle 才能實作出 new/delete, c++ 的 ctor/dtor 非常的厲害, 你也可以說他很邪惡, new/delete 又插了一些程式碼來喚起需 要的 ctor, 反正都要呼叫的, 讓 c++ compiler 來幫我們一把不是壞事。 在 new 裡頭的 malloc 要不到記憶體的時候只要丟出一個 exception 就可以了, 不用繁 複的去檢查是不是有要到記憶體, 以前都沒想到 new 這樣的設計竟然是這麼的精細, 對 於記憶體的配置可以省下好大一番功夫, 重點是, 這並不需要付出很大的代價, 用 c 作 一樣的事情, 一樣要寫這些程式碼。 mymain.cpp 1 #include "myiostream.h" 2 #include "myvec.h" 3 #include "mymap.h" 4 #include "mystring.h" 5 6 using namespace DS; 7 8 int mymain() 9 { 10 int i=0; 11 12 { 13 vector<string> vec; 14 15 vec.push_back("abc"); 16 vec.push_back("xyz"); 17 vec.push_back("123"); 18 vec.push_back("abcdefghijk!@#$%^&*()"); 19 20 for (i=0 ; i < vec.size() ; ++i) 21 cout << vec[i] << endl; 22 cout << "test vec iterator" << endl; 23 for (auto it=vec.begin() ; it!=vec.end() ; ++it) 24 cout << *it << endl; 25 } 26 27 { 28 map<string, int> str_map; 29 30 str_map.insert({"zxcv", 5}); 31 str_map.insert({"abc", 51}); 32 str_map.insert({"abcdefghqq", 151}); 33 str_map.insert({"111", 99}); 34 35 for (auto it=str_map.begin() ; it!=str_map.end() ; ++it) 36 cout << "(" << it->k_ << "," << it->v_ << ")" << endl; 37 38 } 39 40 for (i=0 ; i < 100 ; ++i) 41 { 42 char *c = new char; 43 cout << "i: " << i << endl; 44 } 45 cout << "abc" << endl; 46 } 我沒有實作所有的 function, 只有實作我需要用到的部份, 而為了區分這是作業系統等 級的程式碼, 我用了 mymain 當 c++ 程式進入點, 而不是用 main; namespace 也不是 std 而是 DS。美中不足的是 initializer_list 我看不懂, 直接抄 newlib 的程式碼, 這樣在使用 map 的時候才可以用 {}, 不用 value_type 這麼恐怖的語法。 printf 算是比較難的 function, cout 容易多了, 而支援 float 的輸出也很有成就感, 慢慢學習基本中的基本, 感覺很紮實, 不沈淪於 it 世界中更新快速的技術, 一步一步慢 慢爬, 不急著囫圇吞棗。 gdeque 很類似 std::deque, 不過我這個是不會自己長大的 deque, 有大小限制的, 因為 我寫的時候還沒有實作 malloc, 所以只用固定大小, 也沒什麼不好, 就沒改了。 vector 還算簡單, 不過 capacity(), max_size(), size() 這些東西你平常有注意到嗎 ? iterator 的實作也不算太難, 有了 iterator 真的是好用不少。 list 也算是簡單的, iterator 也不算太難, 可以憑自己的想法搞定。 map 是其中最難的部份, 我當然不是以紅黑樹實作, 而是用很一般的 binary search tree, tree 這個資料結構本身就不容易, 而其 iterator 的實作實在太難了, 我沒有實 作出標準程式庫的版本。你認為 map::iterator++ 後要跑到哪一個呢? 標準程式庫有定 義, 是 inorder 的那個順序, 剛好是由小到大的那個次序。網路上有人用了另外的方法 實作, 而不是用 tree, iterator 就簡單多了。 比較麻煩的是還要提供 template 的版本, 語法看起來就很嚇人, 不熟 template 語法, 正好順道練習。 string 則不是 template 的版本, 而只是很單純的 char * 包裝而已。 目前用到組合語言的部份有: rpi2 start.s setjmp/longjmp (my_setjmp.S) 比想像中的少吧! 能儘量用 c++ 可以完成的我就儘量用 c++, 我並不熟悉組合語言, 還 是別自找苦吃, 而且用 c++ 可攜性比較好, 之前 x86 c++ runtime 我用組合語言寫, 移 植到 arm 就自己找麻煩了, 現在的版本移植回 x86 就簡單多了。 也許在 c 設計為主的 os 不需要這樣的介面, 不過長期使用 c++ 的我已經習慣這些用法 , 我想把這些東西帶到以 c++ 完成的 os kernel (目前尚未完成, 我才剛建好目錄而已) 。 本著對 c++ 的熱愛, 寫了這個 c++ 標準程式庫, 也用了部份 c++11 的語法。有機會的 話應該要移植到 x86 上, 讓 simple os 也可以使用。這個 library 只支援 c++, 雖然 有 printf 之類的 c 標準程式庫 function, 不過都已經是 c++ 了, 無法用 c compiler, 我沒打算支援 c compiler。 如果 c++ 標準程式庫是工業等級的程式碼, 那我這個 simple c++ 標準程式庫就是玩具 等級的程式碼, 雖說只是玩具等級, 但也付出不少功夫才完成的。 能掌握所有的程式碼真的很有滿足感, 沒有黑箱, 了解所有程式的來龍去脈, 真的舒服痛 快。 // 本文使用 Blog2BBS 自動將Blog文章轉成縮址的BBS純文字 http://goo.gl/TZ4E17 // blog 版本 http://descent-incoming.blogspot.tw/2016/01/for-stm32f4discovery-12-c.html -- 紙上得來終覺淺,絕知此事要躬行。 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 118.150.231.195 ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1454856674.A.455.html ※ 編輯: descent (118.150.231.195), 02/07/2016 22:51:38

02/08 00:33, , 1F
02/08 00:33, 1F

02/08 01:10, , 2F
02/08 01:10, 2F

02/08 01:49, , 3F
推有心!
02/08 01:49, 3F

02/08 10:40, , 4F
只能跪了
02/08 10:40, 4F

02/08 20:17, , 5F
02/08 20:17, 5F

02/08 21:29, , 6F
有神快拜
02/08 21:29, 6F

02/08 22:29, , 7F
02/08 22:29, 7F

02/09 17:42, , 8F
02/09 17:42, 8F

02/09 20:57, , 9F
如果是只想在stm32f系列上跑c++的話,mbed os也是個選
02/09 20:57, 9F

02/09 20:57, , 10F
02/09 20:57, 10F

02/09 20:58, , 11F
目前已知401/411/426都可以動
02/09 20:58, 11F

02/09 20:59, , 12F
407應該只要做部份修改
02/09 20:59, 12F

02/11 07:51, , 13F
讚讚讚
02/11 07:51, 13F

02/11 13:04, , 14F
孝心感動天 qq
02/11 13:04, 14F

02/15 11:00, , 15F
02/15 11:00, 15F
文章代碼(AID): #1MjrdYHL (C_and_CPP)
文章代碼(AID): #1MjrdYHL (C_and_CPP)