前言
上一篇我们看了 GCC 的 PGO,是时候来看看 Clang 啦!
源程序和编译参数都和 GCC 的一样,clang 的 wrapper 做的还是很好的!
clang version 10.0.0-4ubuntu1
Target: x86_64-pc-linux-gnu
Thread model: posix
上一篇我们看了 GCC 的 PGO,是时候来看看 Clang 啦!
源程序和编译参数都和 GCC 的一样,clang 的 wrapper 做的还是很好的!
clang version 10.0.0-4ubuntu1
Target: x86_64-pc-linux-gnu
Thread model: posix
编译优化是一个很重要的课题。现代编译器不仅实现了 Link-Time Optimization (LTO),针对跨编译单元优化;也实现了 PGO ,基于实际运行数据来进行优化。
多说无益,直接实验启动!
在这之前,有必要提一下我们的环境和编译参数:
为了反编译的简单起见,使用 -Og
进行编译,链接器参数默认。
玩上了 E3,很好奇通道分配情况。使用的主板是 Z270-Dragon, CPU 是 E3 1240v6。
PCIE 有很多种规格,为了清晰描述,给出以下方便的定义:
PCIE 在实现上可以有两种,即 CPU 直连
和 PCH 提供
。CPU 直连的通道速度最快,不受其他限制;而 PCH 提供的通道虽然多,总体速度受 DMI Link 的速度限制。(小水管)
PCIE 的通道拆分即指 CPU 直连的 PCIE 通道拆分,涉及到 CPU 相应 CFG 引脚的配置,高端芯片组通过 GPIO 连接到这些引脚来提供动态配置拆分的选项。
前几天终于是放了假,好好休息一番后,搞了一套 E3 平台玩,想着搞成服务器,又想在闲置时省点电。从开发板库里面找到了吃灰很久的 ESP8266 (WeMos D1 R1),就它了。
要求 1 很容易实现,因为根据 Intel 的前面板 IO 设计文档并结合实际测试,可以知道按键都是 3v3 上拉输入,而电源灯是推挽 5V 输出。而 COM 口按照标准应该是 RS232 电平,但是我的主板(Z270-Dragon)没有引出来,用的是 COM_Debug 口正好是 TTL 电平。串口控制台最容易了,板子上的 CH340 直接接入 USB 就可以得到 ttyUSB 了。
提示
以下为 PDF 渲染预览版本,没得复制(打印生成的PDF是这样的qwq) 原件戳这里
好久不见!自从上一篇博客,上板成功,到现在已经过去了 3 个月。这几个月,主要是忙活一些学业上的东西,4月跟着老师探索了一点 IC design with LLM 的内容,5月忙活各种大作业、小组作业,6月忙活备考。终于考完试了,开完香槟就开始填坑啦!
由于 Unix 的 “文件即一切” 思想实在很好,所以我们采用这样的思路。
虚拟文件系统,即 Virtual FileSystem, VFS, 是整个文件系统的抽象,向上层提供统一的访问接口。考虑简单的设计,VFS 只需实现真实文件系统的挂载、卸载,以及各种操作的代理即可。
写了那么久,也快一个月了,都是在 QEMU 调试,多没意思。电子人,就是干,火速弄了块 Sipeed 的 LicheeRV Nano,基于 C906 和 A53的 SG2002,不到一够水就有百兆网口和 WiFi。(深圳到广州居然发了两天,真是慢啊,还有这排线还要我自己焊,好傻)
拿到板子,插锭,开机,屏幕没有当然点不亮。电脑上不停的叮咚响,看了下是模拟出了一个串口。那就很简单,直接找到镜像,烧录!再把卡插进去,启动!好了,过了一会,多了一个网卡,ssh一下成功,测试功能正常。那么就开始我们的上板之旅吧!
我们知道,嵌入式 Linux 的启动流程通常是,FSBL -> 固件 -> U-Boot -> 内核,这里的固件就是 OpenSBI。根据 Sipeed 官网的描述,这个片子的启动流程如下:
搞了这么多基础设施,终于到中断的实现了。首先看下 RV 的中断和异常,
Interrupt | Exception Code | Description |
---|---|---|
1 | 0 | Reserved |
1 | 1 | Supervisor software interrupt |
1 | 2 | Reserved |
1 | 3 | Machine software interrupt |
1 | 4 | Reserved |
1 | 5 | Supervisor timer interrupt |
1 | 6 | Reserved |
1 | 7 | Machine timer interrupt |
1 | 8 | Reserved |
1 | 9 | Supervisor external interrupt |
1 | 10 | Reserved |
1 | 11 | Machine external interrupt |
1 | 12–15 | Reserved |
1 | ≥16 | Designated for platform use |
0 | 0 | Instruction address misaligned |
0 | 1 | Instruction access fault |
0 | 2 | Illegal instruction |
0 | 3 | Breakpoint |
0 | 4 | Load address misaligned |
0 | 5 | Load access fault |
0 | 6 | Store/AMO address misaligned |
0 | 7 | Store/AMO access fault |
0 | 8 | Environment call from U-mode |
0 | 9 | Environment call from S-mode |
0 | 10 | Reserved |
0 | 11 | Environment call from M-mode |
0 | 12 | Instruction page fault |
0 | 13 | Load page fault |
0 | 14 | Reserved |
0 | 15 | Store/AMO page fault |
0 | 16–23 | Reserved |
0 | 24–31 | Designated for custom use |
0 | 32–47 | Reserved |
0 | 48–63 | Designated for custom use |
0 | ≥64 | Reserved |
一个 CPU 上可能不止一个核心 (Hart),这些核心在物理上分开,独立运行,但是共享总线和外设。也就是说,它们的物理内存空间是一致的,只不过有自己单独的寄存器和 Cache、MMU。而多线程非常类似,只不过是通过调度的方式实现,将多个线程的寄存器等资源分时复用。在裸机的角度上看,我们可以把多核看成硬件的多线程,并利用类似的技术实现。
在 RISCV 手册中,并没有规定 Hart 的启动顺序。比较通常的做法是,核心全部(近乎)同时从 M-mode 启动,随后 SBI 会通过原子量确定一个启动核心,并且执行初始化。至于 S-mode 中是否同时启动,我并没有明确看到文档指出,所以应该也是 SBI 实现自定义。通过实验,OpenSBI 只会将启动核心带入到 S-mode,其他核心会留在 M-mode 等待。但是,我们仍然有必要通过一个原子量保证 S-mode 初期只有一个核心运行,具体看,就是下面的汇编代码:
由于大部分的 RV64 SoC 都将 DRAM 放置在 0x80000000 以上,其下的空间保留给 IO,如 QEMU 的 virt,其布局如下:
static const MemMapEntry virt_memmap[] = {
[VIRT_DEBUG] = { 0x0, 0x100 },
[VIRT_MROM] = { 0x1000, 0xf000 },
[VIRT_TEST] = { 0x100000, 0x1000 },
[VIRT_RTC] = { 0x101000, 0x1000 },
[VIRT_CLINT] = { 0x2000000, 0x10000 },
[VIRT_ACLINT_SSWI] = { 0x2F00000, 0x4000 },
[VIRT_PCIE_PIO] = { 0x3000000, 0x10000 },
[VIRT_PLATFORM_BUS] = { 0x4000000, 0x2000000 },
[VIRT_PLIC] = { 0xc000000, VIRT_PLIC_SIZE(VIRT_CPUS_MAX * 2) },
[VIRT_APLIC_M] = { 0xc000000, APLIC_SIZE(VIRT_CPUS_MAX) },
[VIRT_APLIC_S] = { 0xd000000, APLIC_SIZE(VIRT_CPUS_MAX) },
[VIRT_UART0] = { 0x10000000, 0x100 },
[VIRT_VIRTIO] = { 0x10001000, 0x1000 },
[VIRT_FW_CFG] = { 0x10100000, 0x18 },
[VIRT_FLASH] = { 0x20000000, 0x4000000 },
[VIRT_IMSIC_M] = { 0x24000000, VIRT_IMSIC_MAX_SIZE },
[VIRT_IMSIC_S] = { 0x28000000, VIRT_IMSIC_MAX_SIZE },
[VIRT_PCIE_ECAM] = { 0x30000000, 0x10000000 },
[VIRT_PCIE_MMIO] = { 0x40000000, 0x40000000 },
[VIRT_DRAM] = { 0x80000000, 0x0 },
};