本开发板是广东先锋板,spi nor上是uboot引导程序,nand上是内核和根系统 LS2K500启动支持spi和nand,先锋板上提供拨码开关,用于选择从spi还是nand启动 本实验试图从nand启动一个裸机程序,使用uboot烧写nand(注意:破坏了原有的内核和根),这样就不用烧录到spi nor,保持先锋板uboot完整

一.准备工作 1.下载龙芯交叉编译器IDE工具(www.loongide.com) loongide loongarch32支持:单片机 LS1C102、LS1C103 loongarch64支持:嵌入式 LS2K500

编程:裸机或RT-Thread

LS2K500虽支持linux,但该IDE工具不支持linux

2.IDE运行平台 WIN7以上或linux/wine32 MSYS 1.0或MSYS2

1)安装MSYS及IDE的步骤详见该网站说明,本文不表 IDE安装到D:盘

2)创建本实验源码目录C:\Users\linlin\ls2k\

3)创建IDE workspace目录C:\Users\linlin\loongprj\

4)在IDE新建项目名myls2k500 向导不勾选[]New Application Use Static Libraries 向导生成 C:\Users\linlin\loongprj\myls2k500\ls2k500\include\regdef.h(同D:\LoongIDE\la64-tool\loongarch64-newlib-elf\ls2k500\include\regdef.h) C:\Users\linlin\loongprj\myls2k500\ld.script

5)将regdef.h和ld.script复制到C:\Users\linlin\ls2k
regdef.h 通用寄存器定义 ld.script 链接脚本

6)在WIN/MSYS下运行命令(编译、链接等等) C:\Users\linlin\ls2k 在MSYS下即是 /c/Users/linlin/ls2k

运行MSYS,进入源码目录

linlin@win ~$ cd /c/Users/linlin/ls2k
linlin@win /c/Users/linlin/ls2k$

3.开发主机平台 linux/debian minicom 调试uboot cutecom 调试本实验程序

4.硬件设备 1)LS2K500开发板 为广东先锋板

2)usb ttl 连接开发板调试串口

3)U盘 创建一个分区 格式化为FAT32 创建目录update

用于烧写程序

4)USB键盘 在minicom下连接开发板,输出显示正常 但不知何故,本人的minicom下主机键盘无法输入,因此准备另外第二个USB键盘(不是主机键盘)插入开发板的usb口,用作输入

如果在minicom下主机键盘输入正常,不需第二个键盘

5.minicom配置

root@debian:/home/linlin# minicom
root@debian:/home/linlin#

设置波特率115200、数据8位、无校验、停止位1位 保存配置名称为ls2k500

linlin@debian:~$ cat /etc/minicom/minirc.ls2k500
# 电脑产生的文件 - 使用“minicom -s”以变更参数。
pu port             /dev/modem /dev/ttyUSB0
pu baudrate         115200
pu bits             8
pu parity           N
pu stopbits         1
linlin@debian:~$ 

6.参考文档 <龙芯架构参考手册>、<龙芯2K0500处理器用户手册>(https://www.loongson.cn/download/index) <广东龙芯2K500迷你开发板用户手册>、<LS2K0500 DEV Board原理图>(https://gitee.com/loongarch_community)

下文分别简称: <架构手册>、<处理器手册> <开发板手册>、<开发板原理图>

二.先锋板 1.

spi nor   uboot
nand      内核和根
UART2     调试串口

2.拨码开关配置启动方式 1)<开发板原理图> 1.1)参见原理图电路拨码开关位置(对照开发板实物位置一致)

NAND_CLE -- [开关](实物近外侧,也见<开发板手册>图拨码开关)
PWM3     -- [开关]
NAND_RDN -- [开关](实物近内侧)

1.2)参见原理图拨码配置描述 位逻辑:

NAND_RD     [2] (第2位)
NAND_CLE    [1] (第1位)
PWM3        [0] (第0位)

或写为:

PWM3        [0]
NAND_CLE    [1]
NAND_RD     [2]

可以看出位逻辑和电路/实物位置的PWM3和NAND_CLE次序颠倒

1.3)参见原理图拨码配置说明

第2 1 0位
--------------------
  X 0 0--spi 启动
  X 1 0--nand启动  (应该在原来先锋板出厂状态将外侧拨码开关/NAND_CLE拨一下就可nand启动,<处理器手册>说是NAND_CLE上拉)
...
  1 1 1--SDIO启动  (先锋板没有sd卡插口)

1.4)原理图拨码配置的其它 好像除选择'启动'提供拨码开关外,其它配置都是开发板上加电路硬配置死

查看原理图电路,选择'启动'有明显的拨码图样,其它配置没见有拨码(有电路,但本人外行看不懂其值)

下面几个其它配置

  描述                     配置说明(只列原理图标红色的值)
--------------------------------------------------------------
nand启动,ecc模式        0-off   (<处理器手册>对应是'nand ecc功能使能',这里的启动两个字很易误导为要设定从nand启动)
...
nand颗粒类型            11-8K   (page 8KB)
...
PLL时钟                 10-soft (其它值略)

以上配置详细信息可参阅<处理器手册>bootcfg

不知标红的值是不是先锋板已配置死

2)<处理器手册> 2.1)参见手册23.1 NAND控制器结构描述 系统启动模式nand启动 外部NAND_CLE上拉,PWM3、LCD_D0下拉 外部NAND FLASH第一个page的数据为普通原始数据

注意:手册、原理图有关信号名称表述不尽相同,但实际同一回事(NAND_RD、NAND_RDN、LCD_D0)

2.2)参见手册23.2.7 参数配置寄存器NAND_PARAMETER 2K页 :1Gb、2Gb(256M=2Gb/8)、4Gb、8Gb 4K页 :16Gb 8K页 :32Gb、64Gb、128Gb 512B页:64Mb、128Mb、256Mb、512Mb、1Gb

有2Gb/2K页,没2Gb/8K页

2.3)参见手册5.2 芯片初始化信号 有关bootcfg(16位)

信号名称                 bootcfg      描述
----------------------------------------------------
{NAND_RD,NAND_CLE,PWM3}   2:0        启动选择输入
----------------------------------------------------
{LCD_d[13],LCD_D[10]}     4:3        启动nand类型选择

启动选择输入: x00=SPI x01=LPC x10=NAND 011=LIO 111=SDIO

启动nand类型选择: 00=512Mb(page 512B) 页512个字节作引导太小了,512Mb的'b'应是'位',大小应是64M=512Mb/8 01=1Gb(page 2KB) 10=16Gb(page 4KB) 11=128Gb(page 8KB)

01 是仅对应1Gb,还是对应1Gb、2Gb? 11 是仅对应128Gb,还是对应32Gb、64Gb、128Gb?

只有首页才能作为nand运行空间

3)先锋板nand 先锋板nand大小是256M,页2K

<开发板原理图>硬配置的8K/页(假定图中标红的是配置死)对应16G(128Gb/8)nand,与先锋板256M不符?

那按照256M(2Gb/8)对应2K页,合理的启动nand类型选择应是01的2K页? 做完本实验后,后续写了一小段汇编(另文),测试到程序只能在512B内正常运行,是否能证明先锋板是00启动nand类型?

3.usb ttl 1)开发板示意图(正面)

    -------------------------------------------------
    |    ---------------------
    |    |23 ...            1|  <=排针
    |    |24 ...            2|
    |    ---------------------
    |                  [][][][] <=LED4~LED1
    |             -----
    | -           |nor|  <=spi nor flash
    | -           -----
    | -  <=LCD
外侧| -              内侧
    |...   [][][]
    | -    [][][]  <=拨码
    | -     \
    |        \--NAND_CLE
    |
    |---------
    | USB    |
    | (两口) |
    |---------
    |    ---------------------
    |    |23 ...         3  1|  <=排针  
    |    |24 ...         4  2|           [key1][key2]
    |    --------------------\-           
    --------------------------\------------------------
                               \--UART2_TX

2)UART调试串口(即为UART2)针脚(示意图下方排针) 1 -- RX 2 -- TX 4 -- gnd

3)串口连接图

主机usb ttl     开发板UART2
----------------------------------------
红 3.3v         (不需连接)
黑 gnd -------- 针脚4 gnd
青 txd -------- 针脚1 RX(输入)
白 rxd -------- 针脚2 TX(输出)

(线颜色请按具体usb ttl上标注信号名为准)

4)<开发板手册>调试串口 波特率115200、数据8位、停止位1位

三.uboot 先锋板的uboot菜单如下


  *** U-Boot Boot Menu ***

     [1] System boot select
     [2] Update kernel   <=烧写内核到nand
     [3] Update rootfs   <=烧写根文件系统到nand
     [4] Update u-boot   <=烧写uboot到spi nor(千万别选此,否则烧写非uboot程序再也无U-Boot启动)
     [5] Update ALL      <=这个也千万别选
     [6] System install or recover
     [7] U-Boot console

四.nand flash 通常nand flash可分4个区: Bootloader Kernel File System User

也就是说,如果uboot支持从nand启动,则可将uboot烧写到nand的Bootloader分区

但查看设备树https://gitee.com/loongarch_community/2k500_gd_mini_kernel/blob/master/arch/loongarch/boot/dts/loongson/ls2k500_mini_dp.dts 先锋板的nand只分Kernel和root两个区,Kernel分区从0开始,那么(结合章节三菜单[2] Update kernel)烧写自己的裸机程序到Kernel分区,应该就能实现从nand启动

注意:不同开发板的nand、uboot功能划分可能不同,以实际为准

五.引脚功能 参考设备树../ls2k500_mini_dp.dts 有配置UART0~UART3

开发板有引出UART2并作为调试串口(参照<开发板手册>图3-6)

所以本实验使用UART2

<处理器手册>给出的硬件寄存器地址是物理地址

因为机器复位是直接地址翻译模式,物理地址等同虚拟地址,系统启动时本实验程序是处在物理空间运行

1.UART寄存器 UART2 物理地址基址 0x1ff40800 寄存器是8位一个字节,所以UART2两个相邻寄存器的地址相差1

2.引脚复用配置寄存器 用于配置引脚的功能 寄存器是32位(在64位C编程需注意定义为32位整型,即int) 配置gpio复用是4位对应一个引脚,所以32/4=8,每8个GPIO一组

 引脚     主功能     描述
-----------------------------------------
GPIO61  UART2_RX   串口2数据输入
GPIO60  UART2_TX   串口2数据输出

由上表UART2对应了GPIO60、GPIO61,虽然主功能是UART,但默认为GPIO输入,还是有必要显式设置引脚复用

GPIO60、GPIO61落在GPIO56~GPIO63复用配置寄存器(物理地址0x1fe104ac) (实际只用到3位) 位域[22:20] GPIO61
位域[18:16] GPIO60
除000~100之外的值是主功能,位域取101表示主功能

设置值=0101 0101 0000 0000 0000 0000=0x550000
       \  /                     \  /
      GPIO61                   GPIO56

六.原厂linux

root@debian:/home/linlin# minicom  ls2k500
欢迎使用 minicom 2.8

选项: I18n 
通信端口 /dev/modem /dev/ttyUSB0

按 CTRL-A Z 说明特殊键 


 _     __   __  _  _  ___  ___  __  _  _    /   ___  __  \ 
 |    |  | |  | |\ | | __ [__  |  | |\ |    |  | __ |  \ | 
 |___ |__| |__| | \| |__] ___] |__| | \|    \  |__] |__/ / 

Trying to boot from SPI


U-Boot 2022.04-v2.0.0-00025-g1f9b0a99 (Oct 31 2022 - 11:35:54 +0800), Build: jenkins-u-boot-2022.04-2k500-mini-dp-40

CPU:   LA264
Speed: Cpu @ 600 MHz/ Mem @ 400 MHz/ Bus @ 100 MHz
Model: loongson-2k500
Board: LS2K500-MINI-DP
DRAM:  512 MiB
Core:  51 devices, 22 uclasses, devicetree: board                               
NAND:  using 4-bit/512 bytes BCH ECC                                            
device found, Manufacturer ID: 0x2c, Chip ID: 0xda                              
Micron NAND 256MiB 3,3V 8-bit     <= 美光(与原理图macronix旺宏的MX30LF1G08AA-TI不一致)                                                
256 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 64  <= nand大小256M,2K/页,OOB应是spare区即备份区64字节              
256 MiB                                                                         
Loading Environment from SPIFlash... SF: Detected w25q80bl with page size 256 Bytes, erase size 4 KiB, total 1 MiB  <= 与<开发板手册>截图(w25q64cv、gd25q80)和原理图(GD25Q80CTIG)都有出入
OK                                                                              
In:    serial                                                                   
Out:   serial                                                                   
Err:   serial vidconsole                                                        
dpm_en:0x1d94                                                                   
dpm_sts:0x1734310                                                               
Net:   eth0: ethernet@0x1f020000                                                
************************** Notice **************************                    
Press c to enter u-boot console, m to enter boot menu                           
************************************************************                    
Bus ehci@1f050000: USB EHCI 1.00                                                
Bus ohci@1f058000: USB OHCI 1.0                                                 
scanning bus ehci@1f050000 for devices... 1 USB Device(s) found                 
scanning bus ohci@1f058000 for devices... 1 USB Device(s) found                 
Autoboot in 0 seconds                                                           
SF: Detected w25q80bl with page size 256 Bytes, erase size 4 KiB, total 1 MiB   
device 0 offset 0xf0000, size 0x10000                                           
SF: 65536 bytes @ 0xf0000 Read: OK                                              
                                                                                
Loading from nand0, offset 0x0                                                  
Scanning device for bad blocks                                                  
   Image Name:   Linux-5.10.0.lsgd-g81224fa0e223                                
   Image Type:   LoongArch Linux Kernel Image (gzip compressed)                 
   Data Size:    7778408 Bytes = 7.4 MiB                                        
   Load Address: 00200000                                                       
   Entry Point:  00c9b78c                                                       
## Booting kernel from Legacy Image at 9000000003000000 ...                     
   Image Name:   Linux-5.10.0.lsgd-g81224fa0e223                                
   Image Type:   LoongArch Linux Kernel Image (gzip compressed)                 
   Data Size:    7778408 Bytes = 7.4 MiB                                        
   Load Address: 00200000                                                       
   Entry Point:  00c9b78c                                                       
   Verifying Checksum ... OK                                                    
   Uncompressing Kernel Image                                                   
Warning: invalid device tree. Used linux default dtb                            
...
[    0.000000] 64-bit Loongson Processor probed (LA264 Core)                    
[    0.000000] CPU0 revision is: 0014a000 (Loongson-64bit)                      
[    0.000000] FPU0 revision is: 00000000                                       
...
[    0.000000] Zone ranges:                                                     
[    0.000000]   DMA32    [mem 0x0000000000200000-0x00000000ffffffff]           
[    0.000000]   Normal   empty                                                 
[    0.000000] Movable zone start for each node                                 
[    0.000000] Early memory node ranges                                         
[    0.000000]   node   0: [mem 0x0000000000200000-0x000000000affffff]          
[    0.000000]   node   0: [mem 0x000000000b000000-0x000000000cffffff]          
[    0.000000]   node   0: [mem 0x000000000d000000-0x000000000effffff]          
[    0.000000]   node   0: [mem 0x0000000090000000-0x000000009fffffff]          
[    0.000000] Initmem setup node 0 [mem 0x0000000000200000-0x000000009fffffff] 
[    0.000000] On node 0, zone DMA32: 128 pages in unavailable ranges           
[    0.000000] On node 0, zone DMA32: 1024 pages in unavailable ranges          
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 31492     
...
[  OK  ] Started Serial Getty on ttyS2.       <= 串口2
[  OK  ] Reached target Login Prompts.                                          
         Starting OpenSSH server daemon...                                      
[  OK  ] Started Dropbear SSH daemon.                                           
                                                                                
Welcome to Loongson-gd                                                          
LS-GD login: root (automatic login)                                             
                                                                                
[root@LS-GD ~]#     <= 无法输入

1.主机键盘 正常情况minicom下主机键盘是能够输入的,但本人实验系统在uboot、linux都无法输入,不清楚原因所在

2.开发板键盘 本实验用开发板的USB键盘作输入,在uboot能输入,但进入linux后也无法输入 应该是linux的会话,当前会话是Serial终端,不对应控制台的键盘(如果开发板接上液晶屏,应该可以进入控制台会话看到键盘的输入,本人没试) 而uboot是固件,不具备也不需会话,所以允许接收一切的外部输入

七. 1.源代码 代码文件为uart2k500.c,放在/c/Users/linlin/ls2k/下

//UART2寄存器(8位),其中三对寄存器DAT/DL_L、IER/DL_H、FCR/DL_D每对地址相同
#define UART_LCR         0x1ff40803   //线路控制寄存器
#define UART_DL_D        0x1ff40802   //分频值小数寄存器
#define UART_DL_H        0x1ff40801   //分频值高字节寄存器
#define UART_DL_L        0x1ff40800   //分频值低字节寄存器
#define UART_FCR         0x1ff40802   //FIFO控制寄存器
#define UART_LSR         0x1ff40805   //线路状态寄存器
#define UART_IER         0x1ff40801   //中断使能寄存器
#define UART_TxData      0x1ff40800   //数据寄存器(DAT)

#define GPIOMUX          0x1fe104ac   //引脚GPIO56~GPIO63复用配置寄存器(32位),每4位对应一个gpio,即8个gpio一组

void main (void)
{ 
    // 主功能也必须设复用才能使用(本人刚开始以为'主'功能是默认不用设置,好像默认的是gpio功能) 
    *(volatile unsigned int*)(GPIOMUX)=0x550000; // GPIO60、GPIO61主功能

    *(volatile unsigned char*)(UART_LCR)=0x80 ;  // 置LCR的第7位dlab(分频模式)为1,访问分频值寄存器(UART_DL_x系列) 

    //--v-- 默认100MHz当clkin,设置波特率9600,得分频=100 000 000/16/9600=651.0416666666667

    *(volatile unsigned char*)(UART_DL_D)=0x0A ; // 16进制小数部分 0x100*0.0416666666667=256*.0416666666667=16*16*.0416666666667=10.6666666666752=10=0x0A

    // 651=0x28B=10 10001011,拆分高、低字节
    *(volatile unsigned char*)(UART_DL_H)=0x02 ; // 高字节
    *(volatile unsigned char*)(UART_DL_L)=0x8B ; // 低字节

    //--^--

    *(volatile unsigned char*)(UART_LCR)=0x2F ;  // 0x2F=00101111;奇校验,2停止位,8个数据位;置LCR的dlab位为0,访问正常寄存器(DAT、IER、FCR等)        
    *(volatile unsigned char*)(UART_FCR)=0x86 ;  // 0x86=10000110;第1位复位接收FIFO,第2位复位发送FIFO,位域[7:6]=0x2=10表示接收trigger值为8字节    

    *(volatile unsigned char*)(UART_IER) = 0 ;   // 默认关闭所有UART中断 
    
    // 以上应是仅串口初始化
     
    int i;
    while(1)
      for ( i=0;i<256;i++) // 发送是以字节为单位,所以测试一个字节范围内的数据0~255
      {    
        while (!( ( *(volatile unsigned char*)(UART_LSR) ) & 0x20)); // 发送FIFO空,0x20=100000,第5位
        *(volatile unsigned char*)(UART_TxData) = i;                 // 发送数据  
      }    
}

2.解析 代码参考了<龙芯LS1C101单片机实验(1)--UART>(LS1C101是mips,其硬件寄存器地址是虚拟地址,在<See MIPS Run Linux>一书中则称为程序地址) LS2K500是嵌入是CPU,LS1C101是MCU,参照这两者的手册,UART使用方式差不多 但毕竟CPU和MCU有天壤之别 MCU内置小段内存,可直接使用 CPU使用外置内存,需初始化内存才能使用 C语言的变量一般情况下都存在于内存中,编译带优化会尽量使用寄存器作变量,所以对于一小段的、简单的C程序还是有机会避免使用内存的

特例说明:见<处理器手册>29.2.1 DVFS功能描述,LS2K500内置小核LA132(MCU),并内置SRAM供LA132用,未知LS2K500的LA264核(CPU)能否直接使用该SRAM?

1)不需看门狗 默认禁用看门狗,程序不需设置看门狗,不会复位,程序会一直运行下去

见<处理器手册>->29.3.1 ACPI寄存器描述->Reset Control Register 复位控制寄存器(rst_cnt)的位域看门狗功能使能(wd_en)

2)波特率 公式

波特率=clkin/(16*DL)

(<处理器手册>里写了公式 波特率=clkin/16*DL,应该错误)

手册没说16是什么意义,参照LS1C101的公式,16应是指采样16份划分(在LS1C101通过采样控制寄存器可设置16份划分或其它份划分,在LS2K500没见有采样控制寄存器)

事实是确定了波特率的值,我们要计算出DL的值,以便程序里设置DL值

通过波特率公式,可得公式 DL=clkin/16/波特率

3)时钟 clkin值是什么?<处理器手册>UART例子是10MHz,但没说明为什么

3.1)loongide loongide的LS2K500 UART驱动里波特率设定就是用APB的频率作clkin

3.2)<处理器手册> 见手册图3-1时钟结构 100MHz参考时钟 5个PLL,图中没见UART,不知UART处在哪个PLL,但图中有个SOC PLL输出三个时钟,其中sb_clock有指向APB,而UART就在APB(见手册图1-1芯片结构图) sb_clock频率范围100~200MHz(见手册图3-4 SOC PLL时钟结构)

见手册5.4.1时钟配置概要,说'系统复位结束后,所有PLL输出均被旁路为参考时钟' 见手册表3-1 PLL硬件配置,说'硬件bypass所有PLL,所有时钟频率与参考时钟相同(100MHz)'

由以上猜测系统上电启动,clkin为100MHz

3.3)uboot uboot源码的低级初始化UART是用REF_CLK固定的100MHz(100 000 000)作clkin,实际应该是启动时所有(包括APB)默认100MHz吧,公式应该还是以APB为准

3.4)小结 <处理器手册>对clkin描述不详,要参考loongide和uboot源码,从而得到开发板上电启动默认clkin值为100MHz 至于clkin值与各个分频的关系,本人未深究,不表

4)DL值 DL值是由三个8位寄存器构成 DL_H 高字节 DL_L 低字节 DL_D 小数

<处理器手册>是UART分频要写到DL_H、DL_L、DL_D三个寄存器,但uboot却直接只两个公式值DIV_HI、DIV_LO ../u-boot-2022.04-2k500-cbd-src/arch/loongarch/mach-loongson/ls2k500/lowlevel_init.S

...
#define UART_REF_CLK	100000000  // clkin=100MHz
#define UART_DIV_HI	(((UART_REF_CLK + (115200*8)) / (115200*16)) >> 8)   //高字节
#define UART_DIV_LO	(((UART_REF_CLK + (115200*8)) / (115200*16)) & 0xff) //低字节
...

按uboot的clkin和波特率115200 4.1)手算 得分频=100 000 000/16/115200=54.25347222222222=DL=clkin/16/波特率=clkin/(波特率*16) 实数DL的整数是54,小数是0.25347222222222 整数54转化为十六进制是0x0036,所以DL的整数高字节是0x00,低字节是0x36

小数需再进一步转化十六进制 十六进制整数,每十六进一位,因此可将十进制小数每乘16(即0x10)得到的十进制整数部就是一位十六进制数 所以十进制小数要取得十六进制小数多少位,就乘多少个16(即16多少次方) DL的小数寄存器只一个字节,即两位十六进制,所以我们只需乘2个16(16 * 16=0x10 * 0x10=0x100) 0x100 * 0.25347222222222=64.88888888888889=64=0x40

4.2)而uboot却省去DL小数(没见设置DL_D),但计算DL整数略有不同 ((UART_REF_CLK + (115200 * 8)) / (115200 * 16))=UART_REF_CLK/(115200 * 16)+(115200 * 8)/(115200 * 16)=UART_REF_CLK/(115200 * 16)+0.5 即在原有DL公式加0.5,不懂为什么,是因'/'运算截断取整加以修正提高精度吗?加不加0.5好像误差也不大啊

应该uboot是采取C语言中整数除法的四舍五入算法,即(b+a/2)/a

4.3)uboot应该是为了在编译阶段计算DL,而不是事先手算好 宏定义的'/'应是整除,DL需要整数值,所以uboot公式先加(115200*8)后才去整除,汇编代码lowlevel_init.S不能DL公式后加0.5小数 汇编代码一般使用定点整除(不可能考虑使用浮点除),因此也无法计算出小数部分,应该也牺牲了波特率精度(省去设置DL_D)

5)UART寄存器 在LS1C101有采样控制寄存器和状态寄存器寄存器(LS1C101手册原文),这两个在LS2K500都没有

对照LS2K500和LS1C101手册,硬件寄存器的位域除UART_FCR位域[7:3]稍微不同,其它的意义都相同

3.编译

linlin@win /c/Users/linlin/ls2k$ /d/LoongIDE/la64-tool/bin/loongarch64-newlib-elf-gcc.exe -mabi=lp64d -march=loongarch64 -G0 -O2  -c uart2k500.c
linlin@win /c/Users/linlin/ls2k$ ls uart2k500.o
uart2k500.o
linlin@win /c/Users/linlin/ls2k$

生成目标文件uart2k500.o

要带-O2优化,这样变量i会尽量优化为使用通用寄存器,见反汇编结果,不会用到栈,不会用到内存(SDRAM需初始化才能使用)

4.链接

linlin@win /c/Users/linlin/ls2k$ /d/LoongIDE/la64-tool/bin/loongarch64-newlib-elf-ld.exe  -g -T ld.script -o uart2k500.exe  uart2k500.o
d:\LoongIDE\la64-tool\bin\loongarch64-newlib-elf-ld.exe: warning: cannot find entry symbol _start; defaulting to 9000000000800000
linlin@win /c/Users/linlin/ls2k$ ls uart2k500.exe
uart2k500.exe
linlin@win /c/Users/linlin/ls2k$

0x9000000000800000起始地址见链接脚本ld.script

上电起始地址是0x1c000000,所以链接命令ld最好指定起始地址 因为本程序比较简单,反汇编结果指令不含绝对地址,所以与链接起始地址无关,可以不指定

5.转换为二进制格式

linlin@win /c/Users/linlin/ls2k$ /d/LoongIDE/la64-tool/bin/loongarch64-newlib-elf-objcopy.exe -O binary uart2k500.exe uart2k500.bin
linlin@win /c/Users/linlin/ls2k$ ls uart2k500.bin
uart2k500.bin
linlin@win /c/Users/linlin/ls2k$ ls -l uart2k500.*
-rw-r--r-- 1 linlin Administrators  4120 Nov  6 15:48 uart2k500.bin
-rw-r--r-- 1 linlin Administrators  3419 Nov  6 15:34 uart2k500.c
-rwxr-xr-x 1 linlin Administrators 22232 Nov  6 15:45 uart2k500.exe
-rw-r--r-- 1 linlin Administrators  1624 Nov  6 15:41 uart2k500.o
linlin@win /c/Users/linlin/ls2k$ /d/LoongIDE/la64-tool/bin/loongarch64-newlib-elf-size.exe uart2k500.exe
   text    data     bss     dec     hex filename
    196    3924   69600   73720   11ff8 uart2k500.exe
linlin@win /c/Users/linlin/ls2k$

uart2k500.bin大小4K左右远大于实际(见第10小节反汇编)

6.复制到U盘 将uart2k500.bin复制到U盘目录update下,并重命名为uImage 即U盘上内核文件/update/uImage 满足uboot烧写kernel格式要求

7.烧写 U盘插到开发板USB口

root@debian:/home/linlin# minicom  ls2k500
...
Trying to boot from SPI                                                         
...
************************** Notice **************************                    
Press c to enter u-boot console, m to enter boot menu    <= 一直按m键(开发板的USB键盘,非主机键盘)
************************************************************                    
Bus ehci@1f050000: USB EHCI 1.00                                                
Bus ohci@1f058000: USB OHCI 1.0                                                 
...
Autoboot in 0 seconds                                                           


  *** U-Boot Boot Menu ***

     [1] System boot select
     [2] Update kernel   <=选此
     [3] Update rootfs
     [4] Update u-boot   <=千万别选此(否则烧写到spi nor,再也无u-boot启动)
     [5] Update ALL
     [6] System install or recover
     [7] U-Boot console


  Press UP/DOWN to move, ENTER to select, ESC/CTRL+C to quit


  *** U-Boot Boot Menu ***

     [1] Update kernel (uImage) to nand flash (by usb)  <=选此,U盘
     [2] Update kernel (uImage) to nand flash (by tftp)
     [3] Return


  Press UP/DOWN to move, ENTER to select


resetting USB...
Bus ehci@1f050000: USB EHCI 1.00
Bus ohci@1f058000: USB OHCI 1.0
scanning bus ehci@1f050000 for devices... EHCI timed out on TD - token=0x80008c80
2 USB Device(s) found
scanning bus ohci@1f058000 for devices... 2 USB Device(s) found
       scanning usb for storage devices... 1 Storage Device(s) found
  Device 0: Vendor: ??????? Rev: 8.07 Prod: USB2.0          
            Type: Removable Hard Disk
            Capacity: 999.0 MB = 0.9 GB (2045952 x 512)
update kernel....................
4120 bytes read in 94 ms (42 KiB/s)
SF: Detected w25q80bl with page size 256 Bytes, erase size 4 KiB, total 1 MiB
update kernel to nand............

NAND erase.part: device 0 offset 0x0, size 0xe00000  <= 0xe00000=14680064=14M 内核分区(见设备树)
Scanning device for bad blocks
Erasing at 0xde0000 -- 100% complete.
OK

NAND write: device 0 offset 0x0, size 0x1018
 4120 bytes written: OK   <= 按文件大小烧写

######################################################                          
### update target: kernel                                                       
### update way   : usb                                                          
### update result: success                                                      
######################################################                          
                                                                                
=> 

8.测试 拨下先锋板外侧第一个拨码开关,nand启动

cutecom设置波特率9600,设置显示输出16进制

一直按住复位键,然后cutecom先清空信息,打开open串口,再松开复位键,开发板程序开始运行,在cutecom输出就整齐了

下表是cutecom接收的正常数据

00000000 00 01 02 03 04 05 06 07   08 09 0a 0b 0c 0d 0e 0f 	
00000016 10 11 12 13 14 15 16 17   18 19 1a 1b 1c 1d 1e 1f 	
00000032 20 21 22 23 24 25 26 27   28 29 2a 2b 2c 2d 2e 2f 	 !"#$%&'  ()*+,-./
00000048 30 31 32 33 34 35 36 37   38 39 3a 3b 3c 3d 3e 3f 	01234567  89:;<=>?
00000064 40 41 42 43 44 45 46 47   48 49 4a 4b 4c 4d 4e 4f 	@ABCDEFG  HIJKLMNO
00000080 50 51 52 53 54 55 56 57   58 59 5a 5b 5c 5d 5e 5f 	PQRSTUVW  XYZ[\]^_
00000096 60 61 62 63 64 65 66 67   68 69 6a 6b 6c 6d 6e 6f 	`abcdefg  hijklmno
00000112 70 71 72 73 74 75 76 77   78 79 7a 7b 7c 7d 7e 7f 	pqrstuvw  xyz{|}~.
00000128 80 81 82 83 84 85 86 87   88 89 8a 8b 8c 8d 8e 8f 	........  ........
00000144 90 91 92 93 94 95 96 97   98 99 9a 9b 9c 9d 9e 9f 	........  ........
00000160 a0 a1 a2 a3 a4 a5 a6 a7   a8 a9 aa ab ac ad ae af 	........  ........
00000176 b0 b1 b2 b3 b4 b5 b6 b7   b8 b9 ba bb bc bd be bf 	........  ........
00000192 c0 c1 c2 c3 c4 c5 c6 c7   c8 c9 ca cb cc cd ce cf 	........  ........
00000208 d0 d1 d2 d3 d4 d5 d6 d7   d8 d9 da db dc dd de df 	........  ........
00000224 e0 e1 e2 e3 e4 e5 e6 e7   e8 e9 ea eb ec ed ee ef 	........  ........
00000240 f0 f1 f2 f3 f4 f5 f6 f7   f8 f9 fa fb fc fd fe ff 	........  ........
00000256 00 01 02 03 04 05 06 07   08 09 0a 0b 0c 0d 0e 0f 	
00000272 10 11 12 13 14 15 16 17   18 19 1a 1b 1c 1d 1e 1f 	
00000288 20 21 22 23 24 25 26 27   28 29 2a 2b 2c 2d 2e 2f 	 !"#$%&'  ()*+,-./
00000304 30 31 32 33 34 35 36 37   38 39 3a 3b 3c 3d 3e 3f 	01234567  89:;<=>?
00000320 40 41 42 43 44 45 46 47   48 49 4a 4b 4c 4d 4e 4f 	@ABCDEFG  HIJKLMNO
00000336 50 51 52 53 54 55 56 57   58 59 5a 5b 5c 5d 5e 5f 	PQRSTUVW  XYZ[\]^_
00000352 60 61 62 63 64 65 66 67   68 69 6a 6b 6c 6d 6e 6f 	`abcdefg  hijklmno
00000368 70 71 72 73 74 75 76 77   78 79 7a 7b 7c 7d 7e 7f 	pqrstuvw  xyz{|}~.
00000384 80 81 82 83 84 85 86 87   88 89 8a 8b 8c 8d 8e 8f 	........  ........
00000400 90 91 92 93 94 95 96 97   98 99 9a 9b 9c 9d 9e 9f 	........  ........
00000416 a0 a1 a2 a3 a4 a5 a6 a7   a8 a9 aa ab ac ad ae af 	........  ........
00000432 b0 b1 b2 b3 b4 b5 b6 b7   b8 b9 ba bb bc bd be bf 	........  ........
00000448 c0 c1 c2 c3 c4 c5 c6 c7   c8 c9 ca cb cc cd ce cf 	........  ........
00000464 d0 d1 d2 d3 d4 d5 d6 d7   d8 d9 da db dc dd de df 	........  ........
00000480 e0 e1 e2 e3 e4 e5 e6 e7   e8 e9 ea eb ec ed ee ef 	........  ........
00000496 f0 f1 f2 f3 f4 f5 f6 f7   f8 f9 fa fb fc fd fe ff 	........  ........
...

9.拨码开关拨回,恢复spi启动

...
Trying to boot from SPI
...
************************** Notice **************************                                                                                                                                   
Press c to enter u-boot console, m to enter boot menu       <= 不敲键盘                                                                                                                                   
************************************************************                                                                                                                                   
...
Loading from nand0, offset 0x0                <= 从nand加载内核                                                                                                                                                       
Scanning device for bad blocks                                                                                                                                                                 
** Unknown image type                                                                                                                                                                          
Wrong Image Format for bootm command          <= 因为烧写了自己的裸机程序,不是Linux kernel                                                                                            
ERROR: can't get kernel image!                                                                                                                                                                 
Bootcmd="setenv bootargs ${bootargs} ubi.mtd=root,${nand_pagesize} mtdparts=${mtdparts} video=${video}; sf probe;sf read ${fdt_addr} dtb;nboot kernel;bootm"  <= 启动Linux操作系统的命令                                 
Boot Kernel failed. Kernel not found or bad.  <= 失败                                                                                                                                                  
=> 

10.反汇编

linlin@win /c/Users/linlin/ls2k$ /d/LoongIDE/la64-tool/bin/loongarch64-newlib-elf-objdump.exe -D -b binary -m loongarch64 -EL uart2k500.bin
uart2k500.bin:     file format binary

Disassembly of section .data:
0000000000000000 <.data>:
       0:       143fc20c        lu12i.w $r12,130576(0x1fe10)
       4:       143fe80d        lu12i.w $r13,130880(0x1ff40) // 立即数20位后补上12个0,0x1ff40 000
       8:       0392b18c        ori     $r12,$r12,0x4ac
       c:       1400aa0e        lu12i.w $r14,1360(0x550)
      10:       2980018e        st.w    $r14,$r12,0
      14:       03a00db0        ori     $r16,$r13,0x803      //   UART_LCR寄存器地址0x1ff40803(0x1ff40 000或上0x803),立即数12位 
      18:       02be000c        addi.w  $r12,$r0,-128(0xf80) //--\
      1c:       2900020c        st.b    $r12,$r16,0          //--/UART_LCR寄存器值=0x80
      20:       03a009ae        ori     $r14,$r13,0x802
      24:       0280280c        addi.w  $r12,$r0,10(0xa)
      28:       290001cc        st.b    $r12,$r14,0
      2c:       0280080f        addi.w  $r15,$r0,2(0x2)
      30:       03a005ac        ori     $r12,$r13,0x801
      34:       2900018f        st.b    $r15,$r12,0
      38:       02be2c11        addi.w  $r17,$r0,-117(0xf8b)
      3c:       03a001af        ori     $r15,$r13,0x800
      40:       290001f1        st.b    $r17,$r15,0
      44:       0280bc11        addi.w  $r17,$r0,47(0x2f)     // 0x2f = 0000 0010 1111,立即数12位,正整数范围0~0x7FF(0111 1111 1111)
      48:       29000211        st.b    $r17,$r16,0           // UART_LCR
      4c:       02be1810        addi.w  $r16,$r0,-122(0xf86)  // 0xf86= 1111 1000 0110=0x1000-122,对12位数来说 -1=0x1000-1=0xFFF
      50:       290001d0        st.b    $r16,$r14,0           // UART_FCR
      54:       29000180        st.b    $r0,$r12,0            // UART_IER
      58:       03a015ad        ori     $r13,$r13,0x805
      5c:       02840010        addi.w  $r16,$r0,256(0x100)
      60:       0015000e        move    $r14,$r0                       //--v-- while(1)
      64:       2a0001ac        ld.bu   $r12,$r13,0                    // -v-  for()
      68:       00c5158c        bstrpick.d      $r12,$r12,0x5,0x5      // 取$r12的第5位,对应C程序 & 0x20 (100000=0x20)
      6c:       43fff99f        beqz    $r12,-8(0x7ffff8) # 0x64
      70:       006781cc        bstrpick.w      $r12,$r14,0x7,0x0      // 取$r14的第7~0位,将int型强制转换为unsigned char
      74:       290001ec        st.b    $r12,$r15,0                    // 写入UART_TxData
      78:       028005ce        addi.w  $r14,$r14,1(0x1)               // i++
      7c:       5fffe9d0        bne     $r14,$r16,-24(0x3ffe8) # 0x64  // -^-  0x7c-24=0x64
      80:       53ffe3ff        b       -32(0xfffffe0) # 0x60          //--^-- 0x80-32=0x60,指令格式解析见下3)小节
      84:       03400000        andi    $r0,$r0,0x0  //--v--以下应不是本文C程序代码
      88:       03400000        andi    $r0,$r0,0x0  // nop
      8c:       03400000        andi    $r0,$r0,0x0
      90:       00000014        0x00000014
      94:       00000000        0x00000000
      98:       00527a01        0x00527a01
      9c:       01017c01        fadd.d  $f1,$f0,$f31
      a0:       00030d0c        0x00030d0c
      a4:       00000000        0x00000000
      a8:       00000018        0x00000018
      ac:       0000001c        0x0000001c
      b0:       00800000        bstrins.d       $r0,$r0,0x0,0x0
      b4:       90000000        0x90000000
      b8:       00000084        0x00000084
        ...
linlin@win /c/Users/linlin/ls2k$

真正运行的程序大小应是0x84-0x0=132字节,满足nand一页内

1)通用寄存器 C程序很简单,没用到函数,所以没用到栈sp($r3)

t0~t8($r12 ~ $r20) 临时寄存器
zero($r0) 恒为0

2)立即数 C程序语句

*(volatile unsigned char*)(UART_FCR)=0x86 ;

对应两条指令

      4c:       02be1810        addi.w  $r16,$r0,-122(0xf86)
      50:       290001d0        st.b    $r16,$r14,0           // UART_FCR

addi.w指令的立即数是12位 C语句本就是要写值0x86到UART_FCR(unsigned char),且该值落在12位数正整数范围内,为何不直接addi.w $r16 , $r0 , 0x86 ?而要转为-122(0xf86)?

st.b指令是写字节(8位) 对8位数来说0x100-122=0x86 是不是编译器先按8位数的-122,然后再按12位数得-122=0xf86 ?

如果自己写汇编,应该可用addi.w $r16 , $r0 , 0x86 对st.b结果应一样吧

3)转移指令 C程序语句 while(1) 对应指令

      60:       0015000e        move    $r14,$r0                       //--v-- while(1)
      ...
      80:       53ffe3ff        b       -32(0xfffffe0) # 0x60          //--^-- 0x80-32=0x60

b指令无条件跳转,相对地址,相对于当前PC

程序运行起始地址是0x1c000000 该条b指令当前PC值 0x1c000080,指令意义是PC+(-32),也就是跳到0x80-32=0x60处

0101 00  11 1111 1111 1110 00  11 1111 1111 = 53ffe3ff 指令代码   
\     /  \                  /  \          /
 b指令       offs[15:0]         offs[25:16]


11 11,11 11,11    11 1111 1111 1110 00  00  = 0xfffffe0 = -32
\            /    \                  /
 offs[25:16]         offs[15:0]

11.调试 编译不加优化会产生sp使用栈

八.参考资料 内核、uboot源码 https://gitee.com/loongarch_community loongide for LS2K500