在上一章节中我们介绍了主要介绍了ClassB软件的作用、框架和流程等,本章节将给大家介绍CPU寄存器检测实现方法。
1. CPU寄存器自检
R0 \~ R12我们称为通用寄存器,其中寄存器R0 \~ R7我们称为低寄存器,寄存器R8 \~ R12我们称为高寄存器,可用于数据操作。
在对R0 \~ R12寄存器进行操作时需要注意:绝大多数 16 位 Thumb 指令只能访问 R0‐R7,而 32 位 Thumb‐2 指令可以访问所有寄存器。
R13作为堆栈指针 SP,SP有两个:
主堆栈指针(MSP):复位后缺省使用的堆栈指针,用于操作系统内核以及异常处理例程(包括中断服务例程)。
进程堆栈指针(PSP):由用户的应用程序代码使用。
R14连接寄存器:当呼叫一个子程序时,由 R14 存储返回地址。
CPU自检在启动时和运行时都会进行,在启动时,所有寄存器(R0\~R12,PSP,MSP)和标记的功能测试都会进行一次自检;运行时,周期性自检,不检测R13,R14,仅检测寄存器R0\~R12。
2. CPU寄存器测试原理
在mm32\_cpu\_startKeil.s与mm32\_cpu\_runKeil.s文件中会对CPU寄存器进行测试,其中mm32\_cpu\_startKeil.s文件中的测试为启动过程中对CPU寄存器进行的测试,mm32\_cpu\_runKeil.s文件中的测试为运行自检流程中的CPU寄存器测试,其原理为对寄存器每一位写0和写1操作,对比是否有错误。
具体实现:分别写入0xAAAAAAAA和0x55555555再进行比较是否为写入的值。
3. CPU寄存器测试实现
以启动时CPU自检程序为例进行分析,将conAA数据定义为0xAAAAAAAA,将con55定义为0x55555555,并使用EXPORT指令进行声明。
conAA DCD 0xAAAAAAAA
con55 DCD 0x55555555
con80 DCD 0x80000000
conA8 DCD 0xAAAAAAA8
con54 DCD 0x55555554
EXPORT conAA
EXPORT con55
声明函数ClassB\_StartUpCPUTest函数,并进行CPU寄存器中的R1寄存器的检测,先将conAA的数据赋值给R0寄存器,然后赋值给R1寄存器,然后比较R0与R1寄存器的数据,如果两者的数据相同则将con55分别赋值给R1与R0寄存器进行比较,否则会跳转执行CPUTestFail函数,如果两者的数据相同则将0x01赋值给R1寄存器,否则将会跳转到CPUTestFail函数中。
; Register R1
LDR R0, =conAA
LDR R1,[R0]
LDR R0,[R0]
CMP R0,R1
BNE CPUTestFail
LDR R0, =con55
LDR R1,[R0]
LDR R0,[R0]
CMP R0,R1
BNE CPUTestFail
MOVS R1, #0x1 ; For ramp test
在汇编程序中大家可以看到,如果检测失败,会跳转到CPUTestFail函数执行,如果检测成功则进行下一步的检测。
依次针对R2 ,R3,R4,R5,R6,R7,R8,R9,R10,R11,R12寄存器进行与R1一样的操作,在上述的寄存器比较完成以后相应的寄存器会被赋予新的数据。
Class_StartUpCPUTest PROC
EXPORT Class_StartUpCPUTest
PUSH {R4-R6} ; Safe critical registers
MOVS R0, #0x00
UXTB R0,R0
ADDS R0,#0 ; Set Z(ero) Flag
BNE CPUTestFail ; Fails if Z clear
BMI CPUTestFail ; Fails if N is set
SUBS R0,#1 ; Set N(egative) Flag
BPL CPUTestFail ; Fails if N clear
ADDS R0,#2 ; Set C(arry) Flag and do not set Z
BCC CPUTestFail ; Fails if C clear
BEQ CPUTestFail ; Fails if Z is set
BMI CPUTestFail ; Fails if N is set
LDR R0,=con80 ; Prepares Overflow test
LDR R0,[R0]
ADDS R0, R0, R0 ; Set V(overflow) Flag
BVC CPUTestFail ; Fails if V clear
; This is for control flow test (ENTRY point)
LDR R0,=CtrlFlowCnt
LDR R1,[R0]
ADDS R1,R1,#0x3 ; CtrlFlowCnt += OxO3
STR R1,[R0]
; Register R1
LDR R0, =conAA
LDR R1,[R0]
LDR R0,[R0]
CMP R0,R1
BNE CPUTestFail
LDR R0, =con55
LDR R1,[R0]
LDR R0,[R0]
CMP R0,R1
BNE CPUTestFail
MOVS R1, #0x1 ; For ramp test
; Register R2
LDR R0, =conAA
LDR R2,[R0]
LDR R0,[R0]
CMP R0,R2
BNE CPUTestFail
LDR R0, =con55
LDR R2,[R0]
LDR R0,[R0]
CMP R0,R2
BNE CPUTestFail
MOVS R2, #0x2 ; For ramp test
; Register R3
LDR R0, =conAA
LDR R3,[R0]
LDR R0,[R0]
CMP R0,R3
BNE CPUTestFail
LDR R0, =con55
LDR R3,[R0]
LDR R0,[R0]
CMP R0,R3
BNE CPUTestFail
MOVS R3, #0x3 ; For ramp test
; Register R4
LDR R0, =conAA
LDR R4,[R0]
LDR R0,[R0]
CMP R0,R4
BNE CPUTestFail
LDR R0, =con55
LDR R4,[R0]
LDR R0,[R0]
CMP R0,R4
BNE CPUTestFail
MOVS R4, #0x4 ; For ramp test
; Register R5
LDR R0, =conAA
LDR R5,[R0]
LDR R0,[R0]
CMP R0,R5
BNE CPUTestFail
LDR R0, =con55
LDR R5,[R0]
LDR R0,[R0]
CMP R0,R5
BNE CPUTestFail
MOVS R5, #0x5 ; For ramp test
; Register R6
LDR R0, =conAA
LDR R6,[R0]
LDR R0,[R0]
CMP R0,R6
BNE CPUTestFail
LDR R0, =con55
LDR R6,[R0]
LDR R0,[R0]
CMP R0,R6
BNE CPUTestFail
MOVS R6, #0x6 ; For ramp test
; Register R7
LDR R0, =conAA
LDR R7,[R0]
LDR R0,[R0]
CMP R0,R7
BNE CPUTestFail
LDR R0, =con55
LDR R7,[R0]
LDR R0,[R0]
CMP R0,R7
BNE CPUTestFail
MOVS R7, #0x7 ; For ramp test
; Register R8
LDR R0, =conAA
LDR R0,[R0]
MOV R8,R0
CMP R0,R8
BNE CPUTestFail
LDR R0, =con55
LDR R0,[R0]
MOV R8,R0
CMP R0,R8
BNE CPUTestFail
MOVS R0, #0x08 ; For ramp test
MOV R8,R0
BAL CPUTstCont
CPUTestFail
BLAL FailSafePOR
CPUTstCont
; Register R9
LDR R0, =conAA
LDR R0,[R0]
MOV R9,R0
CMP R0,R9
BNE CPUTestFail
LDR R0, =con55
LDR R0,[R0]
MOV R9,R0
CMP R0,R9
BNE CPUTestFail
MOVS R0, #0x09 ; For ramp test
MOV R9,R0
; Register R10
LDR R0, =conAA
LDR R0,[R0]
MOV R10,R0
CMP R0,R10
BNE CPUTestFail
LDR R0, =con55
LDR R0,[R0]
MOV R10,R0
CMP R0,R10
BNE CPUTestFail
MOVS R0, #0x0A ; For ramp test
MOV R10,R0
; Register R11
LDR R0, =conAA
LDR R0,[R0]
MOV R11,R0
CMP R0,R11
BNE CPUTestFail
LDR R0, =con55
LDR R0,[R0]
MOV R11,R0
CMP R0,R11
BNE CPUTestFail
MOVS R0, #0x0B ; For ramp test
MOV R11,R0
; Register R12
LDR R0, =conAA
LDR R0,[R0]
MOV R12,R0
CMP R0,R12
BNE CPUTestFail
LDR R0, =con55
LDR R0,[R0]
MOV R12,R0
CMP R0,R12
BNE CPUTestFail
MOVS R0, #0x0C ; For ramp test
MOV R12,R0
LDR R0, =CPUTstCont
R1\~R12寄存器比较完成以后会在Ramp中做进一步的判断,判断R1寄存器的数值是否为0x01,R2寄存器的数值是否为0x02,依次判断到R12寄存器,如果以上的判断都是正确的则说明R1\~R12寄存器验证通过。
; Ramp pattern verification (R0 is not tested)
CMP R1, #0x01
BNE CPUTestFail
CMP R2, #0x02
BNE CPUTestFail
CMP R3, #0x03
BNE CPUTestFail
CMP R4, #0x04
BNE CPUTestFail
CMP R5, #0x05
BNE CPUTestFail
CMP R6, #0x06
BNE CPUTestFail
CMP R7, #0x07
BNE CPUTestFail
MOVS R0, #0x08
CMP R0,R8
BNE CPUTestFail
MOVS R0, #0x09
CMP R0,R9
BNE CPUTestFail
MOVS R0, #0x0A
CMP R0,R10
BNE CPUTestFail
MOVS R0, #0x0B
CMP R0,R11
BNE CPUTestFail
MOVS R0, #0x0C
CMP R0,R12
BNE CPUTestFail
对R13的自检功能与通用寄存器的操作不同,需要先保存堆栈到R0,操作如下:
; Process Stack pointer (banked Register R13)
MRS R0,PSP ; Save process stack value
LDR R1, =conA8 ; Test is different (PSP is word aligned, 2 LSB cleared)
LDR R1,[R1]
MSR PSP,R1 ; load process stack value MRS R2,PSP ; Get back process stack value
CMP R2,R1 ; Verify value
BNE CPUTestFail
LDR R1, =con54 ; Test is different (PSP is word aligned, 2 LSB cleared)
LDR R1,[R1]
MSR PSP,R1 ; load process stack value MRS R2,PSP ; Get back process stack value
CMP R2,R1 ; Verify value
BNE CPUTestFail
MSR PSP, R0 ; Restore process stack value
; Stack pointer (Register R13)
MRS R0,MSP ; Save stack pointer value
LDR R1, =conA8 ; Test is different (SP is word aligned, 2 LSB cleared)
LDR R1,[R1]
MSR MSP,R1 ; load SP value MRS R2,MSP ; Get back SP value
CMP R2,R1 ; Verify value
BNE CPUTestFail
LDR R1, =con54
LDR R1,[R1] ; load SP value
MSR MSP,R1 ; Get back SP value
MRS R2,MSP ; Verify value
CMP R2,R1
BNE CPUTestFail
MSR MSP,R0 ; Restore stack pointer value
; Control flow test (EXIT point)
LDR R0,=CtrlFlowCntInv LDR R1,[R0]
SUBS R1,R1,#0x3 ; CtrlFlowCntInv -= OxO3
STR R1,[R0]
POP {R4-R6} ; Restore critical registers
; If next instruction is not executed, R0 will hold a value different from 0
MOVS R0, #0x1 ; CPUTEST_SUCCESS
BX LR ; return to the caller
ALIGN
ENDP
END
以上代码是在启动时CPU寄存器自检的汇编代码,在运行时CPU寄存器自检功能与启动自检的代码原理相同,只是对寄存器操作个数有差异,运行时不用对R13\14进行操作。
在汇编程序中我们会使用CMP指令进行判断,后面会跟着BNE指令,其作用就是判断如果CMP判断的两个寄存器不相等,则直接执行BNE指令后面的CPUTestFail函数,直接提醒错误报警,或可以添加对应的处理机制。