Keil μVision 5.30 CppUTest测试框架模拟器Simulator测试(2)-被测试工程

被测项目介绍

此项目是一个以C++编写的框架,已经应用到了数十个产品中,虽然已经自认为比较成熟,但是近期出现了比较多的由于未进行数据有效性验证而出现的问题,这虽然可以归结为编程习惯欠佳的问题,但是也暴露了此前项目缺少完整测试导致对可能出现的问题没有预先处理的缺陷

故项目所需要为此前的代码添加测试.这里正好把添加测试的过程记录下来,因为此项目是一个真实的项目,其具有较高的实际意义.

为项目添加测试库

首先添加一个新的Target.因为测试项目要运行在Simulator上,需要修改一些项目配置,所以为了不影响此前的项目配置要新建一个.
image
向项目中添加测试库文件CppUTestCM0.lib
image

Options->C/C++/Include Paths

中,添加CppUTest包含目录,具体的路径要根据你CppUTest实际的路径来.

/cpputest-3.8/include/

添加平台相关文件

在这一步编译项目的话,会看到提示很多L6218E错误,此时要添加平台文件,这个文件中有CppUTest与平台相关的这些函数.
在Projext->TestLib上点击右键,添加文件,导航至:

cpputest-3.8->src->platforms->keil->UtestPlatform.cpp

添加这个Cpp文件,此时可编译通过.需要注意的是这里有个获取执行时间的函数:

///////////// Time in millis
/*
 *  In Keil MDK-ARM, clock() default implementation used semihosting.
 *  Resolutions is user adjustable (1 ms for now)
 */
static long TimeInMillisImplementation() {
    //clock_t t = clock();

    //t = t * 10;

    //return t;
     return 0;
}

如果需要实际时间则需要添加进去获取systemtick等时间基准的函数.

在主函数中运行测试

至此CppUTest已经添加到了项目中,但是还需要在Main中添加代码让测试运行起来.

由于CppUTest是基于C++的框架,因此如果你的Main函数是C语言的,就需要重新创建一个Main.cpp,并

  • 包含文件
#include "CppUTest/CommandLineTestRunner.h"
  • 在main函数加入以下代码
#ifdef SIMULATOR_TEST
const char * av_override[] = { "exe", "-v" };
CommandLineTestRunner::RunAllTests(2, av_override);
#endif
  • 在项目选项中添加宏
Options->C/C++->Define:SIMULATOR_TEST

这样其他Target就可以不包含测试代码(即使使用了同一个main.cpp文件).

  • 将输出重定向到Debug(Printf)窗口
    这里要注意M0内核不支持Itm,无法使用ITM_SendChar函数,所以在模拟器运行的时候需要选择M4内核或者M3内核来模拟.
/typedef int FILEHANDLE;
struct __FILE { int handle; /* Add whatever you need here */ };
FILE __stdout;


int fputc(int ch, FILE *f) {
  #if(USE_DEBUG_LOG)
   return (sendchar(ch));	
  #elif SIMULATOR_TEST
  #include "core_cm4.h"
  return(ITM_SendChar(ch));//此处将输出重定向到Debug(printf)窗口
  #else
   return (ch);
  #endif
}

这里注意下图中的选项要勾选,否则模拟器运行的时候会出错.
image

  • 使用Simulator运行测试
    此时点击Debug按钮进入模拟器测试,发现可以运行,但是Command窗口会不断提示error 65:,如下图所示:
    image
    此时需要为Simulator添加初始化脚本文件,路径如下:
  • 在MDK根目录下新建一个.ini文件,此处为
SimulatorDebugInit.ini
  • 参考数据手册中的地址映射,允许访问这些地址
    这里以M3的初始化文件为例,.ini中的内容为:
MAP  0x00000000, 0x000FFFFF  READ EXEC WRITE    // ALIASED FLASH
MAP  0x08000000, 0x080FFFFF  READ EXEC            // FLASH MEM
MAP  0x20000000, 0x20017FFF     READ EXEC WRITE    // SRAM

MAP  0x1044EBC0, 0x1044FFFF  EXEC READ    // unclear

MAP  0x1FFFE000, 0x1FFFF7FF     READ EXEC WRITE

MAP  0x20018000, 0x3FFFFFFF     READ WRITE

MAP  0x40000000, 0x400003FF  READ WRITE
MAP  0x40000400, 0x400007FF  READ WRITE
MAP  0x40000800, 0x40000BFF  READ WRITE
MAP  0x40000C00, 0x40000FFF  READ WRITE
MAP  0x40001000, 0x400013FF  READ WRITE
MAP  0x40001400, 0x400017FF  READ WRITE
MAP  0x40001800, 0x40001BFF  READ WRITE
MAP  0x40001C00, 0x40001FFF  READ WRITE
MAP  0x40002000, 0x400023FF  READ WRITE

MAP     0x40003C00, 0x40003FFF  READ WRITE // SPI3

MAP  0x40007000, 0x400073FF  READ WRITE
MAP  0x40010000, 0x400103FF  READ WRITE    // AFIO
MAP  0x40010400, 0x400107FF  READ WRITE    // EXIT
MAP  0x40010800, 0x40010bFF  READ WRITE    // GPIOA
MAP  0x40010C00, 0x40010FFF  READ WRITE    // GPIOB
MAP  0x40011000, 0x400113FF  READ WRITE    // GPIOC
MAP  0x40011400, 0x400117FF  READ WRITE    // GPIOD
MAP  0x40011800, 0x40011bFF  READ WRITE    // GPIOE
MAP  0x40011C00, 0x40011FFF  READ WRITE    // GPIOF
MAP  0x40012000, 0x400123FF  READ WRITE    // GPIOG
MAP  0x40012400, 0x400127FF  READ WRITE    // ADC1
MAP  0x40012800, 0x40012bFF  READ WRITE    // ADC2
MAP  0x40012C00, 0x40012FFF  READ WRITE    // TIM1
MAP  0x40013000, 0x400133FF  READ WRITE    // SPI1
MAP  0x40013400, 0x400137FF  READ WRITE    // TIM8
MAP  0x40013800, 0x40013bFF  READ WRITE    // USART1
MAP  0x40013C00, 0x40013FFF  READ WRITE    // ADC3

MAP  0x40014C00, 0x40014FFF  READ WRITE    // TIM9
MAP  0x40015000, 0x400153FF  READ WRITE    // TIM10
MAP  0x40015400, 0x400157FF  READ WRITE    // TIM11

MAP  0x40018000, 0x400183FF  READ WRITE    // SDIO

MAP  0x40020000, 0x400203FF  READ WRITE    // DMA1
MAP  0x40020400, 0x400207FF  READ WRITE    // DMA1

MAP  0x40021000, 0x400213FF  READ WRITE    // RCC

MAP  0x40022000, 0x400223FF  READ WRITE    // FLASH INTERFACES 1&2

MAP  0x40023000, 0x400233FF  READ WRITE // CRC


MAP  0x40021000, 0x400213FF  READ WRITE
MAP  0x60000000, 0x63FFFFFF     READ EXEC WRITE
MAP  0x70000000, 0x73FFFFFF  READ EXEC

MAP  0x78000000, 0x78FFFFFF     READ EXEC // FSMC BANK 2

具体的指令可以参考ArmKeil官方文档
这里简单解释一下,可以看到这里为每个地址都指定了可访问性,这样程序在运行的时候就可以去对应的:读写/执行这部分区域,虽然这里读写这些寄存器并没有什么实际的作用,而如果按照硬件的逻辑去忙等待一个标记为,那么程序在模拟器中运行的时候就会卡死.后面会看到相关的部分.

在添加了初始化文件之后,再次运行,发现已经已经有了打印,但之后便出发了hardfault_handler
image

这是因为当前分配的内存不够CppUTest使用,在模拟器运行的时候我们需要分配充裕的内存,因为没有硬件的限制.
修改栈的空间只应该针对当前Target,不能影响正常工程的配置,因此:
此时复制一份startup.s汇编文件,并重命名为:startup_simulator.s.
栈空间更改为0x00002000,堆空间改为0x00008000

Stack_Size      EQU     0x00002000

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

Heap_Size       EQU     0x00008000

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size

后面点击Debug以及全速运行开始测试,并打断点看下卡住的语句,如果是忙等待硬件赋值,那么将将此语句注释掉(最好新建一个文件在新建文件中操作,以免影响正常的硬件功能).
至此可以正常运行测试,结果如下图
image
可以看到现在系统中并无测试,所以此处测试为0.

添加一个测试

posted @ 2022-07-01 14:21  北风Z  阅读(499)  评论(0编辑  收藏  举报