新闻中心

8051 与 AndesCoreTM 的软件差异与移植

来源:Andes
发布时间:[2015-07-29]

1. 8051 与 AndesCoreTM

本文将介绍使用 8051 与 AndesCore™差异事项,并对两种 CPU 系统相关事 项做说明,后面再介绍从 8051 移植到 AndesCore™上注意事项,举中断向量表 及异常处理函数的例子说明差异及移植,最后简要介绍 AndesCore™在 MCU 应 用的三款 CPU: N705,N801 和 N968A。

2. 8051 与 AndesCoreTM 常见差异事项

2.1. 位宽的差异

位宽是指处理器一次执行指令的数据带宽。8051 是 8 位宽的处理器, 而 AndesCore™是 32 位宽的处理器,支持 32 位与 16 位的混合指令集,位数越宽, 在数据的处理方面就更有效率。

8051 指令例子:

MOV A, Rn ; 寄存器传送到累加器

INC A ; 累加器加 1

AndesCore™指令例子:

1. 32 位指令

MOVI Rt, imm20s ; 将一个立即数 imm20s 赋于寄存器 Rt

ADDI Rt, Ra, imm15s ; 将一个立即数 imm15s 与 Ra 相加结果赋于 Rt

2. 16 位指令: 在操作数范围较小时,可以被编译成 16 位指令

MOVI55 Rt5, imm5s ;

ADDI333 Rt3, Ra3, imm3u ;

2.2. 指令差异

8051 汇编语言共有 111 条指令集,AndeStar™的 V3m 指令集有 157 条, AndeStar™的 V3 指令集有 200 多条,两种 CPU 的指令集大概可以分为以下几 类:算术运算,如加,减,乘,除等操作;数据传送,如数据在寄存器,内存间 的传送,赋值等;逻辑跳转,如函数呼叫,无条件跳转,条件跳转,中断返回等; 在 AndesCore™中还有特权模式的指令部分,关于两种指令集的具体差别,可 以分别参考对应的指令集介绍文档。

2.3. 地址空间映像差异

AndesCore™使用 memory map 方式映像地址空间,主要有两种,内存的空 间映射,如其中的 RAM 或 ROM 地址,它们用于存放程序运行时的代码和数据, 在 AndesCore™上代码在 link 后,程序运行的代码和数据地址会最终确定, Andes 提供了一个简便的 link script 工具 sag,可以很方便的对系统中可用的内 存空间进行分配设定。

另一个是外设所对应的地址空间,可以通过查看 SoC 对应的手册了解对应的 外设映射的空间范围及相应的使用方法。

2.4. 堆栈设置差异

8051 的堆栈的起始位置是固定的(部分衍生 8051 可以做程序设定),它通 常固定在片内的 RAM 中,8051 内存空间有限,非常小,程序中所使用的变量 存放于特定的数据空间中,并不会放在堆栈空间,所以在 8051 中所需要的堆栈 空间很小。而对于 AndesCore™来说,堆栈可以设置在任意合适的 RAM 上。程 序运行时所有的局部变量都存放在堆栈中,只需要确保在设计系统的时候有足够 的堆栈空间。在 AndesCore™中有$sp 寄存器表示栈顶位置,这需要在系统上电 或者是系统 reset 后初始化时进行设置。

2.5. 代码和数据的存储差异

在 8051 系列单片机中,数据存储区可以分为内部数据存储区以及外部数据 存储区。

内部数据存储区有几个区别:data,bdata,idata。

data : 片内 RAM 直接寻址区。bdata: 片内 RAM 位寻址区。idata: 片内 RAM

间接寻址区。

外部数据存储区又有:xdata,pdata。

xdata 和 pdata:是外部存储区,有些芯片会带有 XRAM。

在有些开发工具中,如 Keil,可以通过设置存储模式来处理,存储模式决定 了默认的存储器类型,此存储器类型将应用于函数参数,局部变量和定义时未包含 存储器类型的变量。

SMALL 所有的变量存放在片内 RAM(data 区间)

COMPACT 所有的变量存放在外部存储区(pdata 区间)

LARGE 所有的变量存放在外部存储区(xdata 区间)

AndesCore™以内存映射的方式,内存空间不会有特别的限制,就是说不会 像 8051 那样需放在某处区间,这样的设计更方便灵活,允许程序代码和数据在 可用的空间里自由放置。

有时候需要将某段代码或者数据存放在指定的位置上,在 8051 中,可以在 代码中使用"at"关键字,但该关键字是 8051 中所特有的,会造成可移植性和维 护的问题,在 AndesCore™上,提供了一种简便的 link script 工具,如上所提到 的 sag 工具,在 C 代码中使用 GNU 标准的语法格式,在 link 之后相应的代码和 数据将存放于指定的位置,这样可以避免在代码中使用"at"该平台相关的属性设 置。

2.6. 数据类型及对齐差异

8051 和 AndesCore™是不同类型的 CPU,它们所使用的数据类型所对应的宽 度也不同,如下表所示:

Type AndesCore™ 8051 Notes
char 8-bit signed 8-bit signed  
short 16-bit 16-bit  
int 32-bit 16-bit int is smaller on 8051
long 32-bit 32-bit  
long long 64-bit N/A  
float 32-bit 32-bit  
double 64-bit 32-bit 8051 has no 64-bit floating point type
long double 64-bit N/A  

在链接完成后数据通常都会按照本身的属性对齐,比如 int 类型则会 4 bytes对齐,short 则会 2 bytes 对齐。这样的存放方式可以提高 CPU 对数据读取时的 效率。虽然 AndesCore™是 32bit 的 CPU, 在只需要 8bit 和 16bit 的数据时能 节省存储空间,但在处理 16bit 和 32bit 的数据上则有更高效。

在 8051 中有 sbit 关键字用于设置对特殊功能寄存器 SFR 的直接访问,8051的特殊功能寄存器分布在内存地址 0x80 到 0xFF 处,如下表:

F8H               FFH
F0H B *             F7H
E8H               EFH
E0h ACC *             E7H
D8H               DFH
D0H PSW *             D7H
C8H T2CON *   RCAP2L RCAP2H TL2 TH2   CFH
C0h               C7H
B8H IP *             BFH
B0h P3 *             B7H
A8h IE *             AFH
A0H P2 *             A7H
98H SCON * SBUF           9FH
90H P1 *             97H
88h TCON * TMOD TL0 TL1 TH0 TH1   8FH
80H P0 * SP DPL DPH     PCON 87H

sbit 是 8051 扩展的变量类型,非标准 C 语法,移植的时候需要将其修改成标准 C 操作语法,另外在 AndesCore™中,所有的寄存器都是单独存在的,不 会占用内存的空间。

2.7. 指针使用差异

8051 中两种类型的指针,分别是存储器指针和通用指针,通用指针由 3 个字 节组成,第一个字节用来指明对应的内存类型,所以这种类型的指针类型占用空 间更大也更慢,存储器指针只能用来访问指定类型的存储器空间。

通用指针:

通用指针的声明和标准 C 语言中一样。如:

char *s; /* string ptr */

int *numptr; /* int ptr */ long *state; /* long ptr */

存储器指针:

char data *str; /* ptr to string in data */

int xdata *numtab; /* ptr to int(s) in xdata */

而在 AndesCore™上指针不会有这方面的限制,它是一个 32bit 的数据,普通的寄存 器就 可以 存放指针 内容 ,可 以访问到 系统 4G 范围内的 空间 (N705,N801 地址空间只有 16M,N968A 以上的 CPU 地址空间可达 4G)。

2.8. 函数声明差异

在 8051 中由于堆栈空间有限,如果有函数是可重入的,需要在函数声明的 时候用关 键字 reentrant 做说明。 8051 的中断处理 函数则 需要 使用关键字 interrupt 声明,中断处理函数有时也需要用 using 关键字指明哪一寄存器组会被 使用到。

在 AndesCore™中,都采用标准的 C 语法,在声明函数时并不需要这些附加 的声明。AndesCore™遵行底层的 ABI 机制,编译器处理底层的寄存器及堆栈相 关机制。对于上层用户来说是透明的。

3. 系统相关事项说明

3.1. 操作模式

8051 只有一种 mode,AndesCore™有两种 mode,分别是 superuser mode 和 user mode,当系统上电启动时是在 superuser mode,或者当系统进入到中 断或者异常时也进入到 superuser mode,当从中断或者是异常返回后,会返回到 user mode。由于 8051 没有 mode 切换的问题,所以在移植的时候只需要理 解 AndesCore™在 mode 方面的机制就可以。

3.2. 系统的启动

8051 和 AndesCore™的系统启动过程类似,通常在 0 地址存放中断向量表, 第一个向量表是 reset,当系统上电或者是 reset 后,经过该向量会跳转到一个 启动函数中,该启动函数会完成系统启动所必要的步骤,比如设置 CPU,初始 化 SoC,清理内存,初始化 C 运行环境等, 最后完成所有的准备后跳转到 main 函数。

3.3. 中断处理

8051 有 5 个中断源,通常中断向量表只是一个跳转,会跳到真正的中断处理 函数,8051 只能设置成两级的中断优先级。 

中断源 中断向量
上电复位 0000H
外部中断 0 0003H
定时器 0 溢出 000BH
外部中断 1 0013H
定时器 1 溢出 001BH
串行口中断 0023H

AndesCore™包含了 9 个内部异常,中断向量号对应于从 0 到 8, 9 之后对应于外部中断,在 Internal VIC (IVIC)mode 时可支持 32 个外部中断,

Entry number Entry point
0 Reset/NMI
1 TLB fill
2 PTE not present
3 TLB misc
4 TLB VLPT miss
5 Machine Error
6 Debug related
7 General exception
8 Syscall
9 HW0
10 HW1
11 HW2
12 HW3
40 HW31

当 External VIC(EVIC) mode 时由外部中断控制器决定,最多有 64 个。

Entry number Entry point
0 Reset/NMI
1 TLB fill
2 PTE not present
3 TLB misc
4 TLB VLPT miss
5 Machine Error
6 Debug related
7 General exception
8 Syscall
9-72 VEP 0-63

中断的处理由以下几部分组成:

1. 实现中断处理函数

可以用汇编实现 8051 的中断处理函数,也可以用 C 来实现,在 8051中 C 实现的中断处理函数会有一个"interrupt"的关键字,如果有寄存器 bank 被使用到,还要加上"using"关键字。如果要将中断处理函数固定在特定位置 还需要使用"at"关键字,而 AndesCore™使用的是标准的 C 语法,不需要为中断处理函数做这些设置。

2. 中断向量表的产生

8051 中断向量表摆放在 0 开始的位置,在 AndesCore™中硬件可以设 定启动地址,通常设为 0 地址,也可以是非 0 地址,中断向量表存放在对应 系统启动地址处。在程序编写过程中可以通过标准的 gnu 语法再加上 link

script 的 sag 工具,以使产生的中断向量表在链接的时候存放于特定的位置。

3. 中断配置

在 8051 中,需要做以下设置

● IE 寄存器中 Individual Interrupt Enable 位设 1

● IE 寄存器中 EA(Enable All)位设 1

● 当是外部中断时,配置相关的 pin 为输入,并设置对应的触发属性为edge 或 level 触发。

而在 AndesCore™中需要做以下设置:

● 设置 CPU IVIC 或者 EVIC mode

● 设置 INT_MASK 位

● 设置中断的优先级

4. 关于异常处理差异

在 8051 中没有异常处理向量,所以在 8051 中并没有这部分的处理函数, 在 AndesCore™中有一些系统的 exception 中断向量,比如 Machine Error,GeneralException, 建议在 AndesCore™上实现对应的处理函数,当发生这类异常时做一些基本的处理。

3.4. 时序和延迟

在 8051 中可以采用 NOP 指令来延迟,在 AndesCore™中也有 NOP 指令来 达到类似目的。

3.5. 电源管理

8051 单片机中有两种省电方式,分别是空闲方式和掉电模式,单片机处于空 闲工作方式时,CPU 处于睡眠状态,它的片内其它部件还是会继续工作,片内 RAM 的内容和所有专用寄存器的内容在空闲方式期间都被保存下来了, 可以通 过中断或者硬件复位来终止空闲工作方式。单片机处于掉电工作方式时,片内的 振荡器停止了工作,因此它的一切都被迫停止了。但片内 RAM 的内容和专用寄 存器的内容一直保持到掉电方式结束为止。掉电方式的唤醒方式只有一种,就是 硬件复位。

在 AndesCore™上,可以通过软件 standby 指令使 CPU 进入到低功耗模式,通常标准 c 代码并不能直接控制硬件,Andes 的 compiler 提供了 intrinsic 函数来做到这点。分别是:nds32_standby_no_wake_grant(), nds32_standby_wake_grant(), nds32_standby_wait_done().指定系统进入低功耗模式时被唤醒的方式,分别是外部中断中断唤醒,电源管理模块唤醒,和 中断配合电源管理模块唤醒,可以根据系统需要分别设计。

4. 从 8051 移植到 AndesCoreTM 上注意事项

一个 8051 工程,当移植到 AndesCore™上时有以下注意事项:

1. 内存映射,代码和数据摆放位置相关的设置。

2. 可以不 必考 虑变 量数 目,或 者是 函数 的 overlay, 因为在 32bit 的AndesCore™上开发时内存空间通常不会像 8051 那样小。

3. 如果空间允许,在 AndesCore™上尽量使用 32bit 的数据类型,这样效率 会更高。

4. 在 8051 上用于表示内存区域属性的标志如(idata, xdata, bdata, pdata 等)在 AndesCore™上可以移除。

5. 在 8051 上不需要设置内存区块模式,比如:small, compact, large 等。

6. 在 8051 上用于表 示对像远近的属性 "near" 和 "far", 都可以移 除, AndesCore™上的指针的访问可以达到所有地址空间。

7. 在中断处理函数中不需要像 8051 那样指定哪块寄存器块会被用到的关键 字"using"。

8. 在 8051 上中断处理函数就和普通的函数一样,中不需要设置其它的关键 字,如 interrupt。

9. 如果有 8051 汇编部分移植到 AndesCore™,需要重新实现,尽可能的用c 来实现,便于维护和调试。

10. 在 8051 中使用到的#progma 相关部分需要删除。

11. 在 AndesCore™中函数不需要声明为"reentrant"属性。

12. 如果使用了数学运算,在 8051 中默认是使用 32bit 单精度浮点,如果要 保持和 8051 中相同的精度,需要将函数名做一些调整,如将 sin()改成sinf()。

5. 中断向量及异常处理函数例子

以中断向量及中断处理函数的例子说明差异及移植。

5.1. 汇编实现中段向量表

[8051]

该例子显示怎样用汇编设置 8051 的中断向量和中断处理函数,在 8051 汇编 中 ORG 指定了后面汇编代码的位置,后面的中断向量通常是一个跳转语句。如 下例第一个向量跳到主函数 MAIN 函数中,另外一个外部中断 1,也是一个跳转指 令:LJMP INT 到后面的用汇编实现的中断处理函数 INT 中。

ORG 0000H /*起始地址*/

LJMP MAIN /*跳转到主程序*/

ORG 0013H /*外部中断 1 的地址*/

LJMP INT /*跳转到 INT 执行*/

ORG 0100H /*主程序的起始地址*/

MAIN: MOV A,#0FEH /*将 FEH 送给 A*/

SETB IT1 /*外部中断 1 跳变沿触发方式*/ SETB EX1 /*外部中断 1 开中断*/

SETB EA /*CPU 开中断*/ MOV P0,A /*将 A 送给 P0*/

LOP: LJMP LOP /*循环等待*/

INT: RL A /*A 循环左移*/

MOV P0,A /*将 A 的数值送给 P0*/ RETI /*中断返回*/

END /*程序结束*/

[AndesCore™]

该例子显示怎样用汇编设置 AndesCore™的中断向量表和中断处理函数,该 例子中 exception_vector 是中断向量表的 label, 后面分别表示第 0,1,2,3…个中 断向量,它们只是简单的跳转指令,跳到具体的执行实体中去,如 vector 0 跳到_start,做系统相关的初始化操作,_start 是系统启动代码,用汇编语言来实现。vector 9 后面对应的是外部中断,中断处理函数如 OS_Trap_Interrupt_HW0, OS_Trap_Interrupt_HW1… 它通常用 C 来实现,可以参考后面 5.2 章节的 AndesCore™中断处理函数范例。

! 中断向量表所在的 section,该 section 在链接后会被存放在第一条指令 执行处,通常是 0 位置
.section .vector, "ax"
!====================================================
! Vector table
!====================================================
.align 3
exception_vector: ! 以下是中断向量表
j _start ! (0) Trap Reset
j OS_Trap_TLB_Fill ! (1) Trap TLB fill
j OS_Trap_PTE_Not_Present ! (2) Trap PTE not present
j OS_Trap_TLB_Misc ! (3) Trap TLB misc
j OS_Trap_TLB_VLPT_Miss ! (4) Trap TLB VLPT miss
j OS_Trap_Machine_Error ! (5) Trap Machine error
j OS_Trap_Debug_Related ! (6) Trap Debug related
j OS_Trap_General_Exception ! (7) Trap General exception
j OS_Trap_Syscall ! (8) Syscall
j OS_Trap_Interrupt_HW0 ! (9) Interrupt HW0
j OS_Trap_Interrupt_HW1 ! (10) Interrupt HW1
.......
.......
.......
! _start 函数,是中断向量 0 对应的中断处理函数

.align 2
_start:
! ******** Begin of do-not-modify ************
! Please don’t modify this code
! Initialize the registers used by the compiler
#ifndef CONFIG_NO_NDS32_EXT_EX9
! make sure the instruction before setting ITB
! will not be optimized with ex9
.no_ex9_begin ! disable ex9 generation
#endif
! Support Relax, Set $gp to _SDA_BASE_
la $gp, _SDA_BASE_ ! init GP for small data access
#ifndef CONFIG_NO_NDS32_EXT_EX9
! Initialize the table base of EX9 instruction la $r0, _ITB_BASE_ ! init ITB
mtusr $r0, $ITB
.no_ex9_end
#endif
!*********** End of do-not-modify************
la $fp, _FP_BASE_ ! init $fp
la $sp, _stack ! init $sp 初始化堆栈寄存器
#ifdef CFG_LLINIT
bal _nds32_init_mem ! 初始化 DRAM
#endif
bal __init ! 初始化 CPU,SoC,C 运行环境等
bal main ! 最后跳转到 main 函数
1: b 1b

在上面用汇编设置 AndesCore™的中断向量表的例子中,我们需要将中断向量表最终设定在 0 地址处,可以通过 section 语法配合 sag 工具实现,例子中我们设定 该段 的 section 名为 .vector, 所 以在 sag 中 ,我 们自 定义一个 USER_SECTION 为.vector,并将.vector 放在 0 开始的地方并作为第一个 section。

USER_SECTIONS .vector
SDRAM 0x00000000 0x00800000 ; address base 0x00000000, max_size=8M ;指定 LMA 为从 0 开始
{
EXEC 0x00000000 ;指定 VMA 为 0
{
* (.vector) ;放在 0 开始的地方并作为第一个 section
* (+RO,+RW,+ZI) STACK = 0x00800000
}
}

通过上面的 sag 语法,并使用 andes 提供的 sag 转 ld 的工具,可以产生类似以下的 ld,在工程进行链接的时候选择该 ld 时就能确保 .vector 链接的地址位 于 0 处。

/* This file is generated by nds_ldsag (version 20140127). */ ENTRY(_start)
SECTIONS
{
PROVIDE (__executable_start = 0x00000000); NDS_SAG_LMA = 0x00000000 ; SDRAM_BEGIN = NDS_SAG_LMA;
. = 0x00000000; ; 起始地址为 0
.vector : { *(.vector) } ; .vector 在 0 开始的位置
.nds32_init : { KEEP(*(.nds32_init)) }
.interp : { *(.interp) }
.hash : { *(.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
......

关于详细的 SAG 使用,可以参考我们的另一篇文章:《Andes 的分散聚合(SAG)机制》http://www.andestech.com/cn/news-events/technical-article/2014/Andes20141008.pdf

5.2. 中断处理函数的 C 实现

[8051]

怎样用 C 写 8051 的中断处理函数范例

/* com interrupt handler */
void com_int(void) interrupt 4 // 有指定 interrupt 号
{
/* com interrupt handler here */
}

[AndesCore™]

怎样用 C 写 AndesCore™的中断处理函数范例

void syscall_handler() // 和普通函数的写法相同
{
puts("this is syscall handler\n");
}

6. 适用于 MCU 的 Andes CPUs

Andes 有三款非常适用于 MCU 应用的 CPU,分别是:N705,N801,N968A,如 下图所列:

N705 和 N801 分别采用了两级和三级流水线,都具有很低的功耗和很好的性 能,当应用需要的频率较低时,使用两级流水线的 N705 能发挥出更好的性能和 更低功耗的特性,相比于 8051,两级流水线的 N705 在频率方面高出许多,比 如在 TSMC 40nm LP 工艺下能跑到超过 240MHz,所以完全能胜任 8051 的应 用需求。N968A 使用了五级的流水线,同样有低功耗的特性和很好的性能,同 时该款 CPU 具有很强的可配置性,如支持多种总线接口,还支持了专门为 audio 的加速指令,N968A 是一个多面手,性能好,功耗低,又具备强大的可配置特 性,适合于多种应用。

7. 总结

AndesCore™使用标准的 C 语法开发,方便快捷,同时作为 32 位 RISC(精 简指令集)架构的 CPU,AndesCore™有多款适用于 MCU 应用的 CPU,相对于8051 具有功耗,性能方面优势。想了解更多 AndesCore™细节,可以登陆www.andestech.com