解决在QEMU上仿真STM32F429时出现的若干问题

摘要:
QEMU无法基于陈老师提供的Hello模拟Cortex-M4内核_ RTOS项目:qemu28.00 arm none eabi gcc4.8.2下载项目并编译1个gitclone https://guest_errors-imagehello_RTOS.elf--boardSTM32F429I Discovery选择系统板dunmp,
QEMU无法仿真Cortex-M4内核

基于陈老师提供的Hello_RTOS工程:

  qemu 2.8.0

  arm-none-eabi-gcc 4.8.2

下载工程并编译

1 git clone https://github.com/cbhust/STM32F429_Discovery_FreeRTOS_9.git
2 cd STM32F429_Discovery_FreeRTOS_9/Projects/Hello_RTOS/
3 make

选用STM32F429I-Discovery为系统板,调用qemu仿真。

1 qemu-system-gnuarmeclipse --board STM32F429I-Discovery -d unimp,guest_errors --image hello_rtos.elf

--board STM32F429I-Discovery 选择系统板
-d unimp,guest_errors 设置需要记录的项目
--image hello_rtos.elf 选择系统镜像

出现如下错误:

Attempt to set CP10/11 in SCB->CPACR, but FP is not supported yet.

解决在QEMU上仿真STM32F429时出现的若干问题第1张

经查询,这条错误信息是由于qemu不支持硬浮点

https://sourceforge.net/p/gnuarmeclipse/bugs/159/

解决在QEMU上仿真STM32F429时出现的若干问题第2张

可以通过设置编译选项来选择软浮点,还是硬浮点。

 http://blog.csdn.net/gujintong1110/article/details/23038217

解决在QEMU上仿真STM32F429时出现的若干问题第3张

将Hello_RTOS拷贝一份,到Qemu_with_M4

修改Makefile中的MCFLAGS,改为:

MCFLAGS=-mcpu=cortex-m4 -mthumb -mlittle-endian 
-mfpu=fpv4-sp-d16 -mfloat-abi=soft -mthumb-interwork
DEFS=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX

再次编译后,仍然出现错误:

Error: selected processor does not support `vstmdbeq r0!,{s16-s31}' in Thumb mode
Error: instruction not allowed in IT block -- `stmdb r0!,{r4-r11,r14}'
Error: selected processor does not support `vldmiaeq r0!,{s16-s31}' in Thumb mode
Error: instruction not allowed in IT block -- `msr psp,r0'

解决在QEMU上仿真STM32F429时出现的若干问题第4张

查询后发现,这些代码不支持软浮点。

https://sourceforge.net/p/freertos/discussion/382005/thread/9abf4160/

解决在QEMU上仿真STM32F429时出现的若干问题第5张

直接查看STM32F429_Discovery_FreeRTOS_9/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c发现:

#ifndef __VFP_FP__
    #error This port can only be used when the project options are configured to enable hardware floating point support.
#endif

代码的确不支持软浮点。

经过多次尝试决定,使用兼容软浮点的接口,并修改部分代码。

阅读代码后发现三处涉及硬浮点的代码:

 1 /* This is a naked function. */
 2 static void vPortEnableVFP( void )
 3 {
 4     __asm volatile
 5     (
 6         "    ldr.w r0, =0xE000ED88        
" /* The FPU enable bits are in the CPACR. */
 7         "    ldr r1, [r0]                
"
 8         "                                
"
 9         "    orr r1, r1, #( 0xf << 20 )    
" /* Enable CP10 and CP11 coprocessors, then save back. */
10         "    str r1, [r0]                
"
11         "    bx r14                        "
12     );
13 }
14 /*-----------------------------------------------------------*/

第9行就是硬浮点时,仿真出错的位置

实际上是任务在初始化时对浮点处理器的初始化

 1 void xPortPendSVHandler( void )
 2 {
 3     /* This is a naked function. */
 4 
 5     __asm volatile
 6     (
 7     "    mrs r0, psp                         
"
 8     "    isb                                 
"
 9     "                                        
"
10     "    ldr    r3, pxCurrentTCBConst        
" /* Get the location of the current TCB. */
11     "    ldr    r2, [r3]                     
"
12     "                                        
"
13     "    tst r14, #0x10                      
" /* Is the task using the FPU context?  If so, push high vfp registers. */
14     "    it eq                               
"
15     "    vstmdbeq r0!, {s16-s31}             
"
16     "                                        
"
17     "    stmdb r0!, {r4-r11, r14}            
" /* Save the core registers. */
18     "                                        
"
19     "    str r0, [r2]                        
" /* Save the new top of stack into the first member of the TCB. */
20     "                                        
"
21     "    stmdb sp!, {r3}                     
"
22     "    mov r0, %0                          
"
23     "    msr basepri, r0                     
"
24     "    dsb                                 
"
25     "    isb                                 
"
26     "    bl vTaskSwitchContext               
"
27     "    mov r0, #0                          
"
28     "    msr basepri, r0                     
"
29     "    ldmia sp!, {r3}                     
"
30     "                                        
"
31     "    ldr r1, [r3]                        
" /* The first item in pxCurrentTCB is the task top of stack. */
32     "    ldr r0, [r1]                        
"
33     "                                        
"
34     "    ldmia r0!, {r4-r11, r14}            
" /* Pop the core registers. */
35     "                                        
"
36     "    tst r14, #0x10                      
" /* Is the task using the FPU context?  If so, pop the high vfp registers too. */
37     "    it eq                               
"
38     "    vldmiaeq r0!, {s16-s31}             
"
39     "                                        
"
40     "    msr psp, r0                         
"
41     "    isb                                 
"
42     "                                        
"
43     #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */
44         #if WORKAROUND_PMU_CM001 == 1
45     "            push { r14 }                
"
46     "            pop { pc }                  
"
47         #endif
48     #endif
49     "                                        
"
50     "    bx r14                              
"
51     "                                        
"
52     "    .align 4                            
"
53     "pxCurrentTCBConst: .word pxCurrentTCB   
"
54     ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY)
55     );
56 }

第15,38行就是软浮点时编译出错的位置

实际上是切换任务时的保存、恢复现场

 1 BaseType_t xPortStartScheduler( void )
 2 {
 3     /* configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0.
 4     See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */
 5     configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY );
 6 
 7     /* This port can be used on all revisions of the Cortex-M7 core other than
 8     the r0p1 parts.  r0p1 parts should use the port from the
 9     /source/portable/GCC/ARM_CM7/r0p1 directory. */
10     configASSERT( portCPUID != portCORTEX_M7_r0p1_ID );
11     configASSERT( portCPUID != portCORTEX_M7_r0p0_ID );
12 
13     #if( configASSERT_DEFINED == 1 )
14     {
15         volatile uint32_t ulOriginalPriority;
16         volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER );
17         volatile uint8_t ucMaxPriorityValue;
18 
19         /* Determine the maximum priority from which ISR safe FreeRTOS API
20         functions can be called.  ISR safe functions are those that end in
21         "FromISR".  FreeRTOS maintains separate thread and ISR API functions to
22         ensure interrupt entry is as fast and simple as possible.
23 
24         Save the interrupt priority value that is about to be clobbered. */
25         ulOriginalPriority = *pucFirstUserPriorityRegister;
26 
27         /* Determine the number of priority bits available.  First write to all
28         possible bits. */
29         *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;
30 
31         /* Read the value back to see how many bits stuck. */
32         ucMaxPriorityValue = *pucFirstUserPriorityRegister;
33 
34         /* Use the same mask on the maximum system call priority. */
35         ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue;
36 
37         /* Calculate the maximum acceptable priority group value for the number
38         of bits read back. */
39         ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS;
40         while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE )
41         {
42             ulMaxPRIGROUPValue--;
43             ucMaxPriorityValue <<= ( uint8_t ) 0x01;
44         }
45 
46         /* Shift the priority group value back to its position within the AIRCR
47         register. */
48         ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;
49         ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;
50 
51         /* Restore the clobbered interrupt priority register to its original
52         value. */
53         *pucFirstUserPriorityRegister = ulOriginalPriority;
54     }
55     #endif /* conifgASSERT_DEFINED */
56 
57     /* Make PendSV and SysTick the lowest priority interrupts. */
58     portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
59     portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;
60 
61     /* Start the timer that generates the tick ISR.  Interrupts are disabled
62     here already. */
63     vPortSetupTimerInterrupt();
64 
65     /* Initialise the critical nesting count ready for the first task. */
66     uxCriticalNesting = 0;
67 
68     /* Ensure the VFP is enabled - it should be anyway. */
69     vPortEnableVFP();
70 
71     /* Lazy save always. */
72     *( portFPCCR ) |= portASPEN_AND_LSPEN_BITS;
73 
74     /* Start the first task. */
75     prvPortStartFirstTask();
76 
77     /* Should never get here as the tasks will now be executing!  Call the task
78     exit error function to prevent compiler warnings about a static function
79     not being called in the case that the application writer overrides this
80     functionality by defining configTASK_RETURN_ADDRESS. */
81     prvTaskExitError();
82 
83     /* Should not get here! */
84     return 0;
85 }

第69行实际上是第1段代码的入口,第72行有关于FP的置位操作。

先将ARM_CM4F拷贝一份,到ARM_CM4_Soft

将上述三个地方注释掉,效果如下:

(实际上是两个地方,第一段代码,仅有这一个地方调用)

 1 void xPortPendSVHandler( void )
 2 {
 3     /* This is a naked function. */
 4 
 5     __asm volatile
 6     (
 7     "    mrs r0, psp                         
"
 8     "    isb                                 
"
 9     "                                        
"
10     "    ldr    r3, pxCurrentTCBConst        
" /* Get the location of the current TCB. */
11     "    ldr    r2, [r3]                     
"
12     "                                        
"
13     //"    tst r14, #0x10                    
" /* Is the task using the FPU context?  If so, push high vfp registers. */
14     //"    it eq                             
"
15     //"    vstmdbeq r0!, {s16-s31}           
"
16     "                                        
"
17     "    stmdb r0!, {r4-r11, r14}            
" /* Save the core registers. */
18     "                                        
"
19     "    str r0, [r2]                        
" /* Save the new top of stack into the first member of the TCB. */
20     "                                        
"
21     "    stmdb sp!, {r3}                     
"
22     "    mov r0, %0                          
"
23     "    msr basepri, r0                     
"
24     "    dsb                                 
"
25     "    isb                                 
"
26     "    bl vTaskSwitchContext               
"
27     "    mov r0, #0                          
"
28     "    msr basepri, r0                     
"
29     "    ldmia sp!, {r3}                     
"
30     "                                        
"
31     "    ldr r1, [r3]                        
" /* The first item in pxCurrentTCB is the task top of stack. */
32     "    ldr r0, [r1]                        
"
33     "                                        
"
34     "    ldmia r0!, {r4-r11, r14}            
" /* Pop the core registers. */
35     "                                        
"
36     //"    tst r14, #0x10                    
" /* Is the task using the FPU context?  If so, pop the high vfp registers too. */
37     //"    it eq                             
"
38     //"    vldmiaeq r0!, {s16-s31}           
"
39     "                                        
"
40     "    msr psp, r0                         
"
41     "    isb                                 
"
42     "                                        
"
43     #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */
44         #if WORKAROUND_PMU_CM001 == 1
45     "            push { r14 }                
"
46     "            pop { pc }                  
"
47         #endif
48     #endif
49     "                                        
"
50     "    bx r14                              
"
51     "                                        
"
52     "    .align 4                            
"
53     "pxCurrentTCBConst: .word pxCurrentTCB   
"
54     ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY)
55     );
56 }
 1 BaseType_t xPortStartScheduler( void )
 2 {
 3     /* configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0.
 4     See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */
 5     configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY );
 6 
 7     /* This port can be used on all revisions of the Cortex-M7 core other than
 8     the r0p1 parts.  r0p1 parts should use the port from the
 9     /source/portable/GCC/ARM_CM7/r0p1 directory. */
10     configASSERT( portCPUID != portCORTEX_M7_r0p1_ID );
11     configASSERT( portCPUID != portCORTEX_M7_r0p0_ID );
12 
13     #if( configASSERT_DEFINED == 1 )
14     {
15         volatile uint32_t ulOriginalPriority;
16         volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER );
17         volatile uint8_t ucMaxPriorityValue;
18 
19         /* Determine the maximum priority from which ISR safe FreeRTOS API
20         functions can be called.  ISR safe functions are those that end in
21         "FromISR".  FreeRTOS maintains separate thread and ISR API functions to
22         ensure interrupt entry is as fast and simple as possible.
23 
24         Save the interrupt priority value that is about to be clobbered. */
25         ulOriginalPriority = *pucFirstUserPriorityRegister;
26 
27         /* Determine the number of priority bits available.  First write to all
28         possible bits. */
29         *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;
30 
31         /* Read the value back to see how many bits stuck. */
32         ucMaxPriorityValue = *pucFirstUserPriorityRegister;
33 
34         /* Use the same mask on the maximum system call priority. */
35         ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue;
36 
37         /* Calculate the maximum acceptable priority group value for the number
38         of bits read back. */
39         ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS;
40         while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE )
41         {
42             ulMaxPRIGROUPValue--;
43             ucMaxPriorityValue <<= ( uint8_t ) 0x01;
44         }
45 
46         /* Shift the priority group value back to its position within the AIRCR
47         register. */
48         ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;
49         ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;
50 
51         /* Restore the clobbered interrupt priority register to its original
52         value. */
53         *pucFirstUserPriorityRegister = ulOriginalPriority;
54     }
55     #endif /* conifgASSERT_DEFINED */
56 
57     /* Make PendSV and SysTick the lowest priority interrupts. */
58     portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
59     portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;
60 
61     /* Start the timer that generates the tick ISR.  Interrupts are disabled
62     here already. */
63     vPortSetupTimerInterrupt();
64 
65     /* Initialise the critical nesting count ready for the first task. */
66     uxCriticalNesting = 0;
67 
68     /* Ensure the VFP is enabled - it should be anyway. */
69     //vPortEnableVFP();
70 
71     /* Lazy save always. */
72     //*( portFPCCR ) |= portASPEN_AND_LSPEN_BITS;
73 
74     /* Start the first task. */
75     prvPortStartFirstTask();
76 
77     /* Should never get here as the tasks will now be executing!  Call the task
78     exit error function to prevent compiler warnings about a static function
79     not being called in the case that the application writer overrides this
80     functionality by defining configTASK_RETURN_ADDRESS. */
81     prvTaskExitError();
82 
83     /* Should not get here! */
84     return 0;
85 }

修改Makefile:

# Build Parameters: MCU Flags, Definitions, Includes, 
#                   Compile Flags, Linker Script, Linker Flags
MCFLAGS=-mcpu=cortex-m4 -mthumb -mlittle-endian 
-mfpu=fpv4-sp-d16 -mfloat-abi=softfp -mthumb-interwork
DEFS=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX
INCLUDES=-I. 
-I../../Libraries/CMSIS/Device/ST/STM32F4xx/Include 
-I../../Utilities/STM32F429I-Discovery 
-I../../Libraries/CMSIS/Include 
-I../../Libraries/STM32F4xx_StdPeriph_Driver/inc 
-I../../FreeRTOS/Source/portable/GCC/ARM_CM4_Soft 
-I../../FreeRTOS/Source/include
CFLAGS=-c $(MCFLAGS) $(DEFS) $(INCLUDES)
LDSCRIPT = ./stm32_flash.ld
LDFLAGS=-T $(LDSCRIPT) --specs=nosys.specs $(MCFLAGS)

# Inputs: C Sources, Assembler Sources
SOURCES=main.c stm32f4xx_it.c system_stm32f4xx.c 
../../Utilities/STM32F429I-Discovery/stm32f429i_discovery.c 
../../Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_syscfg.c 
../../Libraries/STM32F4xx_StdPeriph_Driver/src/misc.c 
../../Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_gpio.c 
../../Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_rcc.c 
../../Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_exti.c 
../../Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_i2c.c 
../../Libraries/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_dma.c 
../../FreeRTOS/Source/portable/GCC/ARM_CM4_Soft/port.c 
../../FreeRTOS/Source/portable/MemMang/heap_2.c 
../../FreeRTOS/Source/croutine.c 
../../FreeRTOS/Source/event_groups.c 
../../FreeRTOS/Source/list.c 
../../FreeRTOS/Source/queue.c 
../../FreeRTOS/Source/tasks.c 
../../FreeRTOS/Source/timers.c
ASMSOURCES=../../Libraries/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc_ride7/startup_stm32f429_439xx.s

再次编译后,没有异常信息

解决在QEMU上仿真STM32F429时出现的若干问题第6张

成功运行

解决在QEMU上仿真STM32F429时出现的若干问题第7张

最后,测试浮点运算能力

加入浮点运算后,利用远程调试,直接看到运算结果:

解决在QEMU上仿真STM32F429时出现的若干问题第8张

(运行时间比较久,乘的次数比较多,所以看起来像乱码,)

(实际上,log(468217302516190.94/2.7)/log(1.1) = 344,log(128984051395143.11/3.1)/log(1.2) = 172

实际效果中LED没有闪烁

由于BSRR寄存器写入到LED变化有一点点延迟,但是在硬件中几乎不能感觉出来,

但是在仿真的时候应该是恰好在这一点点的延迟时切换任务。

导致实际效果在下一个任务里才真正出现,即无法出现LED闪烁的效果。

将通过BSRR寄存器修改LED状态,改成直接操作ODR寄存器,便可以解决问题。

生成的Tracealyer数据不包含时间信息

用Tracealyer打开bin文件显示:

This trace does not have a frequency set. Only sequential mode and native trace ticks can be used for timing.

解决在QEMU上仿真STM32F429时出现的若干问题第9张

问题原因大概是qemu在实现dwt(属于pmu的一部分)上有点问题。

查了很长时间也没有查到相关资料。只找到一段源码:

1 if (!cpu->has_pmu || !kvm_enabled()) {
2     cpu->has_pmu = false;
3     unset_feature(env, ARM_FEATURE_PMU);
4 }

只有启动kvm才能使用pmu。

然而试了很久也不知道怎么启动kvm。

解决方法很简单,在添加trcConfig.h如下代码:

#define TRC_CFG_ARM_CM_USE_SYSTICK

即使用systick作为tracealyer的时间信息。

至此,在qemu上仿真STM32F429时出现的问题基本解决,欢迎大家评论留言。

免责声明:文章转载自《解决在QEMU上仿真STM32F429时出现的若干问题》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Oracle的dualFCKeditor的Flv插件下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

使用docker安装宝塔面板

后端使用的命令太多了,容易忘记,所以记录在本文中,以便将来查询: 列出所有的容器 ID docker ps -aq 停止所有的容器 docker stop $(docker ps -aq) 删除所有的容器 docker rm $(docker ps -aq) 删除所有的镜像 docker rmi $(docker images -q) docker i...

pymysql 线程安全pymysqlpool

# -*-coding: utf-8-*- # Author : Christopher Lee # License: Apache License # File : test_example.py # Date : 2017-06-18 01-23 # Version: 0.0.1 # Description: simple test...

STM32组合设备实现USB转双串口

USB转双串口,核心技术就在于组合设备(USB Composite)的实现,组合设备的实现,其核心技术在于描述符的实现,下面我们先给出描述符:设备描述符 [C] 纯文本查看 复制代码 ? 00001 00002 00003 00004 00005 00006 00007 00008 00009 00010 00011 00012 00013 00...

ThinkPHP 6.0 管道模式与中间件的实现分析

设计模式六大原则 开放封闭原则:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。 里氏替换原则:所有引用基类的地方必须能透明地使用其子类的对象. 依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。 单一职责原则:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。 接口隔离原则...

8.nginx防DDOS

配置详解,查看链接:http://www.myhack58.com/Article/60/sort096/2015/59453.htm 配置文件: http {         #白名单         geo $whiteiplist  {         default 1;         192.168.0.225 0;         }  ...

微信默认表情符号的代码对照表

之前有不少朋友问我,微信公众平台如何发带有表情符号的图片,其实只需要在信息里面插入表情代码就可以使用的。 微信表情对照表如下: 表情图片 字符串代码 替换关键字 /::) 微笑 /::~ 伤心 /::B 美女 /::| 发呆 /:8-) 墨镜 /::< 哭 /::$ 羞 /::X 哑 /::...