论文部分内容阅读
摘 要:介绍Strong ARM处理器的寄存器及取得CPU类型、频率的过程;介绍在WINDOWS CE应用程序中,如何直接嵌入二进制机器指令,取得底层的寄存器中的容;介绍利用Strong ARM 指令参考手册中对指令的说明把取得寄存器中数据的过程翻译成机器码;另外对从寄存器中读取CPU的类型信息、从地址端口取得CPU频率的值的过程进行了说明。
关键词:Windows CE;CPU;Strong ARM;emit 处理器;寄存器;机器码
中图分类号:TP332 文献标识码:A文章编号:1007-9599(2012)01-0000-02
How to Obtain Detailed Information of the Strong ARM Processor CPU
Zhang Xuemin
(Inventec Electronics (Tianjin) Co., Ltd,Tianjin300193,China)
Abstract:Strong ARM processor register and to obtain the CPU type and frequency of the process; introduced WINDOWS CE application embedded directly in the binary machine instructions to obtain the capacity in the bottom of the register; describes the use of the Strong ARM Instruction Reference Manual a description of the instruction process to obtain the data in the register translated into machine code; in addition,the CPU type information read from the register, described the process of value from the address port to obtain the CPU frequency.
Keywords:Windows CE;CPU;Strong;ARM;The emit processor;Register;Machine code
一、介绍及背景(Introduction and Background)
取得系统信息,WINDOWS CE SDK提供了一个函数GetSystemInfo(&si),以此函数可以取到"PROCESSOR_STRONGARM"等类似这样的信息。但同样是"PROCESSOR_STRONGARM"类型的CPU,还要细分成SA1110 A0、SA1110 B0、SA1110 B4等不同的类型,我们的程序要取得这些详细信息,这些信息无法利用目前的API或SDK提供的函数取得。应该用汇编从寄存器中取得。另外要取CPU的频率,也要调用底层从寄存器中取得。编译成ARM类型的执行档时,VC++对ARM这种机型不支持汇编语言,查看在WINDOWS CE开发环境eMbedded Vc++3.0下的例子文件,针对ARM这种编译条件,里面有__emit(0xe281d010)这样的语句,这是直接写机器码,所以只能想办法直接在程序中嵌入机器码,来代替汇编语言,完成读取CPU类型、频率的信息的工作。这时还遇到这样一个问题,就是假定已经取得了CPU的类型值,把它放在了通用寄存器R0中,由于不能使用汇编语言,怎样把寄存器的值,放到C语言的变量中,这里还介绍了取得数据的过程。
二、实现过程(main body):
(一)如何在只能嵌入机器码的情况下把通用寄存器中的内容取到VC++中的变量中。
在VC++中嵌入汇编
int a;
_asm{
mov eax,100
mov a, eax
}
这里只要直接写mov a,eax就能把寄存器R0中的值放到变量a中,但是用机器码如何把把寄存器中的值取入变量a中?观察X86编译过程的汇编码,发现BP基址寄存器中存放的地址,与VC中新声明的变量的地址有关,可是在Strong ARM机型中没有与BP基址寄存器类似的寄存器。观察x86上的函数,进入函数时实际是把栈寄存器SP中的值放到了BP中,但随着程序的进行,栈寄存器SP中的内容就有可能再次变化了。因此考虑不在当时的函数体内直接写机器指令,而是另做一个新的函数,在新的函数中声名C的变量,这时栈寄存器SP中的值是指向新声明的变量的地址。因此如果把R0寄存器中的内容传给变量a,就是要设法把寄存器R0中的值写到栈寄存器SP(R13)指向的地址中。
(二)取得CPU的型号。在Cache and MMU Control Registers (Coprocessor 15)中第0个寄存器的后面16位是代表CPU详细类型的信息,见图一,Part Number为A11代表SA1100类型CPU, Part Number为B11代表SA1110类型CPU, Stepping为进一步的划分,例如Part Number为B11,后四位Stepping为0000代表SA1110 A0 类型的CPU。
图一
如果能得到这个寄存器中的值,把它叫做iCpu吧,就可通过下面的程序转换成CPU的类型。
if ((iCpu & 0xfff0) == 0xA110)
{
strCpuType="SA1100 XX";
}
else if ((iCpu & 0xfff0) == 0xB110)
{
switch(iCpu & 15)
{
case 0: strCpuType= "SA1110 A0";
case 4: strCpuType="SA1110 B0";
…
}
}
如何得到Coprocessor 15中第0个寄存器中的内容呢?
读写Coprocessor 15的指令可以采用 MRC/MCR指令。读取Register 0-ID可以采用这个指令读取。就是要设法执行MRC CP15 0,R0,CN0,CN0这条汇编语句,为此需要把汇编语句翻译成机器码。参考ARM汇编指令集中对MRC/MCR的编码解释,见图二,图三
图二
图三
这是对汇编命令MRC/MCR的编码解释,前4位31-28位的是ARM condition code, 参考WINDOWS CE开发环境eMbedded Vc++3.0下的例子文件中的__emit的语句,对应于ARM机型全都是e,那麽我们也认为这4位为1110, 就是e。27-24位是1110没有问题,23-21位介绍registers时, CP15这三位应该是000,第20位,读的时侯是1,写是0,所以是1,19-16位是读CP15中的第几个寄存器,我们是读第0个,因此是0000,15-12是读到哪个通用寄存器中,我们要读到R0,因此也是0000,11-8位参考图二应该为1111,另外7-5位,是000,第4位为1,3-0位也应该为分别是0000,这样这条指令的编码为1110 1110 0001 0000 0000 1111 0001 0000,写成16进制,0xee100f10。因此把Register 0-ID寄存器中的值放到通用寄存器R0的机器码为__emit(0xee100f10); 同样办法找出STR R0, [R13]对应的机器码为0xe50d0000。
因此取出CPU类型的过程可以写成这样:
int CCpuTest::CpuType()
{
int a;
//MRC CP15 0,R0,CN0,CN0
__emit(0xee100f10);
//STR R0, [R13]
__emit(0xe50d0000);
return a;
}
(三)取得CPU的频率。取频率的过程实际上就是取端口地址为0X90020014的中的32位数据过程。把R1的寄存器的值设成0X90020014,再把R1寄存器中地址的内容取到R0。把R0的数据输出,取频率的过程写为:
int CCpuTest::GetFrequency()
{
int a;
/* movR0, #14 */
__emit(0xe3a00014);
/* movR1, #020 */
__emit(0xe3a01020);
/* mov R3, R1, LSL#12 */
__emit(0xe1a03601);
/* add R0,R3*/
__emit(0xe0830000);
/* movR1, #90 */
__emit(0xe3a01090);
/* mov R3, R1, LSL#24 */
__emit(0xe1a03c01);
/* add R0,R3 */
__emit(0xe0830000); //这时查看R0已为0X90020014
/* mov R1,R0 */
__emit(0xe1a01000);//这时查看R1已为0X90020014
/* ldr R0,[R1] */
__emit(0xe5910000);
/* str R13,R0 */
__emit(0xe50d0000);
return a;
}
但这总是取不出频率的值。后来才了解到应该取这个寄存器实际地址对应的Virtual Address的值,Windows ce的应用程序,都是采用Virtual Address形式,从不直接与Physical Address打交道。
查看对照register addresses and the current virtual maps:
内部寄存器:
DCD 0x8A000000, 0xA0000000, 4 ;SA11X0 Memory control registers
DCD 0x89000000, 0x90000000, 4 ;SA11X0 System control registers
这里左面(比如0x89000000)的是Virtual Address,右面(比如0x90000000)是PhysicalAddress, 4代表4M。这就是说0x90000000的Physical Address对应到Virtual Address为0x89000000,映射的空间为4M,所以0x90020014的PhysicalAddress转换为Virtual Address应该是0x89020014。所以取频率的地址应该为0x89020014,把上面的__emit(0xe3a01090)改为__emit(0xe3a01089); 就能取到正确的频率。
另外参考下面的图四 CCF[4:0]的不同数值代表的频率表(只截了部分内容)
图四
可以考虑这样计算,如果从PPCR中取出的整数值为iFrequency, 通过下面的转换变为频率的数值。
switch(iFrequency & 31)
{
case 0:
strFrequency="59 Mhz";
break;
case 1:
strFrequency="74 Mhz";
break;
…
}
三、小结( Summary):
通过这种方式,我成功取到了CPU的详细信息,经过检验是正确的。
关键词:Windows CE;CPU;Strong ARM;emit 处理器;寄存器;机器码
中图分类号:TP332 文献标识码:A文章编号:1007-9599(2012)01-0000-02
How to Obtain Detailed Information of the Strong ARM Processor CPU
Zhang Xuemin
(Inventec Electronics (Tianjin) Co., Ltd,Tianjin300193,China)
Abstract:Strong ARM processor register and to obtain the CPU type and frequency of the process; introduced WINDOWS CE application embedded directly in the binary machine instructions to obtain the capacity in the bottom of the register; describes the use of the Strong ARM Instruction Reference Manual a description of the instruction process to obtain the data in the register translated into machine code; in addition,the CPU type information read from the register, described the process of value from the address port to obtain the CPU frequency.
Keywords:Windows CE;CPU;Strong;ARM;The emit processor;Register;Machine code
一、介绍及背景(Introduction and Background)
取得系统信息,WINDOWS CE SDK提供了一个函数GetSystemInfo(&si),以此函数可以取到"PROCESSOR_STRONGARM"等类似这样的信息。但同样是"PROCESSOR_STRONGARM"类型的CPU,还要细分成SA1110 A0、SA1110 B0、SA1110 B4等不同的类型,我们的程序要取得这些详细信息,这些信息无法利用目前的API或SDK提供的函数取得。应该用汇编从寄存器中取得。另外要取CPU的频率,也要调用底层从寄存器中取得。编译成ARM类型的执行档时,VC++对ARM这种机型不支持汇编语言,查看在WINDOWS CE开发环境eMbedded Vc++3.0下的例子文件,针对ARM这种编译条件,里面有__emit(0xe281d010)这样的语句,这是直接写机器码,所以只能想办法直接在程序中嵌入机器码,来代替汇编语言,完成读取CPU类型、频率的信息的工作。这时还遇到这样一个问题,就是假定已经取得了CPU的类型值,把它放在了通用寄存器R0中,由于不能使用汇编语言,怎样把寄存器的值,放到C语言的变量中,这里还介绍了取得数据的过程。
二、实现过程(main body):
(一)如何在只能嵌入机器码的情况下把通用寄存器中的内容取到VC++中的变量中。
在VC++中嵌入汇编
int a;
_asm{
mov eax,100
mov a, eax
}
这里只要直接写mov a,eax就能把寄存器R0中的值放到变量a中,但是用机器码如何把把寄存器中的值取入变量a中?观察X86编译过程的汇编码,发现BP基址寄存器中存放的地址,与VC中新声明的变量的地址有关,可是在Strong ARM机型中没有与BP基址寄存器类似的寄存器。观察x86上的函数,进入函数时实际是把栈寄存器SP中的值放到了BP中,但随着程序的进行,栈寄存器SP中的内容就有可能再次变化了。因此考虑不在当时的函数体内直接写机器指令,而是另做一个新的函数,在新的函数中声名C的变量,这时栈寄存器SP中的值是指向新声明的变量的地址。因此如果把R0寄存器中的内容传给变量a,就是要设法把寄存器R0中的值写到栈寄存器SP(R13)指向的地址中。
(二)取得CPU的型号。在Cache and MMU Control Registers (Coprocessor 15)中第0个寄存器的后面16位是代表CPU详细类型的信息,见图一,Part Number为A11代表SA1100类型CPU, Part Number为B11代表SA1110类型CPU, Stepping为进一步的划分,例如Part Number为B11,后四位Stepping为0000代表SA1110 A0 类型的CPU。
图一
如果能得到这个寄存器中的值,把它叫做iCpu吧,就可通过下面的程序转换成CPU的类型。
if ((iCpu & 0xfff0) == 0xA110)
{
strCpuType="SA1100 XX";
}
else if ((iCpu & 0xfff0) == 0xB110)
{
switch(iCpu & 15)
{
case 0: strCpuType= "SA1110 A0";
case 4: strCpuType="SA1110 B0";
…
}
}
如何得到Coprocessor 15中第0个寄存器中的内容呢?
读写Coprocessor 15的指令可以采用 MRC/MCR指令。读取Register 0-ID可以采用这个指令读取。就是要设法执行MRC CP15 0,R0,CN0,CN0这条汇编语句,为此需要把汇编语句翻译成机器码。参考ARM汇编指令集中对MRC/MCR的编码解释,见图二,图三
图二
图三
这是对汇编命令MRC/MCR的编码解释,前4位31-28位的是ARM condition code, 参考WINDOWS CE开发环境eMbedded Vc++3.0下的例子文件中的__emit的语句,对应于ARM机型全都是e,那麽我们也认为这4位为1110, 就是e。27-24位是1110没有问题,23-21位介绍registers时, CP15这三位应该是000,第20位,读的时侯是1,写是0,所以是1,19-16位是读CP15中的第几个寄存器,我们是读第0个,因此是0000,15-12是读到哪个通用寄存器中,我们要读到R0,因此也是0000,11-8位参考图二应该为1111,另外7-5位,是000,第4位为1,3-0位也应该为分别是0000,这样这条指令的编码为1110 1110 0001 0000 0000 1111 0001 0000,写成16进制,0xee100f10。因此把Register 0-ID寄存器中的值放到通用寄存器R0的机器码为__emit(0xee100f10); 同样办法找出STR R0, [R13]对应的机器码为0xe50d0000。
因此取出CPU类型的过程可以写成这样:
int CCpuTest::CpuType()
{
int a;
//MRC CP15 0,R0,CN0,CN0
__emit(0xee100f10);
//STR R0, [R13]
__emit(0xe50d0000);
return a;
}
(三)取得CPU的频率。取频率的过程实际上就是取端口地址为0X90020014的中的32位数据过程。把R1的寄存器的值设成0X90020014,再把R1寄存器中地址的内容取到R0。把R0的数据输出,取频率的过程写为:
int CCpuTest::GetFrequency()
{
int a;
/* movR0, #14 */
__emit(0xe3a00014);
/* movR1, #020 */
__emit(0xe3a01020);
/* mov R3, R1, LSL#12 */
__emit(0xe1a03601);
/* add R0,R3*/
__emit(0xe0830000);
/* movR1, #90 */
__emit(0xe3a01090);
/* mov R3, R1, LSL#24 */
__emit(0xe1a03c01);
/* add R0,R3 */
__emit(0xe0830000); //这时查看R0已为0X90020014
/* mov R1,R0 */
__emit(0xe1a01000);//这时查看R1已为0X90020014
/* ldr R0,[R1] */
__emit(0xe5910000);
/* str R13,R0 */
__emit(0xe50d0000);
return a;
}
但这总是取不出频率的值。后来才了解到应该取这个寄存器实际地址对应的Virtual Address的值,Windows ce的应用程序,都是采用Virtual Address形式,从不直接与Physical Address打交道。
查看对照register addresses and the current virtual maps:
内部寄存器:
DCD 0x8A000000, 0xA0000000, 4 ;SA11X0 Memory control registers
DCD 0x89000000, 0x90000000, 4 ;SA11X0 System control registers
这里左面(比如0x89000000)的是Virtual Address,右面(比如0x90000000)是PhysicalAddress, 4代表4M。这就是说0x90000000的Physical Address对应到Virtual Address为0x89000000,映射的空间为4M,所以0x90020014的PhysicalAddress转换为Virtual Address应该是0x89020014。所以取频率的地址应该为0x89020014,把上面的__emit(0xe3a01090)改为__emit(0xe3a01089); 就能取到正确的频率。
另外参考下面的图四 CCF[4:0]的不同数值代表的频率表(只截了部分内容)
图四
可以考虑这样计算,如果从PPCR中取出的整数值为iFrequency, 通过下面的转换变为频率的数值。
switch(iFrequency & 31)
{
case 0:
strFrequency="59 Mhz";
break;
case 1:
strFrequency="74 Mhz";
break;
…
}
三、小结( Summary):
通过这种方式,我成功取到了CPU的详细信息,经过检验是正确的。