Bootstrap

LoongArch32 指令集探索学习1:初入龙芯

1. 1.发现问题

1.1. LoongArch是个啥?

龙芯这个名字一听就是咱这龙的传人该用的东西,这是从MIPS指令集中新设计的指令集,官方名字叫LoongArch,简称LA。这个指令集分为开放的LoongArch32精简版和LoongArch64,前者的32位指令集全部开源,后者64位只开放基础指令集,高级部分的二进制翻译暂且没有详细文档,而是由开发者在对GCC、Linux等基础软件贡献中露出。

为何选MIPS?
唐志敏仔细分析多种指令系统后,最终决定选择采用RISC结构的MIPS指令系统。

这是一种兼容方案,主要是出于市场考虑:MIPS应用面比较广,服务器和嵌入式系统都可以用;同时,技术壁垒比较低,知识产权障碍较少。
——《孕育“龙芯”的478个日夜》|《中国科学报》,2024年8月23日

从龙芯开始设计的2001年到现在的2024年,龙芯团队已经整整奋斗了23年,国家IT产业体系全自主化目标很快就要实现了。龙芯一号的小名叫“狗剩”,音译为“Godson”。

在这黎明前的曙光期,在大学期间的2021年刚释放出LoongArch,就对其产生了极大的兴趣,但是苦于入门困难,很难在我能承受的经济范围内拿到一片龙芯的开发板进行研究。

1.2. LoongArch32

到现在2024年,我看到龙芯与合作伙伴推出了基于LS2K0300芯片的久久派开发板,只要99¥就能拥有一个LoongArch64指令集架构的开发板,性价比非常高,我也入手了。在使用的时候发现,用裸机开发的思路似乎挺难搞,对Linux和Uboot也不甚熟悉。于是想:龙芯有没有嵌入式的开发板,对标STM32F103这样的呢?

于是我找到了LoongArch32架构的两款芯片:ls1C102和ls1C103,其中102有64引脚、6路ADC和SPI、UART、IIC若干,能用于物联网,同时也有LoongIDE移植好的RT-Thread、FreeRTOS等实时操作系统,能和我目前的一个开发需求吻合。何不尝试一下使用全国产的芯片呢?

然后我就在淘宝找到了龙芯的销售,试探性买了几片1C102和1C103的芯片,自己做个开发板。5r的芯片售价确实能承受了,总体做成一顿饭钱的开发板,非常香。

1.3. 开发板制作

第一个龙芯的开发板,我做的是ls1c103芯片的小开发板,能插在面包板上,留出了JTAG的接口。

1.4. 程序烧录

怎么烧录程序呢?有两种办法:

一种是LS1C103使用SPI协议,外接SPI Flash,用烧录器烧录程序。最大支持256MB。还可以用“安装模式”,从外部Flash中读取程序,给内置Flash和连接的SPI Flash烧写程序。“安装模式”是方便工业生产出厂烧
录程序使用的,对个人玩家暂且还没有样例程序。

另一种是使用JTAG调试器连接LS1C103芯片开发板。而JTAG调试器可选LA-LINK(128r)、LX-LINK(?¥)和原厂的EJTAG(980),价格从低到高。于是我选择了价格最低的方案,LA-LINK是使用了OpenOCD的技术,增加了loongarch_la132这个CPU核配置,并且实现了LS1C102的烧写算法。同时也适配la132这个IP核的芯片。

1.5. 点灯遇到问题

很好,从CPU选型、原理图绘制、打版、焊接都没有问题后,接下来可就是九九八十一难了。

1.5.1. 首先遇到的就是“如何进入JTAG模式”这个问题。

LS1C103的启动模式由PA03PB09共同决定。
![[Pasted image 20240903185417.png]]
上图摘自《龙芯1C103处理器数据手册_V1.2》。顺带一提,龙芯的芯片数据手册面向嵌入式硬件工程师讲芯片电气功能,用户手册面向程序员讲寄存器。二者需综合观看。

所以要进入JTAG模式,PB09就需要下拉,也就是用10kΩ电阻接地。

然后接入LA-JTAG,用其提供的openocd执行:

.\openocd.exe -f .\lalink.cfg -f .\ls1c103.cfg

其中lalink.cfgls1c103.cfg文件内容如下:

adapter driver lalink

ls1c103.cfg的内容稍长,定义了JTAG通信速率、Flash大小、地址、IRAM地址、IR指令大小等信息:

adapter speed 2000

reset_config srst_only


transport select jtag
set _CHIPNAME la132


#irlen ir寄存器长度

jtag newtap $_CHIPNAME cpu -irlen 4 -ircapture 0x01 -irmask 0x1f -expected-id 0x5a5a5a5a -ignore-bypass

  
set _TARGETNAME $_CHIPNAME.cpu
set _ENDIAN little

target create $_TARGETNAME loongarch_la132 -endian $_ENDIAN -chain-position $_TARGETNAME


#0x10000000  4K
set _WORKAREASIZE 0x1000

$_TARGETNAME configure -work-area-phys 0x10000000 -work-area-size $_WORKAREASIZE -work-area-backup 0


#0x1c000000 64k
set _FLASHNAME $_CHIPNAME.flash
set _FLASH_SIZE 0x10000


flash bank $_FLASHNAME la132 0x1c000000 $_FLASH_SIZE 0 0 $_TARGETNAME

该配置文件使用了la132这个烧写算法。若要用其他JTAG调试器支持龙芯,例如CH347F等芯片,只要在target中添加loongarch_la132支持、在flash中添加la132、ls1c103、ls1c102烧录算法即可。LoongArch的JTAG ID寄存器值为0x5a5a5a5a或者0x20010819。对调试器设计感兴趣,可参考《龙芯ejtag仿真器原理和实现(2023.9.16)》。
个人推测,0x20010819对应着中科院计算所龙芯课题组第一次在Godson处理器上跑通BIOS、Linux系统的那个日子。

技术参考:
• 在EJTAG运行sudo ./ejtag_debug_usb -t, 运行jtagregs d8 1 1来读处理器的ejtag id寄存器,如果是0x20010819或者是0x5a5a5a5a都说明连接正确
——摘自《龙芯ejtag仿真器手册》 | 龙芯中科

1.5.2. 调试龙芯拓展阅读

“在努力攻坚“狗剩”的几个月里,团队成员付出了难以想象的艰辛。

胡伟武记得,设计工作的全面铺开是2001年“五一”假期后。当时所里通知,10月要展示处理器设计方面的成果。由此,团队进入了夜以继日的工作状态。

胡伟武说,当出现一个错误时,应用程序、操作系统以及处理器本身都是怀疑对象,需要多方面协调与分析,每次都是连续几天几夜的鏖战。

最后一次联合调试尤为“惨烈”。“即使发现一个很小的错误,修改一次设计再形成新的烧制文件,至少也需要8小时。只有24小时不间断,才能保证一天有几次修改的机会。”胡伟武说,8月中旬,为了赶进度,几名骨干决定冒险把联调时间提前一周。

他们周一晚上开始联调,周二凌晨4点写入烧制文件,没有任何动静。发现问题后,他们赶紧修改并赶在中午12点之前形成新的烧制文件,写入后仍无响应。晚上接着调试,发现处理器插卡上有两个焊点短路,擦除后,主板上的液晶显示器终于显示出“Godson”这几个字母。

“跑通了!”大家一片欢呼。团队一鼓作气,于周三晚上成功启动了经过改造的基础输入输出系统(BIOS)。周四一早,他们又试图启动Linux操作系统。不巧,每次都在最后关头报错,直到周六晚饭时才发现问题。大家匆匆扒拉几口饭继续修改,改好已是8月19日凌晨2点多。

2时42分,屏幕上终于出现了“login”字样,登录进去之后可以正常操作。这就是“龙芯1号”,它终于走通了关键的第一步。

胡伟武兴奋地给唐志敏打电话:“‘狗剩’跑起来了!”那天,在场的6位成员都极度疲惫,但也都兴奋得毫无睡意,一直聊到天亮。胡伟武回忆,那个凌晨的北京电闪雷鸣、风雨交加,他回去后连续睡了20多个小时才把觉补回来。”
——《孕育“龙芯”的478个日夜》|《中国科学报》,2024年8月23日

试图全面理解LA132处理器核

LA132,也就是LoongArch32位指令集架构的处理器核心名称,在LS1C102和LS1C103芯片上使用。

1. 问题:点灯失败

我把写好的程序用修改后的LA-LINK烧录进LS1C103中,可是灯不亮。为什么呢?

受到《深入理解Linux进程与内存》书籍的启发,要找到问题,需要深入理解这个CPU到底在干啥,为啥他不听我话,他明明是很听话的“狗剩”Doge。

1.1. LA132指令集架构分析

CPU到底怎么跑的?可以上FPGA模拟出来一个IP核跑,龙芯开源了一个基于LA32R(R为精简版)的设计OpenLA500。同时需要配套书籍《CPU设计实战:LoongArch版 (bookdown.org)》学习CPU是怎么设计的。作为“计算机组成原理”的课程,我觉得再好不过了。以前计组课设也是做MIPS架构的五级流水CPU,实现加减乘除指令,我手动实现了一遍,当他真的在vivado上跑起来的时候,我是非常非常开心的。

在本书中,我们将结合自身20年自主CPU的研发实践,尽可能深入浅出地介绍如何从零开始一步步设计出一个入门级的CPU……
——《CPU设计实战:LoongArch版 (bookdown.org)

1.2. 以LS1C103为例看LA132处理器核

![[Pasted image 20240904001056.png]]
取指、译码、执行、访存、回写这五级流水看,4KB的IRAM和4KB的DRAM是做在CPU核内部。外部的FLASH是挂载在AXI3总线上。

对应到LS1C103上的LA132处理器核的介绍:

1.2.1. 32位单发射

对比99派芯片LS2K0300内部的LA264核,是双发射。

单发射就是CPU一次时钟周期只从存储器取一条指令;一次时钟周期只能执行一次指令计算。

双发射就是CPU一个时钟周期可以同时执行两个指令。

再对比3A6000的6发射,就对其性能有更深刻理解了。

1.2.2. 顺序执行、三级流水

三级流水对应下图:
![[Pasted image 20240904001550.png]]

基于ARMv8指令集架构的鲲鹏920处理器设计。https://www.hikunpeng.com/doc_center/source/zh/perftuning/progtuneg/kunpengprogramming_05_0008.html

理想状态下,处理器核同时处理三条指令的取指fetch、译码decode、执行execute三步骤中其中一步,不浪费任何一个处理单元。这里可以参考ARM的流水线文档。

顺序执行的反义词是乱序执行。可以暂时放下,不是重点。

1.2.3. 无cache、MMU

取数据时候可以用cache加速RAM存取数据速度。但是没有cache就意味着设计简单很多。cache是要从RAM中高速存取大量数据时会用到,例如龙芯3A6000就有4个64位六发射超标量LA664处理器核,有16MB的分体共享3级cache。

LA264内核的2K0300有L1 Cache(I/D) 32KB, L2 Cache 512KB。采用哈佛架构,指令RAM和数据RAM分开。

MMU是内存管理单元,功能是将虚拟地址转换为物理地址。

1.2.4. JTAG调试接口支持断点、单步

也就是说可以用JTAG协议连接这个芯片进行调试,由上文分析得JTAG识别ID为0x5a5a5a5a。LoongArch架构的新芯片都支持JTAG协议。

1.2.5. 4KB 指令SRAM、4KB数据SRAM

说明处理器采用与STM32相同的架构:哈弗架构。
详细地说,就是在LS1C103的地址空间中,0x1000_0000 - 0x1000_0fff 为IRAM。0x1000_1000 - 0x1000_1fff为DRAM。在这颗芯片上,二者都可取指。

SRAM是静态随机存储器,只要通电就能保存数据。和DRAM的不同在于DRAM要时常刷新数据,访存速度也比SRAM慢一些,好处在于DRAM能用相对低成本将存储空间做很大。

2. 程序启动流程分析

3. 程序编译分析

4. JTAG解析

JTAG协议由IEEE1149.1定义,龙芯EJTAG也是参考此标准。龙芯EJTAG的IR为5位,DR是32-64位。
![[Pasted image 20240904133527.png]]
![[Pasted image 20240904133542.png]]

5. 中断/例外

中断就是在正常干活的一个CPU小人,被另一个人拍了拍肩膀:嘿,有情况!

这个“情况”,就是中断信号。

然后这个CPU小人就要放下手里的活看看到底发生了啥,是有八卦?还是外面下雨了?还是以前买的彩票中奖了?

;