Embedded Microprocessor System笔记


👉网页版:嵌入式知识点与笔记

微处理器程序开发基础

程序开发过程与工具

处理器计算 a=b+c 的过程?

可执行代码是什么形式?如何产生的?

指令

形式

机器语言:由CPU理解和执行的二进制代码

机器指令的格式(助记符)

执行代码生成

开发过程

PC程序开发

编辑 编译 汇编 连接 调试

嵌入式程序开发

编辑 编译 汇编 连接 下载 调试

如何下载程序?

开发工具

集成开发环境

调试环境

  • 物理处理器和开发板(Emulator)

没有开发板可以调试程序吗?

  • 模拟处理器(Simulator)

目标处理器如何加载程序?

  • 目标地址

    • Linker-分配
    • Debugger-指定
  • 加载方式

    • Debugger-下载
    • 实际系统-ROM
  • 启动方式

    • 初始化
      • Debugger-程序/Set
      • 实际系统-程序
    • 程序入口
      • Debugger-跳转/Set PC
      • 实际系统 –跳转

还熟悉哪些处理器程序开发环境?

处理器与程序

问题

处理器如何执行程序?

计算机系统

CPU内核

CPU性能

  • CPI-执行一条指令所需的平均时钟周期

  • MIPS-每秒百万条指令

  • MFLOPS-每秒百万次浮点运算次数

执行时间

指令执行过程

取指(IF)->译码 (ID) ->执行(IE) ->写回(WB)

指令执行时间

程序执行时间

影响处理器程序运行速度的因素有哪些?

结构与性能

总线结构

哪种结构的处理器速度更快? 为什么?

处理器字长(ALU 及通用数据寄存器)

  • 8 bits8008, 89C51

  • 16 bits8086, Ti 54X

  • 32 bits80486, Ti C3X, C6XARM V3-6

  • 64 bitsItanium,PowerPC 970, ARM V8

  • 128 bits?

8位处理器可以通过程序进行32位数值运算吗?

如何用C语言编程实现?

流水线

  • 执行时间(n条指令)

影响流水线加速效果的因素?

能否通过程序验证处理器是否采用了流水线?

Cache

程序可以直接访问Cache中指定的数据单元?

Cache 的容量对大小对加速性能的影响?

能否通过程序验证处理器中Cache是否工作?

Write buffer

Write buffer 与 Cache的区别?

指令执行过程中如何访问的数据?

寻址

定义执行单元获取数据(地址)的方式

编址单元宽度

  • 字节(8bit,0x86,ARM

  • 半字 (C54)

  • 字 (C30)

  • 双字

地址空间(独立编址)

  • 三个地址空间

    • 通用寄存器
    • 主存储器
    • I/O设备
  • 两个地址空间

    • 通用寄存器
    • 主存储器与IO设备
  • 一个地址空间

    • 所有存储设备统一编址

如果有多个地址空间,在程序中如何区分?

方式

  • 立即数寻址

ADD R4,#5 ; reg(R4)〈- reg(R4)+5

  • 寄存器寻址

ADD R4,R3 ; reg(R4)〈- reg(R4)+reg(R3)

  • 直接寻址(绝对)

    ADD R1,(1001); reg(R1)〈- reg(R1)+ Mem(1001)

注:1001是内存的地址,取出该地址对应的值

  • 堆栈寻址

PUSH R1

POP R1

注:堆栈操作的对象与内存有关

隐含了什么地址?

  • 间接寻址

    • 寄存器间接寻址 ADD R4,(R1);reg(R4)〈- reg(R4)+ Mem(reg(R1));

      注:R1寄存器的数值对应为内存中的地址,取内存中该地址的数据(R1)

    • 存储器间接寻址 ADD R1,@(R2); reg(R1)〈- reg(R1)+ Mem[Mem[reg(R2)]]

      注:R2寄存器数值对应为内存中的地址,改地址对应的数值所对应的地址,取内存中该地址的数据@(R2)

  • 自动递增寻址

ADD R1,(R2)++ ;Reg(R1) 〈- reg(R4)+ Mem(reg(R2) ); R2+1

  • 自动递减寻址

ADD R1,- -(R2); R2-1 ; Reg(R1) 〈- reg(R1)+ Mem(reg(R2) )

注:自动递增递减会改变所操作寄存器的值

  • 偏移量寻址

ADD R4,±100(R1) ; reg(R4)〈- reg(R4)+ Mem(reg(R1) ± 100)

注:偏移量寻址不会改变所操作寄存器的值

处理器如何处理异常(外部)事件?

事件响应

事件响应过程

检测事件?进入事件处理程序?

中断(interrupt)

定义为导致程序正常执行流程发生改变的事件(不包括程序的分支情况)

来源

  • 外中断:由于CPU 外部的原因而改变程序执行流程的过程,属于异步事件,又称为硬件中断

  • 内中断

    • 自陷(软中断,trap):通过处理器所拥有的软件指令、可预期地使处理器正在执行的程序的执行流程发生变化,以执行特定的程序
    • 异常:异常为CPU 自动产生的自陷(除0,非法指令,内存保护错误)

可屏蔽性

  • 可屏蔽中断

    • 能够被屏蔽掉的中断称为可屏蔽中断。
    • 一般需要先通过CPU外部的中断控制器,再与CPU相应的引脚相连。
  • 不可屏蔽中断

    • 不能够被屏蔽掉的中断称为可屏蔽中断
    • 例:复位(Reset)

中断向量表

  • 中断向量与中断服务程序入口的对照表

支持多任务(多程序)的操作系统,如何为不同的任务分配存储空间?

虚拟地址 VS 物理地址?

内存映射

  • 地址转换
  • 空间保护

程序中如何实现地址转换?

上电后处理器是如何启动boot 程序?

系统启动

处理器启动

条件?

  • Memory(ROM) 可执行程序(指令)

  • PC指到程序入口

处理器系统

你知道哪些CPU?具体型号?

CPU要满足系统需求

系统需求

资源

  • 片内 (MCU, DSP, SoC)

    • RAM,ROM,Programmable ROM
      • DATA
      • INSTRUCTION
  • 片外可扩展 (DSP, MPU)

    • MEMORY
      • DATA
      • INSTRUCTION

接口

  • IO 控制(GPIO)
  • 通信(UART, Ether NET ,USB)
  • 外设 (A/D,D/A,LCD,KEYBOARD,Video)

封装(尺寸, 工艺)

嵌入式系统需求

  • 功率低

    设想一下,普通手机电池能够维持P4(75W)工作多长时间?

    移动领域 X86 败于 ARM

  • 稳定性好

    卫星上的系统出错后如何复位?

  • 体积小

    PC中的处理器有多大?

  • 成本低

    最便宜的处理器价格?

嵌入式处理器

嵌入式处理器?-满足嵌入式系统需求

AI芯片属于哪一类?

嵌入式系统微处理器的发展

What is next to ARM?

处理器发展

微处理器结构上的第一

First Computer ENIAC (University of Pennsylvania,46)

First general-purpose,single-chip microprocessor:Intel 4004,1971

First 8-bit architecture:Intel 8008,1972

First 16-bit architecture:Intel 8080,1974

First 32-bit architecture:Motorola 68000,1979

First RISC microprocessor: MIPS R2000, 1985

First microprocessor to provide integrated support for instruction & data cache: MIPS R2000, 1985

First pipelined microprocessor (sustains 1 instruction/clock): MIPS R2000, 1985

First 64-bit architecture: MIPS R4000, 1991

First multiple issue(superscalar) microprocessor: Intel i860, 1991

First CMP processor: IBM Power 4,2001

First SMT processor: Intel Pentium IV Xeon,2003

提高运行速度

  • 提高主频

  • 提高存储器速度

  • 改进处理器结构-并行化/存储管理

    • 数据
    • 步骤->pipe line
    • 指令->Superscale
    • 内核->Multicore
    • 访存->Cache &Write buffer
  • 降低能耗

并行化

超流水线和超标量

矢量计算?

多核

比较两款处理器功耗

Intel处理器败走移动领域的主要因素?

小结

  • 处理器程序开发过程。

  • 处理器结构提对程序执行速度影响。

  • 嵌入式处理器特点。

ARM 处理器

例程:c=a+b

观察寄存器

va, vb, vc 地址?

小端/大端?

查看内存数值?

C=?

R15?

Option

Device

Target

Output

Listing

User

C/C++

Asm

Linker

Debug

Utilities

aplusb.ini

去掉该文件重新调试,什么现象?

aplusb.lst (armasm)

aplusb.map

Memory Map

Size

ARM9内核

内核结构

桶形移位器

调试程序,观察加运算前后处理器状态

CPSR

ALU

特点

异常与模式

异常与模式

调试程序,在nop处单步执行,观察pc的变化

异常向量

异常模式

寄存器

处理器异常处理

  • 处理过程

处理器处理过程不需要用程序实现吗!

如何恢复到异常发生前的状态?

  • 复位(reset)

  • 中断(irq) (fiq?)

  • 数据终止(abort)

  • 指令终止(undefined)

存储系统

结构

按字节编址!

TCM与Cache 的区别?

Cache

存储管理

ARM9的三类地址

  • 虚拟地址(VA),是程序中的逻辑地址,0x00000000~0xFFFFFFFF。

  • 改进的虚拟地址(MVA),由于多个进程执行,逻辑地址会重合。所以,跟据进程号将逻辑地址分布到整个内存中。MVA = (PID << 25) | VA。PID占7位,所以最多只能有 128 个进程,每个进程只能分到 32MB 的逻辑地址空间。

  • 物理地址(PA),MVA通过MMU转换后的地址。

协处理器CP15

  • 内存管理单元(Memory Manage Unit,MMU)

    • 控制虚拟地址(VA)映射到物理地址(PA)
    • 控制内存的访问权限
    • 控制可缓存性和缓冲性
  • 内存保护单元(Protection Unit,PU)

    • 无MMU的简单内存保护
  • 使能Cache and Write Buffer

  • 快速上下文(进程之间)切换扩展(Fast Context Switch Extension,FCSE)

段(section, 1MB)地址转换

内核扩展协处理器

  • 协处理器接口,允许扩展16个协处理器

  • 协处理器扩展(FFA10)

处理器如何访问协处理器?

ARM发展历程

ARM- Advanced RISC Machine

  • ARM的版本

    • V1, V2, V3,
    • V4 (ARM 7,8,9),
    • V5 (ARM 10),
    • V6 (ARM 11)
    • V7(ARM-Cortex A8,9,15,17)
    • V8(Cortex-A/R/M/, 64/32bit)
    • V9
  • 扩展字母含义

    • T:内含16位压缩指令集 Thumb
    • D:支持片内Debug调试
    • M:采用增强型乘法器(Multiplier)
    • I: 内含嵌入式ICE宏单元
    • E: 具有DSP功能
    • S:可综合的软核Software
    • J: Jazeller,允许直接执行Java字节码

产品线

架构

  • 指令
处理器 流水线 指令集 存储管理 协处理器 多核
ARM7 3 ARM/T N N N
ARM9 5 ARM/T Y Y N
ARM10 6 ARM/T Y Y N
ARM11 8 Thumb-2 Y Y N
Cortex-A8 NEON V7-A Y Y N
Cortex-A9,1X NEON V7-A Y Y Y
Cortex-A5X NEON V8-A Y Y Y
  • 总线
版本 处理器 发布时间 总线(bit) 总线结构
V1V2 ARM1 未商业授权
V3 …… 未商业授权 32
V4 ARM7/9 1996 32 7-Von Neumann 9-Harvard
V5 ARM10 2000 32 Harvard
V6 ARM11 2002 32 Harvard
V7 Ax, 1X 2004 32 Harvard
V8 A53/57 2013 64 Harvard

Cortex-A75-Core(Snapdragon 845)

S3C2410A

硬件工程师在设计包含处理器的电路板时需要掌握处理器的哪些信息?

数据手册(datasheet)

datasheet

软件工程师在为处理器开发程序时需要掌握处理器的哪些信息?

使用手册(manual)

S3C2410

Peripheral Registers

ARM汇编程序

  1. Keil作为半主机模式如何导入导出数据?

  1. 如何测试处理器性能?

  2. Keil工程中startup.s/S3c2410.s的作用?

  • Heap_size:动态分配空间

  • Heap_mem:需指定位置与大小

  • 全局空间是link时自动分配的空间

汇编程序格式

ARM汇编

Keiln(ARM ASM)

AREA    ARMex, CODE, READONLY                                    
        ENTRY
start
        MOV      r0, #10 
        MOV      r1, #3
        ADD      r0, r0, r1      

        END 

Segger (gnu asm )

.weak _start
            .global __start
            .section .init, “ax”
            .type _start, function
            .code 32
            .balign 4
_start:
__start:
        //
        // Setup Stacks
        //for asm testing
        mov r0, #0x1000     
        mov r1, #0x300
        mov r2, #0x400
        add r1,r1,r2
        strb r1,[r0]
       //testing end
    ……
  • ax (allocation execute)

  • .weak _start 有外部调用则调用外部,没有调用内部

  • .code 32 表示32位

  • .balign 4 对齐

    • 有b,表示四个字节对齐;
    • 没有b,表示2的多少次方对齐。
  • _start:缺省入口

  • __start:系统启动定义的

指令级程序

格式

ASM Directive

  • AREA

    • Mark the start of a section
    • Names the section and sets its attributes.
    • The attributes are placed after the name, separated by commas
  • ENTRY

  • Marks the first instruction to be executed

  • Initialization code and exception handlers also contain entry points.

  • END

    • Instructs the assembler to stop processing this source file
    • On a line by itself.

指令格式

助记符

  • GE:Cond,条件判断

  • S:状态位

语句格式

  • Lines in an ARM assembly language module is:

{symbol} {instruction|directive|pseudo-instruction} {;commen}

  • Examples

    lable1 ADD r1,r2,r3 ;”+

AREA     ARMex, CODE, READONLY

        ENTRY     ; Mark first instruction to execute

start
        MOV      r0, #10        ; Set up parameters
        MOV      r1, #3
        ADD      r0, r0, r1      ; r0 = r0 + r1

        END                      ; Mark end of file
  • 标签最左边对齐
  • 语句最左边需要空格

用汇编语言实现把下列C程序功能?

int main(void)
{      int i, j;
    unsigned char Inimage[480][640];
    unsigned char Tmpimg[120][160];
    for(i=0;i<120;i++)
         for(j=0;j<160;j++)                
               Tmpimg[i][j]=Inimage[i*4+2][j*4+2];  
    memcpy(Inimage,Tmpimg,160*120);
    return 1;
}

汇编指令

  • R0-R15和r0-r15
  • a1-a4(参数,结果或者临时寄存器,与r0-r3同意)
  • v1-v8(变量寄存器,与r4-r11同意)
  • sb和SB(静态基址寄存器,与r9同意)
  • sl和SL(堆栈限制寄存器,与r10同意)
  • fp和FP(帧指针,与r11同意)
  • ip和IP(过程调用中间临时寄存器,与r12同意)
  • sp和SP(堆栈指针,与r13同意)
  • lr和LR(连接寄存器,与r14同意)
  • pc和PC(程序计数器,与r15同意)
  • cpsr和CPSR(程序状态寄存器)
  • spsr和SPSR(程序状态寄存器)
  • f0-f7和F0-F7(FPA寄存器)
  • s0-s31和S0-S31(VFP单精度寄存器)
  • d0-d15和D0-D15(VFP双精度寄存器)
  • p0-p15(协处理器0-15)
  • c0-c15(协处理器寄存器0-15)

变量与数据

数据类型? Byte,Halfword,Word,Doubleword,QuadWord ( Signed or unsigned?) – Only unsigned (except for MUL)

变量位置? Memory (Stack,static), Register

变量赋值? Address (immediate,memory,Register)

常量定义? EQU

指令和寄存器大小写有区别?

MOV 立即数寻址可以装载任意数据?

  • MOV 是ALU单元的操作,改变状态

  • MVN 按位取反再装载

Loading immediate

  • MOV
    • 8-bit ,0x0 to 0xFF (0-255).
    • Rotate by any even number.
    • MVN load “NOT” Value
    • Thumb: only 0x00-0xff

理解8位或者偶数移位(Rotate by any even number

例如260的二进制是100000100 可以由1000001循环右移30位或左移2位,位数位偶数,可以作为立即数

在比如258二进制是100000010 可以由10000001循环右移31位或左移1位,位数是奇数,不可以作为立即数

实际理解为为了将12位的数字映射到32位上,用8位作为基:0~255;4位作为rotate,循环右移2*rotate

下列指令是否正确?

MOV  rn ,#1025 错
MOV  rn ,#4096 对
MVN  rn ,#0x111 错
MVN  rn ,#0xffffffff 对

装载任意大小数值

  • LDR Rd, =const

伪指令?

这里两条指令的执行过程与立即数寻址过程的查差别?

  • LDR

    • LDR -> Appropriate instruction (by assember)

    • Constant is in the range of MOV or MVN

      LDR -> MOV (MVN)

      MOV or MVN” depend on what?

    • Constant is over the range of MOV or MVN

      • Places the value in a literal pool
      • LDR instruction with a program-relative address that reads the constant from the literal pool.
LDR r0, =0x23
LDR r0, =0xffffff        ;MVN r0,#ff000000
LDR r0, =0x5555        ;?
;LDR   rn, [pc, #offset to literal pool]

LDR对于不符合的立即数如何转换?

利用pc和literal pool读取

  • How to put the “literal pool” in memory?

    • Marking with “LTORG”
      • After unconditional branch instructions
      • after the return instruction
  • By assembler

    • After the end of “AREA”
  • The offset from the pc to the constant

    • ARM state
      • Less than 4KB
      • Either direction
    • Thumb state
      • Less than 1KB
      • Forward

为什么 “LTORG” 要放在跳转后或返回后?不会当作指令取进去.

下列程序中 LDR 指令能否正确转换?

AREA   Loadcon, CODE, READONLY
        ENTRY

        LDR      r0, =42          ; => MOV R0, #42
       LDR      r1, =0x55555555 ; => LDR R1, [PC, #offset to Literal Pool 1]
       LDR      r2, =0xFFFFFFFF           ; => MVN R2, #0
        LTORG                           ; Literal Pool 1 contains
                                              ; literal Ox55555555
       LDR      r3, =0x55555555  ; => LDR R3, [PC, #offset                
                                             ;Literal Pool 1
       LDR      r4, =0x666 
       END                      
                   ; Literal Pool 2 contains 0x666

寄存器常数装载方式

指令 功能 说明
MOV(MVN) 装载立即数 8bit
LDR 装载常数(伪) 32bit位常量
ADR(L) 装载地址(伪) 32bit 相对地址
MOV{<cond>}{S}   Rd, #const
LDR Rd, =const
ADR Rd, label
  • Load address

    • ADR and ADRL
      • Generate an address, within a certain range
        • program-relative expression
          • Label with an optional offset
          • Be relative to the current pc.
        • Register-relative expression
          • Label with an optional offset
          • Be relative to an address held in a specified general-purpose register.
  • ADR

    • Converts ADR rn,label into a Single ADD or SUB instruction that loads the address, if it is in range
    • The offset range of ADR
      • ±255 bytes for an offset to a non word-aligned address.
      • ±1 020 bytes (255 words) for an offset to a word-aligned address.
    • An error message if the address cannot be reached in a single instruction

分析下列两条ADR指令的转换结果

  • ADRL
    • Converts an ADRL rn,label into two data-processing instructions that load the address, if it is in range
    • The range of an ADRL
      • ± 64KB for a non word-aligned address
      • ± 256KB for a word-aligned address.
    • An error message if the address cannot be constructed in two instructions.
    • There is no ADRL pseudo-instruction for Thumb.

分析下列两条ADRL指令的转换结果

运算

运算符的操作数一定是寄存器或者立即数

  • ASR 算数右移

  • LSL 逻辑左移

  • LSR 逻辑右移

  • ROR 循环右移

  • RRX 带扩展的循环右移

    • L: 0-31; R: 1-32
    • RRX 只能移动一位

分析下指令

ADD      r0, r1, #10
ADD   r11,r12,r3, ASR #5 
ADD   r11,r12,r3,LSL R4 
ADD     r5,r4, r3, RRX 

算术指令

  • 语法

    Op{cond}{s} Rd, Rn, Operand2

  • 标志位

    N, Z, C,V

  • 指令

指令 操作
ADC Rd=Rn + Operand2+C
ADD Rd=Rn + Operand2
RSB Rd=Operand2-Rn
RSC Rd=Operand2-Rn-!C
SBC Rd=Rn-Operand2-!C
SUB Rd=Rn-Operand2

当看溢出时,要把数据看成是有符号数,就是最高位的数字代表是符号,0正1负。如果次高位计算改变符号位则溢出。

当看进位时是将数据看成无符号数,全部是数据没有符号位,如果运算超出数的范围向前进位了,则进位标志置位。

下列指令哪些时错误的?

ADC         r1, r1, #5    对
SUB         r11,r12,r3,ASR #5    对 
RSB         r5,r3,r4, LSR #32        对    
ADD            r3,r7,#1023       错             
SUB         r11,r12,r3,LSL #32    错 最多31
SBC         r5,r4, r4, RRX #3        错 最多1

给出指令的执行结果

  • cpsr=nzcvqiFt_USER;指定当前个标志位状态,并且工作在用户模式

乘法指令

  • MUL和MLA (32bit *32bit -> least 32bit)

    • 语法

      MUL{cond}{S} Rd, Rm, Rs

      MLA{cond}{S} Rd, Rm, Rs, Rn

    • 执行

      MUL: Rs*Rm ->Rd (least)

      MLA: Rs*Rm+Rn -> Rd (least)

    • 标志位

      N, Z

d!=m

32位乘32位低32位!

分析下列指令

MUL     r10,r2,r5
MLA     r10,r2,r1,r5
MULS    r0,r2,r2

MUL     r15,r0,r3        ;错
MLA     r1,r1,r3, r6    ;错
  • U(S)MULL, U(S)MLAL(32bit *32bit -> 64bit)

    • 语法

      Op{cond}{S} RdLo, RdHi, Rm, Rs

    • 执行

      U(S)MULL: Rs*Rm ->RdLo, RdHi

      U(S)MLAL: Rs*Rm+(RdLo,RdHi) -> RdLo , RdHi

    • 标志位

      N, Z

    • 例:

      UMULL       r0,r4,r5,r6
      UMLALS     r4,r5,r3,r8
      UMULL       r1,r15,r10,r2

      目的寄存器需要用两个32位来存储一个64位

      UMLAL MLA 操作的差别?

给出下列指令的执行结果

逻辑指令

  • 语法

    Op{cond}{s} Rd, Rn, Operand2

  • 指令

指令 操作
AND Rd=Rn & Operand2; shift_operand
ORR Rd=Rn | Operand2
EOR Rd=Rn ^ Operand2
BIC Rd=Rn & (~Operand2)

MOV

寄存器间数据复制

PRE
    R0=0x80
    R1=0x50
    MOV  R0, R1

POST
    R0=? 0x50
    R1=? 0x50

使用桶形移位寄存器

PRE
    R0=0x80
    R1=0x50

    MOV  R0, R1, LSL #4

POST
    R0=? 0x500
    R1=? 0x50

移位操作 结果 Y**值范围**
x LSL y x << y #0-31 or Rs
x LSR y (unsigned) x >> y #1-32 or Rs
x ASR y (signed) x >> y #1-32 or Rs
x ROR y ((unsigned)x >>y)|(x<<(32-y)) #1-32 or Rx
x RRX (c flag <<31)|((unsigned)x>>1) none

移位对C标志的影响

状态寄存器小写0,大写1

比较

比较判断    r6< Imageheight? ;  r5<Imagewidth ?
    cmp r6, #Imageheight
    cmp r5, #Imagewidth
  • 语法

    CMP{cond} Rn, Operand2

  • 第二操作数(Operand2)

#<immediate>
<Rm> 

ALU_out[31]->N; alu_out=0, Z=1 else Z=0;

Not borrow from “Rn-Operand2 “ : C=1 else C=0;

Overflow from “ Rn-Operand2”: V=1 else V=0;

自动改变状态寄存器

  • 语法

    Op{cond} Rn, Operand2

  • 指令

指令 操作
CMN flag of Rn + Operand2;<Rm>
CMP flag of Rn - Operand2; <Rm>,
TEQ flag of Rn ^ Operand2;<shift_operand>
TST flag of Rn & Operand2;<Rm>

不改变通用寄存器数值,自动改变状态寄存器状态

跳转

跳转     for (;;)  
行内循环              cmp     r5, #Imagewidth
        bmi     Linecircle
行间循环
                  cmp     r6, #Imageheight
                            bmi     Framecircle
  • 语法
 B{<cond>} label
  • 如果Imagewidth 或 Imageheight 大于0xff,如何处理?

  • label ? 有什么要求?

  • 支持哪些 cond? 如何表达?

  • 语法

B{<cond>} label
BL{<cond>} label
BX{<cond>} Rm
BLX{<cond>} label|Rm
  • 指令
指令 功能
B(跳转) pc=label
BL(带返回跳转) pc=label, lr=BL 后面第一条指令地址
BX(切换状态跳转) pc=Rm & 0xfffffffe, T=Rm & 1
BLX pc= label, T=1 pc=Rm & 0xfffffffe, T=Rm & 1 lr=BLX 后面的第一条指令地址
  • B
B     forward
backward
    ADD     r1, r2, #4
    ADD    r0, r6, #2
    ADD     r3, r7, #5
forward
    SUB    r1, r2, #4
    ADD    r1, r2, #4
    SUB    r1, r2, #4
    ADD    r4, r6, r7
    B     backward

B 可以在4G空间任意范围内跳转吗?

±32MB

  • BX
CODE32 
header
    MOV r0, #0                         
      ADR     r0, start + 1               
        BX      r0  

    CODE16                  
start
            ADD r1, r2
    SUB r2, #0x55
            CMP r0, r1                  
       ……
  • 如何添加指令,从CODE16段跳到header

  • Thumb指令特点:

    • 使用ARM的r0-r7 8个通用寄存器
    • Thumb指令没有条件执行
    • 指令自动更新标志,不需加(s)
    • 仅有LDMIA(STMIA)
      • 只有IA一种形式
      • 必须加“!”
    • 在数据运算指令中,不支持第二操作数移位

    Thumb指令是16位的Arm指令集,可以理解为Arm的阉割版

    https://blog.csdn.net/qq_20880415/article/details/101037010

    指令操作16位

    操作依然是32位

  • BL

BL     subroutine
    CMP     r1, #5
    MOV     r1, #0
    ADD     r1, r2, #4
    ……
subroutine 
    ADD    r1, r2, #4
    SUB    r1, r2, #4
    MOV    pc, lr

函数的返回方式?

Lr是跳转前下一条指令的地址

  • 条件类型

  • 比较下列指令
ADD     r0, r1, r2    ; r0 = r1 + r2, don't update flags
ADDS    r0, r1, r2    ; r0 = r1 + r2, and update flags
ADDCSS  r0, r1, r2    ; If C flag set then r0 = r1 + r2,                 
                                   ;    and update flags
CMP     r0, r1        ; update flags based on r0-r1.
  • 下列程序中哪些指令不执行
    MOV r0, #1
    MOV r1, #2
    CMP r0, r1    ;N=1
    SUBGT r0,r0,r1    ;不执行
    SUBLT r1,r1,r0    ;r1=1
    CMP r0,r1        ;z=1
    SUBGT r0,r0,r1    ;不执行
    SUBLT r1,r1,r0    ;不执行
  • 不同跳转方法的对比

    • 实现方法

    • 执行时间分析(condition branch)

    ​ 不执行的指令也占时间?

小结

//c/c++    
for(i=0;i<120;i++)
{ 
    for(j=0;j<160;j++){……}
} 
Imageheight    equ    120
Imagewidth    equ    160

         area    ImScale , code , readonly                               
         entry        
        mov    r6, #0
Framcircle 
    mov    r5, #0
Linecircle
    ……    
        add r5, r5,#1
        cmp r5, #Imagewidth
      bmi  Linecircle
        add r6, r6, #1
        cmp r6, #Imageheight
        bmi Framecircle
        end

ARM汇编

把下列C程序改成汇编程序 :

int main(void)
{      int i, j;
    unsigned char Inimage[480][640];
    unsigned char Tmpimg[120][160];
    for(i=0;i<120;i++)
         for(j=0;j<160;j++)                
               Tmpimg[i][j]=Inimage[i*4+2][j*4+2];  
    memcpy(Inimage,Tmpimg,160*120);
    return 1;
}

存储访问

内存分配

//c/c++    
Tmpimg[120][160];
Inimage[480][640];

//分配
Tmpimg    space        160*120    ;单位字节
Inimage    space        640*480

//语法
[label]        SPACE        expression
可用 %代替 SPACE

“160×120”和“640×480”中的“×”运算在程序运行时完成吗?

内存区域分配

  • DCX{U-对齐选项}

    • 种类

      • DCB-(1byte)
      • DCD and DCDU-(4 bytes)
      • DCFD and DCFDU-(double-precision float point)
      • DCFS and DCFSU-(single-precision float point)
      • DCI-(like DCD and DCDU)
      • DCQ and DCQU-(8 bytes)
      • DCW and DCWU-(2 bytes)
    • 用法

      {label} DCX{U} expression,{expression,……}

    • Example

data        DCW     -225,2*number
              DCWU    number+4    ;number must be     defined
    DCQ     -225, 2_101
DCFD    1E308, -4E-100
DCFDU   10000, -.1, 3.1E26 
加“U” 不需要对齐
不同命令对其格式不一样
DCD-4Bytes
DCW-2Bytes

分析内存中数据存储状况

    DCB     1,2,3
    DCDU 0x11223344
    DCW     0x2233,0x4455
    DCWU 0x677
    DCD 0x06070809
  • Endian 模式对存储状况的影响?大端小端

  • SPACE与 DCX使用上的区别? DCX分配空间与写入值

  • 程序中如何找到分配区域的起始地址?自定义标签

练习,画出内存中存储位置

数据结构

//Declare 
typedef struct Point
{
        float x;
      float y;
    float z;
} Point;

//Allocate space
Point origin;
//Declare 
PointBase   RN      r11
                 MAP     0,PointBase    ;r11地址作为初始地址
Point_x     FIELD   4    
Point_y     FIELD   4
Point_z     FIELD   4

//Allocate space
origin      SPACE   12

内存访问

//c/c++    
    Tmpimg[i][j]; Inimage[i*4+2][j*4+2];

//i->r5; j->r6; Tmpimg-> r8; Inimage->r9;
    ldr    r8,    =Tmping
    ldr    r9,    =Inimage
//Tmpimg[i][j]->r10;
    mov    r0,    #Imagewidth
    mul    r10, r5, r0   ;i*Imagewidth
    add    r10, r10, r6   ;i*Imagewidth+j
//Inimage[i*4+2][j*4+2]-> r11
    mov    r1, r5, asl #2 ; i*4
    add    r1, r1, #2    ;i*4+2
    mul    r11, r1, r0    ;(i*4+2)*Imagewidth
    mov    r1, r6, asl #2 ;j*4
    add    r1, r1, #2    ;j*4+2
    add    r11, r11, r1; (i*4+2)*Imagewidth+4*j+2
//c/c++    
    Tmpimg[i][j]=Inimage[i*4+2][j*4+2];

//实现    
    ldrb    r0, [r11]    读取方式
    strb    r0, [r10]    存储数据

//语法    
LDR {<cond>}{B} Rd, addressing1
STR{<cond>}{B}  Rd, addressing1

读(Memory->Register)

指令 功能
LDR Rd<-mem32[add]
LDRB Rd<-mem8[add]
LDRH Rd<-mem16[add]
LDRSB Rd<-SignExtend(mem8[add])
LDRSH Rd<-SignExtend(mem16[add])
  • 指令形式

    LDR {<cond>}{B} Rd, address1
    LDR {<cond>}SB|H|SH Rd, address2
  • address

    • 基于寄存器的寻址
    • 地址对齐要求?

LDR 可能改变状态寄存器状态位?

  • Address1

    • [Rn]
    • 前偏移,Rn不变
      • [Rn, # +/- offset_12]
      • [Rn, +/-Rm]
      • [Rn,+/-Rm, shift_imm]
    • 前偏移,Rn更新
      • [Rn,#+/-offset_12]!
      • [Rn,+/-Rm]!
      • [Rn,+/-Rm, shift_imm]!
    • 后偏移,Rn更新
      • [Rn], # +/- offset_12
      • [Rn], +/-Rm
      • [Rn],+/-Rm, shift_imm
  • 带“!”表示需要更新Rn内寄存器的值

解释下列指令

ldr r0, [r1, #0x04]!
ldr r0, [r1, r2]!                ;R1+r2->r0,更新r1
ldr r2,[r1, -r0, LSR #0x04]!    ;R0<<4 +r1->r2, 更新r1
ldr r0, [r1], #0x04                ;r1->r0,r0->r0+4
ldr r0,[r1],r2
rdr r0,[r1], -r0, LSL #0x04   
ldr r0, [r1, #0x04]
ldr r0, [r1, r2]
ldr r2,[r1, -r0, LSR #0x04]

比较五条指令的执行结果

LDR r0, [r1, #4]    r0=0x3030303,r1=0x104
LDR r0, [r1, #4]!    R0=0x3030303,r1=0x108
LDR r0, [r1], #4    r0=0x80808080,r1=0x108
LDRB r0, [r1]        r0=0x00000080
LDRSB r0, [r1]        r0=0xFFFFFF80

  • address2(针对LDR(S)B)
    • [Rn, #+/-offset_8]
    • [Rn,+/-Rm]
    • [Rn, #+/-offset_8]!
    • [Rn,+/-Rm]!
    • [Rn], #+/-offset_8
    • [Rn],+/-Rm

下列指令,哪些是正确的?

LDR r1, r2, r3            错,r2加[]
LDR r1,[r2, #0x111]        对,符合12位
LDR r1,[r2], +#0x11111    错,超出12位
LDRSB r1,[r2], -#0x111    错,SB只支持8位
LDRH [r1],r2            对
LDRSH r1,[r2,r3]        对
LDRB r1,[r2,-r3]        对

写(Register->Memory)

指令 功能
STR Rd->mem32[add]
STRB Rd->mem8[add]
STRH Rd->mem16[add]
STR{<cond>}{B} rd, addressing1
STR{<cond>}{H} rd, addressing2

ldr vs str ?

为什么没有strsb和strsh?

双字访问

语法

LDR|STR{cond}D Rd, [Rn]
LDR|STR{cond}D Rd, [Rn, Offset]{!}
LDR|STR{cond}D Rd, label
LDR|STR{cond}D Rd, [Rn], Offset

例:

LDRD    r6,[r11]        ;隐含r7,r7 = r11+4
STRD    r4,[r9,#24]
STRD    r0,abcd
LDRD    r1,[r6]        ; Rd 必须为偶数
STRD    r14,[r9,#36]   ; Rd 不能是R14.读可以写不可以
STRD    r2,[r3],r6     ; Rn 不能是 Rd 或 R(d+1).

D表示读两个32位,两个字,第一个寄存器是偶数,一个字32位,半字16位

注:32位CPU的话,一个字是32位,正常16位CPU一个字16位

两个数字同时读取到寄存器中嘛?为什么?

一般一条总线或同一区间时,分先后

交换数据

指令

指令 Operation
SWP tmp=mem32[Rn] Mem32[Rn]=Rm Rd=tmp
SWPB tmp=mem8[Rn] Mem8[Rn]=Rm Rd=tmp

格式

SWP {B} {<cond>} Rd, Rm, [Rn]

分析下列指令的执行结果

mem32[0x8000]=0x12345678
R0=0x00000000
R1=0x22223333
R2=0x8000

SWP r0, r1, [r2]

mem32[0x8000]=?    0x22223333
R0=?            0x12345678
R1=?            0x22223333
R2=?            0x8000

结构访问

//C
origin.x = 0;
origin.y = 2;
origin.z = 3;

//asm
        LDR     PointBase,=origin
        MOV    r0,#0
        STR     r0,Point_x
        MOV    r0,#2
        STR     r0,Point_y
        MOV    r0,#3
        STR     r0,Point_z

如何实现数据在内存中的移动?

批量读写

//c/c++    
    memcpy(Inimage,Tmpimg,160*120);

//asm    
    ldr     r10,     =160*120
memcpy    
    ldmia    r8! ,    {r0-r7}    ;r8!地址寄存器
    stmia    r9! ,    {r0-r7}
    subs    r10,r10, #8*4
    bgt    memcpy

多存储器数据传送

指令

格式

  • LDM|STM{cond}address-mode Rn{!},reg-list{^}
    • Cond: condition code.
    • address-mode: the mode of change address.
    • Rn: base register for the load operation.
    • ^ : includes lr and spsr ;注意,cpsr切换异常模式

地址变化方式

符号 变化方式 数据起始地址 数据结束地址 Rn!
IA 后增加 Rn Rn+4N-4 Rn+4N
IB 先增加 Rn+4 Rn+4N Rn+4N
DA 后减少 Rn-4N+4 Rn Rn-4N
DB 先减少 Rn-4N Rn-4 Rn-4N

解释下列指令

LDMIA r1, {r0, r2-r7}    ;共7个
STMIA r1!, {r2,r3}
LDMDA r3, {r4-r7}
STMDA r3!, {r4-r7}
LDMIB r1, {r2, r3}
STMIB r1!, {r2,r3,r7, r10}
LDMDB r3, {r4-r7}
STMDB r3!, {r4-r7}

!表示寄存器读写完最后地址写入

这两条指令的执行结果相同吗?

LDMIA r0, {r1, r2, r3, r4} 
LDMIA r0, {r4, r3, r1, r2}

运行下列程序,看结果

    area datarw , code , readonly, align=3
    entry    
    ldr r0,=dmem    
    LDMIA r0, {r1, r2, r3, r4}
    nop
    LDMIA r0, {r4, r3, r1, r2}
stop
        b stop
dmem     dcd 0x1234,0x2345,0x3456,0x5678
    end

内存中的地址顺序与寄存器序号对应,与列表中的次序无关!

根据图中内存和寄存器的初始值,分析下列指令的执行结果

R0=0x80020
R1=0x1111
R2=0x2222
R3=0x3333
Mem add data
0x80030 0x009
0x8002c 0x008
0x80028 0x007
0x80024 0x006
0x80020 0x005
0x8001c 0x004
0x80018 0x003
0x80014 0x002
0x8000c 0x001
0x80008 0x000
LDMIA r0!,{r1-r3} r0=0x8002c,r1=0x005,r2=0x006
LDMDA r0, {r3,r2} 
LDMIB r0!,{r1-r3} r0=0x80030,r1=0x006,r2=0x007
LDMDB r0, {r3,r2}
STMIA r0!,{r1-r3}
STMDA r0, {r3,r2}
STMIB r0!,{r1-r3}
STMDB r0, {r3,r2}

堆栈操作

  • 堆栈的物理位置?

  • sp?

  • 属性 :基址,指针和限制

符号 功能 pop =LDM push =STM
FA Full In LDMFA LDMDA STMFA STMIB
FD Full de LDMFD LDMIA STMFD STMDB
EA Empty In LDMEA LDMDB STMEA STMIA
ED Empty de LDMED LDMIB STMED STMDA
R1=0x005
R3=0x004
SP=0x80014

STMFD sp!,{r1, r3}

设定sp时如何确定初始值?

R13,必须有效内存空间

标签命名

标签ARM 汇编程序中自定义符号(label, variables, constant等)有限制吗?

规则

  • Can use uppercase letters, lowercase letters, numeric characters, or the underscore character in symbol names.
  • Do not use numeric characters for the first character of symbol names, except in local labels (see Local labels).
  • Symbol names are case-sensitive.
  • All characters in the symbol name are significant.
  • Symbol names must be unique within their scope.
  • Symbols must not use built-in variable names or predefined symbol names

保留的(predefined)字

  • register names

  • r0-r15 and R0-R15

  • a1-a4 (argument, result, or scratch registers, synonyms for r0 to r3)

  • v1-v8 (variable registers, r4 to r11)

  • sb and SB (static base, r9)

  • sl and SL (stack limit, r10)

  • fp and FP (frame pointer, r11)

  • ip and IP (intra-procedure-call scratch register, r12)

  • sp and SP (stack pointer, r13)

  • lr and LR (link register, r14)

  • pc and PC (program counter, r15).

  • program status register names

    • cpsr and CPSR (current program status register)
    • spsr and SPSR (saved program status register).
  • floating-point register names

    • f0-f7 and F0-F7 (FPA registers)
    • s0-s31 and S0-S31 (VFP single-precision registers)
    • d0-d15 and D0-D15 (VFP double-precision registers).
  • coprocessor names

    • p0-p15 (coprocessors 0-15)
    • c0-c15 (coprocessor registers 0-15).

下列自定义符号哪些是正确的?

SPSR        错
Cpsr        对    
MOV
Add
Codesize
moV
||ASSERT||    对    ?
8_123d        错
102srd        错

预计算

指令 “ subs r10, r10, #84”中,“ 84” 在什么时候计算?

汇编器中计算,非处理器计算

双目计算

  • 算术运算:+,-,*,/,MOD

    MOV r0, #(5*4)

    LDR R0, =start+3*n

  • 移位运算:ROL, ROR, SHL, SHR

    A:ROL:B

    (3: ROR:4)

  • 逻辑运算:AND, OR, EOR

    A: AND:B

    (0xcc55:OR:0x55cc)

  • 关系运算:=,<, >, >=, <=, <>, /=

    A<>B

    (7<>7)

单目运算

运算符 用法 说明
?A A所在行的代码长度
BASE :BASE:A (寄存器或程序相对表示)A的基地址寄存器
INDEX :INDEX:A (寄存器相对表示)A的偏移地址
+/- +A, -A 正负号
NOT :NOT:A 按位取反
LNOT :LNOT:A 逻辑取反
DEF :DEF:A A是否定义,{TRUE/FALSE}
SB_OFFSET_19_12 :SB_OFFSET_19_12:label label-sb的bits[19:12]
SB_OFFSET_11_0 :SB_OFFSET_11_0: label label-sb的bits[11:0]

确定运行时寄存器中的数据(汇编后的结果)

Example1

     LDR r0, =? Mydata         ;10*4=40;
     (LDR r0, =mydata)?         ;没有?则读取的是label地址
     LDR r1, =? Mydata1         ;1*4=4

 mydata    DCD 1,2,3,4,5,6,7,8,9,0
 Mydata1   DCB ‘a’, ‘b’, ‘c’, ‘d’

Example 2

datastruc         SPACE   280    
                MAP        0,r8 ; (MAP datastruc) ?
       consta               FIELD   4
       constb             FIELD   4
         x                        FIELD   8
         y              FIELD   8
      string                 FIELD   256
              ……
                            LDR r3,=:BASE:y
                              LDR r4,=:INDEX:y

逻辑变量

  • LAND (处理器指令?)
  • LOR
  • LEOR

字符串变量

双目运算运算符

运算符 用法 说明
LEFT A:LEFT:B 返回字符串A中左起的B个字符
RIGHT A:RIGHT:B 返回字符串A中右起的B个字符
CC A:CC:B A和B字符串相连接,A在左边

单目运算运算符

运算符 用法 说明
LEN :LEN:A 返回字符串A的长度
CHR :CHR:A 反回字符A的ASCII码值,A是单个字符
STR :STR:A 返回数值或逻辑A的字符串

Example

  • :LEN:”I am an excellent student!”
  • : CHR: ‘A’
  • What is the returned result?

宏定义

封装汇编指令模块,作为用户定义的功能单元

ARM格式

GNU格式

不建议在宏内使用固定寄存器,例如v8。

一般定义变量使用

数值计算

64位整数加(减)法

  • 实现64位数0x2200330044和0x9876543210的加(减)法,相加后的结果保存在起始地址为0x20000000的存储空间里。设数据存储采用小端格式。

  • 如何用32位加法器实现64位加法运算?

LDR R0, =0x00000022 ;加载第一个数的高32位放到R0中
LDR R1, =0x00330044  ;加载第一个数的低32位放到R1中
LDR R2, =0x00000098 
LDR R3, =0x76543210 
LDR R6, =0x20000000  ;把存储结果的内存地址放到R6中
ADDS R4, R1, R3( SUBS R4, R1, R3 ) ;低32位加(减),R4存储低32位
ADC R5, R0, R2 ( SUBC R5, R0, R2 ) ;高32位加(减),R5存储高32位
STMIA R6!,{R4,R5}  ;将R4,R5的数据存储到R6指向的地址上,
                   ;R6值更新
  • 一般寄存器大的放高位,这里反了
  • 两数相减大于0,借位为1
  • 借位:A-B-!C

64位乘法

2个无符号64位数a 和 b相乘,得到64位结果

  • 函数参数的传递方式

    • 寄存器 r0-r3 (数目<=4)
    • 堆栈 (>4)
  • 返回

    ​ r0, r1

  • 算法:

    H1L1 *H2L2=L1*L2 +(L1*H2)<<32+ (H1*L2)<<32+(H1*H2)<<64

a_0     RN    0       ;a low
a_1    RN     1    ;a high
b_0     RN    2    ;b low
b_1    RN     3    ;b high
c_0     RN    4    ;c low low
c_1    RN     5    ;c low high
c_2     RN    12    ;c high low
c_3    RN     14    ;c high high
Mul_64 to 64
        stmfd    sp!, {r4,r5,lr}
        umull    c_0,c_1, a_0, b_0     ;low *low 
        mla    c_1, a_0,b_1,c_1    ;low *high
        mla     c_1, a_1, b_0, c_1    ;high* low

        mov     r0, c_0
        mov    r1, c_1            ;return

2个有符号64位数a 和 b相乘,得到128位结果

  • 算法:

(S)H1L1 *(S)H2L2=L1*L2 +(L1*(S)H2)<<32+((S)H1*L2)<<32+((S)H1*(S)H2)<<64

  • 无符号乘法: UMULL, UMLAL

  • 有符号乘法: SMULL, SMLAL

  • 有符号* 无符号?

64位乘法

设 A为无符号数, B为有符号数,用SMULL或SMLAL计算,则结果如何?

(1)如果A[31]=0,则(S)A * (S)B= A*(S)B

(2)如果A[31]=1, 则(S)A * (S)B= (A-232)*(S)B

                                  `A*(S)B = (S)A * (S)B+(1<<32) *(S)B`

宏定义 USMLAL(无符号*有符号)

MACRO
    USMLAL    $cl, $ch, $a, $b    
    ;signed $ch, $cl +=unsigned $a *signed $b
    SMULL        $cl, $ch, $a, $b
    ;c= (signed)a *(signed)b
    TST        $a, #1<<31
    ;if(signed)a<0
    ADDNE          $ch, $ch, $b
    ;c+=(b<<32)
    MEND    
    smul_64_to_128
    stmfd    sp!, {r4, r5, lr}
    umull     c_0, c_1, a_0, b_0     ;low*low
    mov     c_2, #0
    usmlal    c_1, c_2, a_0, b_1    ;low*high
    mov    c_3, #0
    usmlal    c_1, c_3, b_0, a_1    ;high*low
    mov    a_0, c_2, ASR #31
    adds     c_2, c_2, c_3
    adc    c_3, a_0, c_3, ASR #31
    smlal    c_2, c_3, a_1, b_1    ;high*high
    mov    r0, c_0
    mov    r1, c_1
    mov    r2, c_2
    mov    r3, c_3
    ldmfd    sp!,{r4, r5, pc}

宏定义 USMLAL(无符号*有符号)

    MACRO
    USMLAL    $cl, $ch, $a, $b    
    ;signed $ch, $cl +=unsigned $a *signed $b
    SMULL        $cl, $ch, $a, $b
    ;c= (signed)a *(signed)b
    TST        $a, #1<<31
    ;if(signed)a<0
    ADDNE          $ch, $ch, $b
    ;c+=(b<<32)
    MEND    

浮点计算

  • IEEE754浮点格式

  • 浮点数与实数的转换
    • 单精度转换公式
    • V=(-1)^S*2^(E(值)-127)*(1+M) (指数位!=0)
    • V=(-1)^S*2^(1-127)*M (指数位=0)

浮点加法

小结

分析下列程序的功能

    AREA    Block, CODE, READONLY    ; name this block of code
num       EQU     20                       ; set number of words to be copied
    ENTRY                            ; mark the first instruction to call
start
    LDR     r0, =src                 ; r0 = pointer to source block
    LDR     r1, =dst                 ; r1 = pointer to destination block
    MOV     r2, #num                 ; r2 = number of words to copy
    MOV     sp, #0x400               ; Set up stack pointer (r13)
blockcopy   
    MOVS    r3,r2, LSR #3     ; Number of eight word multiples
    BEQ     copywords                ; Less than eight words to move?
    STMFD   sp!, {r4-r11}            ; Save some working registers

octcopy  LDMIA   r0!, {r4-r11}            ; Load 8 words from the source
     STMIA   r1!, {r4-r11}            ; and put them at the destination
     SUBS    r3, r3, #1               ; Decrement the counter
     BNE     octcopy                  ; ... copy more
     LDMFD   sp!, {r4-r11}      ; Don't need these now - restore
                                                     ; originals
copywords   
    ANDS    r2, r2, #7               ; Number of odd words to copy
     BEQ     stop                     ; No words left to copy?

wordcopy    LDR     r3, [r0], #4     ; Load a word from the source  and
       STR     r3, [r1], #4             ; store it to the destination
       SUBS    r2, r2, #1               ; Decrement the counter
       BNE     wordcopy                 ; ... copy more
stop             MOV     r0, #0x18                ; 
       LDR     r1, =0x20026          ; ADP_Stopped_ApplicationExit
       SWI     0x123456                 ; ARM semihosting SWI

      AREA    BlockData, DATA, READWRITE
src               DCD     1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4
dst               DCD     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
                END

C程序

int main(void)
{      int i, j;
    unsigned char Inimage[480][640];
    unsigned char Tmpimg[120][160];
    for(i=0;i<120;i++)
         for(j=0;j<160;j++)                
               Tmpimg[i][j]=Inimage[i*4+2][j*4+2];  
    memcpy(Inimage,Tmpimg,160*120);
    return 1;
}

汇编

Imageheight    equ    120
Imagewidth    equ    160
InImagewidth     equ        640
         area    reset , code , readonly                               
         entry        
        mov    r6, #0
Framecircle 
    mov    r5, #0
Linecircle
;i->r5; j->r6; Tmpimg-> r8; Inimage->r9;
    ldr    r8,    =Tmpimg
    ldr    r9,    =Inimage
;Tmpimg[i][j]->r10;
    mov    r0,    #Imagewidth
    mul    r10, r6, r0   ;i*Imagewidth
    add    r10, r10, r5   ;i*Imagewidth+j
    add    r10,r8

;Inimage[i*4+2][j*4+2]-> r11
    mov r0, #InImagewidth
    mov    r1, r6, lsl #2 ; i*4
    add    r1, r1, #2    ;i*4+2
    mul    r11, r1, r0    ;(i*4+2)*InImagewidth
    mov    r1, r5, lsl #2 ;j*4
    add    r1, r1, #2    ;j*4+2
    add    r11, r11, r1; (i*4+2)*InImagewidth+4*j+2
    add    r11, r9
    ldrb    r0, [r11] ;read Inimage
    strb    r0, [r10] ;write Tmpimg
;circle control
        add r5, r5,#1
        cmp r5, #Imagewidth
      bmi  Linecircle
        add r6, r6, #1
        cmp r6, #Imageheight
        bmi Framecircle

; memcpy    
    ldr     r10,     =160*120
memcpy    
    ldmia    r8! ,    {r0-r7}
    stmia    r9! ,    {r0-r7}
    subs    r10,r10, #8*4
    bgt    memcpy
    ;end

    area  imscale,data, readwrite

Tmpimg    space        160*120
Inimage    space        640*480

    end

ARM程序基础

参考内容:

  • ARM Architecture Reference Manual(V5), Second Edition

  • Andrew N. Sloss 等,沈建华译,ARM嵌入式系统开发-软件设计与优化,北京航空航天大学出版社,2005年5月

函数调用

源文件内

    AREA  function, CODE, READONLY
    ENTRY
start
           MOV     r0, #10
        MOV     r1, #3         ;参数
        BL      doadd            ; call

    ……

doadd                               ;函数
        ADD     r0, r0, r1          ;结果
        MOV     pc, lr                ;返回

        END

不同源文件

  • File1
AREA Main, CODE, READONLY
    IMPORT   Testprg
    ENTRY     
main        ……
        BL Testprg
        ……
        END
  • File2
    AREA  Test, CODE, READONLY
    EXPORT   Testprg
   Testprg
        ADD  r0, r1, r0
        MOV pc,lr
        END

汇编import的函数文件需要加export

ATPCS(ARM-Thumb Procedure Call Standard)

  • 寄存器约定

    • R0-r3 (a1-a4) 传递参数

    • R4-r11 (v1-v8)保存局部变量 (r4-r7 for Thumb)

    • R12 (ip) 过程间自定义数据交换

    • R13 (sp) 数据堆栈指针

    • R14 (lr) 保存子程序的返回地址

    • R15(pc)

  • ATPCS规定数据栈为FD(满递减)类型,并且对数据栈的操作是8字节对齐。

向堆栈写数据,指针减小;数据栈8字节对齐

  • 参数传输规则
    • 小于等于4, r0-r3,依次
    • 大于4,存入数据栈,最后一个数据先入栈

function( char a, int *b, int c, short d, long f)

  • 参数次序与所使用的寄存器?
  • 寄存器的分配与类型(浮点除外)有关?
  • 浮点参数传递(顺序传递)
    • FPA (Floating Point Arithmetic):f0-f7 (s0-s7, d0-d7,e0-e7)
    • VFP (d0-d15, s0-s31)

float function(int v1, float v2, char *v3, int v4, float v5, double v6, short v7) ;

  • v1, v3, v4, v7 => r0, r1, r2, r3

  • v2, v5 => s0, s1

  • v6 => d0

继承协处理器

注意参数传递:整形(字符),float,double

  • 参数返回规则

    • 结果为32bit整数时,通过r0传递

    • 结果为64bit整数时,通过r0和R1传递

    • 结果为浮点数时,通过浮点寄存器返回(f0,d0)

    • 更多的数据通过内存返回

C调用汇编

#include <stdio.h>
extern void strcopy(char *d, const char *s); //ASM
int main()
{       const char *srcstr = "First string - source ";
        char dststr[] = "Second string - destination ";
        strcopy(dststr,srcstr);
        printf("After copying:\n");
        printf("  %s\n  %s\n",srcstr,dststr);
        return (0);
}
        AREA    SCopy, CODE, READONLY
            EXPORT strcopy
strcopy 
        LDRB r2, [r1],#1  ; Load byte and update address.
           STRB r2, [r0],#1  ; 
        CMP r2, #0        ; Check for zero terminator.
            BNE strcopy       ; Keep going if not.
            MOV pc,lr         ; Return.
            END

参数传递过程?

汇编调用C

    AREA f, CODE, READONLY
        IMPORT  g
    ENTRY           
        LDR r4, =data
        LDR  r0, [r4], #4
    LDR  r1, [r4], #4
    LDR  r2, [r4], #4
    LDR  r3, [r4], #4
    LDR  r5, [r4]
    STR r5, [sp, #-4]! 
    BL  g               
       ADD sp, sp, #4     
       END
int g(int a, int b, int c, int d, int e) 
    {  return a + b + c + d + e;}

STR r5, [sp, #-4]! //栈初始满的,需要减去4字节腾出空间

C++调用汇编

struct S { 
        S(int s) {i=s; }
        int i;
    }

extern "C" void asmfunc(S *);                                                     
int f() 
{
        S s(2);                 
        asmfunc(&s); 
    return s.i * 3;
}
AREA   Asm, CODE
        EXPORT asmfunc
asmfunc            ; the definition of the Asm function
        LDR r1, [r0] 
    ADD r1, r1, #5
        STR r1, [r0]
        MOV pc, lr
        END

汇编调用C++

    AREA  Asm, CODE,READONLY
        IMPORT cppfunc
    ENTRY
    MOV    r0,#2
        STR    r0,[sp,#-4]! 
        MOV    r0,sp        
        BL     cppfunc 
    LDR    r0, [sp], #4
        ADD    r0, r0, r0,LSL #1
    END
extern "C" void cppfunc(S * p) {
 // Definition of the C++ function to be called from ASM.
    p->i += 5;}

嵌入汇编

void my_strcpy(const char *src, char *dst)
{    int ch;
        __asm{
            loop:
               LDRB    ch, [src], #1
                      STRB    ch, [dst], #1
              CMP     ch, #0
                     BNE     loop
        }
}
  • 嵌入 vs 汇编程序
    • 不支持 LDR Rn,= XXX 和 ADR, ADRL 伪指令
    • 不支持 BX
    • 用”&”替代 “0x” 表示16位数据

[src], [dst] ?

ch ?

循环结束条件?

c中的变量?

  • 小心使用寄存器,尽量不用 R0-R3,lr,ip 和CPSR 中的NZCV 标志位;不用r0-r3,用v1-v8
__asm
{
    MOV R0, x
    ADD y, R0,  y
}
  • 不要使用寄存器替代变量
int bad_f(int x) // x in R0
{          ……
            __asm { 
               ADD R0, R0, #1 ;wrongly asserts that x is still in R0    
           }    
          return x; // x in R0
}
  • 无需保存和恢复寄存器
int f(int x){
           __asm{
                STMFD sp!, {R0} ;save R0 - illegal: read before write
                ADD R0, x, 1
                EOR x, R0, x
                LDMFD sp!, {R0} ; restore R0 - not needed.    
         }
         return x;
}
  • 汇编语言用“,”作为操作数分隔符。如果有C 或C++表达式作为操作数,必须用“( )”将其归约为一个汇编操作数。
 __asm {ADD x, y, (f()+z)}

异常处理

异常向量表

向量地址 异常中断类型 异常中断模式 优先级
0x0 Reset 特权(SVC) 1
0x4 Undefined Instruction 未定义指令中止模式 6
0x08 SWI 特权模式 6
0x0c Prefetch Abort 中止模式 5
0x10 Data Abort 中止模式 2
0x14 Reserved 未使用 未使用
0x18 IRQ中断 IRQ模式 4
0x1c FIQ快速中断 FIQ模式 3

确定优先级的作用?

  • 异常向量表特点

    • 异常事件与异常处理程序之间的对应关系;
    • 向量表大小32个字节,每个异常向量占据4个字节;
    • 每个字存放PC赋值的语句,或跳转指令;
    • 通常存放在存储器地址的低端(或利用寄存器设置)
  • 向量表-LDR间接寻址

Vectors          LDR     PC, Reset_Addr           ;0x00  
                  LDR     PC, Undef_Addr
                   LDR     PC, SWI_Addr
                   LDR     PC, PAbt_Addr
                   LDR     PC, DAbt_Addr
                   NOP                                    ; Reserved Vector 
                   LDR     PC, IRQ_Addr
                   LDR     PC, FIQ_Addr
        ……                                 ; IRQ_Entry, 9条指令
        Reset_Addr          DCD     Reset_Handler
        Undef_Addr          DCD     Undef_Handler
        SWI_Addr            DCD     SWI_Handler
        PAbt_Addr           DCD     PAbt_Handler
        DAbt_Addr           DCD     DAbt_Handler
                    DCD     0                              ; Reserved Address 
        IRQ_Addr            DCD     IRQ_Handler
        FIQ_Addr            DCD     FIQ_Handler

LDR PC, Reset_Addr 会相对寻址 Ldr [pc, #Reset_Addr to pc]

偏移不能超过4k

  • 向量表-B跳转
Vectors          
             B     Reset_Handler  ;0x00
              B     Undef_Handler
            B     SWI_Handler
               B     PAbt_Handler
               B     DAbt_Handler
            NOP                          ; Reserved Address 
            B     IRQ_Handler
            B     FIQ_Handler
  • B

    • 简单

    • 跳转范围受限 (±32MB)

  • 使用LDR指令

    • 跳转范围不受限

    • 额外空间存地址

    • 地址存放在4KB范围以内

在这里, ”B” 能否改成”BL” ?

  • 向量表-伪指令LDR
Vectors          
              LDR PC, = Reset_Handler  ;0x00
               LDR PC, = Undef_Handler
            LDR PC, = SWI_Handler
                LDR PC, = PAbt_Handler
                LDR PC, = DAbt_Handler
            NOP                              ; Reserved Address 
             LDR PC, = IRQ_Handler
            LDR PC, = FIQ_Handler
                    ……                                 ; IRQ_Entry, 9条指令
        ltorg
        ;Reset_Addr          DCD     Reset_Handler
        ;Undef_Addr          DCD     Undef_Handler
        ;SWI_Addr            DCD     SWI_Handler
        ;PAbt_Addr           DCD     PAbt_Handler
        ;DAbt_Addr           DCD     DAbt_Handler
  • 向量表/异常服务程序
        import  ISR_IRQ_Handler
Vectors          
             B     Reset_Handler  
              B     Undef_Handler
            B     SWI_Handler
               B     PAbt_Handler
               B     DAbt_Handler
            NOP    
            B     ISR_IRQ_Handler
            B     FIQ_Handler

中断服务程序一般没有参数不需要传回值,或通过内存传递

能否在程序运行时注册(安装)异常服务程序?

注册异常服务程序

能直接写入文本格式程序源码?

程序运行时,将指令 “B ISR_IRQ_Handler ”写入向量表中?

构建二进制指令,取高26位,偏移地址:中断向量-中断服务程序入口地址

  • 步骤

​ (1)读取中断处理程序的地址addr1

​ (2)将addr1减去该中断对应的中断向量的地址vector1

​ (3)addr=Addr1-vector1-8 (允许指令预取)

​ (4)addr LSR #2

​ (5)if(addr and 0xff000000 ==0)

​ (6)addr or 0xea00 0000

​ (7)结果写回中断向量表

  • 安装函数
unsigned Install_Handler (unsigned *handlerloc, unsigned *vector)
{  
    unsigned vec, oldvec;
    vec = ((unsigned)handlerloc - (unsigned)vector - 0x8)>>2;
    if ((vec & 0xFF000000) != 0)
        { return 0;}
    vec = 0xEa000000 | vec;
    oldvec = *vector;
    *vector = vec;
    return (oldvec);
}
unsigned *irqvec = (unsigned *)0x18;    
static unsigned pIRQ_Handler = (unsigned)ISR_IRQ_handler    
Install_Handler (&pIRQ_Handler, irqvec);

中断向量表的基地址?

上述例程适用条件?跳转地址和向量表偏移地址不能超过正负32M

如果不能确定异常服务程序的地址范围,如何处理?

如果向量表采用”LDR”语句,需要修改”DCD” 后的地址, 如何做?

  • 获取服务程序的入口地址;
  • 确定异常向量对应的”DCD” 分配的存储地址;
  • 将服务程序入口地址写入对应的存储单元;

中断向量扩展

实际处理器通常有多个外部中断 (IRQ, 外设, IO),这些中断与其对应的服务程序如何关联?

INTOFFSET           EQU    0X4A000014  ;Address of Interrupt 
                            ;offset Register
IntVTAddress           EQU     0x33FFFF20

;Interrupt Vector Table Address                
HandleEINT0            EQU    IntVTAddress           
HandleEINT1            EQU    IntVTAddress +4
……
HandleTIMER0           EQU    IntVTAddress +4*10
HandleTIMER1           EQU    IntVTAddress +4*11
……
HandleUART1            EQU    IntVTAddress +4*23
……
HandleUART0           EQU    IntVTAddress +4*28
HandleADC             EQU    IntVTAddress +4*31
IRQ_Entry
                          sub    sp,sp,#4       ;reserved for PC
                    stmfd    sp!,{r8-r9}                
                    ldr    r9,=INTOFFSET  ;中断序号
                    ldr    r9,[r9]
                    ldr    r8,=HandleEINT0 ;中断扩展表首地址
                    add    r8,r8,r9,lsl #2 ; ?
                    ldr    r8,[r8]   ;Eintx_ Entry Add ->r8
                    str    r8,[sp,#8] ;r8->SP(-#4)

                    ldmfd    sp!,{r8-r9,pc};Eintx_ Entry Add ->pc

非嵌套中断

interrupt_handler
    sub    r14, r14, #4 ; ?
    stmfd    sp!, {r0-r3, r12, r14}
    ldr    r1, =IRQStatus
    ldr    r0,[r1]
    tst    r0, #0x0080  ;测试中断源
    blne    my_isr1
    tst    r0, #0x0001 ;测试中断源
    blne    my_isr2
    ldmfd    sp!, {r0-r3, r12, r14}^
    ldr pc, lr

嵌套中断

外设访问

如何控制实验板上LED显示?

LED连接

Mainboard

  • LED连接

    • LED1 -> GPF4

    • LED2 -> GPF5

    • LED3 -> GPF6

    • LED4 -> GPF7

  • LED 控制

    • n0 -> on

    • n1 -> off

  • GPF控制

    • Registers

  • The value of Registers

    • GPFCON (4-7 Output):0b 0101 0101 XXXX XXXX

    • GPFUP ( 4-7 No concern): 0b XXXX XXXX

    • GPFDAT (4-7 on): 0b 0000 XXXX

    • GPFDAT (4-7 off): 0b 1111 XXXX

C Code

int *rGPFCON = (int *) 0x56000050;
int *rGPFDAT = (int *) 0x56000054;

void main( void)
{
    *rGPFCON=0x5500;
    while(1)
    {
        *rGPFDAT = 0x00;
        delay(1000); 
        *rGPFDAT = 0xf0;
        delay(1000);
    }
}    

如何连接到目标板?

Emulator

如何在目板RAM中运行程序?

Run in RAM

  • Linker Configure

    • RO Base=0x30000000

    • RW Base=0x30100000

  • DRAM 初始化

    • Debug Init
//MEMMAP=0x01; 
map 0x48000000, 0x60000000 read write ;
_WDWORD(0x53000000,0x00000000);          //watchdog Timer Control register
_WDWORD(0x4a000008,0xffffffff);                 //Interrupt Mask Control
_WDWORD(0x4a00001c,0x000007ff);            //Interrupt sub mask
_WDWORD(0x4c000014,0x03);                       //Clock Divider Control Register
_WDWORD(0x4c000004,0x5c042);                 //MPLL Configuration Register
_WDWORD(0x48000000,0x22111110);          //Bus Width and Wait Status Ctrl
_WDWORD(0x48000004,0x00000700);          //Bank 0 Control Register
_WDWORD(0x48000008,0x00000700);          //Bank 1 Control Register
_WDWORD(0x4800000c,0x00000700);          //Bank 2 Control Register
_WDWORD(0x48000010,0x00000700);          //Bank 3 Control Register
_WDWORD(0x48000014,0x00000700);          //Bank 4 Control Register
_WDWORD(0x48000018,0x00000700);          //Bank 5 Control Register
_WDWORD(0x4800001c,0x00018005);          //Bank 6 Control Register
_WDWORD(0x48000020,0x00000700);          //Bank 7 Control Register
_WDWORD(0x48000024,0x008e0459);          //SDRAM Refresh Control Register 
_WDWORD(0x48000028,0x000000b2);          //Flexible Bank Size Register 
_WDWORD(0x4800002c,0x00000030);          //Bank 6 Mode Register
_WDWORD(0x48000030,0x00000030);          //Bank 7 Mode Register
_WDWORD(0x56000014,0x01);                      //Port B Data
_WDWORD(0x56000020,0xaaaa55aa);           //Port C Control
_WDWORD(0x56000028,0x0ffff);                      //Pull-up Control C
_WDWORD(0x56000024,0x00000000);          //Port C Data
_WDWORD(0x56000070,0x00280000);          //Port H Control
_WDWORD(0x56000078,0x00000000);          //Pull-up Control H
//pc=0x30000000

需要在代码中加入初始化,上电后才能正常访问RAM

  • 下载到RAM

如何实现上电启动?

Run in FLASH

  • Linker Configure
    • RO Base=0x00000000
    • RW Base=0x30000000
  • Output Configure
    • Create HEX file
  • DRAM 初始化
    • Initialization by code

Nor Flash

  • Set Jumper ( boot from Nor Flash )(16bit)

    • SW104: OM0 (Open)

    • SW105: OM1 (Close)

Linker CMD

LR_ROM1 0x00000000 
{    ; load region
     ER_ROM1 0x00000000 0x0200000  
    {  ; load address = execution address
       *.o (RESET, +First)
       *(InRoot$$Sections)
       .ANY (+RO)
   }
  RW_RAM1 0x30000000 0x4000000  
  {  ; RW data
       .ANY (+RW +ZI)
   }
   RW_IRAM1 0x40000000 0x00001000  
   {
       .ANY (+RW +ZI)
   }
}

小结

  • 函数调用
  • 异常向量表
  • 异常服务程序注册
  • 外设访问

ARM程序分析

启动程序

Start.s

  • 结构

  • Definitions

  • PRESERVE8 8字节对齐
;/*****************************************************************************/
;/* S3C2410A.S: Startup file for Samsung S3C410A                              */
;/*****************************************************************************/
;/* <<< Use Configuration Wizard in Context Menu >>>                          */ 
;/*****************************************************************************/
;/* This file is part of the uVision/ARM development tools.                   */
;/* Copyright (c) 2005-2006 Keil Software. All rights reserved.               */
;/* This software may only be used under the terms of a valid, current,       */
;/* end user licence from KEIL for a compatible version of KEIL software      */
;/* development tools. Nothing else gives you the right to use this software. */
;/*****************************************************************************/


; *** Startup Code (executed after Reset) ***


; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs

Mode_USR        EQU     0x10
Mode_FIQ        EQU     0x11
Mode_IRQ        EQU     0x12
Mode_SVC        EQU     0x13
Mode_ABT        EQU     0x17
Mode_UND        EQU     0x1B
Mode_SYS        EQU     0x1F

I_Bit           EQU     0x80            ; when I bit is set, IRQ is disabled
F_Bit           EQU     0x40            ; when F bit is set, FIQ is disabled


;//  Stack Configuration (Stack Sizes in Bytes)
;//    Undefined Mode      <0x0-0xFFFFFFFF:8>
;//    Supervisor Mode     <0x0-0xFFFFFFFF:8>
;//    Abort Mode          <0x0-0xFFFFFFFF:8>
;//    Fast Interrupt Mode <0x0-0xFFFFFFFF:8>
;//    Interrupt Mode      <0x0-0xFFFFFFFF:8>
;//    User/System Mode    <0x0-0xFFFFFFFF:8>
;// 

;    Stack/Heap Definition 堆栈定义
UND_Stack_Size  EQU     0x00000000
SVC_Stack_Size  EQU     0x00000008
ABT_Stack_Size  EQU     0x00000000
FIQ_Stack_Size  EQU     0x00000000
IRQ_Stack_Size  EQU     0x00000080
USR_Stack_Size  EQU     0x00000400


; --------------------------------------------------------------------------
Stack_Size      EQU     (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
                         FIQ_Stack_Size + IRQ_Stack_Size + USR_Stack_Size)

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size

; ARM 栈初始位于栈顶 
Stack_Top       EQU     Stack_Mem + Stack_Size

;//  Heap Configuration
;//     Heap Size (in Bytes) <0x0-0xFFFFFFFF>
;// 

Heap_Size       EQU     0x00000000

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
Heap_Mem        SPACE   Heap_Size

; --------------------------------------------------------------------------



; Clock Management definitions
; Clock定义
CLK_BASE        EQU     0x4C000000      ; Clock Base Address
LOCKTIME_OFS    EQU     0x00            ; LOCKTIME Offset
MPLLCON_OFS     EQU     0x04            ; MPLLCON Offset
UPLLCON_OFS     EQU     0X08            ; UPLLCON Offset
CLKCON_OFS      EQU     0x0C            ; CLKCON Offset
CLKSLOW_OFS     EQU     0x10            ; CLKSLOW Offset
CLKDIVN_OFS     EQU     0X14            ; CLDKIVN Offset
CAMDIVN_OFS     EQU     0X18            ; CAMDIVN Offset



;//  Clock Management
;//    MPLL Settings
;//    Mpll = (m * Fin) / (p * 2^s)
;//      MDIV: Main divider <0x0-0xFF>
;//                  m = MDIV + 8
;//        PDIV: Pre-divider  <0x0-0x3F>
;//                  p = PDIV + 2
;//        SDIV: Post Divider <0x0-0x03>
;//                  s = SDIV 
;//   
;//    UPLL Settings
;//    Upll = ( m * Fin) / (p * 2^s),Uclk must be 48MHZ to USB device 
;//      MDIV: Main divider <0x1-0xF8>
;//                  m = MDIV + 8,if Fin=12MHZ MDIV could be 0x38   
;//        PDIV: Pre-divider  <0x1-0x3E>
;//                  p = PDIV + 2,if Fin=12MHZ PDIV could be 0x2
;//        SDIV: Post Divider <0x0-0x03>
;//                  s = SDIV ,if Fin=12MHZ SDIV could be 0x2
;//   
;//   LOCK TIME 
;//        LTIME CNT: MPLL Lock Time Count  <0x0-0xFFF>
;//        LTIME CNT: UPLL Lock Time Count  <0x0-0xFFF>
;//   
;//    Master Clock
;//    PLL Clock:  FCLK = FMPLL
;//    Slow Clock: FCLK = Fin / (2 * SLOW_VAL), SLOW_VAL > 0
;//    Slow Clock: FCLK = Fin, SLOW_VAL = 0
;//           UCLK_ON: UCLK ON
;//                  0: UCLK ON(UPLL is also turned on) 1: UCLK OFF (UPLL is also turned off)      
;//           MPLL_OFF: Turn off PLL
;//                  0: Turn on PLL.After PLL stabilization time (minimum 300us), SLOW_BIT can be cleared to 0. 1: Turn off PLL. PLL is turned off only when SLOW_BIT is 1.
;//           SLOW_BIT: Slow Clock
;//        SLOW_VAL: Slow Clock divider    <0x0-0x7>
;//   
;//    CLOCK DIVIDER CONTROL
;//        HDIVN                            
;//                   0: HCLK = FCLK/1, 01 : HCLK = FCLK/2
;//           PDIVN
;//                   0: PCLK has the clock same as the HCLK/1,1: PCLK has the clock same as the HCLK/2
;//    
;//   Clock Generation
;//          SPI          <0=> Disable  <1=> Enable
;//          IIS          <0=> Disable  <1=> Enable
;//          IIC          <0=> Disable  <1=> Enable
;//          ADC          <0=> Disable  <1=> Enable
;//          RTC          <0=> Disable  <1=> Enable
;//          GPIO         <0=> Disable  <1=> Enable
;//          UART2        <0=> Disable  <1=> Enable
;//          UART1        <0=> Disable  <1=> Enable
;//          UART0        <0=> Disable  <1=> Enable
;//           SDI          <0=> Disable  <1=> Enable
;//           PWMTIMER     <0=> Disable  <1=> Enable
;//           USB device   <0=> Disable  <1=> Enable
;//           USB host     <0=> Disable  <1=> Enable
;//           LCDC         <0=> Disable  <1=> Enable
;//           NAND FLASH Controller       <0=> Disable  <1=> Enable
;//           POWER-OFF    <0=> Disable  <1=> Enable
;//           IDLE BIT     <0=> Disable  <1=> Enable
;//           SM_BIT       <0=> Disable  <1=> Enable
;//   
;// 
CLK_SETUP       EQU     1
MPLLCON_Val     EQU     0x0005C080
UPLLCON_Val     EQU     0x00028080
CLKCON_Val      EQU     0x0007FFF0
CLKSLOW_Val     EQU     0x00000004
LOCKTIME_Val    EQU     0x00FFFFFF
CLKDIVN_Val     EQU     0X00000000



;Interrupt  definitions
INTOFFSET          EQU    0X4A000014                      ;Address of Interrupt offset Register

;// Interrupt Vector Table
;//   Interrupt Vector address     <0x20-0x3fffff78>
;//             You could define Interuupt Vctor Table address.  
;//             The Interrupt Vector Table address must be word aligned adress. 
;//  
IntVT_SETUP      EQU     1
IntVTAddress    EQU     0x33FFFF20

; Watchdog Timer definitions
WT_BASE         EQU     0x53000000      ; WT Base Address
WTCON_OFS       EQU     0x00            ; WTCON Offset
WTDAT_OFS       EQU     0x04            ; WTDAT Offset
WTCNT_OFS       EQU     0x08            ; WTCNT Offset

;//  Watchdog Timer
;//         Watchdog Timer Enable/Disable
;//         Reset Enable/Disable
;//         Interrupt Enable/Disable
;//      Clock Select  
;//               <0=> 1/16  <1=> 1/32  <2=> 1/64  <3=> 1/128
;//                Clock Division Factor
;//     Prescaler Value <0x0-0xFF>
;//     Time-out Value  <0x0-0xFFFF>
;// 
WT_SETUP        EQU     1
WTCON_Val       EQU     0x00008021      
WTDAT_Val       EQU     0x00008000


; Memory Controller definitions
MC_BASE         EQU     0x48000000      ; Memory Controller Base Address

;//  Memory Controller
MC_SETUP        EQU     1

;//    Bank 0
;//        PMC: Page Mode Configuration
;//                 <0=> 1 Data  <1=> 4 Data  <2=> 8 Data  <3=> 16 Data
;//        Tpac: Page Mode Access Cycle
;//                 <0=> 2 clks  <1=> 3 clks  <2=> 4 clks  <3=> 6 clks
;//        Tcah: Address Holding Time after nGCSn
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//        Toch: Chip Select Hold on nOE
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//       Tacc: Access Cycle
;//                 <0=> 1 clk   <1=> 2 clks  <2=> 3 clks  <3=> 4 clks
;//                 <4=> 6 clk   <5=> 8 clks  <6=> 10 clks <7=> 14 clks
;//      Tcos: Chip Select Set-up nOE
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//      Tacs: Address Set-up before nGCSn
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//   
;//
;//    Bank 1
;//        DW: Data Bus Width
;//                 <0=> 8-bit   <1=> 16-bit  <2=> 32-bit  <3=> Rsrvd
;//           WS: WAIT Status
;//                 <0=> WAIT Disable
;//                 <1=> WAIT Enable
;//           ST: SRAM Type
;//                 <0=> Not using UB/LB
;//                 <1=> Using UB/LB
;//        PMC: Page Mode Configuration
;//                 <0=> 1 Data  <1=> 4 Data  <2=> 8 Data  <3=> 16 Data
;//        Tpac: Page Mode Access Cycle
;//                 <0=> 2 clks  <1=> 3 clks  <2=> 4 clks  <3=> 6 clks
;//        Tcah: Address Holding Time after nGCSn
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//        Toch: Chip Select Hold on nOE
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//       Tacc: Access Cycle
;//                 <0=> 1 clk   <1=> 2 clks  <2=> 3 clks  <3=> 4 clks
;//                 <4=> 6 clk   <5=> 8 clks  <6=> 10 clks <7=> 14 clks
;//      Tcos: Chip Select Set-up nOE
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//      Tacs: Address Set-up before nGCSn
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//   
;//
;//    Bank 2
;//        DW: Data Bus Width
;//                 <0=> 8-bit   <1=> 16-bit  <2=> 32-bit  <3=> Rsrvd
;//          WS: WAIT Status
;//                 <0=> WAIT Disable
;//                 <1=> WAIT Enable
;//          ST: SRAM Type
;//                 <0=> Not using UB/LB
;//                 <1=> Using UB/LB
;//        PMC: Page Mode Configuration
;//                 <0=> 1 Data  <1=> 4 Data  <2=> 8 Data  <3=> 16 Data
;//        Tpac: Page Mode Access Cycle
;//                 <0=> 2 clks  <1=> 3 clks  <2=> 4 clks  <3=> 6 clks
;//        Tcah: Address Holding Time after nGCSn
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//        Toch: Chip Select Hold on nOE
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//       Tacc: Access Cycle
;//                 <0=> 1 clk   <1=> 2 clks  <2=> 3 clks  <3=> 4 clks
;//                 <4=> 6 clk   <5=> 8 clks  <6=> 10 clks <7=> 14 clks
;//      Tcos: Chip Select Set-up nOE
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//      Tacs: Address Set-up before nGCSn
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//   
;//
;//    Bank 3
;//      DW: Data Bus Width
;//                 <0=> 8-bit   <1=> 16-bit  <2=> 32-bit  <3=> Rsrvd
;//          WS: WAIT Status
;//                 <0=> WAIT Disable
;//                 <1=> WAIT Enable
;//          ST: SRAM Type
;//                 <0=> Not using UB/LB
;//                 <1=> Using UB/LB
;//        PMC: Page Mode Configuration
;//                 <0=> 1 Data  <1=> 4 Data  <2=> 8 Data  <3=> 16 Data
;//        Tpac: Page Mode Access Cycle
;//                 <0=> 2 clks  <1=> 3 clks  <2=> 4 clks  <3=> 6 clks
;//        Tcah: Address Holding Time after nGCSn
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//        Toch: Chip Select Hold on nOE
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//       Tacc: Access Cycle
;//                 <0=> 1 clk   <1=> 2 clks  <2=> 3 clks  <3=> 4 clks
;//                 <4=> 6 clk   <5=> 8 clks  <6=> 10 clks <7=> 14 clks
;//      Tcos: Chip Select Set-up nOE
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//      Tacs: Address Set-up before nGCSn
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//   
;//
;//    Bank 4
;//      DW: Data Bus Width
;//                 <0=> 8-bit   <1=> 16-bit  <2=> 32-bit  <3=> Rsrvd
;//          WS: WAIT Status
;//                 <0=> WAIT Disable
;//                 <1=> WAIT Enable
;//          ST: SRAM Type
;//                 <0=> Not using UB/LB
;//                 <1=> Using UB/LB
;//        PMC: Page Mode Configuration
;//                 <0=> 1 Data  <1=> 4 Data  <2=> 8 Data  <3=> 16 Data
;//        Tpac: Page Mode Access Cycle
;//                 <0=> 2 clks  <1=> 3 clks  <2=> 4 clks  <3=> 6 clks
;//        Tcah: Address Holding Time after nGCSn
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//        Toch: Chip Select Hold on nOE
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//       Tacc: Access Cycle
;//                 <0=> 1 clk   <1=> 2 clks  <2=> 3 clks  <3=> 4 clks
;//                 <4=> 6 clk   <5=> 8 clks  <6=> 10 clks <7=> 14 clks
;//      Tcos: Chip Select Set-up nOE
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//      Tacs: Address Set-up before nGCSn
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//   
;//
;//    Bank 5
;//      DW: Data Bus Width
;//                 <0=> 8-bit   <1=> 16-bit  <2=> 32-bit  <3=> Rsrvd
;//          WS: WAIT Status
;//                 <0=> WAIT Disable
;//                 <1=> WAIT Enable
;//          ST: SRAM Type
;//                 <0=> Not using UB/LB
;//                 <1=> Using UB/LB
;//        PMC: Page Mode Configuration
;//                 <0=> 1 Data  <1=> 4 Data  <2=> 8 Data  <3=> 16 Data
;//        Tpac: Page Mode Access Cycle
;//                 <0=> 2 clks  <1=> 3 clks  <2=> 4 clks  <3=> 6 clks
;//        Tcah: Address Holding Time after nGCSn
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//        Toch: Chip Select Hold on nOE
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//       Tacc: Access Cycle
;//                 <0=> 1 clk   <1=> 2 clks  <2=> 3 clks  <3=> 4 clks
;//                 <4=> 6 clk   <5=> 8 clks  <6=> 10 clks <7=> 14 clks
;//      Tcos: Chip Select Set-up nOE
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//      Tacs: Address Set-up before nGCSn
;//                 <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//   
;//
;//    Bank 6
;//       BK76MAP: Bank 6/7 Memory Map
;//                 <0=> 32M  <1=> 64M <2=> 128M <4=> 2M   <5=> 4M   <6=> 8M   <7=> 16M
;//      DW: Data Bus Width
;//                 <0=> 8-bit   <1=> 16-bit  <2=> 32-bit  <3=> Rsrvd
;//          WS: WAIT Status
;//                 <0=> WAIT Disable
;//                 <1=> WAIT Enable
;//          ST: SRAM Type
;//                 <0=> Not using UB/LB
;//                 <1=> Using UB/LB
;//      MT: Memory Type
;//                 <0=> ROM or SRAM
;//                 <3=> SDRAM
;//      ROM or SRAM
;//          PMC: Page Mode Configuration
;//                   <0=> 1 Data  <1=> 4 Data  <2=> 8 Data  <3=> 16 Data
;//          Tpac: Page Mode Access Cycle
;//                 <0=> 2 clks  <1=> 3 clks  <2=> 4 clks  <3=> 6 clks
;//          Tcah: Address Holding Time after nGCSn
;//                   <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//          Toch: Chip Select Hold on nOE
;//                   <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//         Tacc: Access Cycle
;//                   <0=> 1 clk   <1=> 2 clks  <2=> 3 clks  <3=> 4 clks
;//                   <4=> 6 clk   <5=> 8 clks  <6=> 10 clks <7=> 14 clks
;//        Tcos: Chip Select Set-up nOE
;//                   <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//        Tacs: Address Set-up before nGCSn
;//                   <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//     
;//      SDRAM
;//          SCAN: Columnn Address Number
;//                   <0=> 8-bit   <1=> 9-bit   <2=> 10-bit  <3=> Rsrvd
;//          Trcd: RAS to CAS Delay
;//                   <0=> 2 clks  <1=> 3 clks  <2=> 4 clks  <3=> Rsrvd
;//            SCKEEN: SCLK Selection (Bank 6/7)
;//                   <0=> Normal
;//                   <1=> Reduced Power   
;//            SCLKEN: SDRAM power down mode (Bank 6/7)
;//                   <0=> DISABLE
;//                   <1=> ENABLE 
;//            BURST_EN: ARM core burst operation (Bank 6/7)
;//                   <0=> DISABLE
;//                   <1=> ENABLE 
;//         BL: Burst Length
;//                   <0=> 1
;//            BT: Burst Type
;//                   <0=> Sequential
;//         CL: CAS Latency
;//                   <0=> 1 clk   <1=> 2 clks  <2=> 3 clks
;//         TM: Test Mode
;//                   <0=> Mode Register Set
;//            WBL: Write Burst Length
;//                   <0=> 0
;//     
;//   
;//
;//    Bank 7
;//       BK76MAP: Bank 6/7 Memory Map
;//                 <0=> 32M  <1=> 64M <2=> 128M <4=> 2M   <5=> 4M   <6=> 8M   <7=> 16M
;//      DW: Data Bus Width
;//                 <0=> 8-bit   <1=> 16-bit  <2=> 32-bit  <3=> Rsrvd
;//          WS: WAIT Status
;//                 <0=> WAIT Disable
;//                 <1=> WAIT Enable
;//          ST: SRAM Type
;//                 <0=> Not using UB/LB
;//                 <1=> Using UB/LB
;//      MT: Memory Type
;//                 <0=> ROM or SRAM
;//                 <3=> SDRAM
;//      ROM or SRAM
;//          PMC: Page Mode Configuration
;//                   <0=> 1 Data  <1=> 4 Data  <2=> 8 Data  <3=> 16 Data
;//          Tpac: Page Mode Access Cycle
;//                 <0=> 2 clks  <1=> 3 clks  <2=> 4 clks  <3=> 6 clks
;//          Tcah: Address Holding Time after nGCSn
;//                   <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//          Toch: Chip Select Hold on nOE
;//                   <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//         Tacc: Access Cycle
;//                   <0=> 1 clk   <1=> 2 clks  <2=> 3 clks  <3=> 4 clks
;//                   <4=> 6 clk   <5=> 8 clks  <6=> 10 clks <7=> 14 clks
;//        Tcos: Chip Select Set-up nOE
;//                   <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//        Tacs: Address Set-up before nGCSn
;//                   <0=> 0 clk   <1=> 1 clk   <2=> 2 clks  <3=> 4 clks
;//     
;//      SDRAM
;//          SCAN: Columnn Address Number
;//                   <0=> 8-bit   <1=> 9-bit   <2=> 10-bit  <3=> Rsrvd
;//          Trcd: RAS to CAS Delay
;//                   <0=> 2 clks  <1=> 3 clks  <2=> 4 clks  <3=> Rsrvd
;//            SCLKEN: SCLK Selection (Bank 6/7)
;//                   <0=> Normal
;//                   <1=> Reduced Power 
;//            SCLKEN: SDRAM power down mode (Bank 6/7)
;//                   <0=> DISABLE
;//                   <1=> ENABLE 
;//            BURST_EN: ARM core burst operation (Bank 6/7)
;//                   <0=> DISABLE
;//                   <1=> ENABLE 
;//         BL: Burst Length
;//                   <0=> 1
;//            BT: Burst Type
;//                   <0=> Sequential
;//         CL: CAS Latency
;//                   <0=> 1 clk   <1=> 2 clks  <2=> 3 clks
;//         TM: Test Mode
;//                   <0=> Mode Register Set
;//            WBL: Write Burst Length
;//                   <0=> 0
;//     
;//   
;//
;//    Refresh
;//          REFEN: SDRAM Refresh
;//                 <0=> Disable <1=> Enable
;//          TREFMD: SDRAM Refresh Mode
;//                 <0=> CBR/Auto Refresh
;//                 <1=> Self Refresh
;//      Trp: SDRAM RAS Pre-charge Time
;//                 <0=> 2 clks 
;//                 <1=> 3 clks 
;//                 <2=> 4 clks 
;//                 <3=> Rsrvd 
;//      Tsrc: SDRAM Semi Row cycle time
;//                 SDRAM Row cycle time: Trc=Tsrc+Trp
;//                 <0=> 4 clks  <1=> 5 clks  <2=> 6 clks  <3=> 7 clks
;//       Refresh Counter <0x0-0x07FF>
;//                  Refresh Period = (2^11 - Refresh Count + 1) / HCLK
;//   
BANKCON0_Val    EQU     0x00000700
BANKCON1_Val    EQU     0x00000700
BANKCON2_Val    EQU     0x00000700
BANKCON3_Val    EQU     0x00000700
BANKCON4_Val    EQU     0x00000700
BANKCON5_Val    EQU     0x00000700
BANKCON6_Val    EQU     0x00018008
BANKCON7_Val    EQU     0x00018008
BWSCON_Val      EQU     0x00000000
REFRESH_Val     EQU     0x00ac0000
BANKSIZE_Val    EQU     0x00000000
MRSRB6_Val      EQU     0x00000020
MRSRB7_Val      EQU     0x00000000

;//  End of MC



; I/O Ports definitions
PIO_BASE        EQU     0x56000000      ; PIO Base Address
PCONA_OFS       EQU     0x00            ; PCONA Offset
PCONB_OFS       EQU     0x10            ; PCONB Offset
PCONC_OFS       EQU     0x20            ; PCONC Offset
PCOND_OFS       EQU     0x30            ; PCOND Offset
PCONE_OFS       EQU     0x40            ; PCONE Offset
PCONF_OFS       EQU     0x50            ; PCONF Offset
PCONG_OFS       EQU     0x60            ; PCONG Offset
PCONH_OFS       EQU     0x70            ; PCONH Offset
PCONJ_OFS       EQU     0xD0            ; PCONJ Offset
PUPB_OFS        EQU     0x18            ; PUPB Offset
PUPC_OFS        EQU     0x28            ; PUPC Offset
PUPD_OFS        EQU     0x38            ; PUPD Offset
PUPE_OFS        EQU     0x48            ; PUPE Offset
PUPF_OFS        EQU     0x58            ; PUPF Offset
PUPG_OFS        EQU     0x68            ; PUPG Offset
PUPH_OFS        EQU     0x78            ; PUPH Offset
PUPJ_OFS        EQU     0xD8            ; PUPJ Offset


;//  I/O Configuration
PIO_SETUP       EQU     1

;//    Port A
;//           PA0  <0=> Output   <1=> ADDR0
;//           PA1  <0=> Output   <1=> ADDR16
;//           PA2  <0=> Output   <1=> ADDR17
;//           PA3  <0=> Output   <1=> ADDR18
;//           PA4  <0=> Output   <1=> ADDR19
;//           PA5  <0=> Output   <1=> ADDR20
;//           PA6  <0=> Output   <1=> ADDR21
;//           PA7  <0=> Output   <1=> ADDR22
;//           PA8  <0=> Output   <1=> ADDR23
;//           PA9  <0=> Output   <1=> ADDR24
;//           PA0  <0=> Output   <1=> ADDR25
;//           PA1  <0=> Output   <1=> ADDR26
;//           PA2  <0=> Output   <1=> nGCS[1]
;//           PA3  <0=> Output   <1=> nGCS[2]
;//           PA4  <0=> Output   <1=> nGCS[3]
;//           PA5  <0=> Output   <1=> nGCS[4]
;//           PA6  <0=> Output   <1=> nGCS[5]
;//           PA7  <0=> Output   <1=> CLE
;//           PA8  <0=> Output   <1=> ALE
;//           PA9  <0=> Output   <1=> nFWE
;//           PA0  <0=> Output   <1=> nFRE
;//           PA1  <0=> Output   <1=> nRSTOUT
;//           PA2  <0=> Output   <1=> nFCE
;//   
PIOA_SETUP      EQU     0
PCONA_Val       EQU     0x000003FF

;//    Port B
;//             PB0  <0=> Input   <1=> Output  <2=> TOUT0    <3=> Reserved 
;//             PB1  <0=> Input   <1=> Output  <2=> TOUT1    <3=> Reserved 
;//             PB2  <0=> Input   <1=> Output  <2=> TOUT2    <3=> Reserved 
;//             PB3  <0=> Input   <1=> Output  <2=> TOUT3    <3=> Reserved 
;//             PB4  <0=> Input   <1=> Output  <2=> TCLK[0]  <3=> Reserved 
;//           PB5  <0=> Input   <1=> Output  <2=> nXBACK   <3=> Reserved 
;//           PB6  <0=> Input   <1=> Output  <2=> nXBREQ   <3=> Reserved 
;//           PB7  <0=> Input   <1=> Output  <2=> nXDACK1  <3=> Reserved 
;//           PB8  <0=> Input   <1=> Output  <2=> nXDREQ1  <3=> Reserved 
;//           PB9  <0=> Input   <1=> Output  <2=> nXDACK0  <3=> Reserved 
;//           PB10 <0=> Input   <1=> Output  <2=> nXDREQ0  <3=> Reserved 
;//      Pull-up Resistors                                        
;//           PB0 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PB1 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PB2 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PB3 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PB4 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PB5 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PB6 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PB7 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PB8 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PB9 Pull-up        <0=> Enabled  <1=> Disabled   
;//          PB10 Pull-up       <0=> Enabled  <1=> Disabled   
;//                                                              
;//   
PIOB_SETUP      EQU     0
PCONB_Val       EQU     0x000007FF
PUPB_Val        EQU     0x00000000 

;//    Port C
;//               PC0  <0=> Input    <1=> Output  <2=> LEND          <3=> Reserved 
;//               PC1  <0=> Input    <1=> Output  <2=> VCLK          <3=> Reserved 
;//               PC2  <0=> Input    <1=> Output  <2=> VLINE         <3=> Reserved 
;//               PC3  <0=> Input    <1=> Output  <2=> VFRAME        <3=> Reserved 
;//               PC4  <0=> Input    <1=> Output  <2=> VM            <3=> Reserved 
;//             PC5  <0=> Input    <1=> Output  <2=> LCDVF2     <3=> Reserved 
;//             PC6  <0=> Input    <1=> Output  <2=> LCDVF1    <3=> Reserved 
;//             PC7  <0=> Input    <1=> Output  <2=> LCDVF0   <3=> Reserved 
;//             PC8  <0=> Input    <1=> Output  <2=> VD[0]         <3=> Reserved 
;//             PC9  <0=> Input    <1=> Output  <2=> VD[1]         <3=> Reserved 
;//             PC10 <0=> Input    <1=> Output  <2=> VD[2]         <3=> Reserved 
;//             PC11  <0=> Input   <1=> Output  <2=> VD[3]         <3=> Reserved 
;//             PC12  <0=> Input   <1=> Output  <2=> VD[4]         <3=> Reserved 
;//             PC13  <0=> Input   <1=> Output  <2=> VD[5]         <3=> Reserved 
;//             PC14  <0=> Input   <1=> Output  <2=> VD[6]         <3=> Reserved 
;//             PC15  <0=> Input   <1=> Output  <2=> VD[7]         <3=> Reserved 
;//      Pull-up Resistors                                        
;//            PC0 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PC1 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PC2 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PC3 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PC4 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PC5 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PC6 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PC7 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PC8 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PC9 Pull-up         <0=> Enabled  <1=> Disabled   
;//           PC10 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PC11 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PC12 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PC13 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PC14 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PC15 Pull-up        <0=> Enabled  <1=> Disabled  
;//                                                              
;//   
PIOC_SETUP      EQU     0
PCONC_Val       EQU     0xAAAAAAAA
PUPC_Val        EQU     0x00000000

;//    Port D
;//               PD0  <0=> Input    <1=> Output  <2=> VD[8]         <3=> Reserved 
;//               PD1  <0=> Input    <1=> Output  <2=> VD[9]         <3=> Reserved 
;//               PD2  <0=> Input    <1=> Output  <2=> VD[10]         <3=> Reserved 
;//               PD3  <0=> Input    <1=> Output  <2=> VD[11]         <3=> Reserved 
;//               PD4  <0=> Input    <1=> Output  <2=> VD[12]         <3=> Reserved 
;//             PD5  <0=> Input    <1=> Output  <2=> VD[13]         <3=> Reserved 
;//             PD6  <0=> Input    <1=> Output  <2=> VD[14]         <3=> Reserved 
;//             PD7  <0=> Input    <1=> Output  <2=> VD[15]         <3=> Reserved 
;//             PD8  <0=> Input    <1=> Output  <2=> VD[16]         <3=> Reserved  
;//             PD9  <0=> Input    <1=> Output  <2=> VD[17]         <3=> Reserved 
;//             PD10 <0=> Input    <1=> Output  <2=> VD[18]         <3=> Reserved 
;//             PD11  <0=> Input   <1=> Output  <2=> VD[19]         <3=> Reserved 
;//             PD12  <0=> Input   <1=> Output  <2=> VD[20]         <3=> Reserved 
;//             PD13  <0=> Input   <1=> Output  <2=> VD[21]         <3=> Reserved 
;//             PD14  <0=> Input   <1=> Output  <2=> VD[22]          <3=> nSS1
;//             PD15  <0=> Input   <1=> Output  <2=> VD[23]          <3=> nSS0 
;//      Pull-up Resistors                                        
;//            PD0 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PD1 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PD2 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PD3 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PD4 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PD5 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PD6 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PD7 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PD8 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PD9 Pull-up         <0=> Enabled  <1=> Disabled   
;//           PD10 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PD11 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PD12 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PD13 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PD14 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PD15 Pull-up        <0=> Enabled  <1=> Disabled  
;//              
;//   
PIOD_SETUP      EQU     0
PCOND_Val       EQU     0x00000000
PUPD_Val        EQU     0x00000000

;//    Port E
;//               PE0  <0=> Input    <1=> Output  <2=> I2SLRCK       <3=> Reserved 
;//               PE1  <0=> Input    <1=> Output  <2=> I2SSCLK       <3=> Reserved  
;//               PE2  <0=> Input    <1=> Output  <2=> CDCLK         <3=> Reserved 
;//               PE3  <0=> Input    <1=> Output  <2=> I2SDI         <3=> nSS0 
;//               PE4  <0=> Input    <1=> Output  <2=> I2SDO         <3=> I2SSDI 
;//             PE5  <0=> Input    <1=> Output  <2=> SDCLK         <3=> Reserved 
;//             PE6  <0=> Input    <1=> Output  <2=> SDCMD         <3=> Reserved 
;//             PE7  <0=> Input    <1=> Output  <2=> SDDAT0        <3=> Reserved 
;//             PE8  <0=> Input    <1=> Output  <2=> SDDAT1        <3=> Reserved
;//             PE9  <0=> Input    <1=> Output  <2=> SDDAT2        <3=> Reserved
;//             PE10 <0=> Input    <1=> Output  <2=> SDDAT3        <3=> Reserved
;//             PE11  <0=> Input   <1=> Output  <2=> SPIMISO0      <3=> Reserved 
;//             PE12  <0=> Input   <1=> Output  <2=> SPIMOSI0      <3=> Reserved 
;//             PE13  <0=> Input   <1=> Output  <2=> SPICLK0       <3=> Reserved 
;//             PE14  <0=> Input   <1=> Output  <2=> IICSCL        <3=> Reserved
;//             PE15  <0=> Input   <1=> Output  <2=> IICSDA        <3=> Reserved
;//      Pull-up Resistors                                                      
;//            PE0 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PE1 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PE2 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PE3 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PE4 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PE5 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PE6 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PE7 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PE8 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PE9 Pull-up         <0=> Enabled  <1=> Disabled   
;//           PE10 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PE11 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PE12 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PE13 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PE14 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PE15 Pull-up        <0=> Enabled  <1=> Disabled  
;//              
;//   
PIOE_SETUP      EQU     0
PCONE_Val       EQU     0x00000000
PUPE_Val        EQU     0x00000000

;//    Port F
;//             PF0  <0=> Input   <1=> Output  <2=> EINT[0]  <3=> Reserved 
;//             PF1  <0=> Input   <1=> Output  <2=> EINT[1]  <3=> Reserved 
;//             PF2  <0=> Input   <1=> Output  <2=> EINT[2]  <3=> Reserved 
;//             PF3  <0=> Input   <1=> Output  <2=> EINT[3]  <3=> Reserved 
;//             PF4  <0=> Input   <1=> Output  <2=> EINT[4]  <3=> Reserved 
;//           PF5  <0=> Input   <1=> Output  <2=> EINT[5]  <3=> Reserved 
;//           PF6  <0=> Input   <1=> Output  <2=> EINT[6]  <3=> Reserved 
;//           PF7  <0=> Input   <1=> Output  <2=> EINT[7]  <3=> Reserved 
;//      Pull-up Resistors                                        
;//           PF0 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PF1 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PF2 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PF3 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PF4 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PF5 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PF6 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PF7 Pull-up        <0=> Enabled  <1=> Disabled   
;//      
;//   
PIOF_SETUP      EQU     1
PCONF_Val       EQU     0x0000511A
PUPF_Val        EQU     0x00000000

;//    Port G
;//               PG0  <0=> Input    <1=> Output  <2=> EINT[8]   <3=> Reserved 
;//               PG1  <0=> Input    <1=> Output  <2=> EINT[9]   <3=> Reserved 
;//               PG2  <0=> Input    <1=> Output  <2=> EINT[10]   <3=> nSS0 
;//               PG3  <0=> Input    <1=> Output  <2=> EINT[11]   <3=> nSS1 
;//               PG4  <0=> Input    <1=> Output  <2=> EINT[12]   <3=> LCD_PWRDN 
;//             PG5  <0=> Input    <1=> Output  <2=> EINT[13]   <3=> SPIMISO1 
;//             PG6  <0=> Input    <1=> Output  <2=> EINT[14]   <3=> SPIMOSI1
;//             PG7  <0=> Input    <1=> Output  <2=> EINT[15]   <3=> SPICLK1 
;//             PG8  <0=> Input    <1=> Output  <2=> EINT[16]   <3=> Reserved 
;//             PG9  <0=> Input    <1=> Output  <2=> EINT[17]   <3=> Reserved 
;//             PG10 <0=> Input    <1=> Output  <2=> EINT[18]   <3=> Reserved 
;//             PG11  <0=> Input   <1=> Output  <2=> EINT[19]   <3=> TCLK1
;//             PG12  <0=> Input   <1=> Output  <2=> EINT[20]   <3=> XMON
;//             PG13  <0=> Input   <1=> Output  <2=> EINT[21]   <3=> nXPON 
;//             PG14  <0=> Input   <1=> Output  <2=> EINT[22]   <3=> YMON
;//             PG15  <0=> Input   <1=> Output  <2=> EINT[23]   <3=> nYPON
;//      Pull-up Resistors                                        
;//            PG0 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PG1 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PG2 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PG3 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PG4 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PG5 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PG6 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PG7 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PG8 Pull-up         <0=> Enabled  <1=> Disabled   
;//            PG9 Pull-up         <0=> Enabled  <1=> Disabled   
;//           PG10 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PG11 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PG12 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PG13 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PG14 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PG15 Pull-up        <0=> Enabled  <1=> Disabled  
;//                                                         
;//   
PIOG_SETUP      EQU     0
PCONG_Val       EQU     0x00000000
PUPG_Val        EQU     0x00000000

;//    Port H
;//             PH0  <0=> Input   <1=> Output  <2=> nCTS0    <3=> Reserved 
;//             PH1  <0=> Input   <1=> Output  <2=> nRTS0    <3=> Reserved 
;//             PH2  <0=> Input   <1=> Output  <2=> TXD[0]    <3=> Reserved 
;//             PH3  <0=> Input   <1=> Output  <2=> RXD[0]    <3=> Reserved 
;//             PH4  <0=> Input   <1=> Output  <2=> TXD[1]  <3=> Reserved 
;//           PH5  <0=> Input   <1=> Output  <2=> RXD[1]   <3=> Reserved 
;//           PH6  <0=> Input   <1=> Output  <2=> TXD[2]   <3=> nRTS1
;//           PH7  <0=> Input   <1=> Output  <2=> RXD[2]  <3=> nCTS1 
;//           PH8  <0=> Input   <1=> Output  <2=> UCLK    <3=> Reserved 
;//           PH9  <0=> Input   <1=> Output  <2=> CLKOUT0  <3=> Reserved 
;//           PH10 <0=> Input   <1=> Output  <2=> CLKOUT1  <3=> Reserved 
;//      Pull-up Resistors                                        
;//           PH0 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PH1 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PH2 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PH3 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PH4 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PH5 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PH6 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PH7 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PH8 Pull-up        <0=> Enabled  <1=> Disabled   
;//           PH9 Pull-up        <0=> Enabled  <1=> Disabled   
;//          PH10 Pull-up       <0=> Enabled  <1=> Disabled   
;//                                                              
;//   
PIOH_SETUP      EQU     0
PCONH_Val       EQU     0x000007FF
PUPH_Val        EQU     0x00000000 

;// 




                PRESERVE8


; Area Definition and Entry Point
;  Startup Code must be linked first at Address at which it expects to run.


                AREA    RESET, CODE, READONLY
                IMPORT INISDRAM
                ARM


; Exception Vectors
;  Mapped to Address 0.
;  Absolute addressing mode must be used.
;  Dummy Handlers are implemented as infinite loops which can be modified.


; --------------------------------------------------------------------------

Vectors         LDR     PC, Reset_Addr         
                LDR     PC, Undef_Addr
                LDR     PC, SWI_Addr
                LDR     PC, PAbt_Addr
                LDR     PC, DAbt_Addr
                NOP                            ; Reserved Vector 
                LDR     PC, IRQ_Addr
                LDR     PC, FIQ_Addr
; --------------------------------------------------------------------------



                IF      IntVT_SETUP <> 0

;Interrupt Vector Table Address                
HandleEINT0          EQU    IntVTAddress           
HandleEINT1          EQU    IntVTAddress +4
HandleEINT2          EQU    IntVTAddress +4*2
HandleEINT3          EQU    IntVTAddress +4*3
HandleEINT4_7      EQU    IntVTAddress +4*4
HandleEINT8_23    EQU    IntVTAddress +4*5
HandleReserved      EQU    IntVTAddress +4*6
HandleBATFLT      EQU    IntVTAddress +4*7
HandleTICK          EQU    IntVTAddress +4*8
HandleWDT          EQU    IntVTAddress +4*9
HandleTIMER0       EQU    IntVTAddress +4*10
HandleTIMER1       EQU    IntVTAddress +4*11
HandleTIMER2       EQU    IntVTAddress +4*12
HandleTIMER3       EQU    IntVTAddress +4*13
HandleTIMER4       EQU    IntVTAddress +4*14
HandleUART2        EQU    IntVTAddress +4*15
HandleLCD           EQU    IntVTAddress +4*16
HandleDMA0          EQU    IntVTAddress +4*17
HandleDMA1          EQU    IntVTAddress +4*18
HandleDMA2          EQU    IntVTAddress +4*19
HandleDMA3          EQU    IntVTAddress +4*20
HandleMMC          EQU    IntVTAddress +4*21
HandleSPI0          EQU    IntVTAddress +4*22
HandleUART1          EQU    IntVTAddress +4*23
;HandleReserved          EQU    IntVTAddress +4*24
HandleUSBD          EQU    IntVTAddress +4*25
HandleUSBH          EQU    IntVTAddress +4*26
HandleIIC          EQU    IntVTAddress +4*27
HandleUART0       EQU    IntVTAddress +4*28
HandleSPI1           EQU    IntVTAddress +4*39
HandleRTC           EQU    IntVTAddress +4*30
HandleADC           EQU    IntVTAddress +4*31

; --------------------------------------------------------------------------
; 分析寄存器过程
IRQ_Entry
                sub    sp,sp,#4       ;reserved for PC
                stmfd    sp!,{r8-r9}

                ldr    r9,=INTOFFSET  ;中断序号
                ldr    r9,[r9]
                ldr    r8,=HandleEINT0    ;中断扩展首地址
                add    r8,r8,r9,lsl #2    ;偏移量*4 + 基地址
                ldr    r8,[r8]            ;Eintx_Entry Add -> r8
                str    r8,[sp,#8]         ;r8 -> sp(-#4)
                ldmfd    sp!,{r8-r9,pc}  ;Eintx_Entry Add -> pc 

; --------------------------------------------------------------------------


                ENDIF

Reset_Addr      DCD     Reset_Handler
Undef_Addr      DCD     Undef_Handler
SWI_Addr        DCD     SWI_Handler
PAbt_Addr       DCD     PAbt_Handler
DAbt_Addr       DCD     DAbt_Handler
                DCD     0                      ; Reserved Address 
IRQ_Addr        DCD     IRQ_Handler
FIQ_Addr        DCD     FIQ_Handler

Undef_Handler   B       Undef_Handler
SWI_Handler     B       SWI_Handler
PAbt_Handler    B       PAbt_Handler
DAbt_Handler    B       DAbt_Handler

                IF      IntVT_SETUP <> 1
IRQ_Handler     B       IRQ_Handler
                ENDIF

                IF      IntVT_SETUP <> 0
IRQ_Handler     B       IRQ_Entry
                ENDIF

FIQ_Handler     B       FIQ_Handler



; Memory Controller Configuration
                IF      MC_SETUP <> 0
MC_CFG
                DCD     BWSCON_Val
                DCD     BANKCON0_Val
                DCD     BANKCON1_Val
                DCD     BANKCON2_Val
                DCD     BANKCON3_Val
                DCD     BANKCON4_Val
                DCD     BANKCON5_Val
                DCD     BANKCON6_Val
                DCD     BANKCON7_Val
                DCD     REFRESH_Val
                DCD     BANKSIZE_Val
                DCD     MRSRB6_Val
                DCD     MRSRB7_Val
                ENDIF


; Clock Management Configuration
                IF      CLK_SETUP <> 0
CLK_CFG
                DCD     LOCKTIME_Val     
                DCD     CLKDIVN_Val 
                DCD     MPLLCON_Val 
                DCD     UPLLCON_Val 
                DCD     CLKSLOW_Val 
                DCD     CLKCON_Val 
                ENDIF 


; Reset Handler

                EXPORT  Reset_Handler
Reset_Handler   

                IF      WT_SETUP <> 0
                LDR     R0, =WT_BASE
                LDR     R1, =WTCON_Val
                LDR     R2, =WTDAT_Val
                STR     R2, [R0, #WTCNT_OFS]
                STR     R2, [R0, #WTDAT_OFS]
                STR     R1, [R0, #WTCON_OFS]
                ENDIF


                IF      CLK_SETUP <> 0         
                LDR     R0, =CLK_BASE            
                ADR     R8, CLK_CFG
                LDMIA   R8, {R1-R6}            
                STR     R1, [R0, #LOCKTIME_OFS]
                STR     R2, [R0, #CLKDIVN_OFS]  
                STR     R3, [R0, #MPLLCON_OFS] 
                STR     R4, [R0, #UPLLCON_OFS]  
                STR     R5, [R0, #CLKSLOW_OFS]
                STR     R6, [R0, #CLKCON_OFS]
                ENDIF                          


                IF      MC_SETUP <> 0
                ADR     R14, MC_CFG
                LDMIA   R14, {R0-R12}
                LDR     R14, =MC_BASE
                STMIA   R14, {R0-R12}
                ENDIF                            


                IF      PIO_SETUP <> 0
                LDR     R14, =PIO_BASE

                IF      PIOA_SETUP <> 0
                LDR     R0, =PCONA_Val
                STR     R0, [R14, #PCONA_OFS]
                ENDIF

                IF      PIOB_SETUP <> 0
                LDR     R0, =PCONB_Val
                LDR     R1, =PUPB_Val
                STR     R0, [R14, #PCONB_OFS]
                STR     R1, [R14, #PUPB_OFS]
                ENDIF

                IF      PIOC_SETUP <> 0
                LDR     R0, =PCONC_Val
                LDR     R1, =PUPC_Val
                STR     R0, [R14, #PCONC_OFS]
                STR     R1, [R14, #PUPC_OFS]
                ENDIF

                IF      PIOD_SETUP <> 0
                LDR     R0, =PCOND_Val
                LDR     R1, =PUPD_Val
                STR     R0, [R14, #PCOND_OFS]
                STR     R1, [R14, #PUPD_OFS]
                ENDIF

                IF      PIOE_SETUP <> 0
                LDR     R0, =PCONE_Val
                LDR     R1, =PUPE_Val
                STR     R0, [R14, #PCONE_OFS]
                STR     R1, [R14, #PUPE_OFS]
                ENDIF

                IF      PIOF_SETUP <> 0
                LDR     R0, =PCONF_Val
                LDR     R1, =PUPF_Val
                STR     R0, [R14, #PCONF_OFS]
                STR     R1, [R14, #PUPF_OFS]
                ENDIF

                IF      PIOG_SETUP <> 0
                LDR     R0, =PCONG_Val
                LDR     R1, =PUPG_Val
                STR     R0, [R14, #PCONG_OFS]
                STR     R1, [R14, #PUPG_OFS]
                ENDIF

                IF      PIOH_SETUP <> 0
                LDR     R0, =PCONH_Val
                LDR     R1, =PUPH_Val
                STR     R0, [R14, #PCONH_OFS]
                STR     R1, [R14, #PUPH_OFS]
                ENDIF

                ENDIF

; ini sdram 

                BL INISDRAM


; Setup Stack for each mode

                LDR     R0, =Stack_Top

;  Enter Undefined Instruction Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #UND_Stack_Size

;  Enter Abort Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #ABT_Stack_Size

;  Enter FIQ Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #FIQ_Stack_Size

;  Enter IRQ Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #IRQ_Stack_Size

;  Enter Supervisor Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
                MOV     SP, R0
                SUB     R0, R0, #SVC_Stack_Size

;  Enter User Mode and set its Stack Pointer
                MSR     CPSR_c, #Mode_USR
                MOV     SP, R0
                SUB     SL, SP, #USR_Stack_Size


; Enter the C code
; !!!注意这里的__main是跳转到arm库函数中的init,然后再到用户写的main
                IMPORT  __main
                LDR     R0, =__main
                BX      R0


; User Initial Stack & Heap
                AREA    |.text|, CODE, READONLY

                IMPORT  __use_two_region_memory
                EXPORT  __user_initial_stackheap
__user_initial_stackheap

                LDR     R0, =  Heap_Mem
                LDR     R1, =(Stack_Mem + USR_Stack_Size)
                LDR     R2, = (Heap_Mem +      Heap_Size)
                LDR     R3, = Stack_Mem
                BX      LR


                END

Stack/Heap Definition

UND_Stack_Size      EQU     0x00000000
SVC_Stack_Size       EQU     0x00000008
ABT_Stack_Size       EQU     0x00000000
FIQ_Stack_Size       EQU     0x00000000
IRQ_Stack_Size       EQU     0x00000400
USR_Stack_Size      EQU     0x00010000

Stack_Size      EQU     (UND_Stack_Size + SVC_Stack_Size\
     + ABT_Stack_Size + FIQ_Stack_Size + IRQ_Stack_Size \
                 + USR_Stack_Size)

Heap_Size       EQU     0x00010000

Clock

CLK_BASE                EQU     0x4C000000          ; Base Address
LOCKTIME_OFS        EQU     0x00                ; LOCKTIME Offset
MPLLCON_OFS        EQU     0x04               ; MPLLCON Offset
UPLLCON_OFS         EQU     0X08                ; UPLLCON Offset
CLKCON_OFS          EQU     0x0C                ; CLKCON Offset
CLKSLOW_OFS         EQU     0x10                ; CLKSLOW Offset
CLKDIVN_OFS         EQU     0X14                ; CLDKIVN Offset

CLK_SETUP               EQU     1
MPLLCON_Val         EQU     0x0005C080
UPLLCON_Val         EQU     0x00028080
CLKCON_Val          EQU     0x0007FFF0
CLKSLOW_Val         EQU     0x00000004
LOCKTIME_Val        EQU     0x00FFFFFF
CLKDIVN_Val         EQU     0X00000000

Watchdog Timer

WT_BASE           EQU     0x53000000      ; WT Base Address
WTCON_OFS         EQU     0x00                ; WTCON Offset
WTDAT_OFS      EQU     0x04               ; WTDAT Offset
WTCNT_OFS         EQU     0x08                ; WTCNT Offset

WT_SETUP            EQU     1
WTCON_Val          EQU     0x00008021
WTDAT_Val           EQU     0x00008000

Memory Controller

MC_BASE             EQU     0x48000000      ; Base Address
MC_SETUP            EQU     1

BANKCON0_Val    EQU     0x00000700    ; Bank Control
BANKCON1_Val    EQU     0x00000700
BANKCON2_Val    EQU     0x00000700
BANKCON3_Val    EQU     0x00000700
BANKCON4_Val    EQU     0x00000700
BANKCON5_Val    EQU     0x00000700
BANKCON6_Val    EQU     0x00018008
BANKCON7_Val    EQU     0x00018008
BWSCON_Val        EQU     0x00000000    ; Bus Width & Wait Control 
REFRESH_Val        EQU     0x00ac0000    ; Refresh
BANKSIZE_Val      EQU     0x00000000    ; Banksize
MRSRB6_Val         EQU     0x00000020    ; SDRAM Mode Register Set 
MRSRB7_Val         EQU     0x00000000

IO PORT

PIO_BASE            EQU     0x56000000          ; PIO Base Address
PCONA_OFS           EQU     0x00         ; PCONA Offset
……
PCONJ_OFS           EQU     0xD0       ; PCONJ Offset
PUPB_OFS            EQU     0x18                ; PUPB Offset
……
PUPJ_OFS            EQU     0xD8                ; PUPJ Offset
PIO_SETUP           EQU     1

PIOA_SETUP      EQU     0
PCONA_Val           EQU     0x000003FF

PIOB_SETUP        EQU     0
PCONB_Val           EQU     0x000007FF
PUPB_Val            EQU     0x00000000 

Area, Stack/Heap

        AREA    STACK, NOINIT, READWRITE, ALIGN=3

Stack_Mem          SPACE   Stack_Size

Stack_Top           EQU     Stack_Mem + Stack_Size

        AREA        HEAP, NOINIT, READWRITE, ALIGN=3
Heap_Mem        SPACE   Heap_Size
  • Arm栈满减

RESET

Vectors

Vectors      

    LDR     PC, Reset_Addr         
              LDR     PC, Undef_Addr
               LDR     PC, SWI_Addr
               LDR     PC, PAbt_Addr
               LDR     PC, DAbt_Addr
               NOP                            ; Reserved Vector 
               LDR     PC, IRQ_Addr
               LDR     PC, FIQ_Addr

IRQ_Entry

IRQ_Entry
                    sub    sp,sp,#4 ;reserved for PC      
                stmfd    sp!,{r8-r9}

                ldr    r9,=INTOFFSET
                ldr    r9,[r9]
                ldr    r8,=HandleEINT0
                add    r8,r8,r9,lsl #2
                ldr    r8,[r8]
                str    r8,[sp,#8]

                ldmfd    sp!,{r8-r9,pc} 

LDMFD->LDMIA

STMFD->STMDB

只用r8,r9可以少备份

Reset_Handler

WT_Setup MC_Setup

    IF      WT_SETUP <> 0
    LDR     R0, =WT_BASE
        LDR     R1, =WTCON_Val
       LDR     R2, =WTDAT_Val
             STR     R2, [R0, #WTCNT_OFS]
              STR     R2, [R0, #WTDAT_OFS]
              STR     R1, [R0, #WTCON_OFS]  
    ENDIF

    IF      MC_SETUP <> 0        
    ADR         R14, MC_CFG
             LDMIA  R14, {R0-R12}
               LDR         R14, =MC_BASE
             STMIA  R14, {R0-R12}
    ENDIF 

CLK_Setup

    IF      CLK_SETUP <> 0        
    LDR     R0, =CLK_BASE            
    ADR     R8, CLK_CFG
       LDMIA   R8, {R1-R6}            
             STR     R1, [R0, #LOCKTIME_OFS]
              STR     R2, [R0, #CLKDIVN_OFS]  
             STR     R3, [R0, #MPLLCON_OFS] 
              STR     R4, [R0, #UPLLCON_OFS]  
             STR     R5, [R0, #CLKSLOW_OFS]
            STR     R6, [R0, #CLKCON_OFS]                
    ENDIF 

PIO_Setup

    IF      PIO_SETUP <> 0        
    LDR     R14, =PIO_BASE
    IF      PIOA_SETUP <> 0
    LDR     R0, =PCONA_Val
    STR     R0, [R14, #PCONA_OFS]
    ENDIF
     IF      PIOB_SETUP <> 0
            LDR     R0, =PCONB_Val
           LDR     R1, =PUPB_Val
          STR     R0, [R14, #PCONB_OFS]
           STR     R1, [R14, #PUPB_OFS]
             ENDIF
    ……
    ENDIF 

Stack_Setup

; Setup Stack for each mode
            LDR     R0, =Stack_Top
;  Enter Undefined Instruction Mode and set its Stack Pointer                
    MSR        CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
            MOV        SP, R0
              SUB         R0, R0, #UND_Stack_Size

Enter C Code

     IMPORT      __main
              LDR             R0, =__main
              BX              R0

Msr :通用寄存器写到状态寄存器

Mrs:反过来

__mian调用arm库函数

__main -> init -> main

__user_initial_stackheap

    EXPORT  __user_initial_stackheap
__user_initial_stackheap
                LDR     R0, =  Heap_Mem
                LDR     R1, =(Stack_Mem + USR_Stack_Size)
                LDR     R2, = (Heap_Mem +      Heap_Size)
                LDR     R3, = Stack_Mem
                BX          LR

__user_initial_stackheap(C)

 struct __initial_stackheap {  
      unsigned heap_base;                 
        unsigned stack_base;              
       unsigned heap_limit;                
       unsigned stack_limit; 
    };  

_value_in_regs struct __initial_stackheap 
    __user_initial_stackheap(void);
/*
        __user_initial_stackheap(unsigned SP)  
     {  
    struct __initial_stackheap config;  
    config.stack_base = SP;  
    config.stack_limit = SP-STACK_SIZE;  
    config.heap_base  = (unsigned)(USER_HEAP_ADDRESS)    
    config.heap_limit = ((unsigned)(USER _HEAP_ADDRESS))
        +USER_SRAM_SIZE;  
    return config;   
}  
*/

如果调试时无法进入main,可能的原因是什么?

软中断

如何通过软中断方式调用一个函数,该函数计算4个整数的和。

过程

SWI:软中断指令

XXX:中断信号

SWI_Handler

Get SWI Num

        AREA TopSwi, CODE, READONLY
        IMPORT        C_SWI_Handler 
        EXPORT         SWI_Handler
SWI_Handler
        STMFD      sp!,{r0-r12,lr}
        LDR        r0,[lr,#-4]             ;获取 SWI 指令
        BIC        r0,r0,#0xff000000     ; 参数1,NUM
        MOV            R1, SP                   ;参数2,传递堆栈指针
         BL C_SWI_Handler                 ;To Function
           LDMFD        sp!, {r0-r12,pc}^
           END

r0,[lr,#-4] 把swi指令读到r0

BIC清除高8位,得到中断号

这里用BL是因为lr已经备份到栈里面了

通常减8获取上一条指令,SWI特殊需要减4

Function (C)

void C_SWI_handler (int swi_num, int *reg )
{     switch (swi_num)
    {
        case 0 : 
            ……               /* SWI number 0 code */
                    break;
            case 1 :                 
            ……            /* SWI number 1 code */
                    break;
        ……
            default :   
            break/* Unknown SWI - report error */
    }
    return;
}

R0:中断号

R1:栈指针

Function (asm)

         AREA SecondSwi, CODE, READONLY 
         EXPORT     C_SWI_Handler
C_SWI_Handler 
        STMFD      sp!,{r0-r12,lr}
    CMP    r0,#MaxSWI          ; Range check?
        LDRLE  pc, [pc,r0,LSL #2] ;(PC-〉DCD SWInum0)
       B          SWIOutOfRange 
SWIJumpTable    DCD    SWInum0
        DCD    SWInum1
SWInum0   ; SWI number 0 code
            B    EndofSWI
SWInum1   ; SWI number 1 code
            B    EndofSW
EndofSW    SUB lr, lr, #4
        LDMFD        sp!, {r0-r12,pc}^
           END

SWI异常

Swi(0)关键字,产生软中断,0为中断信号

函数参数存到r0

函数

声明

__swi(0) int add_four(int, int, int, int);   〈- 寄存器传数据

调用

#include <stdio.h>
#include "swi.h"
unsigned *swi_vec = (unsigned *)0x08;
extern void SWI_Handler(void);
int main( void )
{     
    int res; 

    Install_Handler( (unsigned) SWI_Handler, swi_vec );

    res=add_four(3,4,5,6);

    return 0;
}
  • Install_Handler注册中断服务程序

函数体

void C_SWI_Handler( int swi_num, int *ptr )
{
    switch( swi_num )
    {    
    case    0:
        ptr[0] = ptr[0] +ptr[1]+ptr[2]+ptr[3];
            break;

    case   1:
        //(next page)
            break}
}
  • 之所以用栈指针是因为在软中断发生r0到r12和lr都存到了栈中,最低的地址就是r0;且返回结果就是栈底,即r0

返回结构体

struct four_results
{
    int a;
    int b;
    int c;
    int d;
};

__swi(1)  struct four_results many_operations(int, int, int, int);

函数体

case    1:

    int w, x, y, z;

    w = ptr[0];
            x = ptr[1];
            y = ptr[2];
            z = ptr[3];

           ptr[0] = w + x + y + z;
            ptr[1] = w - x - y - z;
            ptr[2] = w * x * y * z;
            ptr[3] =(w + x) * (y - z);

add_four

参数传递和结果返回

Build

  • armasm
  • input file
  • -o filename //output file name
  • -16|-32 //Thumb or ARM Instruction
  • -cpu cpu // Set the target CPU
  • -bigend|-littleend
  • -FPU name // target float-point unit
  • -apcs // ARM/Thumb Procedure Call Standard

编译时的-16|-32 与code里的命令不一样时,优先code

  • -output file

    • 指定了输出文件名,该文件可能是部分链接的目标文件,也可能是可执行映像文件。
  • -elf

    • 生成ELF格式的映像文件,armlink所支持的唯一的一种输出格式。
  • -reloc

    • 生成可重定址的映像。程序地址使用相对地址
  • -ro-base address

    • 包含有RO(Read-Only属性)输出段的加载地址和运行地址设置为address.
  • -ropi

    • 使得包含有RO输出段的加载域和运行域是位置无关的。
  • rw-base address

    • 设置包含RW(Read/Write属性)输出段的域的运行时地址.
  • -rwpi

    • 使得包含有RW和ZI(Zero Initialization,初始化为0)属性的输出段的加载和运行时域为位置无关的。
  • -split

    • 将包含RO和RW属性的输出段的加载域,分割成2个加载域。
  • -scatter file

    • 使用在file中包含的分组和定位信息来创建映像内存映射。
  • -debug

    • 使输出文件包含调试信息,调试信息包括,调试输入段,符号和字符串表。
  • -entry location

    • 指定映像文件中唯一的初始化入口点。

      • 数值地址,如-entry 0x0;

      • 符号所代表的地址处,如:-entry int_handler

      • 段内偏移量,如:-entry offset+object(section)

  • -first section-id

    • 将被选择的输入段放在运行域的开始
      • symbol 选择定义symbol的段。
      • object(section) 从目标文件中选择段放在映像文件的开始位置。例如: -first init.o(init)
      • object 选择只有一个输入段的目标文件。如: -first init.o
  • -last section-id

    • 将所选择的输入段放在运行域的最后
  • -map

    • 创建映像文件的信息图

Scatter

如何理解和编写.sct文件?

分散加载文件

使用者:liner用的

组成

示例一:

文件

映射

示例二:

文件

ROM_LOAD 0x0
{    
    ROM_EXEC 0x0
    {     vectors.o (Vect, +First)
                * (+RO) 
    }        
    RAM 0x28000000     FIXED
    {    * (+RW,+ZI) }    
    HEAP +0 UNINIT
    {    heap.o (+ZI) }
    STACKS 0x28080000 UNINIT
    {    stack.o (+ZI) }    
    UART0 0x16000000 UNINIT
     {    uart.o (+ZI) }
   }

* (+RO) 所有RO属性的值

UART0

映射

At a specific address without scatter-loading

main.c

#include <stdio.h>
extern int sqr(int n1);
int gSquared __attribute__((at(0x5000))); //Place at 0x5000
int main()
{
     gSquared=sqr(3);
     printf("Value squared is: %d\n", gSquared);
}

function.c

int sqr(int n1)
{
      return n1*n1;
}
  • 栈里全局变量,全局变量可以直接指定地址;局部变量需要用栈

In a named section with scatter-loading

main.c

#include <stdio.h>
extern int sqr(int n1);
int gsquaed__attribute__((section(“foo”)))//place in section “foo”
int main()
{
     gSquared=sqr(3);
     printf("Value squared is: %d\n", gSquared);
}

scatter.sct

LR1 0x0000 0x20000
{  ……
   ER3 0x10000 0x2000{
     function.o
     *(foo) ; Place gSquared in ER3
    }
     ……}

At a specific address with scatter-loading

main.c

#include <stdio.h>
extern int sqr(int n1);
const int gValue __attribute__((section(".ARM.__at_0x10000"))) = 3;
int main()
{
     gSquared=sqr(gValue);
     printf("Value squared is: %d\n", gSquared);
}

scatter.sct

LR1 0x0000 0x20000
{  ……
   ER2 +0
   {
    function.o
      *(.ARM.__at_0x10000) ; Place gValue at 0x10000
    }    
     ……}

Placement a function a specific address

function.c

int sqr(int n1) __attribute__((section(".ARM.__at_0x20000")));
int sqr(int n1)
{
      return n1*n1;
}

// place flash_key in a section called .ARM.__at_0x8000
long flash_key __attribute__((section(".ARM.__at_0x8000")));

scatter.sct

ER_FLASH 0x8000 0x2000
{
        *(+RW)
       *(.ARM.__at_0x8000) ; key
}

C代码全局变量的设置地址一般放在sct文件中设置

place a named section with scatter-loading

init.c

int foo() __attribute__((section("INIT")));
int foo() {  return 1; }
int bar() {  return 2; }

data.c

const long padding=123;
int z=5;

scatter.sct

LR1 0x0 0x10000
{ ; Root Region, containing init code
      ER1 0x0 0x2000
     {
             init.o (INIT, +FIRST) ; place init code at exactly 0x0
    *(+RO) ; rest of code and read-only data
     }
; RW & ZI data to be placed at 0x400000
     RAM_RW 0x400000 (0x1FF00-0x2000)
     {      *(+RW)  }
    RAM_ZI +0 { *(+ZI) 
      }
; execution region at 0x1FF00, maximum space available for table is 0xFF
    DATABLOCK 0x1FF00 0xFF
    { data.o(+RO-DATA) ; place RO data between 0x1FF00 and 0x1FFFF}    
}

Placement of unassigned sections with the .ANY

scatter.sct

lr1 0x8000 1024
{
    er1 +0 512
    {
        .ANY1(+RO) ; evenly distributed with er3
    }
    er2 +0 256
    {
        .ANY2(+RO) ; Highest priority, so filled first
    }
    er3 +0 256
    {
        .ANY1(+RO) ; evenly distributed with er1
    }
}

Placement of sections with overlays

scatter.sct

EMB_APP 0x8000
{
    …
    STATIC_RAM 0x0 ; contains most of the RW and ZI code/data
    {
        * (+RW,+ZI)
    }
    OVERLAY_A_RAM 0x1000 OVERLAY ; start address of overlay…
    {
        module1.o (+RW,+ZI)
    }
    OVERLAY_B_RAM 0x1000 OVERLAY
    {
        module2.o (+RW,+ZI)
    }
    … ; rest of scatter-loading description
}

Reserving an empty region

scatter.sct

EMB_APP 0x8000
{
    STACK 0x800000 EMPTY -0x10000 ; region     ;ends at 0x800000 because of the negative
    ; length. The start of the region is     ;calculated using the length.
    {
    ; Empty region for placing the stack
    }
    HEAP +0 EMPTY 0x10000 ; 
    ;region starts at the end of previous
    ; region. End of region calculated using
    ; positive length
    {
    ; Empty region for placing the heap
    }
    … ; rest of scatter-loading description
}

placing ARM C library code

scatter.sct

LR1 0x0
{
    ROM1 0
    {    * (InRoot$$Sections)
    * (+RO)
    }
    ROM2 0x1000
    {     *armlib/c_* (+RO) ; all ARM-supplied C library functions}
    ROM3 0x2000
    {    *armlib/h_* (+RO) ; just the ARM-supplied __ARM_*
    ; redistributable library functions
    }
    RAM1 0x3000
    {    *armlib* (+RO) ; all other ARM-supplied library code
    ; for example, floating-point libraries
     }
     RAM2 0x4000
     { * (+RW, +ZI)  }
}

placing ARM C++ library code

LR 0x0
{
    ER1 0x0
    {      *armlib*(+RO)  }
    ER2 +0
    {    *cpplib*(+RO)
    *(.init_array) ; Section .init_array must be placed explicitly,
    ; otherwise it is shared between two regions, and
    ; the linker is unable to decide where to place it.
    }
    ER3 +0
    {    *(+RO) }
    ER4 +0
    {    *(+RW,+ZI) }
}

Creation of regions on page boundaries

LR1 GetPageSize() + SizeOfHeaders()
{
    ER_RO +0
    {    *(+RO) }
    ER_RW AlignExpr(+0, GetPageSize())
    {    *(+RW)  }
    ER_ZI AlignExpr(+0, GetPageSize())
   {      *(+ZI) }
}

程序优化

问题

Hennessy and Patterson ,A New Golden Age for Computer Architecture: Domain-Specific Hardware/Software Co-Design, Enhanced Security, Open Instruction Sets, and Agile Chip Development, lecture, June 3, 2018

  • Speed on different environments

概述

程序优化

  • 指在不改变程序功能的情况下,根据处理器及系统的特性,通过修改原来程序的算法、结构,或利用软件开发工具对程序进行改进。使修改后的程序运行速度更快或占用空间更小或能耗最低。

优化原则

  • 等效原则,优化前后程序实现的功能一致。
  • 有效原则,优化后要比优化前运行速度快或占用存储空间小或能耗低,或三者兼有。
  • 经济原则,优化程序要付出较小的代价,取得较好的结果。

优化方法

  • 算法优化

    • 选择一种高效的算法

    • 对算法进行优化

    • 例:在数据搜索时,二分查找法要比顺序查找法快

  • 数据结构优化

    • 采用访问比较快的数据结构
    • 例:在一些无序的数据中多次进行插入、删除数据项操作,那么采用链表结构就会比较快
  • 编译优化

    • 编译器有不同级别的优化选项,选用一种合适的优化方式。
    • 针对体系结构进行了优化设计
  • 代码优化

    • 采用汇编语言或更精简的程序代码来代替原有的代码

空间优化

Code 空间

选择短长度指令集

  • ARM与THUMB

减少rom空间

通过状态寄存器T来判断哪种指令

运算替换

循环替换

数据类型

  • const表示只读
  • Volatile(挥发)直接访问物理单元或IO端口,不是cache,保证读取数据的一致性
  • Register尽量将变量存进寄存器,否则局部变量存进栈

内联函数

Data 空间

结构体

  • 设处理器数据总线宽度为BUS_W,结构体中成员数据类型的最大宽度为 DATA_W,该结构体的自然边界为N,则 :

    • N=min(BUS_W, DATA_W)
  • 结构体内:

    • 成员类型宽度小于自然边界,以该类型宽度对齐;
    • 成员类型宽度大于等于自然边界,以自然边界对齐。
  • 结构体之间:

    • 结构体的自然边界是该结构体中最大数据成员的自然边界。
    • 结构体占用的空间是其自然边界的整数倍。

数据压缩

数据类型

  • 每个像素为 8bit,大小为 M 行 X N列的图像,选择不同的数据类型所占用的空间。
unsinged char image[M][N]; 

short image[M][N];

int image[M][N];

空间复用

静态分配与动态分配

下面两个使用数据空间的方式哪个更经济?

S3c2410A 的堆heap size初始为0,动态分配时需要注意修改,左边栈右边堆

软中断进入svc异常模式,不同模式对应不同堆栈,软中断的栈svc

降低能耗

能耗与功率

功率状态

功率: Dynamic>Standby(待机)>Sleep(idle)>off

能耗管理

  • 处理器工作状态(功耗状态,多核模式);

  • 系统工作状态(外设);

  • 系统工作时间。

程序优化

  • 程序执行功耗
    • 减少指令数,减少执行时间(t)
    • 选用功耗低的指令(P)
  • 系统管理(P)
    • 控制处理器
      • 降低主频
      • 状态管理
      • 模式管理
    • 控制系统
      • 减少内存访问次数
      • 关断空闲外设

比较下列指令的功耗

寄存器善良影响功耗

Mul > Add

Add r0,r1,r2 > add r0,r0,r0

功耗状态(S3C2410)

这里power-off对应sleep,idle对应standby,只能外部事件中断唤醒,slow可通过程序进入normal

存储访问

提高速度

目标

  • 减少程序运行时实际执行的指令数;
  • 减少指令执行时间。

途径

  • 算法
  • 数据类型
  • 循环体
  • 计算
  • 存储访问

对于处理器,如果某类型数的存访地址是自然边界的整数倍,则访问效率最高。

变量

  • 对于char 数据类型,在i 和64比较之前,编译器增加了AND指令来保证i的范围在0-255之间;
  • 计算过程中的局部变量应尽量避免使用char 和short 数据类型,除非需要使用char 和short的数据溢出特性。
  • 存在主存中的数组和全局变量尽可能使用小尺寸的数据类型,以节省存储空间

参数

  • 宽参数传递,被调用者把参数缩小到正确的范围。

  • 宽返回,调用者把返回值缩小到正确的范围。

  • 窄参数传递,调用者把参数缩小到正确的范围。

  • 窄返回,被调用者把参数缩小到正确的范围。

  • GCC是宽参数传递和宽返回。

  • armcc 是窄参数传递与窄返回

  • 尽量用int 或 unsigned int 型参数。

  • 对于返回值尽量避免使用char和short类型。

  • 防止编译器做不必要的类型转换

类型转换

对于没有浮点运算指令的处理器,把浮点数转换成整数,用整数运算替代浮点运算,提高程序的执行速度。

有符号数与无符号数

对于非负数的计算,采用无符号数类型效率更高

  • ARM C中, 如果x 是负数,则x/2不是右移一位,而是加1后右移。例如:-3/2=-1。

  • 用右移计算除法时要考虑符号位

  • 对于非负数的计算,采用无符号数类型效率更高。

循环体

  • 减少使整个循环过程中执行的指令数;
  • 增加循环过程中连续顺序执行的指令数。

循环展开

  • 通过循环展开,可以减少循环开销,提高程序执行速度;
  • 展开循环可以减少程序的跳转,从而降低流水线中断的次数,提高程序执行的效率。

循环展开后,循环开销从4N个周期,减少到4N/4=N个周期,提高速度。

固定次数循环

  • 递加循环时增加一条CMP指令

  • 尽量采用递减循环

不定次数循环

减少重复运算

计算替换

软件流水线

计算替换

查找表

  • 对既消耗时间又消费资源的运算,应尽量使用查表的方式,并且将数据表置于程序存储区,但这需要预先计算出表的所有项。

  • 如果表很大,则直接生成所需的表比较困难,此时可以在启动时的初始化函数中先计算,然后在数据存储器中生成所需的表,以后在程序运行直接查表就可以了,减少了程序执行过程中重复计算的工作量。

存储管理

局部变量全局变量

边界不对齐

巧用Cache

一幅M行, N列的图像数据 AA,在内存中按行的顺序连续存放,下列哪段程序速度更快?

右边是连续访问,提高cache命中率

TCM

  • Atmel 公司的AT91SAM9G45内有64KB, 映射在内空间的起始地址为0x300000。设系统DDR2 RAM的容量为64MB, 映射到内存空间的起始地址为:0x70000000。

  • 用该处理器实现DCT变换运算,连接时采用下列哪个.sct文件所生成的程序执行速度更快

  • 将计算量较大的代码和常用数据放到片内RAM中

减少存储器访问

  • 访问片外RAM或者Flash中的数据时,当需要多次读取或修改时,应遵循“读――改――写”模式,即首先读取片外RAM或者Flash中的数据,将其保存在片内RAM中,针对本地变量进行计算,计算完毕后再写回到片外RAM或者Flash中;而不是每修改一次就进行回写操作。

去除相关性

哪种更有效?

  • 编译器不能确定写sum对in1和in2有无影响,从而in1和in2的读操作必须等到写sum操作完成之后才能进行,降低了流水效率 。
  • 使用了关键字const,消除了指令之间的相关,从而使编译器能够判别内存操作之间的相关性,找到更好的指令执行方案。
  • 消除数据之间的相关性,可以更有效地利用流水线提高程序的执行速度。

Register

volatile

分支跳转

  • Switch-case

  • if- else

  • 如果不同case值出现的频率不同,且可以预估。例如:ca1<ca2<ca3<ca4, 则下判断语句,哪一个平均速度快?

  • 如何验证空间优化后的效果?
  • 测试系统功耗的方法?
  • 怎样观察程序的执行时间?

risc-v程序开发

RISC-V概述

业界动态与发展过程

  • RISC-I

    • David A. Patterson and Carlo H. Sequin, RISC I: A reduced instruction set VLSI computer. In ISCA, 1981
  • RISC-II

    • Manolis G. H Katevenis, Robert W. Sherburne, Jr., David A. Patterson and Carlo H. Sequin, The RISC II micro-architecture. In Proceedings VLSI 83 Conference, 1983
  • RISC-III(SOAR)

    • David Ungar, Ricki Blau, Peter Foley, Dain Samples, and Patterson, Architecture of SOAR: Smalltalk on a RISC. In ISCA, 1984
  • RISC-IV(SPUR)

    • David D. Lee, Shing I. Kong, Mark D. Hill, Georges S. Taylor, David A. Hodges, Randy H. Katz, and David A. Patterson. A VLSI chip set for a multiprocessor workstation, IEEE JSSC, 1989
  • RISC-V

    • Krste Asanovic, 2010
    • V1.0, 2014
    • “V” , The fifth version, variation, vectors

RISC-V 特点

RISC-V 生态

RISC-V Software Development

开发过程

开发板

开发环境和工具

IDE

  • Nuclei Studio IDE, Nuclei

https://www.nucleisys.com/download.php

  • Freedom Studio, SiFive

https://www.sifive-china.com/site/Software_tools

  • RiscFree, Ashling

http://tools.emdoor.com/products/compiler/ashling/1693.html

  • Embedded Studio, segger

https://www.segger.com/products/development-tools/embedded-studio/editions/risc-v/

平台

  • Windows
  • macOS
  • Ubuntu

https://www.sifive-china.com/site/Software_tools

RISC-V ISA

架构特点

指令集模块

基本指令集 指令数 描述
RV32I 47 32位地址空间、整数指令,32个寄存器
RV32E 47 RV32I子集,仅支持16个
RV64I 59 64位地址空间、整数指令,一部分32位整数指令
RV128I 71 128位地址空间、整数指令,一部分64、32位整数指令
扩展指令集
M 8 整数乘法与除法
A 11 存储器原子(atom)操作
F 26 单精度浮点
D 26 双精度浮点
C 46 压缩指令,16bit
Q 28 四精度浮点运算扩展
L 10进制浮点扩展
Zifencei 1 指令获取界限

Registers

CSR寄存器是处理器内核内部的寄存器,使用专有的12位地址编码空间,对一个hart,可以配置4k的CSR寄存器。

addressing mode

Unprivileged /privileged Mode

Levels

等级 编码 名称 缩写
0 00 用户/应用模式(user/application Mode) U
1 01 监督者模式(supervisor Mode) S
2 10 管理者模式(Hypervisor Mode) H
3 11 机器模式(Machine Mode) M

Different Packages

模式数量 支持模式 目标应用
1 M 简单嵌入式系统
2 M,U 安全嵌入式系统
3 M,S,U 支持Unix,Linux,Windows等操作系统
4 M,H,S,U 支持虚拟机系统

RISC-V Processor

RISC-V core

  • Rocket,Berkeley
  • Boom(Out-of-Order Machine),Berkeley

  • SiFive

  • RISC-V SOC

  • Nuclei(芯来科技)

RV32I

RV32I指令

指令长度

指令类型

  • 数值计算、存储访问、跳转控制、CSR访问、其他指令

RV32I-运算

助记符

格式

算术运算

指令 示例 操作
加法 add t0,t1,t2 t0=t1+t2
减法 sub t0,t1,t2 t0=t1-t2
立即数加法 addi t0,t1,200 t0=t1+200
  • RV32I算术运算指令中,立即数的数值范围是imm[11:0],12位

逻辑指令

指令 示例 操作
and t0,t1,t2 t0=t1&t2 ;按位与
or t0,t1,t2 t0=t1|t2;按位或
异或 xor t0,t1,t2 t0=t1^t2;按位异或
立即数与 andi t0,t1,200 t0=t1&200;按位与
立即数或 ori t0,t1,200 t0=t1|200;按位或
立即数异或 xori t0,t1,200 t0=t1^200;按位异或
  • RV32I逻辑运算指令中立即数的数值范围是imm[11:0],12位

移位指令

指令 示例 操作
逻辑左移 sll t0,t1,t2 t0=t1<<t2;低位补0
逻辑右移 srl t0,t1,t2 t0=t1>>t2;高位补0
算术右移 sar t0,t1,t2 t0=t1>>t2;负数高位补1,正数高位补0
立即数逻辑左移 slli t0,t1,10 t0=t1<<10;低位补0
立即数逻辑右移 srli t0,t1,10 t0=t1>>10;高位补0
立即数算术右移 srai t0,t1,10 t0=t1>>10;负数高位补1,正数高位补0
  • RV32I移位操作指令中立即数的数值范围imm[4:0],5位

RV32I-储存访问

助记符

选项U,设计扩展时才考虑

指令格式

访存

指令 示例 操作
装载字(32位) lw t0, 50(t1) t0=memory[t1+50];将起始地址t1+50内存中4字节数写入t0。
装载半字(16位) lh t0, 50(t1) t0=memory[t1+50];将起始地址t1+50内存中2字节数写入t0低16位;正数,高16位补0;负数,高16位补1。
装载半字(无符号) lhu t0, 50(t1) t0=memory[t1+50];将起始地址t1+50内存中2字节数写入t0低16位;高16位补0。
装载字节(8位) lb t0, 50(t1) t0=memory[t1+50];将地址t1+50内存中1字节数写入t0;正数,高24位补0;负数,高24位补1。
装载字节(无符号) lbu t0, 50(t1) t0=memory[t1+50];将地址t1+50内存1字节数写入t0;高24位补0。
写字 sw t0, 50(t1) memory[t1+50]=t0;将t0中数据写入起始地址t1+50内存中。
写半字 sh t0, 50(t1) memory[t1+50]=t0;将t0中数据低16位写入起始地址t1+50内存中。
写字节 sb t0, 50(t1) memory[t1+50]=t0;将t0中数据低8位写入地址t1+50内存中。

50不是4的倍数,有问题,lw是4字节对齐,lh是2字节对齐,lb无对齐

  • RV32I load和store指令中偏移量数值范围offset[11:0], 12位;
  • “lui”指令将立即数装载到目标寄存器的高20位,目标寄存器的低12位置0;

  • “auipc”指令将立即数加到pc的高20位;

  • “lui”和“auipc”指令中立即数的范围是imm[19:0],20位。

RV32I-跳转

助记符

指令格式

分支跳转

指令 示例 操作
相等时分支 beq t0,t1,200 if(t0==t1) go to pc+200;pc相对跳转
不等时分支 bne t0,t1,200 if(t0!=t1) go to pc+200
小于时分支 blt t0,t1,200 if(t0<t1) go to pc+200
大于等于时分支 bge t0,t1,200 if(t0>=t1) go to pc+200
小于时分支(无符号) bltu t0,t1,200 if(t0<t1) go to pc+200;无符号数比较
大于小于时分支 (无符号) bgeu t0,t1,200 if(t0>=t1) go to pc+200;无符号数比较

无条件跳转

指令 示例 操作
带返回跳转 jal ra, 200 ra= pc+4,保存下条指令指针 pc=pc+200, pc 相对跳转
带返回跳转(寄存器) jalr ra, 200(t0) ra= pc+4,保存下条指令指针 pc=t0+200,寄存器相对跳转
  • 无条件跳转指令包含两个操作数,返回指针寄存器(ra)和跳转目标地址。
  • 对于指令jal,跳转目标地址是语句中的立即数表示与当前PC值之和。立即数的范围是imm[20:1],20位。
  • 对于指令jalr,跳转的目标地址为地址寄存器(t0)中的值与偏移量之和。偏移量的数值范围是offset[11:0],12位。
1.start:
2.    add x10, x10, x22 
3.    lw x9, 0(x10) 
4.    bne x9, x24, end;if(x9!=x24) PC=PC+8 
5.    addi x12, x12, 1 
6.    beq x0, x0, start ; pc=pc-16
7. end:

RV32I-CSR

助记符

格式

指令 示例 操作
先读后清除CSR csrrc t0, 0x123,t1 t0=[0x123]; [0x123]=t0 & (~t1); 把0x123中的值读入t0,然后用计算得到结果更新0x123中的值。
先读后置位CSR csrrs t0,0x123,t1 t0=[0x123];[0x123]=t0 | t1; 把0x123中的值读入t0,然后用计算得到结果更新0x123中的值。
先读后写CSR csrrw t0,0x123,t1 t0=[0x123];[0x123]=t1; 把0x123中的值读入t0,然后将t1中的值写入0x123中。
立即数先读后清除CSR csrrci t0, 0x123,20 t0=[0x123]; [0x123]=t0 & (~20); 把0x123中的值读入t0,然后用计算得到结果更新0x123中的值。
立即数先读后置位CSR csrrsi t0,0x123,20 t0=[0x123];[0x123]=t0 | 20; 把0x123中的值读入t0,然后用计算得到结果更新0x123中的值。
立即数先读后写CSR csrrwi t0,0x123,20 t0=[0x123];[0x123]=20; 把0x123中的值读入t0,然后将20中的值写入0x123中。

RV32I-其他

助记符

异常与中断

异常响应过程

  • 通过CSR寄存器管理处理器异常和中断事件的响应和处理过程。

特权模式转换

不设委托模式,任何模式中断都回到机器模式,自己模式不能处理自己的异常,必须委托高一级处理

操作 助记符 解释
Machine-mode trap return mret M模式异常返回
Supervisor-mode trap return sret S模式异常返回
Supervisor-mode fence.virtual memory address sfence S模式内存访问同步

异常和中断CSR

符号 名称 功能描述 CSR空间地址
mstatus 机器模式状态寄存器 (Machine Status Register) 寄存器中MIE和MPIE用于中断全局使能 0x300
mcause 机器模式异常原因寄存器 (Machine Cause Register) 进入异常的原因 0x342
mtvec 机器模式异常入口基地址寄存器 (Machine Trap-Vector Based-Address Register) 中断向量表基地址,进入异常的PC地址 0x305
mtval 机器模式异常值寄存器 (Machine Trap Value Register) 进入异常的信息 0x343
mepc 机器模式异常PC寄存器 (Machine Exception Program Counter) 保存异常返回地址 0x341
mie 机器模式中断使能寄存器 (Machine Interrupt Enable Register) 中断局部使能 0x304
mip 机器模式中断等待寄存器 (Machine Interrupt Pending Register) 中断等待状态 0x344

进入异常/中断

异常 vs 中断

GD32VF103

芯片

  • BumleBee内核,32位RISC-V通用微控制器;
  • 三条高速总线,即指令(I)总线、数据(D)总线和系统(System)总线;
  • 采用哈佛体系结构,内存映射和高达4GB的内存空间;
  • 32 KB的片上SRAM,地址0x2000 0000;
  • 128KB 主FLASH ,地址0x0800 0000;
  • 18KB 启动区,地址 0x000 000;
  • 支持字节、半字(16位)和字(32位)访问

BumbleBee

  • 支持RV32IMAC指令子集,机器模式(M)和用户模式(U)
  • 两个私有64位计数器单元,时钟计数器(Timer) 和指令计数器(Counter)。
  • 增强内核中断控制器(ECLIC)
  • 软件中断、计时器中断和外部中断、支持16个中断级别、支持向量中断处理机制。
  • 低功耗管理,支持WFI与WFE指令进入休眠模式,支持浅与深两级休眠模式。
  • 不支持虚拟地址管理单元(MMU),所有地址访问操作都使用物理地址。

中断管理

内核中断控制器(Enhanced Core Local Interrupt Controller,ECLIC)

ECLIC 特点

  • 可以支持4096个中断源(Interrupt Source),并为每个中断分配唯一编号(ID);
  • 控制每一个中断使能(IE)位,标志每一个中断的状态(IP)
  • 设置每一个中断的电平或边沿属性(Level or Edge-Triggered)
  • 设置中断级别和优先级(Level and Priority),
  • 可以选择向量或非向量(Vector or Non-Vector Mode)中断响应方式。

ECLIC 寄存器

CLIC寄存器映射在处理器内存地址空间,以访存方式进行读写。

偏移量 属性 名称 宽度
0x0000 可读可写 中断设置 cliccfg 8位
0x0004 只读,写忽略 中断信息 clicinfo 32位
0x000b 可读可写 阈值等级寄存器 mth 8位
0x1000+4*i 可读可写 中断标志寄存器 clicintip[i] 8位
0x1001+4*i 可读可写 中断使能寄存器 clicintie[i] 8位
0x1002+4*i 可读可写 中断属性寄存器 clicintattr[i] 8位
0x1003+4*i 可读可写 中断控制寄存器 clicintctl[i] 8位

Timer

  • TIMER采用自动增加计数模式,其计数寄存器(mtime)由两个32位寄存器{mtime_hi,mtime_lo}拼成,分别保存计数的高32位和低32位。
偏移量 属性 名称 功能描述
0x0 可读写 mtime_lo 计时器mtime的低32位值
0x4 可读写 mtime_hi 计时器mtime的高32位值
0x8 可读写 mtimecmp_lo 计时器比较值mtimecmp低32位
0xC 可读写 mtimecmp_hi 计时器比较值mtimecmp高32位
0xFF8 可读写 mstop 计时器的暂停控制
0xFFC 可读写 msip 产生软件中断

键盘中断

GD32VF103 -EVAL

EVAL Board

LED和KEY

GD-Linker & Jtag

中断处理

中断处理模式

  • 向量中断处理模式和非向量中断处理模式。

向量中断

向量表

非向量中断

GD32VF103中断机制

  • 三层管理机制
  • RISC-V异常处理;
  • BumbleBee内核中断控制模块ECLIC;
  • 外部中断和事件控制器(External interrupt and event,EXTI)。

EXTI寄存器组

名称 地址偏移 初始值 功能
中断使能 EXTI_INTEN 0x00 0x0000 0000 Bit 18:0, INT18:0使能 0:关闭;1:使能
事件使能 EXTI_EVEN 0x04 0x0000 0000 Bit 18:0, EV18:0使能 0:关闭;1:使能
上升沿触发 EXTI_RTEN 0x08 0x0000 0000 Bit 18:0, RT18:0使能 0:关闭;1:使能
下降沿触发 EXTI_FTEN 0x0c 0x0000 0000 Bit 18:0, FT18:0使能 0:关闭;1:使能
软中断事件 EXTI_SWIEV 0x10 0x0000 0000 Bit 18:0, SWI18:0使能 0:关闭;1:使能
中断状态 EXTI_PD 0x14 0x0000 0014 Bit 18:0, PD18:0使能 0:无触发;1:触发

GD32VF103中断向量

  • GD32VF103支持内核定时器中断,USART、I2C、ADC、DAC、FMC、SPI和RTC等所有芯片集成外设中断,以及EXIT[0:4]和EXTI[10:15]共11个外中断线。

系统设置

外中断请求路径

EXIT设置

初始化

void exti_init(exti_line_enum linex, exti_mode_enum mode, exti_trig_type_enum trig_type)
{switch (mode) {    //选则请求模式:中断/事件?
        case EXTI_INTERRUPT:
                EXTI_INTEN |= (uint32_t) linex;  break
        case EXTI_EVENT:
                EXTI_EVEN |= (uint32_t) linex;   break;.    }
   switch (trig_type) {//选择触边沿:上升/下降/上升和下降
        case EXTI_TRIG_RISING:
                EXTI_RTEN |= (uint32_t) linex;
                EXTI_FTEN &= ~(uint32_t) linex;  break;
        case EXTI_TRIG_FALLING:
                EXTI_RTEN &= ~(uint32_t) linex;
                EXTI_FTEN |= (uint32_t) linex;  break;
        case EXTI_TRIG_BOTH:
                EXTI_RTEN |= (uint32_t) linex;
                EXTI_FTEN |= (uint32_t) linex;  break;
    ……}
}

使能

void exti_interrupt_enable(exti_line_enum linex)
{
    EXTI_INTEN |= (uint32_t) linex;  //使能外中断: x=0...18
}
void exti_event_enable(exti_line_enum linex)
{
    EXTI_EVEN |= (uint32_t) linex;    //事件使能
}

读取状态

FlagStatus exti_flag_get(exti_line_enum linex)
{
    if (RESET != (EXTI_PD & (uint32_t) linex)) {
        return SET;
    } else {
        return RESET;
    }
}

ECLIC设置

初始化

void eclic_init(uint32_t num_irq) {
        typedef volatile uint32_t vuint32_t;
        *(volatile uint8_t*) (ECLIC_ADDR_BASE + ECLIC_CFG_OFFSET) = 0;//清除设置
        *(volatile uint8_t*) (ECLIC_ADDR_BASE + ECLIC_MTH_OFFSET) = 0; //清除设置
         vuint32_t * ptr;   //清除IP/IE/ATTR/CTRL位
         vuint32_t * base = (vuint32_t*) (ECLIC_ADDR_BASE + ECLIC_INT_IP_OFFSET);
         vuint32_t * upper = (vuint32_t*) (base + num_irq * 4);
         for (ptr = base; ptr < upper; ptr = ptr + 4) {
        *ptr = 0; }
         eclic_set_nlbits(ECLIC_GROUP_LEVEL2_PRIO2);
}

中断使能

void eclic_irq_enable(uint32_t source, uint8_t level, uint8_t priority) {
    eclic_enable_interrupt(source);   //使能
    eclic_set_int_level(source, level); //设定源级
    eclic_set_int_priority(source, priority); //设定优先级
}

设置中断级

uint8_t eclic_set_int_level(uint32_t source, uint8_t level) { //设定中断级
    uint8_t nlbits = eclic_get_nlbits(); // 获取特定位
    if (nlbits > ECLICINTCTLBITS) { nlbits = ECLICINTCTLBITS; }
     level = level << (8 - nlbits); //移位
     uint8_t current_intctrl = eclic_get_intctrl(source); //写入控制位
     current_intctrl = current_intctrl << nlbits; //移位
      current_intctrl = current_intctrl >> nlbits; //移位
     eclic_set_intctrl(source, (current_intctrl | level));
     return level;
}

设置优先级

uint8_t eclic_set_int_priority(uint32_t source, uint8_t priority) {
    uint8_t nlbits = eclic_get_nlbits();
    if (nlbits >= ECLICINTCTLBITS) {nlbits = ECLICINTCTLBITS; return 0;}
    priority = priority << (8 - ECLICINTCTLBITS);
    uint8_t current_intctrl = eclic_get_intctrl(source);
    current_intctrl = current_intctrl >> (8-nlbits);
    current_intctrl = current_intctrl << (8-nlbits);
    eclic_set_intctrl(source, (current_intctrl | priority));
     return priority;}

初始相关寄存器

void eclic_set_intctrl(uint32_t source, uint8_t intctrl) {  //中断源控制寄存器
    *(volatile uint8_t*) (ECLIC_ADDR_BASE + ECLIC_INT_CTRL_OFFSET + source * 4) =     
     intctrl;
}
void eclic_set_intattr(uint32_t source, uint8_t intattr) {  //中断源属性寄存器
    *(volatile uint8_t*) (ECLIC_ADDR_BASE + ECLIC_INT_ATTR_OFFSET + source * 4) =
            intattr;
}
void eclic_set_ecliccfg(uint8_t ecliccfg) { // 配置寄存器
     *(volatile uint8_t*) (ECLIC_ADDR_BASE + ECLIC_CFG_OFFSET) = ecliccfg;
}
void eclic_set_mth(uint8_t mth) { //阈值
     *(volatile uint8_t*) (ECLIC_ADDR_BASE + ECLIC_MTH_OFFSET) = mth;
}
void eclic_enable_interrupt(uint32_t source) { //使能中断源
       *(volatile uint8_t*) (ECLIC_ADDR_BASE + ECLIC_INT_IE_OFFSET + source * 4) = 1;
}

CORE设置

全局中断

void eclic_global_interrupt_enable()   //使能全局中断
{
     set_csr(mstatus, MSTATUS_MIE);
     return; 
}

设置中断处理方式(ECLIC/普通)

void eclic_mode_enable() {                                                       
    uint32_t mtvec_value = read_csr(mtvec);
    mtvec_value = mtvec_value & 0xFFFFFFC0;
    mtvec_value = mtvec_value | 0x00000003;   //ECLIC中断方式
    write_csr(mtvec, mtvec_value);
}

设置向量响应方式(向量/非向量)

void eclic_set_shv(uint32_t source, uint8_t shv) { //shv=1, 向量中断
    uint8_t attr = eclic_get_intattr(source);
    if (shv) {
        attr |= 0x01;
        eclic_set_intattr(source, attr);}
      }

键盘中断

原理图

中断向量表

.section .vectors, "ax"                  //向量段
   ……
  .weak  EXTI5_9_IRQHandler           //声明中断ID5至ID9处理程序
    ……
  .weak  EXTI10_15_IRQHandler         //声明中断ID10至ID15处理程序
    ……
.globl vector_base
vector_base:                     //中断向量表基地址
  j Reset_Handler                //跳转到复位处理程序
  .align    2                 //四字节对齐
   ……
  .word     EXTI5_9_IRQHandler      //中断ID5至ID9处理程序入口地址
   ……
  .word     EXTI10_15_IRQHandler    //中断ID10到15处理程序入口地址,Int59
   ……

选择向量EXTI13中断模式

#define ECLIC_ADDR_BASE      0xd2000000    //ECLIC 映射基地址
#define ECLIC_INT_ATTR_OFFSET  _AC(0x1002,UL) //中断属性寄存器偏移
eclic_set_shv(EXTI10_15, 1) ;  //设为向量中断型, EXTI10_15=59

开启中断

//使能全局中断
global_interrupt_enable()
//函数调用
eclic_enable_interrupt(eclic_set_shv(EXTI10_15, 1)) ;

设置向量表基址(ECLIC)

 //将向量表基地址写入寄存器t0
la t0, vector_base
//写入CSR寄存器, ECLIC 向量基址寄存器
csrw CSR_MTVT, t0

中断服务程序

__attribute__((interrupt)) void EXTI10_15_IRQHandler()
{
    if(RESET != exti_interrupt_flag_get(EXTI_13)){ //EXTI_13?
        exti_interrupt_flag_clear(EXTI_13); //清除中断状态  
        led_flash(2);                       //执行中断任务,LED闪烁 
        }  
      eclic_global_interrupt_enable();     //打开全局中断使能
      return;
}

主程序

int main(void)
{
    //初始化连接LED的GPIO引脚
    led_init();      //初始化LED
    //使能引脚时钟
    rcu_periph_clock_enable(RCU_GPIOC);    //开启GPIO时钟
    rcu_periph_clock_enable(RCU_AF);    //开启功能引脚时钟
    eclic_set_shv(EXTI10_15, 1) ;    //设为向量中断型, EXTI10_15=59
    global_interrupt_enable();     //使能全局中断
    eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1);//设优先级
    gpio_init(GPIOC, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ,                         GPIO_PIN_13); //设置PC13属性
    gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOC,                             GPIO_PIN_SOURCE_13);//将PC13设为中断
    eclic_irq_enable(EXTI10_15_IRQn, 1, 1);      //使能中断 EXTI10-15
    exti_init(EXTI_13, EXTI_INTERRUPT, EXTI_TRIG_FALLING); //设置触发方式
    exti_interrupt_flag_clear(EXTI_13);     //清除中断状态位
    while(1){__asm(“wfi”)}          //等待        
}

小结

Lab

  • 在评估版上调试键盘中断程序(可选触屏)

Embedded studio

  • 在三实验中选择一个,编译,调试。

  • 修改参考程序(界面或功能),重新编译,调试。

课后

  • 安装 OpenMP开发环境,并调试运行下列程序。
#include "stdafx.h"
#include "omp.h"
int _tmain(int argc, _TCHAR* argv[])
{
    printf("Hello from serial.\n");
    printf("Thread number = %d\n",
    omp_get_thread_num());  //serial
    #pragma omp parallel        //parallel
    {
    printf("Hello from parallel.Thread number = %d\n",
        omp_get_thread_num());
    }
    printf("Hello from serial again.\n");
    return;
}

Programming with VS

多核处理器-OpenMP

并行计算

处理器结构

存储结构

线程映射

  • HAL:硬件抽象层
  • 多核必须操作系统支持

ARM MPcore

Accelerator Coherency Port, ACP

Snoop Control Unit,SCU

MPIDR:内核 ID 寄存器

Advanced Custom Extension (ACE)

提供一个额外的PE(process element)识别机制属性:MPIDR_EL1是一个64位的寄存器MPIDR_EL1是一个64位的寄存器。

域值:

[63:40]:Reserved, RES0;

[39:32]:Affinity level3;

[31]:Reserved, RES1.

[30]:U 表示一个单处理器系统,与多处理器系统中的pe0不同。这个位的可能值是:0b0 :处理器是多处理器系统的一部分。0b1 :处理器是单处理器系统的一部分[29:25]:Reserved, RES0.

[24]:MT指示关联的最低级别是否由使用多线程类型方法实现的逻辑PEs组成。这个位的值可能是:0b0 当PEs的性能关联级别最低,或者使用MPIDR_EL1.MT的PEs被设置为1时,级别0的不同值或者级别1的相同值或更高级别,是相互独立的0b1 当PEs的性能关联级别最低,或者使用MPIDR_EL1.MT的PEs被设置为1时,级别0的不同值或者级别1的相同值或更高级别的相关性非常高。

[23:16]:Affinity level 2.

[15:8]:Affinity level 1. [11:8], 0x00-0x07: Core0-core7

[7:0]:Affinity level 0. 这个Affinity等级对于确定PE行为最为重要。更高级别的affinity 等级的重要性越低。分配给MPIDR的值的域的集合{Aff2, Aff1, Aff0}在整个系统中必须是不同的访问MPIDR_EL1:MRS , MPIDR_EL1

Interrupt

Power on flow

可以用CPU1作为启动核吗?

Bootloader

     /*CPU0 */
    mrs  x4, mpidr_el1    //读取 寄存器    
     tst    x4,#15               //testwether the current cpu is CPU0,                    //ie. mpidr_el1=15         
    b.eq 2f
    /*  Secondary CPUs */
1: 
               wfe
    ldr x4, mbox               
    cbz x4, 1b        //if x4==0(ie. The value in address of mbox                   
                        //is 0) dead loop,or jump to x4
    br x4                // branch to thegiven address 
2:
                ……                  //UART initialisation
  • mbox的地址在Makefile中写定,保存在dts 文件中。
  • dts即Device Tree Source 设备树源码, DeviceTree是一种描述硬件的数据结构.

el0:用户模式

el1:管理员模式(什么中断,复位以后的模式,最常用的)

el2:虚拟机

el3:更高级的安全模式

Secondary_holding_pen()

/* provides a"holding pen" to hold all secondary cores */      ENTRY(secondary_holding_pen)         
    bl      el2_setup                          // Drop to EL1         
    mrs  x0, mpidr_el1         
    and  x0, x0, #15                        // CPU number         
    adr   x1, 1b         
    ldp   x2, x3, [x1]         
    sub   x1, x1, x2         
    add  x3, x3, x1
pen:     
    ldr    x4, [x3]         
    cmp x4,x0         
    b.eq secondary_startup         
    wfe         
    b       pen
    ENDPROC(secondary_holding_pen)
  • ldp x1 , x0, [x1] ; 将[x1]中的值取出来, 放入x1 和 x0.

Fetching Instructions

软件架构

并行方法

OpenMP

  • 直接控制共享内存式并行编程的应用程序接口(API)

  • 由三个主要的API组成:

    • 编译指令
    • 运行库 Runtime Library Routines
    • 环境变量 Environment Variables
  • 特点

    • 可移植性Portable:

    • 适合C/C++和Fortran的API

    • 已在多种主要系统实现( Unix/Linux platforms and Windows NT )

    • 由主要的硬件及软件厂家共同制定和支持

    • 主流 SMP 并行程序开发库

程序结构

Fork-Joint Mode

编程

  • Implementation
    • #pragma
    • Run time library
  • Using Environment varies to control program execution threads.

For example: OMP_NUM_THREADS

例程

#include "stdafx.h"
#include "omp.h"
int _tmain(int argc, _TCHAR* argv[])
{
    printf("Hello from serial.\n");
    printf("Thread number = %d\n",
    omp_get_thread_num());  //serial
    #pragma omp parallel        //parallel
    {
    printf("Hello from parallel.Thread number = %d\n",
        omp_get_thread_num());
    }
    printf("Hello from serial again.\n");
    return;
}

指令

  • #pragma

    • parallel:表示这段代码将被并行执行。

    • for:表示将循环计算任务分配到多个线程中并行执行。

    • sections:用于实现多个结构块语句的任务分担。

    • parallel sections:类似于parallel for;

    • single:表示一段只被单个线程执行的代码;

    • critical:保证每次只有一个OpenMP线程进入;

    • flush:保证各个OpenMP线程的数据映像的一致性;

    • barrier:用于并行域内代码的线程同步,线程执行到barrier时要停下等待,直到所有线程都执行到barrier时才继续往下执行;atomic:用于指定一个数据操作需要原子性地完成;

    • master:用于指定一段代码由主线程执行;

    • Thread private:用于指定一个或多个变量是线程专用。

后面会解释线程专有和私有的区别。

循环

The results of two program ?

Are these two code segments equivalent?

Loop restrictions

  • The loop variable must be type of signed integer (v2.5) .

  • The comparison operation must be in the form loop_variable

    • <, <=, >, or >=loop_invariant_integer.
  • The increment portion of the for loop must be integer addition or integer subtraction and by a loop_invariant_value.

  • The loop must be a single entry and single exit, meaning no jump from the inside of loop to outside or outside to the inside are permitted with the exception of the exit statement. No use break, return and goto.

变量

Any problem for parallelizing next loop?

bool calsum(unsigned char *image, unsigned int *sum)
{    int i;
    unsigned in hist[256];
    for(i=255;i>=0;i--) 
    {    hist[i]=0; 
        *(sum+i)=0;  
    }
    i=IMAGE_WIDTH*IMAGE_HEIGHT;    
     //需要用for
    do{  i--;
        hist[*image++]++;   
    }while(i>0);
     image=image-IMAGE_WIDTH*IMAGE_HEIGHT; 
    *sum=hist[0];
     //for循环不能将i计算
    for( i=1;i<256;i++)
    {   *(sum+i)=hist[i]+*(sum+i-1); } 
}

Challenge?

Loop-carrier dependency

下列程序哪个执行时间短?

共享数据打架,并行反而更慢

  • In parallel region, default behavior is that all variables are shared except loop index
    • All threads read and write the same memory location for each variable
    • This is ok if threads are accessing different elements of an array
    • Problem if threads write same scalar or array element
    • Loop index is private, so each thread has its own copy

看看下列程序的执行时间

ifirst = 10;
int k[600];
for(j = 0; j <= 60; j++)
{
#pragma omp parallel for private(i2)
    for(i=0;i<6000000;i++){ 
     i2 = i-(i/600)*600;
     k[i2] = ifirst + i;
    }
}

private

How about variables in function?

for(i = 0; i < 100; i++)
{
    mycalc(i,x,y);
}

how about x, y ?

试试这个程序

float sum = 0.0;
float a[10000],b[10000]; 
#pragma omp parallel for private(sum)
   for(int i=0; i<10000; i++) {
     sum += a[i] * b[i];
   }
  return sum;

编译错误?

试试(shared)

float sum = 0.0;
float a[10000],b[10000];
For(int j=0;j<10;j++)
{ 
    sum=0.0;
#pragma omp parallel for shared(sum)
   for(int i=0; i<10000; i++) {
     sum += a[i] * b[i];
   }
    printf(“j=%d, sum=%f\n”,j,sum);
}
  return sum;

每次结果相同?为什么?

float sum = 0.0;
float a[10000],b[10000];
for(int j=0;j<10;j++)
{ 
    sum=0.0;
#pragma omp parallel for
   for(int i=0; i<10000; i++) {
     sum += a[i] * b[i];
   }
    printf(“j=%d, sum=%f\n”,j,sum);
}
  return sum;

Critical

Solution 1

float sum = 0.0;
flaot a[10000],b[10000];
for(int j=0;j<10;j++)
{
    sum=0.0;
    #pragma omp parallel for shared(sum)
       for(int i=0; i<N; i++) {
    #pragma omp critical    //让每次输出结果相同
         sum += a[i] * b[i];
       }
    printf(“j=%d, sum=%f\n”,j,sum);
}
return sum;

Critical Construct

加锁:RES_lock

void dequeue(NODE * node) 
{
   #pragma omp critical(x)
   {
    node=node->next;
   }
}

void do_work(NODE *node)
{
 #pragma omp critical(x)
   {
    node->next->data=fn1(node->data);
    node=dequeue(node);
   }
}

What’s wrong? Deadlock!

Atomic

#pragma omp parallel for shared(x, y, index, n)
   for (i = 0; i < n; i++) {
      #pragma omp atomic
        x[index[i]] += work1(i);
      y[i] += work2(i);
   } 
  • Special case of a critical section
  • Applies only to simple update of memory location

reduction

Solution 2

float dot_prod(float* a, float* b, int N) 
{
  float sum = 0.0;
#pragma omp parallel for reduction(+:sum)
   for(int i=0; i<N; i++) {
    sum += a[i] * b[i];
   }
  return sum;
}
  • Each thread performs its own reduction (sum, in this case);

  • Results from all threads are automatically reduced (summed) at the end of the loop

线程

Any program paralleled?

Fibonacci Sequence 0, 1, 1, 2, 3, 5, 8, 13……

a[1] = 0;
a[2] = 1;
for(i = 3; i <= 100; i++){
    a[i] = a[i-1] + a[i-2];
}

程序运行结果?

doube a[100];
a[0] = 0.0;
a[1] = 1.0;
for (i = 2; i < 100; i++)
    a[i] = 0;
#pragma omp parallel for
for (i = 2; i < 100; i++)
{
    a[i] = a[i - 1] + a[i - 2];
    printf("a[%d]= %f\n",i,a[i]);
}

为什么?

线程

  • Assigning Iterations
    • Which Schedule to Use

  • Assigning Iterations
    • schedule(static [,chunk])
      • Blocks of iterations of size “chunk” to threads
      • Round robin distribution
#pragma omp parallel for schedule (static, 8)
for (i = 0; i <20; i++){
    a[i] =i*i;
    printf("static_a[%d]= %d, num=%d\n", i,a[i],     omp_get_thread_num());
}
  • Iterations are divided into chunks of 8

  • If start = 0, then first chunk is

    • thread0{0, 1,2,3,4,5,6,7}
    • thread1{8, 9,2,10,11,12,13,14,15}
    • thread2{16,16,18,19}
  • schedule(dynamic[,chunk])

    • Threads grab “chunk” iterations

    • When done with iterations, thread requests next set

#pragma omp parallel for schedule (dynamic, 8)
for (i = 0; i <20; i++){
    a[i] =i*i;
    printf("static_a[%d]= %d, num=%d\n", i,a[i],     omp_get_thread_num());
}
  • Iterations are divided into chunks of 8

  • If start = 0, then first chunk is

    • thread0{0, 1,2,3,4,5,6,7}
    • thread2{8, 9,2,10,11,12,13,14,15}
    • thread3{16,16,18,19}
  • schedule(guided[,chunk])

    • Dynamic schedule starting with large block
    • Size of the blocks shrink; no smaller than “chunk”
#pragma omp parallel for schedule (guided)
for (i = 0; i <20; i++){
    a[i] =i*i;
    printf("static_a[%d]= %d, num=%d\n", i,a[i],     omp_get_thread_num());
}
  • thread0{0, 1,2,3,4}
  • thread1{9, 10,11}
  • thread2{5,6,7,8} {14,15,16,17,18,19}
  • thread3{12,13}

Section

Barrier

#pragma omp parallel private(myid,istart,iend)
myrange(myid,istart,iend);
for(i=istart; i<=iend; i++){
   a[i] = a[i] – b[i];
}
#pragma omp barrier
myval[myid] = a[istart] + a[0]
  • Barrier synchronizes threads

  • Here barrier assures that a[istart] or a[0] is available before computing myval

Nowait

Master

#pragma omp parallel private(myid, istart, iend)
 myrange(myid, istart, iend);
 for(i=istart; i<=iend; i++){
    a[i] = a[i] – b[i];
 }
 #pragma omp barrier
 #pragma omp master
 fwrite(fid, sizeof(float), iend-istart+1, a);
 #pragma omp end master
 do_work(istart, iend);
 #pragma omp end parallel 
  • If you want part of code to be executed only on master thread, use master directive

  • “non-master” threads will skip over master region and continue

Single

#pragma omp parallel
{
   DoManyThings();
#pragma omp single
   {
     ExchangeBoundaries();
   }  // threads wait here for single
   DoManyMoreThings();
}
  • Denotes block of code to be executed by only one thread
  • First thread to arrive is chosen
  • Implicit barrier at end

Unequal work loads lead to idle threads and wasted time.

Load Balancing

Lost time waiting for locks

示例

Numerical Integration

static long num_steps=100000; 
double step, pi;

void main()
{  int i;    
   double x, sum = 0.0;

   step = 1.0/(double) num_steps;
 #pragma omp parallel for private(x) reduction(+:sum)
   for (i=0; i< num_steps; i++){
      x = (i+0.5)*step;
      sum = sum + 4.0/(1.0 + x*x);
   }
   pi = step * sum;
   printf(“Pi = %f\n”,pi);
}

小结

请下载并安装CUDA

Heterogeneous Multi-core

reference

https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html

http://blog.csdn.net/kkk584520/article/details/9413973

AMP

Kirin9000

A15 bionic

OMAP3530 (Ti)

AMP 存储架构

AMP编程

Multi-thread for AMP?

  • The method of Programming for SMP?

    Multi- thread (parallelizing).

  • Is “Multi-thread”method available for AMP programming? Why?

    No!

    Different architecture processors with different Instructions .

  • Programming for different processors?

​ Independent

​ Integrated

  • How to download binary code to different processors?

​ Master and Coprocessor independently by tools

​ From Master to coprocessor

  • How to start up the different processors?

​ Master – Power(reset)

​ Coprocessor – Set by master

Independent Programming

Integrated Programming

Programming OMAP3530

  • xDM算法接口;IPC:Inter-Process Communication

HSA

HSA(Heterogeneous System Architecture)

  • APU:Accelerated Processing Unit ; NUMA: Non Uniform Memory Access Architecture; UMA Uniform Memory Architecture

Programming

GPU

Reference

GPU

  • GPUs are massively multithreaded many core chips
  • Hundreds of scalar processors
  • Tens of thousands of concurrent threads
  • n TFLOP peak performance
  • Fine-grained data-parallel computation
  • Suppliers
  • Intel: Iris™ Pro Graphics[, i740
  • NVIDIA: Geforce, Telsa, Tegra
  • AMD(ATI):RX, FirePro
  • Matrox
  • 3dfx
  • SiS
  • VIA

GPU Applications

CPU vs GPU

A GPU Device(G80)

  • Vtx =Vertex , 线的顶点;Geom=Geometry

Texture Processor Cluster

GPU in System

GPU in System

Connected to CPU chipset by PCIe

PCIe x16 Gen 2: 8 GB/s in one direction, 16 GB/s bi-directionally

http://arstechnica.com/old/content/2004/07/pcie.ars

CUDA

What is CUDA

  • CUDA is the acronym for Compute Unified Device Architecture.
    • A parallel computing architecture developed by NVIDIA.
    • The computing engine in GPU.
    • CUDA can be accessible to software developers through industry standard programming languages.
  • CUDA gives developers access to the instruction set and memory of the parallel computation elements in GPUs.

Processing Flow

CUDA Environment

Installing CUDA.

  • Windows – VSXXXX
  • Linux – gcc

https://developer.nvidia.com/cuda-downloads

新建例程,运行例程,结果?

源程序的扩展名?

哪一段代码是在GPU上运行的?

如何调试GPU上运行的程序?

Cuda_test

Testing-CPU

// hello_world.c: 
#include <stdio.h> 

void hello_world_kernel()
{ 
    printf(“Hello World\n”); 
}

int main() 
{
    hello_world_kernel(); 
}

Testing-GPU

//hello_world.cu: 
#include <stdio.h> 

__global__ void hello_world_kernel()
{ 
    printf(“Hello World\n”); 
}

int main() 
{ 
     hello_world_kernel<<<1,1>>>(); 
}

What’s Wrong?

Why?

CUDA 编程

CUDA Kernel program

CUDA Programming Mode

  • CUDA C extends C by allowing the programmer to define C functions, called kernels.

  • When called, kernels are executed N times in parallel by N different CUDA threads.

  • A kernel is defined using the __global__ .

  • The number of CUDA threads that execute the kernels is specified using <<<…>>> (Threads)

    • cuda_kernel<<<nBlk, nTid>>>(…)
  • Each thread that executes the kernel is given a unique thread ID.

  • A thread block is a batch of threads

  • All threads run the same code

  • Each thread has an ID that it uses to compute memory addresses and make control decision

  • Threads can cooperate with each other by

  • sharing data and synchronizing their execution

  • Threads from different blocks cannot cooperate

  • There is a limit to the number of threads per block

Thread ID

BlockID= blockIdx.y*gridDim.x+blockIdx.x

ThreadID=BlockID*blockDim.x*blockDim.y*blockDim.z
    +threadIdx.z*blockdIm.x*blockDim.y*
    +threadIdx.y*blockDim.x
    +threadIdx.x

How about 2D or 1D blocks?

dim3 grid(3,2) ;dim3 block(12,1,1);
gridDim.x=3; gridDim.y=2; 
blockDim.x=12; blockDim.y=1; blockDim.z=1;
blockIdx.x=1; blockIdx.y=1;
threadIdx.x=4, threadIdx.y=0; threadIdx.z=0;
threadID=(1*3+1)*12+4=52

Data Partition-example

Example (1D-Block)

// Kernel definition
__global__ void VecAdd(float* A, float* B, float* C)
{
    int i = threadIdx.x;
    C[i] = A[i] + B[i];
}

int main()
{
    ...
    // Kernel invocation with N threads
    VecAdd<<<1, N>>>(A, B, C);
    ...
}

Example (2D-Block)

// Kernel definition
__global__ void MatAdd(float A[N][N], float B[N][N],
                       float C[N][N])
{
    int i = threadIdx.x;
    int j = threadIdx.y;
    C[i][j] = A[i][j] + B[i][j];
}

int main()
{
    ...
    // Kernel invocation with one block of N * N * 1 threads
    int numBlocks = 1;
    dim3 threadsPerBlock(N, N);
    MatAdd<<<numBlocks, threadsPerBlock>>>(A, B, C);
    ...
}
  • Example (Multi-blocks)
// Kernel definition
__global__ void MatAdd(float A[N][N], float B[N][N],
float C[N][N])
{
    int i = blockIdx.x * blockDim.x + threadIdx.x;
    int j = blockIdx.y * blockDim.y + threadIdx.y;
    if (i < N && j < N)
        C[i][j] = A[i][j] + B[i][j];
}

int main()
{
    ...
    // Kernel invocation
    dim3 threadsPerBlock(16, 16);
    dim3 numBlocks(N / threadsPerBlock.x, N / threadsPerBlock.y);
    MatAdd<<<numBlocks, threadsPerBlock>>>(A, B, C);
    ...
}

Programming

Heterogeneous Programming

GPU Input and Output

GPU Memory

Global Memory

  • Accessible by all threads as well as host (CPU)
  • Data lifetime = from allocation to deallocation

Shared Memory

  • Accessible by all threads in the block

  • Data lifetime = the longest thread lifetime

Register & Local memory

  • Automatic variables (scalar/array) inside kernels
  • Spills to local memory
  • Data lifetime = thread lifetime

Memory Allocation

Memory Copies

示例

Arrays Plus

#include "cuda_runtime.h"

#include <stdio.h>

cudaError_t addWithCuda(int *c, const int *a, 
            const int *b, unsigned int size);

__global__ void addKernel(int *c, const int *a, 
                const int *b)
{
    int i = threadIdx.x;
    c[i] = a[i] + b[i];
}

int main()
{
    const int arraySize = 5;
    const int a[arraySize] = { 1, 2, 3, 4, 5 };
    const int b[arraySize] = { 10, 20, 30, 40, 50 };
    int c[arraySize] = { 0 };

    // Add vectors in parallel.
    cudaError_t cudaStatus = addWithCuda(c, a, b, arraySize);
    if (cudaStatus != cudaSuccess) { return 1; }

    printf("{1,2,3,4,5} + {10,20,30,40,50} = 
             {%d,%d,%d,%d,%d}\n", c[0], c[1], c[2], c[3], c[4]);

   cudaStatus = cudaDeviceReset();
    if (cudaStatus != cudaSuccess) {return 1; }

    return 0;
}


cudaError_t addWithCuda(int *c, const int *a, const int *b, 
                unsigned int size)
{
    int *dev_a = 0; int *dev_b = 0; int *dev_c = 0;
    cudaError_t cudaStatus;
    // Allocate GPU buffers for three vectors (two input, one output)    .
    cudaStatus = cudaMalloc((void**)&dev_c, size * sizeof(int));
    if (cudaStatus != cudaSuccess) { goto Error;} 

    cudaStatus = cudaMalloc((void**)&dev_a, size * sizeof(int));
    if (cudaStatus != cudaSuccess) {goto Error;}

    cudaStatus = cudaMalloc((void**)&dev_b, size * sizeof(int));
    if (cudaStatus != cudaSuccess) {goto Error;}

    // Copy input vectors from host memory to GPU buffers.
    cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(int), 
                cudaMemcpyHostToDevice);
    if (cudaStatus != cudaSuccess) {goto Error;}

     cudaStatus = cudaMemcpy(dev_b, b, size * sizeof(int), 
                cudaMemcpyHostToDevice);
    if (cudaStatus != cudaSuccess) {goto Error;}
    // Launch a kernel on the GPU with one thread for each element.
    addKernel<<<1, size>>>(dev_c, dev_a, dev_b);

    // Check for any errors launching the kernel
    cudaStatus = cudaGetLastError();
    if (cudaStatus != cudaSuccess) {goto Error;}

    // cudaDeviceSynchronize waits for the kernel to finish, and returns
    // any errors encountered during the launch.
    cudaStatus = cudaDeviceSynchronize();
    if (cudaStatus != cudaSuccess) {goto Error;}

    // Copy output vector from GPU buffer to host memory.
    cudaStatus = cudaMemcpy(c, dev_c, size * sizeof(int), 
                cudaMemcpyDeviceToHost);
    if (cudaStatus != cudaSuccess) {goto Error;}
Error:
    cudaFree(dev_c); cudaFree(dev_a); cudaFree(dev_b);  
    return cudaStatus;
}

Run the code !

一维信号均值滤波

Case

  • Using global memory
    • One element per thread
__global__ void stencil(int* in, int* out)
 {
    int globIdx = blockIdx.x * blockDim.x +     
            threadIdx.x;

    int value = 0;

    for (offset = -RADIUS; offset <= RADIUS;     
                offset++)
        value += in[globIdx + offset];
    out[globIdx] = value;
}

A lot of redundant read in neighboring threads.

  • Using shared memory

    • One element per thread

    • Read (BLOCK_SIZE + 2 * RADIUS) elements from global memory to shared memory

    • Compute BLOCK_SIZE output elements in shared memory

    • Write BLOCK_SIZE output elements to global memory

Shared Memory

__global__ void stencil(int* in, int* out)
 {
    __shared__ int shared[BLOCK_SIZE + 2 * RADIUS];
    int globIdx= blockIdx.x* blockDim.x+ threadIdx.x;
    int locIdx= threadIdx.x+ RADIUS;

    shared[locIdx] = in[globIdx];
    if (threadIdx.x< RADIUS) 
    {
        shared[locIdx–RADIUS] = in[globIdx–RADIUS];  
        shared[locIdx+ blockDim.x] = in[globIdx+ 
                BLOCK_SIZE];
    }
    __syncthreads();
    int value = 0;
    for (offset = -RADIUS; offset <= RADIUS; offset++)
        value += shared[locIdx+ offset];
    out[globIdx] = value;
}

Matrix Multiplication

__int main(void)
{
     //allocate and initialize the matrices M, N, P
      //IO to read the input matrices M, N
   ……
      //M * N on the device
      MatrixMultiplication(M, N, P, Width);
    ……
      // IO to write the Output Matrices P
      // Free M, N, P 
      return 0;

}
void MatrixMulOnHost(float* M, float* N, float* P, int Width)
{
    for (int i = 0; i < Width; ++i)
    {
        for (int j = 0; j < Width; ++j) 
       {
    double sum = 0;
               for (int k = 0; k < Width; ++k)
     {
       double a = M[i * width + k];
       double b = N[k * width + j];
       sum += a * b;
    }
              P[i * Width + j] = sum;
            }
   }
}    

Allocating Memory on GPU

void MatrixMultiplication(float* M, float* N, float* P, int Width)
{ int size=Width*Width*sizeof(float);
      float * Md, Nd, Pd;
      dim3 grid(1,1,1); dim3 block(Width,Width,1);
    //Allocate device memory
      cudaMalloc((void)**&Md, size);
      cudaMalloc((void)**&Nd, size); 
       cudaMalloc((void)**&Pd, size);
    //transfer M,N from host to device
       cudaMemcpy(Md, M,size, HostToDevice);
       cudaMemcpy(Nd, N,size, HostToDevice);
    //kernel innovation code
     MatrixMulOnKernel<<<grid, block >>>(Md, Nd, Pd, Width);
     //transfer p from device to host
      cudaMemcpy(P, Pd,size, DeviceToHost);
      cudaFree(Md);cudaFree(Nd);cudaFree(Pd);
}

Kernel on GPU

__global__void MatrixMulOnKernel(float* Md, float* Nd, float* Pd, int width)
{
    //2D threadID
    int tx=threadIdx.x;
    int ty=threadIdx.y;
   // pvalue stores the pdelement
    for(int k=0;k<width;++k)
    {    
         float Mdelement=Md[ty*width+k];
         float Ndelement=Nd[k*width+tx];
         pvalue+=Mdelement*Ndelement;
    }
   //write pvalue to device memory
    Pd[ty*width+tx]=pvalue;
}

写个程序计算5x5矩阵相乘,并运行!

©北京大学 JackHCC


文章作者: 杰克成
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 杰克成 !
评论
  目录
Copyright © 2020 杰克成 | Powered by Hexo | Theme Matery
  站点总字数: 3594.4k 字  总访问量:  次  总访问人数:  人
载入运行时间...