From beb33ffab3f966446ccbcf11093de2383c8844bd Mon Sep 17 00:00:00 2001 From: armink Date: Tue, 27 Dec 2016 09:09:00 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E3=80=90=E5=A2=9E=E5=8A=A0=E3=80=91?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E7=89=88=E6=9C=AC=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: armink --- .gitattributes | 40 ++ LICENSE | 22 + README.md | 163 +++++++ cm_backtrace/cm_backtrace.c | 610 +++++++++++++++++++++++++++ cm_backtrace/cm_backtrace.h | 40 ++ cm_backtrace/cmb_cfg.h | 47 +++ cm_backtrace/cmb_def.h | 342 +++++++++++++++ cm_backtrace/cmb_fault.s | 53 +++ demo/non-os/stm32f10x/README.md | 5 + demo/os/freertos/stm32f10x/README.md | 5 + demo/os/rtthread/stm32f4xx/README.md | 5 + demo/os/ucosii/stm32f20x/README.md | 5 + 12 files changed, 1337 insertions(+) create mode 100644 .gitattributes create mode 100644 LICENSE create mode 100644 README.md create mode 100644 cm_backtrace/cm_backtrace.c create mode 100644 cm_backtrace/cm_backtrace.h create mode 100644 cm_backtrace/cmb_cfg.h create mode 100644 cm_backtrace/cmb_def.h create mode 100644 cm_backtrace/cmb_fault.s create mode 100644 demo/non-os/stm32f10x/README.md create mode 100644 demo/os/freertos/stm32f10x/README.md create mode 100644 demo/os/rtthread/stm32f4xx/README.md create mode 100644 demo/os/ucosii/stm32f20x/README.md diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a6ec1ac --- /dev/null +++ b/.gitattributes @@ -0,0 +1,40 @@ +* text=auto + +*.S text +*.asm text +*.c text +*.cc text +*.cpp text +*.cxx text +*.h text +*.htm text +*.html text +*.in text +*.ld text +*.m4 text +*.mak text +*.mk text +*.py text +*.rb text +*.s text +*.sct text +*.sh text +*.txt text +*.xml text +Makefile text +AUTHORS text +COPYING text + +*.LZO -text +*.Opt -text +*.Uv2 -text +*.ewp -text +*.eww -text +*.vcproj -text +*.bat -text +*.dos -text +*.icf -text +*.inf -text +*.ini -text +*.sct -text +*.xsd -text diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6b761d3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016 Armink (armink.ztl@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..69e6b54 --- /dev/null +++ b/README.md @@ -0,0 +1,163 @@ +# CmBacktrace: ARM Cortex-M 系列 MCU 错误追踪库 + +--- + +## 0、CmBacktrace 是什么 + +[CmBacktrace](https://github.com/armink/CmBacktrace) (Cortex Microcontroller Backtrace)是一款开源的针对 ARM Cortex-M 系列 MCU 的错误追踪库。主要特性如下: + +- 追踪的对象包括: + - 断言(assert) + - 故障(Hard Fault, Memory Management Fault, Bus Fault, Usage Fault, Debug Fault) +- **自动诊断** 故障原因,无需手动分析故障寄存器; +- 输出断言及故障现场时的 **函数调用栈**(需配合 addr2line 工具进行精确定位),定位问题代码更加快捷、精准。也可以在正常状态下使用该库,获取当时的函数调用栈; +- 支持 裸机 及以下操作系统平台: + - [RT-Thread](http://www.rt-thread.org/) + - UCOS + - FreeRTOS +- 根据错误现场状态,输出对应的 线程栈 或 C 主栈; +- 故障诊断信息支持多国语言(目前:简体中文、英文); +- 适配 Cortex-M0/M3/M4/M7 MCU; +- 支持 IAR、KEIL、GCC 编译器; + +## 1、为什么选择 CmBacktrace + +**入门新人** :对于从 C51 、MSP430 等简单单片机转而使用更加复杂的 ARM 新人来说,时不时出现的 "hard falut" 死机会让新人瞬间懵掉。定位错误的方法也往往是连接上仿真器,一步步 F10/F11 单步,定位到具体的错误代码,再去猜测、排除、推敲错误原因,这种过程十分痛苦。 + +**熟练老手** :慢慢的大家知道可以通过故障寄存器信息来定位故障原因及故障代码地址,虽然这样能解决一小部分问题,但是重复的、繁琐的分析过程也会耽误很多时间。而且对于一些复杂问题,只依靠代码地址是无法解决的,必须得还原错误现场的函数调用逻辑关系。此时产品可能无法连接仿真器或者即便连接仿真器,问题也很难被复现,所以定位这类问题就显得难上加难。 + +**使用本库** :上述所有问题都迎刃而解,可以将错误信息输出到控制台上,还可以将错误信息使用 [EasyFlash](https://github.com/armink/EasyFlash) 的 Log 功能保存至 Flash 中,设备死机后重启依然能够读取上次的错误信息。CmBacktrace 输出的信息包括函数调用栈、故障诊断结果、堆栈、故障寄存器及产品固件信息,极大的提升了错误定位的效率及准确性。 + +俗话说,工欲善其事,必先利其器。所以有时候做事效率低的原因也许是,你会用的工具种类太少。 + +**合作、贡献** :开源软件的发展离不开大家的支持,欢迎大家多提建议,也希望更多的人一起参与进来,共同提高 。如果觉得这个开源项目很赞,可以点击 [项目主页](https://github.com/armink/CmBacktrace) **([Github](https://github.com/armink/CmBacktrace)|[OSChina](http://git.oschina.net/armink/CmBacktrace)|[Coding](https://coding.net/u/armink/p/CmBacktrace/git))** 右上角的 **Star** ,同时把它推荐给更多有需要的朋友。 + +## 2、CmBacktrace 如何使用 + +### 2.1 演示 + +- 制造除零异常 +- 查看错误诊断信息 +- 查看函数调用栈 +- 定位错误代码 +- 检查出错代码 + +### 2.2 Demo + +|目录|平台|链接| +|:--|:--:|:--:| +| `\demo\non-os\stm32f10x` |裸机 STM32 Cortex-M3|[点击查看](https://github.com/armink/CmBacktrace/tree/master/demo/non_os/stm32f10x)| +| `\demo\os\freertos\stm32f10x` |FreeRTOS STM32 Cortex-M3|[点击查看](https://github.com/armink/CmBacktrace/tree/master/demo/os/freertos/stm32f10x)| +| `\demo\os\ucosii\stm32f20x` |UCOSII STM32 Cortex-M3|[点击查看](https://github.com/armink/CmBacktrace/tree/master/demo/os/ucosii/stm32f20x)| +| `\demo\os\rtthread\stm32f4xx`|RT-Thread STM32 Cortex-M4|[点击查看](https://github.com/armink/CmBacktrace/tree/master/demo/os/rtthread/stm32f4xx)| + +### 2.3 移植说明 + +#### 2.3.1 准备工作 + +- 1、查看 `\demo` 目录下有没有合适自己的 Demo ,如有类似,则建议在其基础上修改 +- 2、明确操作系统/裸机平台及 CPU 平台 +- 3、将 `\src` 下的全部源文件添加至产品工程中,并保证源码目录被添加至头文件路径 +- 4、cmb_fault.s 汇编文件可以选择性添加至工程,添加后一方面需要将文件中的需要将文件中的 `#if 0` 改为 `#if 1` ,另一方面需要把项目原有的 `HardFault_Handler` 注释掉 +- 5、把 `cm_backtrace_init` 函数放在项目初始化地方执行 +- 6、将 `cm_backtrace_assert` 放在项目的断言函数中执行,具体使用方法参照下面的 API 说明 +- 7、如果第 4 步骤没有将 cmb_fault.s 汇编文件启用,则需要将 `cm_backtrace_fault` 放到故障处理函数(例如: `HardFault_Handler` )中执行,具体使用方法参照下面的 API 说明 + +#### 2.3.2 配置说明 + +配置文件名: `cmb_cfg.h` ,针对不同的平台和场景,用户需要自自行手动配置,常用配置如下: + +| 配置名称 |功能|备注| +|:--|:--|:--| +|cmb_println(...)|错误及诊断信息输出|必须配置| +|CMB_USING_BARE_METAL_PLATFORM|是否使用在裸机平台|使用则定义该宏| +|CMB_USING_OS_PLATFORM|是否使用在操作系统平台|操作系统与裸机必须二选一| +|CMB_OS_PLATFORM_TYPE|操作系统平台|RTT/UCOSII/UCOSIII/FREERTOS| +|CMB_CPU_PLATFORM_TYPE|CPU平台|M0/M3/M4/M7| +|CMB_USING_DUMP_STACK_INFO|是否使用 Dump 堆栈的功能|使用则定义该宏| +|CMB_PRINT_LANGUAGE|输出信息时的语言|CHINESE/ENGLISH| + +> 注意:以上部分配置的内容可以在 `cmb_def.h` 中选择,更多灵活的配置请阅读源码 + +### 2.4 API 说明 + +#### 2.4.1 库初始化 + +```C +void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver) +``` + +|参数 |描述| +|:----- |:----| +|firmware_name |固件名称,需与编译器生成的固件名称对应| +|hardware_ver |硬件版本号| +|software_ver |软件版本号| + +> **注意** :以上入参将会在断言或故障时输出,主要起了追溯的作用 + +#### 2.4.2 获取函数调用栈 + +```C +size_t cm_backtrace_call_stack(uint32_t *buffer, size_t size, uint32_t sp) +``` + +|参数 |描述| +|:----- |:----| +|buffer |存储函数调用栈的缓冲区| +|size |缓冲区大小| +|sp |待获取的堆栈指针| + +示例: + +```C +/* 建立深度为 16 的函数调用栈缓冲区,深度大小不应该超过 CMB_CALL_STACK_MAX_DEPTH(默认16) */ +uint32_t call_stack[16] = {0}; +size_t i, depth = 0; +/* 获取当前环境下的函数调用栈,每个元素将会以 32 位地址形式存储, depth 为函数调用栈实际深度 */ +depth = cm_backtrace_call_stack(call_stack, sizeof(call_stack), __get_SP()); +/* 输出当前函数调用栈信息 + * 注意:查看函数名称及具体行号时,需要使用 addr2line 工具转换 + */ +for (i = 0; i < depth; i++) { + printf("%08x ", call_stack[i]); +} +``` + +#### 2.4.3 追踪断言错误信息 + +```C +void cm_backtrace_assert(uint32_t sp) +``` + +|参数 |描述| +|:----- |:----| +|sp |断言环境时的堆栈指针| + +> **注意** :入参 SP 尽量在断言函数内部获取,而且尽可能靠近断言函数开始的位置。当在断言函数的子函数中(例如:在 RT-Thread 的断言钩子方法中)使用时,由于函数嵌套会存在寄存器入栈的操作,此时再获取 SP 将发生变化,就需要人为调整(加减固定的偏差值)入参值,所以作为新手 **不建议在断言的子函数** 中使用该函数。 + +#### 2.4.4 追踪故障错误信息 + +```C +void cm_backtrace_fault(uint32_t fault_handler_lr, uint32_t fault_handler_sp) +``` + +|参数 |描述| +|:----- |:----| +|fault_handler_lr |故障处理函数环境下的 LR 寄存器值| +|fault_handler_sp |故障处理函数环境下的 SP 寄存器值| + +该函数可以在故障处理函数(例如: `HardFault_Handler`)中调用。另外,库本身提供了 `HardFault` 处理的汇编文件,会在故障时自动调用 `cm_backtrace_fault` 方法。所以移植时,最简单的方式就是直接使用该汇编文件(把 cmb_fault.s 添加到工程,并修改 `#if 0` 为 `#if 1` )。 + +### 2.5 常见错误 + +#### 编译出错,提示需要 C99 支持 + +开启 C99 教程: [点击查看]() + +#### 无法准确查看到函数调用栈中的函数名及代码行号 +#### 无法准备获取到故障时的寄存器信息 +#### HardFault_Handler 重复定义 + +### 2.6 许可 + +采用 MIT 开源协议,细节请阅读项目中的 LICENSE 文件内容。 \ No newline at end of file diff --git a/cm_backtrace/cm_backtrace.c b/cm_backtrace/cm_backtrace.c new file mode 100644 index 0000000..bee6dd3 --- /dev/null +++ b/cm_backtrace/cm_backtrace.c @@ -0,0 +1,610 @@ +/* + * This file is part of the CmBacktrace Library. + * + * Copyright (c) 2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Initialize function and other general function. + * Created on: 2016-12-15 + */ + +#include +#include +#include +#include + +#if defined(__CC_ARM) + #define SECTION_START(_name_) _name_##$$Base + #define SECTION_END(_name_) _name_##$$Limit + #define IMAGE_SECTION_START(_name_) Image$$##_name_##$$Base + #define IMAGE_SECTION_END(_name_) Image$$##_name_##$$Limit + #define CSTACK_BLOCK_START(_name_) SECTION_START(_name_) + #define CSTACK_BLOCK_END(_name_) SECTION_END(_name_) + #define CODE_SECTION_START(_name_) IMAGE_SECTION_START(_name_) + #define CODE_SECTION_END(_name_) IMAGE_SECTION_END(_name_) + + extern const int CODE_SECTION_START(CMB_CODE_SECTION_NAME); + extern const int CODE_SECTION_END(CMB_CODE_SECTION_NAME); + extern const int CSTACK_BLOCK_START(CMB_CSTACK_BLOCK_NAME); + extern const int CSTACK_BLOCK_END(CMB_CSTACK_BLOCK_NAME); + //TODO +#elif defined(__ICCARM__) + #pragma section=CMB_CSTACK_BLOCK_NAME + #pragma section=CMB_CODE_SECTION_NAME +#elif defined(__GNUC__) + #error "not implemented, I hope you can do this" + //TODO ʵ +#else + #error "not supported compiler" +#endif + +enum { + PRINT_FIRMWARE_INFO, + PRINT_ASSERT_ON_THREAD, + PRINT_ASSERT_ON_HANDLER, + PRINT_THREAD_STACK_INFO, + PRINT_MAIN_STACK_INFO, + PRINT_CALL_STACK_INFO, + PRINT_CALL_STACK_ERR, + PRINT_FAULT_ON_THREAD, + PRINT_FAULT_ON_HANDLER, + PRINT_REGS_TITLE, + PRINT_HFSR_VECTBL, + PRINT_MFSR_IACCVIOL, + PRINT_MFSR_DACCVIOL, + PRINT_MFSR_MUNSTKERR, + PRINT_MFSR_MSTKERR, + PRINT_MFSR_MLSPERR, + PRINT_BFSR_IBUSERR, + PRINT_BFSR_PRECISERR, + PRINT_BFSR_IMPREISERR, + PRINT_BFSR_UNSTKERR, + PRINT_BFSR_STKERR, + PRINT_BFSR_LSPERR, + PRINT_UFSR_UNDEFINSTR, + PRINT_UFSR_INVSTATE, + PRINT_UFSR_INVPC, + PRINT_UFSR_NOCP, + PRINT_UFSR_UNALIGNED, + PRINT_UFSR_DIVBYZERO, + PRINT_DFSR_HALTED, + PRINT_DFSR_BKPT, + PRINT_DFSR_DWTTRAP, + PRINT_DFSR_VCATCH, + PRINT_DFSR_EXTERNAL, +}; + +static const char *print_info[] = { +#if (CMB_PRINT_LANGUAGE == CMB_PRINT_LANGUAGE_ENGLISH) + [PRINT_FIRMWARE_INFO] = {"Firmware name: %s, hardware version: %s, software version: %s"}, + [PRINT_ASSERT_ON_THREAD] = {"Assert on thread %s"}, + [PRINT_ASSERT_ON_HANDLER] = {"Assert on interrupt or bare metal(no OS) environment"}, + [PRINT_THREAD_STACK_INFO] = {"===== Thread stack information ====="}, + [PRINT_MAIN_STACK_INFO] = {"====== Main stack information ======"}, + [PRINT_CALL_STACK_INFO] = {"Show more call stack info by run: addr2line -e %s%s -a -f %.*s"}, + [PRINT_CALL_STACK_ERR] = {"Dump call stack has an error"}, + [PRINT_FAULT_ON_THREAD] = {"Fault on thread %s"}, + [PRINT_FAULT_ON_HANDLER] = {"Fault on interrupt or bare metal(no OS) environment"}, + [PRINT_REGS_TITLE] = {"=================== Registers information ===================="}, + [PRINT_HFSR_VECTBL] = {"Hard fault is caused by failed vector fetch"}, + [PRINT_MFSR_IACCVIOL] = {"Memory management fault is caused by instruction access violation"}, + [PRINT_MFSR_DACCVIOL] = {"Memory management fault is caused by data access violation"}, + [PRINT_MFSR_MUNSTKERR] = {"Memory management fault is caused by unstacking error"}, + [PRINT_MFSR_MSTKERR] = {"Memory management fault is caused by stacking error"}, + [PRINT_MFSR_MLSPERR] = {"Memory management fault is caused by floating-point lazy state preservation"}, + [PRINT_BFSR_IBUSERR] = {"Bus fault is caused by instruction access violation"}, + [PRINT_BFSR_PRECISERR] = {"Bus fault is caused by precise data access violation"}, + [PRINT_BFSR_IMPREISERR] = {"Bus fault is caused by imprecise data access violation"}, + [PRINT_BFSR_UNSTKERR] = {"Bus fault is caused by unstacking error"}, + [PRINT_BFSR_STKERR] = {"Bus fault is caused by stacking error"}, + [PRINT_BFSR_LSPERR] = {"Bus fault is caused by floating-point lazy state preservation"}, + [PRINT_UFSR_UNDEFINSTR] = {"Usage fault is caused by attempts to execute an undefined instruction"}, + [PRINT_UFSR_INVSTATE] = {"Usage fault is caused by attempts to switch to an invalid state (e.g., ARM)"}, + [PRINT_UFSR_INVPC] = {"Usage fault is caused by attempts to do an exception with a bad value in the EXC_RETURN number"}, + [PRINT_UFSR_NOCP] = {"Usage fault is caused by attempts to execute a coprocessor instruction"}, + [PRINT_UFSR_UNALIGNED] = {"Usage fault is caused by indicates that an unaligned access fault has taken place"}, + [PRINT_UFSR_DIVBYZERO] = {"Usage fault is caused by Indicates a divide by zero has taken place (can be set only if DIV_0_TRP is set)"}, + [PRINT_DFSR_HALTED] = {"Debug fault is caused by halt requested in NVIC"}, + [PRINT_DFSR_BKPT] = {"Debug fault is caused by BKPT instruction executed"}, + [PRINT_DFSR_DWTTRAP] = {"Debug fault is caused by DWT match occurred"}, + [PRINT_DFSR_VCATCH] = {"Debug fault is caused by Vector fetch occurred"}, + [PRINT_DFSR_EXTERNAL] = {"Debug fault is caused by EDBGRQ signal asserted"}, +#elif (CMB_PRINT_LANGUAGE == CMB_PRINT_LANUUAGE_CHINESE) + [PRINT_FIRMWARE_INFO] = {"̼ƣ%sӲ汾ţ%s汾ţ%s"}, + [PRINT_ASSERT_ON_THREAD] = {"߳(%s)з"}, + [PRINT_ASSERT_ON_HANDLER] = {"жϻ·"}, + [PRINT_THREAD_STACK_INFO] = {"=========== ̶߳ջϢ ==========="}, + [PRINT_MAIN_STACK_INFO] = {"============ ջϢ ============"}, + [PRINT_CALL_STACK_INFO] = {"鿴ຯջϢУaddr2line -e %s%s -a -f %.*s"}, + [PRINT_CALL_STACK_ERR] = {"ȡջʧ"}, + [PRINT_FAULT_ON_THREAD] = { "߳(%s)з쳣" }, + [PRINT_FAULT_ON_HANDLER] = {"жϻ·쳣"}, + [PRINT_REGS_TITLE] = {"========================= ĴϢ ========================="}, + [PRINT_HFSR_VECTBL] = {"Ӳԭȡжʱ"}, + [PRINT_MFSR_IACCVIOL] = {"洢ԭͼӲʵȡָ"}, + [PRINT_MFSR_DACCVIOL] = {"洢ԭͼӲʵд"}, + [PRINT_MFSR_MUNSTKERR] = {"洢ԭ򣺳ջʱͼʲ"}, + [PRINT_MFSR_MSTKERR] = {"洢ԭջʱͼʲ"}, + [PRINT_MFSR_MLSPERR] = {"洢ԭ򣺶Ա渡״̬ʱ"}, + [PRINT_BFSR_IBUSERR] = {"ߴԭָߴ"}, + [PRINT_BFSR_PRECISERR] = {"ߴԭ򣺾ȷߴ"}, + [PRINT_BFSR_IMPREISERR] = {"ߴԭ򣺲ȷߴ"}, + [PRINT_BFSR_UNSTKERR] = {"ߴԭ򣺳ջʱ"}, + [PRINT_BFSR_STKERR] = {"ߴԭջʱ"}, + [PRINT_BFSR_LSPERR] = {"ߴԭ򣺶Ա渡״̬ʱ"}, + [PRINT_UFSR_UNDEFINSTR] = {"÷ԭͼִδָ"}, + [PRINT_UFSR_INVSTATE] = {"÷ԭͼл ARM ״̬"}, + [PRINT_UFSR_INVPC] = {"÷ԭЧ쳣"}, + [PRINT_UFSR_NOCP] = {"÷ԭͼִЭָ"}, + [PRINT_UFSR_UNALIGNED] = {"÷ԭͼִзǶ"}, + [PRINT_UFSR_DIVBYZERO] = {"÷ԭͼִг 0 "}, + [PRINT_DFSR_HALTED] = {"ԴԭNVIC ͣ"}, + [PRINT_DFSR_BKPT] = {"Դԭִ BKPT ָ"}, + [PRINT_DFSR_DWTTRAP] = {"Դԭݼƥ"}, + [PRINT_DFSR_VCATCH] = {"Դԭ򣺷"}, + [PRINT_DFSR_EXTERNAL] = {"Դԭⲿ"}, +#else + #error "CMB_PRINT_LANGUAGE defined error in 'cmb_cfg.h'" +#endif +}; + +static char fw_name[CMB_NAME_MAX] = {0}; +static char hw_ver[CMB_NAME_MAX] = {0}; +static char sw_ver[CMB_NAME_MAX] = {0}; +static uint32_t main_stack_start_addr = 0; +static size_t main_stack_size = 0; +static uint32_t code_start_addr = 0; +static size_t code_size = 0; +static bool init_ok = false; +static char call_stack_info[CMB_CALL_STACK_MAX_DEPTH * (8 + 1)] = { 0 }; +static bool on_fault = false; +static struct cmb_hard_fault_regs regs; +static bool on_thread_before_fault = false; +static bool statck_has_fpu_regs = false; + +/** + * library initialize + */ +void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver) { + strncpy(fw_name, firmware_name, CMB_NAME_MAX); + strncpy(hw_ver, hardware_ver, CMB_NAME_MAX); + strncpy(sw_ver, software_ver, CMB_NAME_MAX); + +#if defined(__CC_ARM) + main_stack_start_addr = (uint32_t)&CODE_SECTION_START(CMB_CODE_SECTION_NAME); + main_stack_size = (uint32_t)&CODE_SECTION_END(CMB_CODE_SECTION_NAME) - main_stack_start_addr; + code_start_addr = (uint32_t)&CSTACK_BLOCK_START(CMB_CSTACK_BLOCK_NAME); + code_size = (uint32_t)&CSTACK_BLOCK_END(CMB_CSTACK_BLOCK_NAME) - code_start_addr; + //TODO +#elif defined(__ICCARM__) + main_stack_start_addr = (uint32_t)__section_begin(CMB_CSTACK_BLOCK_NAME); + main_stack_size = (uint32_t)__section_end(CMB_CSTACK_BLOCK_NAME) - main_stack_start_addr; + code_start_addr = (uint32_t)__section_begin(CMB_CODE_SECTION_NAME); + code_size = (uint32_t)__section_end(CMB_CODE_SECTION_NAME) - code_start_addr; +#elif defined(__GNUC__) + #error "not implemented, I hope you can do this" + //TODO ʵ +#else + #error "not supported compiler" +#endif + + init_ok = true; +} + +/** + * print firmware information, such as: firmware name, hardware version, software version + */ +void cm_backtrace_firmware_info(void) { + cmb_println(print_info[PRINT_FIRMWARE_INFO], fw_name, hw_ver, sw_ver); +} + +#ifdef CMB_USING_OS_PLATFORM +/** + * Get current thread stack information + * + * @param sp stack current pointer + * @param start_addr stack start address + * @param size stack size + */ +static void get_cur_thread_stack_info(uint32_t sp, uint32_t *start_addr, size_t *size) { + CMB_ASSERT(start_addr); + CMB_ASSERT(size); + +#if (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT) + *start_addr = (uint32_t) rt_thread_self()->stack_addr; + *size = rt_thread_self()->stack_size; +#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSII) + extern OS_TCB *OSTCBCur; + +#if OS_TASK_PROFILE_EN > 0 + *start_addr = (uint32_t) OSTCBCur->OSTCBStkBase; + *size = (sp + OSTCBCur->OSTCBStkUsed) - *start_addr; +#else + #error "OS_TASK_PROFILE_EN isn't enable in 'OS_CFG.H'" +#endif /* OS_TASK_PROFILE_EN > 0 */ + + //TODO +#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSIII) + #error "not implemented, I hope you can do this" + //TODO ʵ +#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_FREERTOS) + #error "not implemented, I hope you can do this" + //TODO ʵ +#endif +} + +/** + * Get current thread name + */ +static const char *get_cur_thread_name(void) { +#if (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT) + return rt_thread_self()->name; +#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSII) + extern OS_TCB *OSTCBCur; + +#if OS_TASK_NAME_SIZE > 1 + return OSTCBCur->OSTCBTaskName; +#else + return NULL; +#endif /* OS_TASK_NAME_SIZE > 1 */ + + //TODO +#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSIII) + #error "not implemented, I hope you can do this" + //TODO ʵ +#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_FREERTOS) + #error "not implemented, I hope you can do this" + //TODO ʵ +#endif +} + +/** + * dump current thread stack information + */ +static void dump_cur_thread_stack(uint32_t stack_start_addr, size_t stack_size, uint32_t *stack_pointer) { + cmb_println(print_info[PRINT_THREAD_STACK_INFO]); + for (; (uint32_t) stack_pointer < stack_start_addr + stack_size; stack_pointer++) { + cmb_println(" addr: %08x data: %08x", stack_pointer, *stack_pointer); + } + cmb_println("===================================="); +} +#endif /* CMB_USING_OS_PLATFORM */ + +/** + * dump current main stack information + */ +static void dump_main_stack(uint32_t stack_start_addr, size_t stack_size, uint32_t *stack_pointer) { + cmb_println(print_info[PRINT_MAIN_STACK_INFO]); + for (; (uint32_t) stack_pointer < stack_start_addr + stack_size; stack_pointer++) { + cmb_println(" addr: %08x data: %08x", stack_pointer, *stack_pointer); + } + cmb_println("===================================="); +} + +/** + * backtrace function call stack + * + * @param buffer call stack buffer + * @param size buffer size + * @param sp stack pointer + * + * @return depth + */ +size_t cm_backtrace_call_stack(uint32_t *buffer, size_t size, uint32_t sp) { + uint32_t stack_start_addr = main_stack_start_addr, stack_size = main_stack_size, cur_stack_pointer; + size_t depth = 0; + + if (on_fault) { + /* first depth is PC */ + buffer[depth++] = regs.saved.pc; + /* program is running on thread before fault */ + if (on_thread_before_fault) { + get_cur_thread_stack_info(sp, &stack_start_addr, &stack_size); + } + } else { + cur_stack_pointer = __get_SP(); + +#ifdef CMB_USING_OS_PLATFORM + /* OS environment */ + if (cur_stack_pointer == __get_PSP()) { + get_cur_thread_stack_info(sp, &stack_start_addr, &stack_size); + } +#endif /* CMB_USING_OS_PLATFORM */ + + } + + /* copy called function address */ + for (; sp < stack_start_addr + stack_size; sp += sizeof(size_t)) { + if ((*((uint32_t *) sp) >= code_start_addr) && (*((uint32_t *) sp) <= code_start_addr + code_size) + && (depth < CMB_CALL_STACK_MAX_DEPTH) && (depth < size)) { + /* this get value maybe LR, so need decrease a word to PC */ + buffer[depth++] = *((uint32_t *) sp) - sizeof(size_t); + } + } + + return depth; +} + +/** + * dump function call stack + * + * @param sp stack pointer + */ +static void print_call_stack(uint32_t sp) { + size_t i, cur_depth = 0; + uint32_t call_stack_buf[CMB_CALL_STACK_MAX_DEPTH] = {0}; + + cur_depth = cm_backtrace_call_stack(call_stack_buf, CMB_CALL_STACK_MAX_DEPTH, sp); + + for (i = 0; i < cur_depth; i++) { + sprintf(call_stack_info + i * (8 + 1), "%08x", call_stack_buf[i]); + call_stack_info[i * (8 + 1) + 8] = ' '; + } + + if (cur_depth) { + cmb_println(print_info[PRINT_CALL_STACK_INFO], fw_name, CMB_ELF_FILE_EXTENSION_NAME, cur_depth * (8 + 1), + call_stack_info); + } else { + cmb_println(print_info[PRINT_CALL_STACK_ERR]); + } +} + +/** + * backtrace for assert + * + * @param sp the stack pointer when on assert occurred + */ +void cm_backtrace_assert(uint32_t sp) { + uint32_t stack_start_addr = main_stack_start_addr, stack_size = main_stack_size, cur_stack_pointer; + + CMB_ASSERT(init_ok); + + cur_stack_pointer = __get_SP(); + cm_backtrace_firmware_info(); + +#ifdef CMB_USING_DUMP_STACK_INFO +#ifdef CMB_USING_OS_PLATFORM + /* OS environment */ + if (cur_stack_pointer == __get_MSP()) { + cmb_println(print_info[PRINT_ASSERT_ON_HANDLER]); + dump_main_stack(stack_start_addr, stack_size, (uint32_t *) sp); + } else if (cur_stack_pointer == __get_PSP()) { + cmb_println(print_info[PRINT_ASSERT_ON_THREAD], get_cur_thread_name()); + get_cur_thread_stack_info(sp, &stack_start_addr, &stack_size); + dump_cur_thread_stack(stack_start_addr, stack_size, (uint32_t *) sp); + } +#else + /* bare metal(no OS) environment */ + dump_main_stack(); +#endif /* CMB_USING_OS_PLATFORM */ +#endif /* CMB_USING_DUMP_STACK_INFO */ + + print_call_stack(sp); +} + +#if (CMB_CPU_PLATFORM_TYPE != CMB_CPU_ARM_CORTEX_M0) +/** + * fault diagnosis then print cause of fault + */ +static void fault_diagnosis(void) { + if (regs.hfsr.bits.VECTBL) { + cmb_println(print_info[PRINT_HFSR_VECTBL]); + } + if (regs.hfsr.bits.FORCED) { + /* Memory Management Fault */ + if (regs.mfsr.value) { + if (regs.mfsr.bits.IACCVIOL) { + cmb_println(print_info[PRINT_MFSR_IACCVIOL]); + } + if (regs.mfsr.bits.DACCVIOL) { + cmb_println(print_info[PRINT_MFSR_DACCVIOL]); + } + if (regs.mfsr.bits.MUNSTKERR) { + cmb_println(print_info[PRINT_MFSR_MUNSTKERR]); + } + if (regs.mfsr.bits.MSTKERR) { + cmb_println(print_info[PRINT_MFSR_MSTKERR]); + } + +#if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) + if (regs.mfsr.bits.MLSPERR) { + cmb_println(print_info[PRINT_MFSR_MLSPERR]); + } +#endif + + if (regs.mfsr.bits.MMARVALID) { + //TODO + } + } + /* Bus Fault */ + if (regs.bfsr.value) { + if (regs.bfsr.bits.IBUSERR) { + cmb_println(print_info[PRINT_BFSR_IBUSERR]); + } + if (regs.bfsr.bits.PRECISERR) { + cmb_println(print_info[PRINT_BFSR_PRECISERR]); + } + if (regs.bfsr.bits.IMPREISERR) { + cmb_println(print_info[PRINT_BFSR_IMPREISERR]); + } + if (regs.bfsr.bits.UNSTKERR) { + cmb_println(print_info[PRINT_BFSR_UNSTKERR]); + } + if (regs.bfsr.bits.STKERR) { + cmb_println(print_info[PRINT_BFSR_STKERR]); + } + +#if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) + if (regs.bfsr.bits.LSPERR) { + cmb_println(print_info[PRINT_BFSR_LSPERR]); + } +#endif + + if (regs.bfsr.bits.BFARVALID) { + //TODO + } + } + /* Usage Fault */ + if (regs.ufsr.value) { + if (regs.ufsr.bits.UNDEFINSTR) { + cmb_println(print_info[PRINT_UFSR_UNDEFINSTR]); + } + if (regs.ufsr.bits.INVSTATE) { + cmb_println(print_info[PRINT_UFSR_INVSTATE]); + } + if (regs.ufsr.bits.INVPC) { + cmb_println(print_info[PRINT_UFSR_INVPC]); + } + if (regs.ufsr.bits.NOCP) { + cmb_println(print_info[PRINT_UFSR_NOCP]); + } + if (regs.ufsr.bits.UNALIGNED) { + cmb_println(print_info[PRINT_UFSR_UNALIGNED]); + } + if (regs.ufsr.bits.DIVBYZERO) { + cmb_println(print_info[PRINT_UFSR_DIVBYZERO]); + } + } + } + /* Debug Fault */ + if (regs.hfsr.bits.DEBUGEVT) { + if (regs.dfsr.value) { + if (regs.dfsr.bits.HALTED) { + cmb_println(print_info[PRINT_DFSR_HALTED]); + } + if (regs.dfsr.bits.BKPT) { + cmb_println(print_info[PRINT_DFSR_BKPT]); + } + if (regs.dfsr.bits.DWTTRAP) { + cmb_println(print_info[PRINT_DFSR_DWTTRAP]); + } + if (regs.dfsr.bits.VCATCH) { + cmb_println(print_info[PRINT_DFSR_VCATCH]); + } + if (regs.dfsr.bits.EXTERNAL) { + cmb_println(print_info[PRINT_DFSR_EXTERNAL]); + } + } + } +} +#endif /* (CMB_CPU_PLATFORM_TYPE != CMB_CPU_ARM_CORTEX_M0) */ + +static uint32_t statck_del_fpu_regs(uint32_t fault_handler_lr, uint32_t sp) { + statck_has_fpu_regs = (fault_handler_lr & (1UL << 4)) == 0 ? true : false; + + /* the stack has S0~S15 and FPSCR registers when statck_has_fpu_regs is true, double word align */ + return statck_has_fpu_regs == true ? sp + sizeof(size_t) * 18 : sp; +} + +/** + * backtrace for fault + * @note only call once + * + * @param fault_handler_lr the LR register value on fault handler + * @param fault_handler_sp the stack pointer on fault handler + */ +void cm_backtrace_fault(uint32_t fault_handler_lr, uint32_t fault_handler_sp) { + uint32_t stack_start_addr = main_stack_start_addr, stack_size = main_stack_size, stack_pointer = + fault_handler_sp, saved_regs_addr = stack_pointer; + const char *regs_name[] = { "R0 ", "R1 ", "R2 ", "R3 ", "R12", "LR ", "PC ", "PSR" }; + + CMB_ASSERT(init_ok); + /* only call once */ + CMB_ASSERT(!on_fault); + + on_fault = true; + cm_backtrace_firmware_info(); + + /* delete saved R0~R3, R12, LR, PC, xPSR registers address */ + stack_pointer += sizeof(size_t) * 8; + +#if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) + stack_pointer = statck_del_fpu_regs(fault_handler_lr, stack_pointer); +#endif + +#ifdef CMB_USING_DUMP_STACK_INFO +#ifdef CMB_USING_OS_PLATFORM + on_thread_before_fault = fault_handler_lr & (1UL << 2); + /* check which stack was used before (MSP or PSP) */ + if (on_thread_before_fault) { + cmb_println(print_info[PRINT_FAULT_ON_THREAD], get_cur_thread_name()); + saved_regs_addr = stack_pointer = __get_PSP(); + get_cur_thread_stack_info(stack_pointer, &stack_start_addr, &stack_size); + + /* delete saved R0~R3, R12, LR,PC,xPSR registers space */ + stack_pointer += sizeof(size_t) * 8; + +#if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M4) || (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M7) + stack_pointer = statck_del_fpu_regs(fault_handler_lr, stack_pointer); +#endif + + dump_cur_thread_stack(stack_start_addr, stack_size, (uint32_t *) stack_pointer); + + } else { + cmb_println(print_info[PRINT_FAULT_ON_HANDLER]); + dump_main_stack(stack_start_addr, stack_size, (uint32_t *) stack_pointer); + } +#else + /* bare metal(no OS) environment */ + cmb_println(print_info[PRINT_FAULT_ON_HANDLER]); + dump_main_stack(stack_start_addr, stack_size, (uint32_t *) stack_pointer); +#endif /* CMB_USING_OS_PLATFORM */ +#endif /* CMB_USING_DUMP_STACK_INFO */ + + /* dump register */ + cmb_println(print_info[PRINT_REGS_TITLE]); + + regs.saved.r0 = ((uint32_t *)saved_regs_addr)[0]; // Register R0 + regs.saved.r1 = ((uint32_t *)saved_regs_addr)[1]; // Register R1 + regs.saved.r2 = ((uint32_t *)saved_regs_addr)[2]; // Register R2 + regs.saved.r3 = ((uint32_t *)saved_regs_addr)[3]; // Register R3 + regs.saved.r12 = ((uint32_t *)saved_regs_addr)[4]; // Register R12 + regs.saved.lr = ((uint32_t *)saved_regs_addr)[5]; // Link register LR + regs.saved.pc = ((uint32_t *)saved_regs_addr)[6]; // Program counter PC + regs.saved.psr.value = ((uint32_t *)saved_regs_addr)[7]; // Program status word PSR + + + cmb_println(" %s: %08x %s: %08x %s: %08x %s: %08x", regs_name[0], regs.saved.r0, + regs_name[1], regs.saved.r1, + regs_name[2], regs.saved.r2, + regs_name[3], regs.saved.r3); + cmb_println(" %s: %08x %s: %08x %s: %08x %s: %08x", regs_name[4], regs.saved.r12, + regs_name[5], regs.saved.lr, + regs_name[6], regs.saved.pc, + regs_name[7], regs.saved.psr.value); + cmb_println("=============================================================="); + + /* the Cortex-M0 is not support fault diagnosis */ +#if (CMB_CPU_PLATFORM_TYPE != CMB_CPU_ARM_CORTEX_M0) + regs.syshndctrl.value = CMB_SYSHND_CTRL; // System Handler Control and State Register + regs.mfsr.value = CMB_NVIC_MFSR; // Memory Fault Status Register + regs.bfsr.value = CMB_NVIC_BFSR; // Bus Fault Status Register + regs.bfar = CMB_NVIC_BFAR; // Bus Fault Manage Address Register + regs.ufsr.value = CMB_NVIC_UFSR; // Usage Fault Status Register + regs.hfsr.value = CMB_NVIC_HFSR; // Hard Fault Status Register + regs.dfsr.value = CMB_NVIC_DFSR; // Debug Fault Status Register + regs.afsr = CMB_NVIC_AFSR; // Auxiliary Fault Status Register + + fault_diagnosis(); +#endif + + print_call_stack(stack_pointer); +} diff --git a/cm_backtrace/cm_backtrace.h b/cm_backtrace/cm_backtrace.h new file mode 100644 index 0000000..9fc9a61 --- /dev/null +++ b/cm_backtrace/cm_backtrace.h @@ -0,0 +1,40 @@ +/* + * This file is part of the CmBacktrace Library. + * + * Copyright (c) 2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is an head file for this library. You can see all be called functions. + * Created on: 2016-12-15 + */ + +#ifndef _CORTEXM_BACKTRACE_H_ +#define _CORTEXM_BACKTRACE_H_ + +#include "cmb_def.h" + +void cm_backtrace_init(const char *firmware_name, const char *hardware_ver, const char *software_ver); +void cm_backtrace_firmware_info(void); +size_t cm_backtrace_call_stack(uint32_t *buffer, size_t size, uint32_t sp); +void cm_backtrace_assert(uint32_t sp); +void cm_backtrace_fault(uint32_t fault_handler_lr, uint32_t fault_handler_sp); + +#endif /* _CORTEXM_BACKTRACE_H_ */ diff --git a/cm_backtrace/cmb_cfg.h b/cm_backtrace/cmb_cfg.h new file mode 100644 index 0000000..43dbbf2 --- /dev/null +++ b/cm_backtrace/cmb_cfg.h @@ -0,0 +1,47 @@ +/* + * This file is part of the CmBacktrace Library. + * + * Copyright (c) 2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is the configure head file for this library. + * Created on: 2016-12-15 + */ + +#ifndef _CMB_CFG_H_ +#define _CMB_CFG_H_ + +#include + +#define cmb_println(...) elog_e("cmb", __VA_ARGS__) +/* ʹƽ̨ */ +//#define CMB_USING_BARE_METAL_PLATFORM +/* ʹòϵͳƽ̨ */ +#define CMB_USING_OS_PLATFORM +/* ϵͳƽ̨ */ +#define CMB_OS_PLATFORM_TYPE CMB_OS_PLATFORM_RTT +/* CPU ƽ̨ */ +#define CMB_CPU_PLATFORM_TYPE CMB_CPU_ARM_CORTEX_M4 +/* dump ջϢ */ +#define CMB_USING_DUMP_STACK_INFO +/* ӡϢ */ +#define CMB_PRINT_LANGUAGE CMB_PRINT_LANUUAGE_CHINESE +#endif /* _CMB_CFG_H_ */ diff --git a/cm_backtrace/cmb_def.h b/cm_backtrace/cmb_def.h new file mode 100644 index 0000000..369bc66 --- /dev/null +++ b/cm_backtrace/cmb_def.h @@ -0,0 +1,342 @@ +/* + * This file is part of the CmBacktrace Library. + * + * Copyright (c) 2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is the macro definition head file for this library. + * Created on: 2016-12-15 + */ + +#ifndef _CMB_DEF_H_ +#define _CMB_DEF_H_ + +#include +#include + +/* library software version number */ +#define CMB_SW_VERSION "0.1.0" + +#define CMB_CPU_ARM_CORTEX_M0 0 +#define CMB_CPU_ARM_CORTEX_M3 1 +#define CMB_CPU_ARM_CORTEX_M4 2 +#define CMB_CPU_ARM_CORTEX_M7 3 + +#define CMB_OS_PLATFORM_RTT 0 +#define CMB_OS_PLATFORM_UCOSII 1 +#define CMB_OS_PLATFORM_UCOSIII 2 +#define CMB_OS_PLATFORM_FREERTOS 3 + +#define CMB_PRINT_LANGUAGE_ENGLISH 0 +#define CMB_PRINT_LANUUAGE_CHINESE 1 + +/* name max length, default size: 16 */ +#ifndef CMB_NAME_MAX +#define CMB_NAME_MAX 16 +#endif + +/* print information language, default is English */ +#ifndef CMB_PRINT_LANGUAGE +#define CMB_PRINT_LANGUAGE CMB_PRINT_LANGUAGE_ENGLISH +#endif + + +#if defined(__CC_ARM) + /* C stack block name, default is STACK */ + #ifndef CMB_CSTACK_BLOCK_NAME + #define CMB_CSTACK_BLOCK_NAME STACK + #endif + /* code section name, default is ER_IROM1 */ + #ifndef CMB_CODE_SECTION_NAME + #define CMB_CODE_SECTION_NAME ER_IROM1 + #endif +#elif defined(__ICCARM__) + /* C stack block name, default is 'CSTACK' */ + #ifndef CMB_CSTACK_BLOCK_NAME + #define CMB_CSTACK_BLOCK_NAME "CSTACK" + #endif + /* code section name, default is '.text' */ + #ifndef CMB_CODE_SECTION_NAME + #define CMB_CODE_SECTION_NAME ".text" + #endif +#elif defined(__GNUC__) + //TODO + /* C stack block name, default is STACK */ + #ifndef CMB_CSTACK_BLOCK_NAME + #define CMB_CSTACK_BLOCK_NAME STACK + #endif + /* code section name, default is ER_IROM1 */ + #ifndef CMB_CODE_SECTION_NAME + #define CMB_CODE_SECTION_NAME ER_IROM1 + #endif +#else + #error "not supported compiler" +#endif + +/* supported function call stack max depth, default is 16 */ +#ifndef CMB_CALL_STACK_MAX_DEPTH +#define CMB_CALL_STACK_MAX_DEPTH 16 +#endif + +/* system handler control and state register */ +#ifndef CMB_SYSHND_CTRL +#define CMB_SYSHND_CTRL (*(volatile unsigned int*) (0xE000ED24u)) +#endif + +/* memory management fault status register */ +#ifndef CMB_NVIC_MFSR +#define CMB_NVIC_MFSR (*(volatile unsigned char*) (0xE000ED28u)) +#endif + +/* bus fault status register */ +#ifndef CMB_NVIC_BFSR +#define CMB_NVIC_BFSR (*(volatile unsigned char*) (0xE000ED29u)) +#endif + +/* usage fault status register */ +#ifndef CMB_NVIC_UFSR +#define CMB_NVIC_UFSR (*(volatile unsigned short*)(0xE000ED2Au)) +#endif + +/* hard fault status register */ +#ifndef CMB_NVIC_HFSR +#define CMB_NVIC_HFSR (*(volatile unsigned int*) (0xE000ED2Cu)) +#endif + +/* debug fault status register */ +#ifndef CMB_NVIC_DFSR +#define CMB_NVIC_DFSR (*(volatile unsigned short*)(0xE000ED30u)) +#endif + +/* bus fault manage address register */ +#ifndef CMB_NVIC_BFAR +#define CMB_NVIC_BFAR (*(volatile unsigned int*) (0xE000ED38u)) +#endif + +/* auxiliary fault status register */ +#ifndef CMB_NVIC_AFSR +#define CMB_NVIC_AFSR (*(volatile unsigned short*)(0xE000ED3Cu)) +#endif + +/** + * Cortex-M fault registers + */ +struct cmb_hard_fault_regs{ + struct { + unsigned int r0; // Register R0 + unsigned int r1; // Register R1 + unsigned int r2; // Register R2 + unsigned int r3; // Register R3 + unsigned int r12; // Register R12 + unsigned int lr; // Link register + unsigned int pc; // Program counter + union { + unsigned int value; + struct { + unsigned int IPSR : 8; // Interrupt Program Status register (IPSR) + unsigned int EPSR : 19; // Execution Program Status register (EPSR) + unsigned int APSR : 5; // Application Program Status register (APSR) + } bits; + } psr; // Program status register. + } saved; + + union { + unsigned int value; + struct { + unsigned int MEMFAULTACT : 1; // Read as 1 if memory management fault is active + unsigned int BUSFAULTACT : 1; // Read as 1 if bus fault exception is active + unsigned int UnusedBits1 : 1; + unsigned int USGFAULTACT : 1; // Read as 1 if usage fault exception is active + unsigned int UnusedBits2 : 3; + unsigned int SVCALLACT : 1; // Read as 1 if SVC exception is active + unsigned int MONITORACT : 1; // Read as 1 if debug monitor exception is active + unsigned int UnusedBits3 : 1; + unsigned int PENDSVACT : 1; // Read as 1 if PendSV exception is active + unsigned int SYSTICKACT : 1; // Read as 1 if SYSTICK exception is active + unsigned int USGFAULTPENDED : 1; // Usage fault pended; usage fault started but was replaced by a higher-priority exception + unsigned int MEMFAULTPENDED : 1; // Memory management fault pended; memory management fault started but was replaced by a higher-priority exception + unsigned int BUSFAULTPENDED : 1; // Bus fault pended; bus fault handler was started but was replaced by a higher-priority exception + unsigned int SVCALLPENDED : 1; // SVC pended; SVC was started but was replaced by a higher-priority exception + unsigned int MEMFAULTENA : 1; // Memory management fault handler enable + unsigned int BUSFAULTENA : 1; // Bus fault handler enable + unsigned int USGFAULTENA : 1; // Usage fault handler enable + } bits; + } syshndctrl; // System Handler Control and State Register (0xE000ED24) + + union { + unsigned char value; + struct { + unsigned char IACCVIOL : 1; // Instruction access violation + unsigned char DACCVIOL : 1; // Data access violation + unsigned char UnusedBits : 1; + unsigned char MUNSTKERR : 1; // Unstacking error + unsigned char MSTKERR : 1; // Stacking error + unsigned char MLSPERR : 1; // Floating-point lazy state preservation (M4/M7) + unsigned char UnusedBits2 : 1; + unsigned char MMARVALID : 1; // Indicates the MMAR is valid + } bits; + } mfsr; // Memory Management Fault Status Register (0xE000ED28) + + union { + unsigned char value; + struct { + unsigned char IBUSERR : 1; // Instruction access violation + unsigned char PRECISERR : 1; // Precise data access violation + unsigned char IMPREISERR : 1; // Imprecise data access violation + unsigned char UNSTKERR : 1; // Unstacking error + unsigned char STKERR : 1; // Stacking error + unsigned char LSPERR : 1; // Floating-point lazy state preservation (M4/M7) + unsigned char UnusedBits : 1; + unsigned char BFARVALID : 1; // Indicates BFAR is valid + } bits; + } bfsr; // Bus Fault Status Register (0xE000ED29) + unsigned int bfar; // Bus Fault Manage Address Register (0xE000ED38) + + union { + unsigned short value; + struct { + unsigned short UNDEFINSTR : 1; // Attempts to execute an undefined instruction + unsigned short INVSTATE : 1; // Attempts to switch to an invalid state (e.g., ARM) + unsigned short INVPC : 1; // Attempts to do an exception with a bad value in the EXC_RETURN number + unsigned short NOCP : 1; // Attempts to execute a coprocessor instruction + unsigned short UnusedBits : 4; + unsigned short UNALIGNED : 1; // Indicates that an unaligned access fault has taken place + unsigned short DIVBYZERO : 1; // Indicates a divide by zero has taken place (can be set only if DIV_0_TRP is set) + } bits; + } ufsr; // Usage Fault Status Register (0xE000ED2A) + + union { + unsigned int value; + struct { + unsigned int UnusedBits : 1; + unsigned int VECTBL : 1; // Indicates hard fault is caused by failed vector fetch + unsigned int UnusedBits2 : 28; + unsigned int FORCED : 1; // Indicates hard fault is taken because of bus fault/memory management fault/usage fault + unsigned int DEBUGEVT : 1; // Indicates hard fault is triggered by debug event + } bits; + } hfsr; // Hard Fault Status Register (0xE000ED2C) + + union { + unsigned int value; + struct { + unsigned int HALTED : 1; // Halt requested in NVIC + unsigned int BKPT : 1; // BKPT instruction executed + unsigned int DWTTRAP : 1; // DWT match occurred + unsigned int VCATCH : 1; // Vector fetch occurred + unsigned int EXTERNAL : 1; // EDBGRQ signal asserted + } bits; + } dfsr; // Debug Fault Status Register (0xE000ED30) + + unsigned int afsr; // Auxiliary Fault Status Register (0xE000ED3C), Vendor controlled (optional) +}; + +/* assert for developer. */ +#define CMB_ASSERT(EXPR) \ +if (!(EXPR)) \ +{ \ + cmb_println("(%s) has assert failed at %s.", #EXPR, __FUNCTION__); \ + while (1); \ +} + +/* ELF(Executable and Linking Format) file extension name for each compiler */ +#if defined(__CC_ARM) + #define CMB_ELF_FILE_EXTENSION_NAME ".o" +#elif defined(__ICCARM__) + #define CMB_ELF_FILE_EXTENSION_NAME ".out" +#elif defined(__GNUC__) + #define CMB_ELF_FILE_EXTENSION_NAME ".o" +#else + #error "not supported compiler" +#endif + +#ifndef cmb_println + #error "cmb_println isn't defined in 'cmb_cfg.h'" +#endif + +#ifndef CMB_CPU_PLATFORM_TYPE + #error "CMB_CPU_PLATFORM_TYPE isn't defined in 'cmb_cfg.h'" +#endif + +#if __STDC_VERSION__ < 199901L + #error "not supported compiler, must be C99 or higher. try to add '-std=c99' to compile parameters" +#endif + +#if (defined(CMB_USING_BARE_METAL_PLATFORM) && defined(CMB_USING_OS_PLATFORM)) + #error "CMB_USING_BARE_METAL_PLATFORM and CMB_USING_OS_PLATFORM only one of them can be used" +#elif defined(CMB_USING_OS_PLATFORM) + #if !defined(CMB_OS_PLATFORM_TYPE) + #error "CMB_OS_PLATFORM_TYPE isn't defined in 'cmb_cfg.h'" + #endif +#endif /* (defined(CMB_USING_BARE_METAL_PLATFORM) && defined(CMB_USING_OS_PLATFORM)) */ + +#if (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT) + #include +#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSII) + #include + //TODO +#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSIII) + #include + //TODO +#elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_FREERTOS) + #error "not yet implemented, I hope you can done this" + //TODO ʵ +#else + #error "not supported OS type" +#endif + +/* include or export for supported __get_MSP, __get_PSP, __get_SP function */ +#if defined(__CC_ARM) + static __inline __asm uint32_t __get_MSP(void) { + mrs r0, msp + bx lr + } + static __inline __asm uint32_t __get_PSP(void) { + mrs r0, psp + bx lr + } + static __inline __asm uint32_t __get_SP(void) { + mrs r0, sp + bx lr + } +#elif defined(__ICCARM__) + #include +#elif defined(__GNUC__) + //TODO + __attribute__( ( always_inline ) ) static inline uint32_t __get_MSP(void) { + register uint32_t result; + __asm volatile ("MRS %0, msp\n" : "=r" (result) ); + return(result); + } + __attribute__( ( always_inline ) ) static inline uint32_t __get_PSP(void) { + register uint32_t result; + __asm volatile ("MRS %0, psp\n" : "=r" (result) ); + return(result); + } + __attribute__( ( always_inline ) ) static inline uint32_t __get_SP(void) { + register uint32_t result; + __asm volatile ("MRS %0, sp\n" : "=r" (result) ); + return(result); + } +#else + #error "not supported compiler" +#endif + +#endif /* _CMB_DEF_H_ */ diff --git a/cm_backtrace/cmb_fault.s b/cm_backtrace/cmb_fault.s new file mode 100644 index 0000000..c72d441 --- /dev/null +++ b/cm_backtrace/cmb_fault.s @@ -0,0 +1,53 @@ +/* + * This file is part of the CmBacktrace Library. + * + * Copyright (c) 2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Fault handler by assembly + * Created on: 2016-12-16 + */ + +#if defined(__GNUC__) + +#elif defined(__CC_ARM) || defined(__ICCARM__) + +#endif + + SECTION .text:CODE(2) + THUMB + REQUIRE8 + PRESERVE8 + +#if 0 +; NOTE: If use this file's HardFault_Handler, please comments the HardFault_Handler code on other file. + IMPORT cm_backtrace_fault + EXPORT HardFault_Handler + +HardFault_Handler: + MOV r0, lr ; get lr + MOV r1, sp ; get stack pointer (current is MSP) + BL cm_backtrace_fault +#endif + + END + + diff --git a/demo/non-os/stm32f10x/README.md b/demo/non-os/stm32f10x/README.md new file mode 100644 index 0000000..23fbc60 --- /dev/null +++ b/demo/non-os/stm32f10x/README.md @@ -0,0 +1,5 @@ +# stm32f10x non-os Demo + +--- + +Ͼ \ No newline at end of file diff --git a/demo/os/freertos/stm32f10x/README.md b/demo/os/freertos/stm32f10x/README.md new file mode 100644 index 0000000..5f8ef32 --- /dev/null +++ b/demo/os/freertos/stm32f10x/README.md @@ -0,0 +1,5 @@ +# stm32f10x FreeRTOS Demo + +--- + +Ͼ \ No newline at end of file diff --git a/demo/os/rtthread/stm32f4xx/README.md b/demo/os/rtthread/stm32f4xx/README.md new file mode 100644 index 0000000..3085e84 --- /dev/null +++ b/demo/os/rtthread/stm32f4xx/README.md @@ -0,0 +1,5 @@ +# stm32f4xx RT-Thread Demo + +--- + +Ͼ \ No newline at end of file diff --git a/demo/os/ucosii/stm32f20x/README.md b/demo/os/ucosii/stm32f20x/README.md new file mode 100644 index 0000000..4d0669b --- /dev/null +++ b/demo/os/ucosii/stm32f20x/README.md @@ -0,0 +1,5 @@ +# stm32f20x UCOSII Demo + +--- + +Ͼ \ No newline at end of file