1. 程式人生 > >如何解決在QEMU上仿真STM32F429時出現的問題

如何解決在QEMU上仿真STM32F429時出現的問題

clas oca core msi simple ports org errors 遇到

基於陳老師提供的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 選擇系統鏡像

出現如下錯誤:

技術分享

經查詢,這條錯誤信息是由於qemu不支持硬浮點

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

技術分享

可以通過設置編譯選項來選擇軟浮點,還是硬浮點。

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

技術分享

將Hello_RTOS拷貝一份,到Qemu_with_M4

修改Makefile中的MCFLAGS,改為:

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

再次編譯後,仍然出現錯誤:

技術分享

查詢後發現,這些代碼不支持軟浮點。

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

技術分享

直接查看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        \n" /* The FPU enable bits are in the CPACR. */
 7         "    ldr r1, [r0]                \n"
 8         "                                \n"
 9         "    orr r1, r1, #( 0xf << 20 )    \n" /* Enable CP10 and CP11 coprocessors, then save back. */
10         "    str r1, [r0]                \n"
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                         \n"
 8     "    isb                                 \n"
 9     "                                        \n"
10     "    ldr    r3, pxCurrentTCBConst        \n" /* Get the location of the current TCB. */
11     "    ldr    r2, [r3]                     \n"
12     "                                        \n"
13     "    tst r14, #0x10                      \n" /* Is the task using the FPU context?  If so, push high vfp registers. */
14     "    it eq                               \n"
15     "    vstmdbeq r0!, {s16-s31}             \n"
16     "                                        \n"
17     "    stmdb r0!, {r4-r11, r14}            \n" /* Save the core registers. */
18     "                                        \n"
19     "    str r0, [r2]                        \n" /* Save the new top of stack into the first member of the TCB. */
20     "                                        \n"
21     "    stmdb sp!, {r3}                     \n"
22     "    mov r0, %0                          \n"
23     "    msr basepri, r0                     \n"
24     "    dsb                                 \n"
25     "    isb                                 \n"
26     "    bl vTaskSwitchContext               \n"
27     "    mov r0, #0                          \n"
28     "    msr basepri, r0                     \n"
29     "    ldmia sp!, {r3}                     \n"
30     "                                        \n"
31     "    ldr r1, [r3]                        \n" /* The first item in pxCurrentTCB is the task top of stack. */
32     "    ldr r0, [r1]                        \n"
33     "                                        \n"
34     "    ldmia r0!, {r4-r11, r14}            \n" /* Pop the core registers. */
35     "                                        \n"
36     "    tst r14, #0x10                      \n" /* Is the task using the FPU context?  If so, pop the high vfp registers too. */
37     "    it eq                               \n"
38     "    vldmiaeq r0!, {s16-s31}             \n"
39     "                                        \n"
40     "    msr psp, r0                         \n"
41     "    isb                                 \n"
42     "                                        \n"
43     #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */
44         #if WORKAROUND_PMU_CM001 == 1
45     "            push { r14 }                \n"
46     "            pop { pc }                  \n"
47         #endif
48     #endif
49     "                                        \n"
50     "    bx r14                              \n"
51     "                                        \n"
52     "    .align 4                            \n"
53     "pxCurrentTCBConst: .word pxCurrentTCB   \n"
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                         \n"
 8     "    isb                                 \n"
 9     "                                        \n"
10     "    ldr    r3, pxCurrentTCBConst        \n" /* Get the location of the current TCB. */
11     "    ldr    r2, [r3]                     \n"
12     "                                        \n"
13     //"    tst r14, #0x10                    \n" /* Is the task using the FPU context?  If so, push high vfp registers. */
14     //"    it eq                             \n"
15     //"    vstmdbeq r0!, {s16-s31}           \n"
16     "                                        \n"
17     "    stmdb r0!, {r4-r11, r14}            \n" /* Save the core registers. */
18     "                                        \n"
19     "    str r0, [r2]                        \n" /* Save the new top of stack into the first member of the TCB. */
20     "                                        \n"
21     "    stmdb sp!, {r3}                     \n"
22     "    mov r0, %0                          \n"
23     "    msr basepri, r0                     \n"
24     "    dsb                                 \n"
25     "    isb                                 \n"
26     "    bl vTaskSwitchContext               \n"
27     "    mov r0, #0                          \n"
28     "    msr basepri, r0                     \n"
29     "    ldmia sp!, {r3}                     \n"
30     "                                        \n"
31     "    ldr r1, [r3]                        \n" /* The first item in pxCurrentTCB is the task top of stack. */
32     "    ldr r0, [r1]                        \n"
33     "                                        \n"
34     "    ldmia r0!, {r4-r11, r14}            \n" /* Pop the core registers. */
35     "                                        \n"
36     //"    tst r14, #0x10                    \n" /* Is the task using the FPU context?  If so, pop the high vfp registers too. */
37     //"    it eq                             \n"
38     //"    vldmiaeq r0!, {s16-s31}           \n"
39     "                                        \n"
40     "    msr psp, r0                         \n"
41     "    isb                                 \n"
42     "                                        \n"
43     #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */
44         #if WORKAROUND_PMU_CM001 == 1
45     "            push { r14 }                \n"
46     "            pop { pc }                  \n"
47         #endif
48     #endif
49     "                                        \n"
50     "    bx r14                              \n"
51     "                                        \n"
52     "    .align 4                            \n"
53     "pxCurrentTCBConst: .word pxCurrentTCB   \n"
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

再次編譯後,沒有異常信息

技術分享

成功運行

技術分享

最後,測試浮點運算能力

加入浮點運算後,利用遠程調試,直接看到運算結果:

技術分享

(運行時間比較久,乘的次數比較多,所以看起來像亂碼,)

(實際上,log(468217302516190.94/2.7)/log(1.1) = 344,log(128984051395143.11/3.1)/log(1.2) = 172

中間還遇到一個小問題,由於BSRR寄存器寫入到LED變化有一點點延遲,但是在硬件中幾乎不能感覺出來,

但是在仿真的時候這一點點延遲被放大,無法出現LED一閃一閃的效果。

將通過BSRR寄存器修改LED狀態,改成直接操作ODR寄存器,便可以解決問題。

至此,在qemu上仿真STM32F429時出現的問題基本解決,歡迎大家評論留言。

log(128984051395143.11/3.1)/log(1.2)

如何解決在QEMU上仿真STM32F429時出現的問題