LS1C102学习板有LED1、LED2两个LED灯 上电,LED2常亮,LED1可编程点亮、熄灭

一.准备工作 编译工具 loongide 烧录工具 flashrom

1.创建本实验源码目录/home/linlin/ls1c102/

2.在loongide新建项目名myls1c102,将向导生成的../myls1c102/ls1c102/include/regdef.h(通用寄存器定义)和../myls1c102/ld.script(链接脚本)复制到/home/linlin/ls1c102/

3.在WINE/DOS下运行命令(编译、链接等等)

linlin@debian:~$ /opt/usr/bin/wine /home/linlin/.wine/drive_c/windows/syswow64/cmd.exe
Z:\home\linlin>cd /home/linlin/ls1c102/
Z:\home\linlin\ls1c102>

4.参考 <linux/wine64/wow64运行32位win程序龙芯嵌入式开发工具> (https://blog.51cto.com/u_13752418/10735509) <龙芯LS1C102单片机学习板(1)--烧录> (https://blog.51cto.com/u_13752418/10819318)

5.文档 1)架构->处理器核->cpu 如LS1C102、LS1C103是loongarch架构,LS1C101是MIPS架构 如LS2K500是LA264核,LS2K2000是LA364核,两者同为loongarch 64位架构

<龙芯架构参考手册--基础架构>(应含64位、32位)、<龙芯架构32位精简版参考手册> <龙芯1C102处理器用户手册> <LS1C102学习板原理图>

下文分别简称: <基础架构手册>、<架构32位精简版手册> <处理器手册> <原理图>

<处理器手册>中的'阅读指南'说到'请参阅<龙芯LA132处理器核用户手册>',但没见龙芯公司公开处理器核手册

<原理图> LED1接gpio20 LED2接3.3V

2)LS1C102和LS1C101的异同 首先,两者架构、指令集完全不同 但是,对比<龙芯1C102处理器用户手册>和<龙芯1C101处理器用户手册>两者文档内容,几乎一模一样,这可从硬件寄存器功能、地址空间分布、位域定义看出,怀疑LS1C102仅在LS1C101基础上更换指令集而已,以达C源码级兼容

2.1)地址空间分布起始地址

          LS1C101       LS1C102       LS1C103       LS2K500
-------------------------------------------------------------
RAM     0xa0000000    0x80000000    0x10000000
boot    0xbfc00000    0x1c000000    0x1c000000    0x1c000000
UART0   0xbfe80000    0xbfe80000    0x00009000    0x1ff40000

其它的spi、i2c等等硬件寄存器地址就不一一列出了,LS1C101和LS1C102完全相同,LS1C103不同于前两者

地址空间分布,loongarch文档列出的是物理地址,MIPS文档列出的是虚拟地址(MIPS资料常作程序地址)

MIPS的程序地址0x80000000 ~ 0x9fffffff(可cache)和0xa0000000 ~ 0xbfffffff(不可cache)截断最高前三位得物理地址,以LS1C101为例,0xa0000000/0x80000000直接映射到物理地址0x0

LS1C103、LS2K500等新产品明显看出是物理地址 LS1C102的boot也明显是物理地址,但RAM、硬件寄存器地址不知是物理地址还是虚拟地址? 所以要完全弄明白loongarch,还是以新产品LS1C103为好

cpu复位,loongarch是进入物理地址空间,MIPS是进入虚拟地址空间 loongarch可从物理地址空间切换到虚拟地址空间,以LS1C103的UART0为例,在编写程序代码中,物理地址空间必须写为0x00009000,虚拟地址空间必须写为0x80009000或0xa0009000(根据设置的直接映射配置窗口) MIPS以程序地址运行,不以物理地址,以LS1C101的UART0为例,在编写程序代码中,必须写为0xbfe80000(cpu自动映射到0x1fe80000物理地址),不能写为0x1fe80000

LS1C102是单片机,以物理地址空间运行就可,没必要切换到虚拟地址空间(如何切换,手册没介绍,可参考LS2K500的uboot源码、loongide)

2.2)寄存器位域 LS1C102和LS1C101相同的硬件寄存器,其位域定义基本一致,仅有极少部分寄存器某位域略有差别,如<处理器手册>章节 3.2.1芯片全局配置 的位域[27]、[14:12]、[11]两CPU不同

2.3)中断 <处理器手册>章节 4.1中断结构

             LS1C101      LS1C102
-----------------------------------
IP          只IP0~IP7    IP0~IP11
CPU定时器   IP1          IP11

IP0、IP2~7功能定义两CPU相同

<处理器手册>章节 4.2中断处理 说'CPU所有中断和例外的处理入口为0x1c000000'(这个地址是boot,令人迷惑)

         LS1C101       LS1C102
-----------------------------------
入口   0xbfc00380    0x1c000000(软件可配置为其它地址)

以上IP及入口的异同,更确切地说,是MIPS和loongarch的异同

<架构手册>比<处理器手册>多了IP12(核间中断) <基础架构手册>比<架构32位精简版手册>多了IP10(性能监测计数溢出中断)

2.4)CPU定时器 <处理器手册>第六章 定时器 的定时器是硬件寄存器,LS1C101和LS1C102相同,没有本质上区别

本小节的CPU定时器是指架构上自带的定时器,LS1C101和LS1C102(实为MIPS和loongarch)迥乎不同,需参考<架构手册>

<架构手册>章节 7.1控制状态寄存器一览 控制状态寄存器地址是独立编址,读写也是独立一套指令,不同于内存编址、访问指令 控制状态寄存器有很多,CPU定时器便是其中之一,下面仅列出CPU定时器相关的控制状态寄存器

地址       名称         简称         描述
-------------------------------------------------------------------------
0x40    定时器编号      TID
0x41    定时器配置      TCFG
0x42    定时器值        TVAL     当前的计数值,只读
0x43    计时器补偿      CNTC     见于<基础架构手册>和loongide,<架构32位精简版手册>和<处理器手册>没见有
0x44    定时中断清除    TICLR    第0位置1,清除时钟中断标记

<架构32位精简版手册>上定义的控制状态寄存器远少于<基础架构手册> 一个cpu的实现应满足<架构手册>最低要求,龙芯各cpu产品的处理器手册都没叙述控制状态寄存器,所以某具体cpu添加了<架构手册>之外控制状态寄存器不为人知

<处理器手册>章节 4.1中断结构 说'CPU定时器。清中断通过写compare寄存器来实现',在<架构手册>和loongIDE没见有compare控制状态寄存器,怀疑系照抄<龙芯1C101处理器用户手册>之误 LS1C102的CPU定时器清中断应该通过TICLR

二.实验目的 用定时器产生中断,了解中断处理过程 看门狗设置约400秒,没喂狗,400秒足够验证中断实验

三.实验过程 操作GPIO(即LED灯)和定时器 1.GPIO 1.1 ) LS1C102共64个GPIO,操作GPIO,通常的做法是分成两组,每组共32位域,输入输出分不同的地址硬件寄存器,寄存器32位对应32个GPIO,即每一位对应一个GPIO 对输入地址写32位整型值便是同时对32个GPIO输入电平 对输出地址写32位整型值便是同时使32个GPIO输出电平

1.2 ) 本实验不要求同时点亮/熄灭多个LED,所以用另一种方式,即采用LS1C102提供的GPIO位访问端口方式,很为方便 LS1C102提供64个字节的空间,对应64个GPIO,需按字节访问,即每一个字节对应一个GPIO 第0位应该是输入/输出高低电平,对应本实验值1为亮,值0为灭 第1位应该是方向,对应本实验值1为输出电平 第2~7位<处理器手册>没说明

第一个GPIO是gpio00,其GPIO位访问端口地址是0xbfeb0080

2.定时器 2.1 ) LS1C102集成的硬件控制UART、SPI、I2C等都有硬件寄存器,定时器也不例外 LS1C102提供了硬件定时器,包括计数寄存器、比较寄存器、步进寄存器 硬件寄存器和内存是统一编址,所以读写硬件寄存器内容通过ld.w、ld.b、st.w、st.b指令访问地址

2.2)LS1C102是loongarch架构,提供了CPU定时器,其读写是通过csrrd、csrwr、csrxchg指令,不同于硬件定时器

2.3)本实验是CPU定时器中断,没用硬件定时器,所以下文的定时器是指CPU定时器

3.验证 LED1灯即为gpio20


上电->LED1亮--3秒-->LED1灭(while里)--10秒-->LED1快闪一次(中断里点亮)---
                        ^                                             |
                        |---------------------------------------------|
                                然后LED1每10秒快闪一次

说明能发生中断并返回到断点while(1)

四. 1.源代码 代码文件为irqtime.S,放在/home/linlin/ls1c102/下

#include <regdef.h>      // #1
#define DELAY(count)     \
    li.w   s1,count;     \
    li.w   s2,0x1;       \
88:                      \
    nop;                 \
    sub.w  s1,s1,s2;     \
    bne    s1,zero,88b;  \
    nop

nop
li.w t1,0xbfeb0030       // watchdog地址
li.w t2,0xFBFF0400       // 看门狗复位等待时间长为0x400秒
st.w t2,t1,0

//--v--
// #2
//--^--

li.w t1,0xbfeb0080       // 0xbfeb0000+0x80 对应gpio00地址,以下用t1作GPIO位访问端口基址
li.w t5,0x3              // 0x3 即0b11为点亮
st.b t5,t1,20            // 字节写,相对t1偏移20个字节,即代表gpio20
DELAY(0x9fff)            // 2秒左右

//关中断
li.w    t0,(1<<2)        // CRMD.IE,disable interrupt,(1<<2)=4=0b100,第2位是IE
csrxchg zero,t0,0x0      // CRMD,current mode  clear IE bit,zero的第2位值为0写入到CRMD对应t0的IE位

//设中断屏蔽
li.w    t0,0x800         // set interrupt mask,0x800=(1<<11),第11位是CPU定时器中断
csrwr   t0,0x4           // ECFG,Exception config

//设中断入口地址
li.w    t0,0x1c001000    // #3
csrwr   t0,0xC           // EENTRY,Exception entry base address 

//设定时器
li.w    t0,0x5FFFF03     // 十秒循环,0x5FFFF03=0x5FFFF00+3,0x3=0b11(第1位置1循环,如置0则仅一次),计数初始值=0x5FFFF00=100663040约等于10秒*8M
csrwr   t0,0x41          // TCFG,如设0x5FFFF01,则仅发生一次中断,返回while(1)后不再有中断(除非再次设置)

//开中断
li.w    t0,4             // CRMD.IE  
csrxchg t0,t0,0x0        // CRMD,t0的第2位值为1写入到CRMD对应t0的IE位

nop

DELAY(0x5fff)            // 1秒左右
//--v-- while(1)         // 死循环,中断断点将发生在此里边,便于实验验证
99:        
li.w t4,0x2              // 0x2为灯灭,中断里使用t5值点、灭灯,为排除实验干扰,所以这里不能用t5(正常中断代码必须处理上下文的保存、恢复,本文省略,所以要避免冲突)
st.b t4,t1,20
b 99b
//--^--
nop

//--^ 上面的代码要确保编译后地址不能超过中断入口地址
.org 0x1000              // #4
is_int:

li.w t5,0x3
st.b t5,t1,20            // 点亮LED1灯

//在中断处理函数里必须清中断标志,最后ertn
li.w    t2,1
csrwr   t2,0x44          // TICLR

ertn                     // 返回

nop

2.解析 1)必须regdef.h 去掉#1处一行的regdef.h,编译出错,如下

Z:\home\linlin\ls1c102>/home/linlin/.wine/drive_c/LoongIDE/la32-tool/bin/loongarch32-newlib-elf-gcc.exe -G0 -O2 -I . -c irqtime.S
...
irqtime.S: Assembler messages:
irqtime.S:13: Fatal error: no match insn: li.w  t1,0xbfeb0030
Z:\home\linlin\ls1c102>

2)定时器相关的寄存器 见<架构手册> 2.1)TID loongide没见设置TID,所以本实验也不设置

2.2)TCFG

位域           说明
--------------------------------
0       定时器使能,置1才倒计时
1       定时器循环,置1循环
n-1:2   其位宽由具体实现(<处理器手册>没有TCFG内容,所以不知LS1C102多少位宽)
31:n    只读恒为0,写被忽略

<架构手册>位域[n-1:2] 说'定时器倒计时自减计数的初始值。要求该初始值必须是4的整倍数。硬件将自动在该域数值的最低位补上两比特0后再使用' 不大明白手册位域[n-1:2]数值表达的意思,是域数值=初始值?还是域数值*4=初始值?

根据下面TVAL[n-1:0],TCFG[n-1:2]意思应该为(初始值/4)也即域值*4=计数初始值

2.3)TVAL n-1:0 当前定时器的计数值 31:n 只读恒为0,写被忽略

2.4)计数初始值 因为不知LS1C102的n是多少,所以本实验取计数初始值尽量不要取太大

0x5FFFF00=0b0101 1111 1111 1111 1111 0000 00 00 = 计数初始值
            /   \                            \/
           26   24                          TCFG[1:0]

3)<架构手册>章节 7.4基础控制状态寄存器 除了定时器,还经常用到以下

地址       名称         简称         描述
-------------------------------------------------------------------------
0x00    当前模式信息    CRMD       特权等级、全局中断、地址翻译
0x04    例外配置        ECFG       控制各中断的局部使能
0x0C    例外入口地址    EENTRY     配置中断入口地址

4)#4处为中断入口地址,链接的时候应该会安排其下面语句到此地址,最终0x1c000000(链接开始地址)+0x1000=0x1c001000

5)正常中断处理需判断区分例外类型,分别处理不同类型,本实验省略了(因为设了中断屏蔽只CPU定时器中断)

3.编译

Z:\home\linlin\ls1c102>/home/linlin/.wine/drive_c/LoongIDE/la32-tool/bin/loongarch32-newlib-elf-gcc.exe -G0 -O2 -I . -c irqtime.S
0214:err:environ:init_peb starting L"C:\\LoongIDE\\la32-tool\\bin\\loongarch32-newlib-elf-gcc.exe" in experimental wow64 mode
021c:err:environ:init_peb starting L"c:\\LoongIDE\\la32-tool\\libexec\\gcc\\loongarch32-newlib-elf\\8.3.0\\cc1.exe" in experimental wow64 mode
021c:fixme:ntdll:NtQuerySystemInformation info_class SYSTEM_PERFORMANCE_INFORMATION
0224:err:environ:init_peb starting L"c:\\LoongIDE\\la32-tool\\loongarch32-newlib-elf\\bin\\as.exe" in experimental wow64 mode
Z:\home\linlin\ls1c102>

不用管 err

linlin@debian:~/ls1c102$ ls irq*
irqtime.o  irqtime.S
linlin@debian:~/ls1c102$

已编译生成irqtime.o

4.链接

Z:\home\linlin\ls1c102>/home/linlin/.wine/drive_c/LoongIDE/la32-tool/bin/loongarch32-newlib-elf-ld.exe -g -T ld.script -o irqtime.exe  irqtime.o
022c:err:environ:init_peb starting L"C:\\LoongIDE\\la32-tool\\bin\\loongarch32-newlib-elf-ld.exe" in experimental wow64 mode
/home/linlin/.wine/drive_c/LoongIDE/la32-tool/bin/loongarch32-newlib-elf-ld.exe: warning: cannot find entry symbol start; defaulting to 000000001c000000
Z:\home\linlin\ls1c102>

5.转换为二进制格式

Z:\home\linlin\ls1c102>/home/linlin/.wine/drive_c/LoongIDE/la32-tool/bin/loongarch32-newlib-elf-objcopy.exe -O binary irqtime.exe irqtime.bin
0234:err:environ:init_peb starting L"C:\\LoongIDE\\la32-tool\\bin\\loongarch32-newlib-elf-objcopy.exe" in experimental wow64 mode
Z:\home\linlin\ls1c102>
linlin@debian:~/ls1c102$ ls -l irq*
-rw-r--r-- 1 linlin linlin   4120  5月20日 16:17 irqtime.bin
...
linlin@debian:~/ls1c102$

6.烧写 使用flashrom工具

板上闪存为4194304 B(4M),需用dd命令将irqtime.bin填满到4M 计算填充大小 4194304-4120=4190184

linlin@debian:~/ls1c102$ dd if=/dev/zero bs=1 count=4190184  >> irqtime.bin
输入了 4190184+0 块记录
输出了 4190184+0 块记录
4190184 字节 (4.2 MB, 4.0 MiB) 已复制,10.5275 s,398 kB/s
linlin@debian:~/ls1c102$ ls -l irq*
-rw-r--r-- 1 linlin linlin 4194304  5月20日 16:24 irqtime.bin
...
root@debian:/home/linlin/ls1c102# /usr/sbin/flashrom -p ch341a_spi  -w irqtime.bin

五.后记 1.中断入口地址 本人一开始是以<架构32位精简版手册>为参考,掉入坑

1)精简版手册章节 7.4.8 例外入口地址(EENTRY)

位域          描述
-----------------------------------------
5:0           读恒为0,写忽略
31:6          入口地址的[31:6]位,意味入口地址低6位必须为0

所以一开始按MIPS传统相对boot偏移0x0380(满足低6位为0)作为中断入口 即#3处改为 li.w t0,0x1c000380

也#4处改为 .org 0x380

然后实验现象 上电->LED1亮--3秒-->LED1灭(while里)--10秒->LED1常亮(没灭过)

根据现象,猜测可能原因 情况1:能进入0x380处(点亮),但中断返回又立即发生中断(反复),没能返回到断点while(1) 情况2:没进入0x380处,但第一次应该确实是发生定时器中断,然后后续不断发生例外(是否中断标志未清?是否是其它例外不是定时器中断?),进入0x1c000000(反复),但没进入while(1)

情况1是当没清中断标志时,所以可以排除

情况2原先认为进入0x1c000000是复位,但如果是复位,则必定会再进入while(1)并10秒后发生定时器中断,所以很困惑 后来看到<处理器手册>说'CPU所有中断和例外的处理入口为0x1c000000',所以判定情况2为例外而不是复位

最后各种实验(gpio35外接led灯,关中断代码前后亮灭gpio35),判断0x380为非法中断入口,然后发生例外,进入0x1c000000(然后又好像是在关中断发生例外?反反复复),个人理解

2)后先参考loongide的入口0x80000000,但这个是内存地址,所以硬编码把中断代码复制到0x80000000开始处,实验成功 即#3处改为 li.w t0,0x80000000

在#2处添加(复制中断处理指令到内存)

li.w    t1,0x80000000

li.w    t0,0x03800c11
st.w  t0,t1,0

li.w    t0,0x290051b1
st.w  t0,t1,4

li.w    t0,0x0380040e
st.w  t0,t1,8

li.w    t0,0x0401102e
st.w  t0,t1,12

li.w    t0,0x06483800
st.w  t0,t1,16

//        ...
//    1000:       03800c11        ori             $t5, $zero, 0x3
//    1004:       290051b1        st.b            $t5, $t1, 20(0x14)
//    1008:       0380040e        ori             $t2, $zero, 0x1
//    100c:       0401102e        csrwr           $t2, 0x44
//    1010:       06483800        ertn
//    1014:       03400000        andi            $zero, $zero, 0x0

3)后又参考原厂自带程序,发现其入口是相对boot偏移0x1000,好像必须满足低12位都为0

4)再后参考<基础架构手册>章节 7.4.10 例外入口地址,入口的定义完全和精简版手册不同

位域          描述
-----------------------------------------
11:0          读恒为0,写忽略
GRLEN-1:12    入口地址所在页的页号(入口地址的计算方式还与页内偏移有关,详见<基础架构手册>章节6.3.1例外入口)

5)页 精简版手册章节 5.4.2 TLB的表项 说'龙芯架构32位精简版只支持4KB和4MB两种页大小' 而基础手册章节 5.4.2 TLB的表项 的页大小不止4KB和4MB,好像常见为16KB

我不大明白入口地址页号是不是指TLB'页'?

LS1C102无cache,无TLB,我不懂LS1C102是否具备有'页'?

6)入口地址举例 6.1)相对boot偏移0x0380

0x0380=0b00 0011 1000 0000
                   \     /
                    低6位

明显不满足基础手册

6.2)相对boot偏移0x1000

0x1000=0b01 0000 0000 0000
         /
     第12位([31:12]=0b01,第1页号?)

而且低12位为0,说满足精简版也说得过去

4KB/页=0x1000

页号1*4KB/页+页内偏移0 刚好等于0x1000这个入口地址(偏移) 所以LS1C102是不是例外入口地址寄存器的域值[31:0]满足低12位为0(刚好4KB/页)就是入口地址?

7)EENTRY值 按基础手册,该寄存器域值[31:0]只是代表含有页号部分,不一定就是地址 例如EENTRY值0x1c001000 按16KB/页,那0x1c001 * 16KB+0=0x70004000才是入口地址? 按 4KB/页,那0x1c001 * 4KB+0 =0x1c001000就是入口地址等同EENTRY值

2.原厂自带程序片段

Z:\home\linlin\ls1c102>/home/linlin/.wine/drive_c/LoongIDE/la32-tool/bin/loongarch32-newlib-elf-objdump.exe -D -b binary -m loongarch32 -EL ls1c102_old.bin
0314:err:environ:init_peb starting L"C:\\LoongIDE\\la32-tool\\bin\\loongarch32-newlib-elf-objdump.exe" in experimental wow64 mode

ls1c102_old.bin:     file format binary


Disassembly of section .data:

00000000 <.data>:
       0:       0015000d        move            $t1, $zero
       4:       50002000        b               32(0x20)        # 0x24
       8:       1438006f        lu12i.w         $t3, 114691(0x1c003)
       c:       03a721ef        ori             $t3, $t3, 0x9c8
      10:       001035f0        add.w           $t4, $t3, $t1
      14:       2880020f        ld.w            $t3, $t4, 0
      18:       00103590        add.w           $t4, $t0, $t1
      1c:       2980020f        st.w            $t3, $t4, 0
      20:       028011ad        addi.w          $t1, $t1, 4(0x4)
      24:       1500002c        lu12i.w         $t0, -524287(0x80001)
      28:       0380018c        ori             $t0, $t0, 0x0
      2c:       1500002f        lu12i.w         $t3, -524287(0x80001)
      30:       038051ef        ori             $t3, $t3, 0x14
      34:       0010358e        add.w           $t2, $t0, $t1
      38:       5fffd1cf        bne             $t2, $t3, -48(0x3ffd0)  # 0x8
      3c:       1500000c        lu12i.w         $t0, -524288(0x80000)
      40:       0380018c        ori             $t0, $t0, 0x0
      44:       1500000d        lu12i.w         $t1, -524288(0x80000)
      48:       038001ad        ori             $t1, $t1, 0x0
      4c:       580011ac        beq             $t1, $t0, 16(0x10)      # 0x5c
      50:       29800180        st.w            $zero, $t0, 0
      54:       0280118c        addi.w          $t0, $t0, 4(0x4)
      58:       5ffff9ac        bne             $t1, $t0, -8(0x3fff8)   # 0x50
      5c:       1438002c        lu12i.w         $t0, 114689(0x1c001)
      60:       0400302c        csrwr           $t0, 0xc
      64:       0015000c        move            $t0, $zero
      68:       0400102c        csrwr           $t0, 0x4
      6c:       1400002c        lu12i.w         $t0, 1(0x1)
      70:       03bffd8c        ori             $t0, $t0, 0xfff
      74:       0400118c        csrxchg         $t0, $t0, 0x4
      78:       15ffe3ec        lu12i.w         $t0, -225(0xfff1f)
      7c:       03bffd8c        ori             $t0, $t0, 0xfff
      80:       0406442c        csrwr           $t0, 0x191
      84:       14eca06c        lu12i.w         $t0, 484611(0x76503)
      88:       0388418c        ori             $t0, $t0, 0x210
      8c:       0406402c        csrwr           $t0, 0x190
      90:       0380200c        ori             $t0, $zero, 0x8
      94:       04000180        csrxchg         $zero, $t0, 0x0
      98:       15000023        lu12i.w         $sp, -524287(0x80001)
      9c:       03bdf063        ori             $sp, $sp, 0xf7c
      a0:       54212000        bl              8480(0x2120)    # 0x21c0
      a4:       03400000        andi            $zero, $zero, 0x0
      a8:       4c000020        jirl            $zero, $ra, 0
      ac:       06488000        idle            0x0
      b0:       4c000020        jirl            $zero, $ra, 0
        ...
     800:       140007ec        lu12i.w         $t0, 63(0x3f)
     804:       03be018c        ori             $t0, $t0, 0xf80
     808:       157fcc0d        lu12i.w         $t1, -262560(0xbfe60)
     80c:       0014b08c        and             $t0, $a0, $t0
     810:       038061af        ori             $t3, $t1, 0x18
     814:       02804010        addi.w          $t4, $zero, 16(0x10)
     818:       1540000e        lu12i.w         $t2, -393216(0xa0000)
     81c:       298001f0        st.w            $t4, $t3, 0
     820:       0015398e        or              $t2, $t0, $t2
     824:       298001ae        st.w            $t2, $t1, 0
     828:       1480000f        lu12i.w         $t3, 262144(0x40000)
     82c:       14b54aae        lu12i.w         $t2, 371285(0x5aa55)
     830:       0396a9ce        ori             $t2, $t2, 0x5aa
     834:       298001af        st.w            $t3, $t1, 0
     838:       15c0000f        lu12i.w         $t3, -131072(0xe0000)
     83c:       2980008e        st.w            $t2, $a0, 0
     840:       00153d8c        or              $t0, $t0, $t3
     844:       298001ac        st.w            $t0, $t1, 0
     848:       4c000020        jirl            $zero, $ra, 0
        ...
    1000:       0400c435        csrwr           $x, 0x31              // 中断入口,相对boot偏移0x1000
    1004:       15000055        lu12i.w         $x, -524286(0x80002)
    1008:       29bff2ac        st.w            $t0, $x, -4(0xffc)    //--v-- 现场保护
    100c:       29bfe2ad        st.w            $t1, $x, -8(0xff8)
    1010:       29bfd2ae        st.w            $t2, $x, -12(0xff4)
    1014:       29bfc2af        st.w            $t3, $x, -16(0xff0)
    1018:       29bfb2b0        st.w            $t4, $x, -20(0xfec)
    101c:       29bfa2b1        st.w            $t5, $x, -24(0xfe8)
    1020:       29bf92b2        st.w            $t6, $x, -28(0xfe4)
    1024:       29bf82b3        st.w            $t7, $x, -32(0xfe0)
    1028:       29bf72b4        st.w            $t8, $x, -36(0xfdc)
    102c:       29bed2a4        st.w            $a0, $x, -76(0xfb4)
    1030:       29bec2a5        st.w            $a1, $x, -80(0xfb0)
    1034:       29beb2a6        st.w            $a2, $x, -84(0xfac)
    1038:       29bea2a7        st.w            $a3, $x, -88(0xfa8)
    103c:       29be92a8        st.w            $a4, $x, -92(0xfa4)
    1040:       29be82a9        st.w            $a5, $x, -96(0xfa0)
    1044:       29be72aa        st.w            $a6, $x, -100(0xf9c)
    1048:       29be62ab        st.w            $a7, $x, -104(0xf98)
    104c:       29be52a1        st.w            $ra, $x, -108(0xf94)
    1050:       29be42a3        st.w            $sp, $x, -112(0xf90)  //--^--
    1054:       0400140c        csrrd           $t0, 0x5              // 读ESTAT
    1058:       036ef18d        andi            $t1, $t0, 0xbbc
    105c:       400065a0        beqz            $t1, 100(0x64)  # 0x10c0
    1060:       0340118d        andi            $t1, $t0, 0x4
    1064:       44002da0        bnez            $t1, 44(0x2c)   # 0x1090
    1068:       0340218d        andi            $t1, $t0, 0x8
    106c:       44002da0        bnez            $t1, 44(0x2c)   # 0x1098
    1070:       0340418d        andi            $t1, $t0, 0x10
    1074:       44002da0        bnez            $t1, 44(0x2c)   # 0x10a0
    1078:       0340818d        andi            $t1, $t0, 0x20
    107c:       44002da0        bnez            $t1, 44(0x2c)   # 0x10a8
    1080:       0342018d        andi            $t1, $t0, 0x80
    1084:       44002da0        bnez            $t1, 44(0x2c)   # 0x10b0
    1088:       0360018d        andi            $t1, $t0, 0x800
    108c:       44002da0        bnez            $t1, 44(0x2c)   # 0x10b8
    1090:       541cd000        bl              7376(0x1cd0)    # 0x2d60
    1094:       50003c00        b               60(0x3c)        # 0x10d0
    1098:       541d0800        bl              7432(0x1d08)    # 0x2da0
    109c:       50003400        b               52(0x34)        # 0x10d0
    10a0:       541d6000        bl              7520(0x1d60)    # 0x2e00
    10a4:       50002c00        b               44(0x2c)        # 0x10d0
    10a8:       541d7800        bl              7544(0x1d78)    # 0x2e20
    10ac:       50002400        b               36(0x24)        # 0x10d0
    10b0:       541c2000        bl              7200(0x1c20)    # 0x2cd0
    10b4:       50001c00        b               28(0x1c)        # 0x10d0
    10b8:       541e9800        bl              7832(0x1e98)    # 0x2f50
    10bc:       50001400        b               20(0x14)        # 0x10d0
    10c0:       0341018d        andi            $t1, $t0, 0x40
    10c4:       40000da0        beqz            $t1, 12(0xc)    # 0x10d0
    10c8:       541de800        bl              7656(0x1de8)    # 0x2eb0
    10cc:       50000400        b               4(0x4)  # 0x10d0
    10d0:       15000055        lu12i.w         $x, -524286(0x80002)
    10d4:       28bff2ac        ld.w            $t0, $x, -4(0xffc)    //--v-- 现场恢复
    10d8:       28bfe2ad        ld.w            $t1, $x, -8(0xff8)
    10dc:       28bfd2ae        ld.w            $t2, $x, -12(0xff4)
    10e0:       28bfc2af        ld.w            $t3, $x, -16(0xff0)
    10e4:       28bfb2b0        ld.w            $t4, $x, -20(0xfec)
    10e8:       28bfa2b1        ld.w            $t5, $x, -24(0xfe8)
    10ec:       28bf92b2        ld.w            $t6, $x, -28(0xfe4)
    10f0:       28bf82b3        ld.w            $t7, $x, -32(0xfe0)
    10f4:       28bf72b4        ld.w            $t8, $x, -36(0xfdc)
    10f8:       28bed2a4        ld.w            $a0, $x, -76(0xfb4)
    10fc:       28bec2a5        ld.w            $a1, $x, -80(0xfb0)
    1100:       28beb2a6        ld.w            $a2, $x, -84(0xfac)
    1104:       28bea2a7        ld.w            $a3, $x, -88(0xfa8)
    1108:       28be92a8        ld.w            $a4, $x, -92(0xfa4)
    110c:       28be82a9        ld.w            $a5, $x, -96(0xfa0)
    1110:       28be72aa        ld.w            $a6, $x, -100(0xf9c)
    1114:       28be62ab        ld.w            $a7, $x, -104(0xf98)
    1118:       28be52a1        ld.w            $ra, $x, -108(0xf94)
    111c:       28be42a3        ld.w            $sp, $x, -112(0xf90)  //--^--
    1120:       0400c415        csrrd           $x, 0x31
    1124:       06483800        ertn
        ...
    1130:       157fda0c        lu12i.w         $t0, -262448(0xbfed0)
    1134:       2880018c        ld.w            $t0, $t0, 0
    1138:       00149184        and             $a0, $t0, $a0

3.loongide代码片断 主要是有关CPU定时器中断部分

1)启动 1.1)/home/linlin/loongprj/myls1c102/core/start.S

...
start:

    li.w        t0, CSR_CRMD_IE             /* disable interrupt */
    csrxchg     zero, t0, LA_CSR_CRMD       /* clear IE bit */

    li.w        t1, CSR_ECFG_IM_MASK        /* set interrupt mask 0x1fff */
    csrwr       t1, LA_CSR_ECFG

    li.w        t2, 0x80000000              /* set common-exception entry */
    csrwr       t2, LA_CSR_EBASE
...
    la.abs      t0, bsp_start               /* 跳到 bsp_start 入口 */
    jirl        ra, t0, 0
    nop
loop:                                       /* 如果main函数返回, 则死循环 */
    nop
    b           loop
    nop
...

1.2)/home/linlin/loongprj/myls1c102/core/bsp_start.c

...
void bsp_start(void)
{
    unsigned int eentry;

    loongarch_interrupt_disable();

    /**
     * 把中断代码复制到0x80000000
     */
    eentry = __csrrd_w(LA_CSR_EBASE);
    memcpy((void *)eentry, (void *)exception_common_entry, 32);

    g_pmu->CommandW = 0;            // CMDSR_SLEEP_EN;

    OSC_init_outer();               // 设置为外部8M时钟, TODO delay_ms() 不运行。
    // 不使用外部 8M 时钟会导致串口打印乱码,因为内部时钟的频率不精确,和波特率不匹配。
    
    get_frequency();                /* 获取频率配置 */

    ls1c102_init_isr_table();       /* 初始化中断向量表 */

    console_init(115200);           /* initialize Console */

    Clock_initialize();             /* initialize system ticker */

    loongarch_interrupt_enable();   /* Enable all interrupts */

    main();                         /* goto main function */
    
    while (1);                      /* XXX never goto HERE! */
    
    return;
}
...

2)CPU定时器 主要是为实现延时功能

2.1)/home/linlin/loongprj/myls1c102/core/tick.c

...
#include <stdio.h>
#include <stdint.h>
#include <larchintrin.h>
#include "cpu.h"
#include "regdef.h"
#include "ls1c102.h"
#include "ls1c102_irq.h"
extern void printk(const char *fmt, ...);
//-----------------------------------------------------------------------------
#define MICROSECONDS_PER_TICKS  2000                /* us per tick */
extern unsigned int cpu_frequency;
static volatile unsigned int delay_1us_insns;       /* 延时1us的指令数 */
static volatile unsigned int Clock_driver_ticks;    /* Clock ticks since initialization */
//-----------------------------------------------------------------------------
unsigned int get_clock_ticks(void)
{
    return Clock_driver_ticks;
}
/*
 *  Clock_isr
 *
 *  This is the clock tick interrupt handler.
 */
void ls1c102_ticker_isr(int vector, void *arg)
{
    ++Clock_driver_ticks;           /* 计数加 1 */
}
/*
 * Clock_initialize
 */
void Clock_initialize(void)
{
    unsigned int tcfg;
    
    Clock_driver_ticks = 0;
    __csrwr_w(0, LA_CSR_TVAL);
    __csrwr_w(0, LA_CSR_CNTC);
    /**
     * 安装中断向量
     */
    ls1c102_install_isr(LS1C102_IRQ_TICKER,
                        ls1c102_ticker_isr,
                        NULL);
    /*
     * XXX 每4个CPU节拍+1 ?
     */
    tcfg = (cpu_frequency / 1000) * MICROSECONDS_PER_TICKS / 1000 / 4;
    tcfg <<= CSR_TCFG_VAL_SHIFT;
    tcfg |= CSR_TCFG_PERIOD | CSR_TCFG_EN;
    __csrwr_w(tcfg, LA_CSR_TCFG);
    printk("\nClock: %i us per tick\n", MICROSECONDS_PER_TICKS);
    delay_1us_insns = cpu_frequency / 1000000;
}
//-----------------------------------------------------------------------------
/*
 * us 级延时
 */
void delay_us(int us)
{
...
}
/*
 * ms 级延时
 */
void delay_ms(int ms)
{
    register unsigned int startTicks, endTicks, curTicks;
    endTicks = ms * 1000 / MICROSECONDS_PER_TICKS;
    /*
     * 如果关中断, 调用 delay_us 延时
     */
    if (!endTicks || !(__csrrd_w(LA_CSR_CRMD) & CSR_CRMD_IE))
    {
        delay_us(ms * 1000);
        return;
    }
    startTicks = Clock_driver_ticks;
    endTicks  += startTicks;
    while (1)
    {
        curTicks = Clock_driver_ticks;
        /*
         * 防止数值溢出
         */
        if (((endTicks > startTicks) && (curTicks >= endTicks)) ||
            ((endTicks < startTicks) && (curTicks < startTicks) && (curTicks >= endTicks)))
            break;
    }
}

delay_us代码略去,实际用循环,没用到中断 delay_ms代码的Clock_driver_ticks由中断产生,但仍然循环读 调用关系

Clock_initialize
        |
    ls1c102_install_isr( ,ls1c102_ticker_isr, )
                                   |
                              ++Clock_driver_ticks

3)中断处理框架 3.1)/home/linlin/loongprj/myls1c102/core/irq.c

...
void c_interrupt_handler(unsigned int *stack)
{
...
    /**
     * IP11 定时器中断
     */
    if (estat & CSR_ECFG_TI)
    {
        ls1c102_dispatch_isr(LS1C102_IRQ_TICKER, stack);
        __csrwr_w(1, LA_CSR_TINTCLR);               /* 清除 Timer 中断 */
    }
...

LS1C102各中断是共同的入口地址,所以需读取ESTAT(例外状态寄存器)区分

这是loongide 1.2正式版生成代码,可以看出是先处理中断,后清除中断标志

3.2)loongide另一版本生成的代码,却是先清中断再处理

...
    if (estat & CSR_ECFG_TI)
    {
        __csrwr_w(1, LA_CSR_TINTCLR);               /* 清除 Timer 中断 */
        ls1c102_dispatch_isr(LS1C102_IRQ_TICKER, stack);        
    }
...

4 ) 4.1)/home/linlin/loongprj/myls1c102/ls1c102/include/cpu.h

...
/******************************************************************************
 * LoongArch32 CSR Registers
 */
/**
 * basic csr register
 */
#define LA_CSR_CRMD                 0x000       /* current mode */
#define CSR_CRMD_WE                 (1<<9)
...
#define CSR_CRMD_IE                 (1<<2)
...
#define LA_CSR_ECFG                 0x004       /* Exception config */
#define CSR_ECFG_IPI                (1<<12)        // 核间中断
#define CSR_ECFG_TI                    (1<<11)        // 定时器中断
#define CSR_ECFG_HWI7                (1<<9)
...
#define CSR_ECFG_HWI0                (1<<2)
#define CSR_ECFG_SWI1                (1<<1)
#define CSR_ECFG_SWI0                (1<<0)
...
#define LA_CSR_EBASE                0x00C       /* Exception entry base address */   // <架构手册>为EENTRY
...
/**
 * Timer registers
 */
#define LA_CSR_TMID                 0x040       /* Timer ID */
#define LA_CSR_TCFG                 0x041       /* Timer config */
#define CSR_TCFG_VAL_SHIFT          2
#define CSR_TCFG_PERIOD             (1<<1)
#define CSR_TCFG_EN                 (1<<0)
#define LA_CSR_TVAL                 0x042       /* Timer value */
#define LA_CSR_CNTC                 0x043       /* Timer offset */
#define LA_CSR_TINTCLR              0x044       /* Timer interrupt clear */
#define CSR_TINTCLR_TI              (1<<0)

...
/**
 * 开中断
 */
#define loongarch_interrupt_enable() \
    __csrxchg_w(CSR_CRMD_IE, CSR_CRMD_IE, LA_CSR_CRMD)
/**
 * 关中断
 */
#define loongarch_interrupt_disable() \
    __csrxchg_w(~CSR_CRMD_IE, CSR_CRMD_IE, LA_CSR_CRMD)
...

4.2 ) 在loongide界面搜索菜单查找打开的工程项目是搜不到__csrxchg_w定义 工程项目是有引用/home/linlin/.wine/drive_c/LoongIDE/la32-tool/lib/gcc/loongarch32-newlib-elf/8.3.0/include/的 可能loongide界面搜索菜单查找功能只搜项目自身源码,不会搜引用的文件

到文件管理器搜索/home/linlin/.wine/drive_c/LoongIDE/目录下文件含__csrxchg_w内容 搜索到在/home/linlin/.wine/drive_c/LoongIDE/la32-tool/lib/gcc/loongarch32-newlib-elf/8.3.0/include/larchintrin.h

...
/* Assembly instruction format:    rd, rj, ui14.  */
/* Data types in instruction templates:  USI, USI, USI, USI.  */
#define __csrxchg_w(/*unsigned int*/ _1, /*unsigned int*/ _2, /*ui14*/ _3) \
    ((unsigned int) __builtin_loongarch_csrxchg_w ((unsigned int) (_1), \
            (unsigned int) (_2), (_3)))
...

继续搜索/home/linlin/.wine/drive_c/LoongIDE/目录下文件含__builtin_loongarch_csrxchg_w内容,但是搜不到定义 难道__builtin_的意思是内置在编译器?为什么不用源码内嵌汇编宏定义形式?

因此下面干脆就自己写一段C代码反汇编看看

5)开中断

#define CSR_CRMD_IE                 (1<<2)
#define LA_CSR_CRMD                 0x000       /* current mode */
#define __csrxchg_w(/*unsigned int*/ _1, /*unsigned int*/ _2, /*ui14*/ _3) \
    ((unsigned int) __builtin_loongarch_csrxchg_w ((unsigned int) (_1), \
            (unsigned int) (_2), (_3)))
#define loongarch_interrupt_enable() \
    __csrxchg_w(CSR_CRMD_IE, CSR_CRMD_IE, LA_CSR_CRMD)
int main(void)
{
  loongarch_interrupt_enable();
}
Z:\home\linlin\ls1c102>/home/linlin/.wine/drive_c/LoongIDE/la32-tool/bin/loongarch32-newlib-elf-gcc.exe -G0 -O2 -I . -c main.c
Z:\home\linlin\ls1c102>/home/linlin/.wine/drive_c/LoongIDE/la32-tool/bin/loongarch32-newlib-elf-ld.exe -g -T ld.script -o main.exe  main.o
Z:\home\linlin\ls1c102>/home/linlin/.wine/drive_c/LoongIDE/la32-tool/bin/loongarch32-newlib-elf-objcopy.exe -O binary main.exe main.bin
Z:\home\linlin\ls1c102>/home/linlin/.wine/drive_c/LoongIDE/la32-tool/bin/loongarch32-newlib-elf-objdump.exe -D -b binary -m loongarch32 -EL main.bin
main.bin:     file format binary

Disassembly of section .data:
00000000 <.data>:
   0:   00000010        0x00000010
   4:   00000000        0x00000000
   8:   00527a01        0x00527a01
   c:   01017c01        fadd.d  $f1,$f0,$f31
  10:   00030d0b        0x00030d0b
  14:   00000010        0x00000010
  18:   00000018        0x00000018
  1c:   1c000030        pcaddu12i       $r16,1(0x1)
  20:   00000010        0x00000010
        ...
  30:   0280100c        addi.w  $r12,$r0,4(0x4)
  34:   0400018c        csrxchg $r12,$r12,0x0
  38:   00150004        move    $r4,$r0
  3c:   4c000020        jirl    $r0,$r1,0
Z:\home\linlin\ls1c102>

已见到loongarch_interrupt_enable()对应是

addi.w  $r12,$r0,4
csrxchg $r12,$r12,0x0

6)关中断

#define CSR_CRMD_IE                 (1<<2)
#define LA_CSR_CRMD                 0x000
#define __csrxchg_w(/*unsigned int*/ _1, /*unsigned int*/ _2, /*ui14*/ _3) \
    ((unsigned int) __builtin_loongarch_csrxchg_w ((unsigned int) (_1), \
            (unsigned int) (_2), (_3)))
#define loongarch_interrupt_disable() \
    __csrxchg_w(~CSR_CRMD_IE, CSR_CRMD_IE, LA_CSR_CRMD)
int main(void)
{
  loongarch_interrupt_disable();
}

编译、链接后,反汇编

Z:\home\linlin\ls1c102>/home/linlin/.wine/drive_c/LoongIDE/la32-tool/bin/loongarch32-newlib-elf-objdump.exe -D -b binary -m loongarch32 -EL main.bin
main.bin:     file format binary


Disassembly of section .data:

00000000 <.data>:
   0:   00000010        0x00000010
   4:   00000000        0x00000000
   8:   00527a01        0x00527a01
   c:   01017c01        fadd.d          $fa1, $fa0, $fs7
  10:   00030d1b        0x00030d1b
  14:   00000010        0x00000010
  18:   00000018        0x00000018
  1c:   00000014        0x00000014
  20:   00000014        0x00000014
        ...
  30:   02bfec0c        addi.w          $t0, $zero, -5(0xffb)   // 0xffb=0b111111111011,对12位立即数0xffb=-5,对32位数-5=0xFFFFFffb,t0=0-5=0+0xFFFFFffb ?
  34:   0280100d        addi.w          $t1, $zero, 4(0x4)      // 0x4  =0b         100,第2位是CRMD.IE(全局中断使能,高有效),写掩码
  38:   040001ac        csrxchg         $t0, $t1, 0x0           // 0x0为CRMD(当前模式信息寄存器),t0的第2位值为0写入到CRMD对应t1的IE位
  3c:   00150004        move            $a0, $zero
  40:   4c000020        jirl            $zero, $ra, 0

Z:\home\linlin\ls1c102>

已见到loongarch_interrupt_disable()对应是

addi.w          $t0, $zero, -5
addi.w          $t1, $zero, 4
csrxchg         $t0, $t1, 0x0

4.addi.w 反汇编addi.w显示的格式 -5(0xffb),应该是针对12位立即数显示0xffb,表示的数学意义是减去5或等效加上0xFFFFFffb,不是表达加上0xffb 12位数范围-0x800 ~ -1,0 ~ 0x7ff 编写汇编源码会要求按范围格式,如 1 ) addi.w t0,zero, 0x800 // -0x801、0xffb、0x800等都为Immediate overflow

gcc编译结果出错 main.S: Assembler messages: main.S:3: Fatal error: Immediate overflow. format: s

2 ) addi.w t0,zero, 0x7ff 编译成功

3 ) addi.w t0,zero, -0x800

编译成功、反汇编结果

Disassembly of section .data:
00000000 <.data>:
   0:   02a0000c        addi.w  $r12,$r0,-2048(0x800)

4 ) addi.w t0,zero, -0x7ff

Disassembly of section .data:
00000000 <.data>:
   0:   02a0040c        addi.w  $r12,$r0,-2047(0x801)

指令编码的第21:10为立即数

0x02a0040c=0b10 10 10 0000 0000 01 00 0000 1100
                   \             /
                         立即数  =0b1000 0000 0001=0x801