LoongArch64 工具链构建

最近因为龙芯杯的原因,想自己搞个 LoongArch64 的交叉编译工具链试试,结果遇到了很多坑,最后终于算是搞出来了。 一开始是想搞一个 newlib 的工具链,比较简单,而且之前做过一个仓库:jiegec/riscv-toolchain,就是构建的 riscv64-unknown-elf 工具链,照着 riscv-gnu-toolchain 就可以了。不过研究发现,newlib 还不支持 loongarch,目前只有 glibc 支持,只好硬着头皮上了。 于是我就在 riscv-toolchain 的基础上搞 loongarch64-unknown-linux-gnu,也就是带 glibc 的工具链,结果发现遇到很多坑。首先编译 libgcc 的时候就找不到头文件,于是先要从 glibc 和 linux 安装头文件到 sysroot 里面,对于 sysroot 里面的头文件路径到底是 include 还是 usr/include 也折腾了半天。然后编译 libgcc 又各种出问题,最后折腾了半天,结果是 gcc stage1 和 glibc 都没问题,gcc stage2 会报链接错误,但是不管它也能用,可以编译出正常的程序,毕竟 libc 是好的。 于是转念一想,要不要试试 crosstool-ng。克隆了一份上游的版本,照着 riscv 的部分抄了一份变成了 loongarch,然后把 config 里面的 linux/glibc/gcc/binutils-gdb 都替换为 custom location,这样我就可以用上游的最新版本了。中途还遇到了 crosstool-ng 对 gcc 12/13 不兼容的 bug,还好下面有人提出了解决方法。这些都搞定以后,终于构建出了一个完整的 loongarch64-unknown-linux-gnu 工具链。仓库地址是 jiegec/ct-ng-loongarch64,需要配合添加了 LoongArch 的 jiegec/crosstool-ng loongarch 分支 使用。

Read More

试用沁恒 CH32V307 评估板

背景 之前有一天看到朋友在捣鼓 CH32V307,因此自己也萌生了试用 CH32V307 评估板的兴趣,于是在沁恒官网申请样品,很快就接到电话了解情况,几天后就顺丰送到了,不过因为疫情原因直到现在才拿到手上,只能说疫情期间说不定货比人还快。 开箱 收到的盒子里有一个 CH32V307 评估板,和一个 WCH-Link,相关资料可以在 官网 或者 openwch/ch32v307 下载。在说明书中有如下的图示: 板子自带的跳线帽不是很多,建议自备一些,或者用杜邦线替代。比较重要的是 WCH-Link 子板上 CH549 和 CH2V307 连接的几个信号,和下面 BOOT0/1 的选择。 WCH-Link 可以看到评估板自带了一个 WCH-Link,所以不需要附赠的那一个,直接把 11 号 Type-C 连接到电脑上即可。这里还遇到一个小插曲,用 Type-C to Type-C 的线连电脑上不工作,连 PWR LED 都点不亮,换一根 Type-A to Type-C 的就可以,没有继续研究是什么原因。电脑上可以看到 WCH-Link 的设备:VID=1a86, PID=8010。比较有意思的是,在 RISC-V 模式(CON 灯不亮)的时候 PID 是 8010,ARM 模式(CON 灯亮)的时候 PID 是 8011,从 RISC-V 模式切换到 ARM 模式的方法是连接 TX 和 GND 后上电,反过来要用 MounRiver,详见 WCH-Link 使用说明 V1.0 V1.3 和原理图 V1.1。

Read More

导出 Vivado 下载 Bitstream 的 SVF 文件

背景 最近在研究如何实现一个远程 JTAG 的功能,目前实现在 jiegec/jtag-remote-server,实现了简单的 XVC 协议,底层用的是 libftdi 的 MPSSE 协议来操作 JTAG。但是,在用 Vivado 尝试的时候,SysMon 可以正常使用,但是下载 Bitstream 会失败,所以要研究一下 Vivado 都做了什么(目前已经修好,是最后一个字节的部分位读取的处理问题)。 SVF SVF 格式其实是一系列的 JTAG 上的操作。想到这个,也是因为在网上搜到了一个 dcfeb_v45.svf,里面描述的就是一段 JTAG 操作: // Created using Xilinx Cse Software [ISE - 12.4] // Date: Mon May 09 11:00:32 2011 TRST OFF; ENDIR IDLE; ENDDR IDLE; STATE RESET; STATE IDLE; FREQUENCY 1E6 HZ; //Operation: Program -p 0 -dataWidth 16 -rs1 NONE -rs0 NONE -bpionly -e -loadfpga TIR 0 ; HIR 0 ; TDR 0 ; HDR 0 ; TIR 0 ; HIR 0 ; HDR 0 ; TDR 0 ; //Loading device with 'idcode' instruction.

Read More

浅谈乱序执行 CPU(二)

背景 之前写过一个浅谈乱序执行 CPU,随着学习的深入,内容越来越多,页面太长,因此把后面的一部分内容独立出来,变成了这篇博客文章。之后也许会有(三)(四)等等。 内存访问 内存访问是一个比较复杂的操作,它涉及到缓存、页表、内存序等问题。在乱序执行中,要尽量优化内存访问对其他指令的延迟的影响,同时也要保证正确性。这里参考的是 BOOM 的 LSU 设计。 首先是正确性。一般来说可以认为,Load 是没有副作用的(实际上,Load 会导致 Cache 加载数据,这也引发了以 Meltdown 为首的一系列漏洞),因此可以很激进地预测执行 Load。但是,Store 是有副作用的,写出去的数据就没法还原了。因此,Store 指令只有在 ROB Head 被 Commit 的时候,才会写入到 Cache 中。 其次是性能,我们希望 Load 指令可以尽快地完成,这样可以使得后续的计算指令可以尽快地开始进行。当 Load 指令的地址已经计算好的时候,就可以去取数据,这时候,首先要去 Store Queue 里面找,如果有 Store 指令要写入的地址等于 Load 的地址,说明后面的 Load 依赖于前面的 Store,如果 Store 的数据已经准备好了,就可以直接把数据转发过来,就不需要从 Cache 中获取,如果数据还没准备好,就需要等待这一条 Store 完成;如果没有找到匹配的 Store 指令,再从内存中取。不过,有一种情况就是,当 Store 指令的地址迟迟没有计算出来,而后面的 Load 已经提前从 Cache 中获取数据了,这时候就会出现错误,所以当 Store 计算出地址的时候,需要检查后面的 Load 指令是否出现地址重合,如果出现了,就要把这条 Load 以及依赖这条 Load 指令的其余指令重新执行。POWER8 处理器微架构论文中对此也有类似的表述: The POWER8 IFU also implements mechanisms to mitigate performance degradation associated with pipeline hazards.

Read More

用 sv2v+yosys 把 fpnew 转为 verilog 网表

背景 FPnew 是一个比较好用的浮点计算单元,但它是 SystemVerilog 编写的,并且用了很多高级特性,虽然闭源软件是支持的,但是开源拖拉机经常会遇到这样那样的问题。所以一直想用 sv2v 把它翻译成 Verilog,但此时的 Verilog 还有很多复杂的结构,再用 yosys 转换为一个通用可综合的网表。 步骤 经过一系列踩坑,一个很重要的点是要用最新的 sv2v(v0.0.9-24-gf868f06) 和 yosys(0.15+70)。Debian 打包的 yosys 版本比较老,不能满足需求。 首先,用 verilator 进行预处理,把一堆 sv 文件合成一个: $ cat a.sv b.sv c.sv > test.sv $ verilator -E test.sv > merged.sv $ sed -i '/^`line/d' merged.sv 注意这里用 sed 去掉了无用的行号信息。然后,用 sv2v 进行转换: $ sv2v merged.sv > merge.v $ sed -i '/\$$fatal/d' merge.v 这里又用 sed 把不支持的 $fatal 去掉。最后,用 yosys 进行处理: $ yosys -p 'read_verilog -defer merge.

Read More

Synopsys Design Compiler 综合实践

工艺库 综合很重要的一步是把 HDL 的逻辑变成一个个单元,这些单元加上连接方式就成为了网表。那么,基本单元有哪些,怎么决定用哪些基本单元? 这个就需要工艺库了,工艺库定义了一个个单元,单元的引脚、功能,还有各种参数,这样 Design Compiler 就可以按照这些信息去找到一个优化的网表。 Liberty 格式 网上可以找到一些 Liberty 格式的工艺库,比如 Nangate45,它的设定是 25 摄氏度,1.10 伏,属于 TT(Typical/Typical)的 Process Corner。 在里面可以看到一些基本单元的定理,比如 AND2_X1,就是一个 drive strength 是 1 的二输入与门: cell (AND2_X1) { drive_strength : 1; pin (A1) { direction : input; } pin (A2) { direction : input; } pin (ZN) { direction : output; function : "(A1 & A2)"; } /* ... */ } 这样就定义了两个输入 pin,一个输出 pin,还有它实现的功能。还有很重要的一点是保存了时序信息,比如: lu_table_template (Timing_7_7) { variable_1 : input_net_transition; variable_2 : total_output_net_capacitance; index_1 ("0.

Read More

OpenROAD Flow 初尝试

背景 最近在尝试接触一些芯片前后端的知识。正好有现成的开源工具链 OpenROAD 来做这个事情,借此机会来学习一下整个流程。 尝试过程 首先 clone 仓库 OpenROAD-flow-scripts,然后运行:./build_openroad.sh,脚本会克隆一些仓库,自动进行编译。 编译中会找不到一些库,比如可能需要安装这些依赖:liblemon-dev libeigen3-dev libreadline-dev swig,此外运行的时候还需要 klayout 依赖。 如果遇到解决 cmake 找不到 LEMON 的问题,这是一个 BUG,可以运行下面的命令解决: cd /usr/lib/x86_64-linux-gnu/cmake/lemon cp lemonConfig.cmake LEMONConfig.cmake 编译后整个目录大概有 4.8G,输出的二进制目录是 133M。 如果要跑一下样例里的 nangate45 工艺的 gcd 例子,运行: cd flow make DESIGN_CONFIG=./designs/nangate45/gcd/config.mk 分析 GCD 测例 这个测例的代码提供了这样一个接口: module gcd ( input wire clk, input wire [ 31:0] req_msg, output wire req_rdy, input wire req_val, input wire reset, output wire [ 15:0] resp_msg, input wire resp_rdy, output wire resp_val ); endmodule 从名字可以推断出,外部通过 req 发送请求到 GCD 模块,然后模块计算出 GCD 后再返回结果。

Read More

通过 JTAG 对 VCU128 上的 Rocket Chip 进行调试

前言 两年前,我尝试过用 BSCAN JTAG 来配置 Rocket Chip 的调试,但是这个方法不是很好用,具体来说,如果有独立的一组 JTAG 信号,配置起来会更方便,而且不用和 Vivado 去抢,OpenOCD 可以和 Vivado hw_server 同时运行和工作。但是,苦于 VCU128 上没有 PMOD 接口,之前一直没考虑过在 VCU128 上配置独立的 JTAG。然后最近研究了一下,终于解决了这个问题。 寻找 JTAG 接口 前几天在研究别的问题的时候,看到 VCU128 文档中的这段话: The FT4232HL U8 multi-function USB-UART on the VCU128 board provides three level-shifted UART connections through the single micro-AB USB connector J2. • Channel A is configured in JTAG mode to support the JTAG chain • Channel B implements 4-wire UART0 (level-shifted) FPGA U1 bank 67 connections • Channel C implements 4-wire UART1 (level-shifted) FPGA U1 bank 67 connections • Channel D implements 2-wire (level-shifted) SYSCTLR U42 bank 501 connections 其中 Channel A 是到 FPGA 本身的 JTAG 接口,是给 Vivado 用的,如果是通过 BSCAN 的方式,也是在这个 Channel 上,但是需要经过 FPGA 自己的 TAP 再隧道到 BSCAN 上,比较麻烦。Channel B 和 C 是串口,Channel D 是连接 VCU128 上的 System Controller 的。之前的时候,都是直接用 Channel B 做串口,然后突发奇想:注意到这里是 4-wire UART,说明连接到 FPGA 是四条线,那是不是也可以拿来当 JTAG 用?

Read More

解决 k3s 中 traefik 不会转发 X-Forwarded-For 等头部的问题

背景 把应用迁移到 k3s 中,然后用了 traefik 作为 Ingress Controller,发现无法获得真实的用户 IP 地址,而是 cni 内部的地址。搜索了一番,找到了靠谱的解决方案: Traefik Kubernetes Ingress and X-Forwarded-Headers 具体来说,需要给 traefik 传额外的参数,方法是在 k3s 的配置目录下,添加一个 HelmChartConfig: # edit /var/lib/rancher/k3s/server/manifests/traefik-config.yaml # content: apiVersion: helm.cattle.io/v1 kind: HelmChartConfig metadata: name: traefik namespace: kube-system spec: valuesContent: |- additionalArguments: - "--entryPoints.web.proxyProtocol.insecure" - "--entryPoints.web.forwardedHeaders.insecure" 这样相当于让 traefik 信任前一级代理传过来的这些头部。更精细的话,还可以设置信任的 IP 地址范围,不过如果 traefik 不会直接暴露出去,就不用考虑这个问题了。

Read More

在 M1 上运行 Windows ARM 虚拟机

目前 Windows ARM 出了预览版,可以从 Windows Insider Preview Downloads 下载,得到一个 9.5GB 的 vhdx 文件。 接着,用 qemu-img 转换为 vmdk 格式: $ qemu-img convert Windows11_InsiderPreview_Client_ARM64_en-us_22533.vhdx -O vmdk -o adapter_type=lsilogic Windows11_InsiderPreview_Client_ARM64_en-us_22533.vmdk 转换后,在 VMWare Fusion for Apple Silicon Tech Preview 中,选择从已有的 vmdk 中创建虚拟机,启动前修改一些设置,特别是内存,默认 256MB 肯定不够,默认单核 CPU 也太少了一些。内存不足可能导致安装失败,记住要第一次启动前设置。 启动以后会无法访问网络,按照下面网页里的方法设置网络: https://www.gerjon.com/vmware/vmware-fusion-on-apple-silicion-m1/ 需要注意的是,bcdedit 选项填的 IP 地址一般是 bridge 上的地址,比如 bridge101 的地址。 然后就可以正常工作了! 在 VMWare 论坛里,还谈到了下面几个问题的解决方法: 为了让声音工作,可以修改 vmx 文件,设置 guestOS: guestOS = "arm-windows11-64" 这样声音就可以正常播放了。 分辨率的问题,可以用 RDP 来解决:首先在虚拟机里打开 Remote Desktop,然后用 macOS 的 Microsoft Remote Desktop Beta 访问即可。

Read More