linux驱动系列之arm汇编

发布时间:2023-07-21  

     在arm平台学习linux时,会遇到arm汇编指令,arm汇编指令与8086汇编指令很多地方都不同,在此记下来以免后面忘了,同时在学习了汇编指令之后分析一些汇编指令编写的代码。

一、相对跳转指令b、bl

       b、bl指令都实现短跳转,bl指令执行后会在链接寄存器r14中保存下一条指令的地址。

二、数据传送指令mov

  mov指令会把一个寄存器的数赋值给另一个寄存器,或者把一个常数传递给另一个寄存器。

  如:mov  r0,r1  //将r1中的值传递给r0,mov r0,#0xff //将常数0xff传递给r0寄存器。

  mov指令传递的常数必须能够用立即数表示,当不知道一个数是否能够用“立即数传递”时,可以用ldr指令进行传递。

  如:ldr r0,=0xff。

三、内存访问指令str、ldr、ldm、stm

  ldr指令从内存中读取数到寄存器中,str指令将寄存器中的数传递到内存中,ldr、str指令操作的数都是32位的。ldm、stm是批量内存访问指令,用一个指令就能访问多个数据。下面是从s3c2440 datasheet上面的截图

  {cond}表示指令的执行条件

  Rn中保存内存地址,如果后面有!指令执行后会更新为下一个内存单元的地址

  寄存器列表对于ldm指令相当于将内存的数据取出放入列表中的寄存器中,stm指令相当于将列表中的寄存器中的值放入内存中。

  {^}有两种含义:如果有PC寄存器时,它表示指令执行后,spsr寄存器的值会自动复制cpsr寄存器中,这个常用于从中断处理函数中返回。如果中没有PC寄存器时,{^}表示操作的是用户模式下的寄存器,不是特权模式下的寄存器。

  指令中列表中的寄存器与内存对应关系为:编号底的寄存器对应低地址内存单元,编号搞的对应高地址内存单元。

四、加减指令add、sub

   如:add r0,r1,#0xff //r0=r1+0xff  sub r0,r1,#0xff  //表示r0=r1-0xff

五、程序状态寄存器访问指令msr、mrs

     arm有个程序状态寄存器cpsr,它用来控制处理器的工作模式和设置中断的总开关。

  msr cpsr,r0  //复制r0到cpsr中

  mrs r0,cpsr //复制cpsr到r0中

六、伪指令

  .gloabl  _start

  _start:

  .text

  .extern main

  .gloabl将本文件中的某个程序定义为全局的

  .extern 将某个变量或者函数引用到本文件中

  .text 表示下面的语句都属于代码段

八、uboot启动过程分析

   在看了韦东山老师的书和视频后,对汇编指令及bootloader的工作流程有了一个新的认识,我们接触比较的bootloader就是电脑的bios了,bootloader就是一段将我们硬盘上的代码搬运到内存中指定位置运行的程序。

  在说硬盘内存这些概念时我们首先要对s3c24xx或者其他的微处理器的存储空间有一个大致的了解。我做实验主要用的是2440,下面也就2440进行说明。在2440中我们一般使用三种存储设备:SDRAM、Nandflash、Norflash。这三个存储设备相对于我们平常的PC就是:SDRAM====>内存条,(Nandflash、Norflash)====>硬盘,对于Nandflash和Norflash的区别主要是前者不能直接运行代码,后者代码可以直接运行但是不能进行写数据,所以我们常常将Nandflash 存放程序,Norflash存放数据。

  对于存储空间2440有一个专门的存储控制器,

 

  一般我们把Norflash、Nandflash都与nGCS0相连,然后通过选择OM0、OM1选择哪种启动方式,如果选择Norflash我们的代码就会从Norflash的0地址开始执行,如果选择Nandflash我们的代码会被处理器将Nandflash的前4k拷贝到芯片内部的4k RAM中,然后程序在内部4k RAM中开始执行,这个拷贝过程是一个硬件过程,芯片内部自动完成。由于只有4k大小如果我们的程序小于4k,我们就直接让其在芯片内部RAM中执行,如果我们的代码大于4k,一般我们会用4k大小的程序将Nandflash里面的其余部分程序直接拷贝到SDRAM(0x3000 0000)中执行。

  我们的bootloader肯定是大于4k的,说以如果在Nandflash中运行我们就必须在4k 大小的代码中将bootloader拷贝到SDRAM,然后在SDRAM中将Nandflash 中的Kernerl拷贝到SDRAM中完成内核的启动,大的流程就如前面所说,但是具体在实现的过程中有很多的小细节。

8.1 bootloader启动前的准备

  1、关看门狗

  2、设置时钟

  3、初始化SDRAM

  4、重定位将bootloader的代码从Nandflash中拷贝到SDRAM中

  5、执行main

      ====》在main函数中所做的工作

          1、初始化串口(因为要加载内核需要打印一些信息,内核没有串口初始化的代码所以需要在内核启动前进行初始化)

          2、从Nandflash中将内核读入SDRAM

          3、设置参数(这个工作非常重要,因为内核在启动过程中需要设置启动参数比如:设置内存标记(内存的起始地址、内存大小)、设置命令行标记(命令行就是一个字符串,用于控制内核的一些行为。比如"noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0" 表示根文件系统在MTD3分区上,启动系统后执行的第一个程序是linuxrc,控制台为ttySAC0(第一个串口)))。在启动时bootloader与Kernel的交互式单项的所以只能是在bootloader启动阶段将我们用于设置内核的参数放在内存的某个地方,bootloader与Kernel约定好内核启动时就在这个地方去读取这些配置参数完成启动,一般这些参数存放在SDRAM 0x3100 0000地址上。

 1 /* 3. 跳转执行 */

 2 puts("Boot kernelnr");

 3 theKernel = (void (*)(int, int, unsigned int))0x30008000;

 4 theKernel(0, 362, 0x30000100);  

 5 /* 

 6  *  mov r0, #0

 7  *  ldr r1, =362

 8  *  ldr r2, =0x30000100

 9 *  mov pc, #0x30008000 

10  */


第3行代码就是将函数指针的首地址放到内核存放的内存首地址中去执行,下面的参数因为是函数调用所以会把我们上面说的bootloader与Kernel交互的设置参数保存在内存中的地址0x3000 0100放在r2寄存器中,内核执行时会从r2寄存器中取出地址然后找到我们在bootloader启动阶段存放的设置参数,这个过程相当的巧妙,主要是利用了函数调用时参数一般会保存在r0-r3,被调用的子程序返回前无需回复r0-r3的内容。  


参考主要代码:


1、bootloader启动汇编部分


 1 #define S3C2440_MPLL_200MHZ     ((0x5c<<12)|(0x01<<4)|(0x02))

 2 #define S3C2440_MPLL_400MHZ     ((0x5c<<12)|(0x01<<4)|(0x01))

 3 #define MEM_CTL_BASE    0x48000000

 4 

 5 .text

 6 .global _start

 7 _start:

 8 

 9 /* 1. 关看门狗 */

10     ldr r0, =0x53000000

11     mov r1, #0

12     str r1, [r0]

13 

14 /* 2. 设置时钟 */

15     ldr r0, =0x4c000014

16     //    mov r1, #0x03;              // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1

17     mov r1, #0x05;              // FCLK:HCLK:PCLK=1:4:8

18     str r1, [r0]

19        

20     /* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */

21     mrc    p15, 0, r1, c1, c0, 0        /* 读出控制寄存器 */ 

22     orr    r1, r1, #0xc0000000            /* 设置为“asynchronous bus mode” */

23     mcr    p15, 0, r1, c1, c0, 0        /* 写入控制寄存器 */

24 

25     /* MPLLCON = S3C2440_MPLL_200MHZ */

26     ldr r0, =0x4c000004

27     ldr r1, =S3C2440_MPLL_400MHZ

28     str r1, [r0]

29 

30     /* 启动ICACHE */

31     mrc p15, 0, r0, c1, c0, 0    @ read control reg

32     orr r0, r0, #(1<<12)

33     mcr    p15, 0, r0, c1, c0, 0   @ write it back

34 

35 

36 /* 3. 初始化SDRAM */

37     ldr r0, =MEM_CTL_BASE

38     adr r1, sdram_config     /* sdram_config的当前地址 */

39     add r3, r0, #(13*4)

40 1:

41     ldr r2, [r1], #4

42     str r2, [r0], #4

43     cmp r0, r3

44     bne 1b

45 

46 /* 4. 重定位 : 把bootloader本身的代码从flash复制到它的链接地址去 */

47     ldr sp, =0x34000000

48 

49     bl nand_init

50 

51     mov r0, #0

52     ldr r1, =_start

53     ldr r2, =__bss_start

54     sub r2, r2, r1

55     

56     bl copy_code_to_sdram

57     bl clear_bss

58     

59 /* 5. 执行main */

60     ldr lr, =halt

61     ldr pc, =main

62 halt:

63     b halt

64 

65 sdram_config:

66     .long 0x22011110     //BWSCON

67     .long 0x00000700     //BANKCON0

68     .long 0x00000700     //BANKCON1

69     .long 0x00000700     //BANKCON2

70     .long 0x00000700     //BANKCON3  

71     .long 0x00000700     //BANKCON4

72     .long 0x00000700     //BANKCON5

73     .long 0x00018005     //BANKCON6

74     .long 0x00018005     //BANKCON7

75     .long 0x008C04F4     // REFRESH

76     .long 0x000000B1     //BANKSIZE

77     .long 0x00000030     //MRSRB6

78     .long 0x00000030     //MRSRB7


  2、main函数部分


  1 #include "setup.h"

  2 

  3 extern void uart0_init(void);

  4 extern void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);

  5 extern void puts(char *str);

  6 extern void puthex(unsigned int val);

  7 

  8 

  9 static struct tag *params;

 10 

 11 void setup_start_tag(void)

 12 {

 13     params = (struct tag *)0x30000100;

 14 

 15     params->hdr.tag = ATAG_CORE;

 16     params->hdr.size = tag_size (tag_core);

 17 

 18     params->u.core.flags = 0;

 19     params->u.core.pagesize = 0;

 20     params->u.core.rootdev = 0;

 21 

 22     params = tag_next (params);

 23 }

 24 

 25 void setup_memory_tags(void)

 26 {

 27     params->hdr.tag = ATAG_MEM;

 28     params->hdr.size = tag_size (tag_mem32);

 29     

 30     params->u.mem.start = 0x30000000;

 31     params->u.mem.size  = 64*1024*1024;

 32     

 33     params = tag_next (params);

 34 }

 35 

 36 int strlen(char *str)

 37 {

 38     int i = 0;

 39     while (str[i])

 40     {

 41         i++;

 42     }

 43     return i;

 44 }

 45 

 46 void strcpy(char *dest, char *src)

 47 {

 48     while ((*dest++ = *src++) != '

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>