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