引言

大家设计了NEMU和单周期处理器, 如何让它们运行程序?

  • 程序和硬件有什么关系?
  • 为什么要有AM?
  • AM代码导读

从零开始设计计算机

现代处理器芯片

  • 你笔记本CPU的特性
cat /proc/cpuinfo | grep "^flag" | uniq

复杂系统是如何设计的?

A: 迭代改进

 

其他例子:

  • x86系列处理器的诞生和发展
    • 从1978年80条指令到2015年3600条
  • Windows操作系统
    • 窗口重叠, 双击运行, Ctrl+C/Ctrl+V…

 

让大家设计一个简单的x86/Windows还是有点难 😂

但就连计算机技术, 也是迭代式发展的

图灵机(1936)

可以看成一个强化的状态机

  • 多了读写头和可移动的纸带
  • 状态转移表 = 指令
    • <input, output, move, next state>
  • 跳转到未定义状态时停机

通用图灵机 - 通用计算机的数学模型

图灵在1936年发表论文《论可计算数及其在可判定性问题上的应用》

  • 当时他才本科毕业两年
  • 据说图灵机的想法是他在一次长跑后坐在草坪上发呆时产生的

 

  • 论文开创了计算机领域
    • 提出通用图灵机UTM, 它输入任意一个图灵机T, 其执行结果与T的执行结果等价
      • UTM = 计算机, T = 程序: 类似在NEMU中执行程序
      • 今天计算机能做的事情, 图灵机也能做
  • 论文还证明了计算机领域的能力上界
    • 提出图灵停机问题, 推翻了希尔伯特计划的第三步(可判定性问题)
      • 无法正确判断 “任意给出的一个程序是否会陷入死循环”

冯诺依曼计算机(1945)

  • 第一台电子计算机 - Atanasoff-Berry computer (ABC, 1942)
    • 不可编程, 只能解线性方程组
  • 第一台通用电子计算机 - ENIAC(1945)

冯诺依曼计算机的意义

  • 具备输入输出的真实机器
    • 不是理论上的计算模型了
    • 和物理世界产生联系, 用户可以操作计算机
    • ENIAC从读卡器中的程序卡片读入指令, 程序结果打印到其他卡片

 

  • 第一台存储程序的通用电子计算机 - 改进版的ENIAC(1948)
    • 存储程序(stored-program) - 将需要执行的程序存放在内存
      • 内存是可写的, 可以由程序自己决定运行什么指令
  • 大幅增强了计算机的实用性

更多硬件机制

  • 自陷异常 -> 依次运行多个程序
    • 批处理系统
  • 中断 + 虚拟内存 -> 并发运行多个程序
    • 分时多任务
  • 多处理器 -> 并行运行多个程序
    • 多处理器系统

从Abstract Machine理解计算机

两个问题

  1. 计算机硬件几乎都支持这些机制, 但细节却不尽相同
    • 例如串口的地址不同, 甚至是型号不同
    • 直接在这些机制上编程, 就需要程序来处理这些细节

 

  1. 我们的自制计算机(处理器)也是迭代发展的
    • 例如目前大家的自制处理器应该跑不了操作系统
    • 程序的执行也需要运行时环境提供支持

 

如何应对?

计算机系统的两个诀窍

  1. 抽象 - 引入一个新的抽象层, 把这些机制的功能抽象成API
    • 程序只需要调用这些API, 就可以使用相应的机制, 而无需了解这些机制的细节
    • 我们给这套API起个名字, 叫Abstract Machine(AM)
      • 它对计算机的基础机制进行了抽象

 

  1. 模块化 - 把这些机制分模块, 并按需组合
    • TRM(Turing Machine) - 计算的支持
    • IOE(I/O Extension) - 输出输入扩展
    • CTE(ConText Extension) - 上下文扩展
    • VME(Virtual Memory Extension) - 虚拟存储扩展
    • MPE(Multi-Processor Extension) - 多处理器扩展

最简单的计算机

如果程序只想计算, 计算机需要提供什么?

  • 可以计算的部件 - 运算器(数据通路的一部分)
  • 可以驱动计算机进行计算的部件 - 控制器(生成控制信号)
  • 可以放置程序的部件 - 存储器(存储程序)
  • 指示当前程序执行进度的计数器 - PC
  • 自动执行程序的机制 - 指令周期

 

单周期处理器就可以满足程序的需求!

TRM - 最简单的裸机运行时环境

如果程序只想计算, 计算机需要提供什么?

在程序看来, 它需要什么样的运行时环境?

  • 可以用来自由计算的内存区间 - 堆区
  • 程序 “入口” - main(const char *args)
  • “退出”程序的方式 - halt()
  • 打印字符 - putch()
    • 输出是程序很自然的需求, 图灵机也要输出0/1

 

有了这些API, 程序就可以输出Hello World了!

  • 还记得之前提出的, 只支持两条RISC-V指令的自制运行时环境吗?
    • 其实它只是TRM的简化版
  • freestanding运行时环境是implementation-defined的, 不违反C标准

IOE - 输入输出

如果程序想输入输出, 运行时环境需要提供什么?

  • 输入函数ioe_read()和输出函数ioe_write()
  • 还有一些约定的, 与系统无关的抽象设备
    • 时钟, 键盘, 2D GPU[, 串口, 声卡, 磁盘, 网卡]
  • 我们得到了一个冯诺依曼计算机系统!

在x86-nemu上运行超级玛丽

第三期 “一生一芯”低配版超级玛丽

CTE - 上下文管理

想进行中断/异常处理, 运行时环境需要提供什么?

  • 上下文保存/恢复
  • 事件处理回调函数
  • kcontext()创建内核上下文
  • yield()自陷操作
  • ienabled()/iset()中断查询/设置

 

  • 我们得到了一个批处理系统!
    • 小霸王100 in 1
    • 还能跑仙剑!
  • 甚至是分时多线程操作系统
    • 类似RT-Thread

VME和MPE - 虚存管理和多处理器

迈向现代计算机系统

 

  • VME的运行时环境
    • protect()创建虚拟地址空间
    • map()添加va到pa的映射关系
    • ucontext()创建用户上下文

 

  • MPE的运行时环境
    • cpu_count()返回处理器数量
    • cpu_current()返回当前处理器编号
    • atomic_xchg()共享内存的原子交换

AM的设计原则和取舍

KISS原则: 提供最少的API来实现现代计算机系统软件

  • TRM - 最小的裸机运行时环境
  • IOE - 仅提供读写接口和少量抽象设备
  • CTE - 统一但相对低效的中断异常处理
  • VME - 页式虚存管理的基本机制
  • MPE - 简化的并发模型和原子操作

 

失去: 真实系统中某些特性的支持

  • 不连续/热插拔的内存, PCIe等现代设备, 快速中断, 页表的A/D位, 其他的原子操作

得到: 适合教学的简洁概念和实现

AM的意义

AM = 按照计算机发展史将计算机功能抽象模块化裸机运行时环境

 

  1. 按计算机发展史模块化 - 从需求的角度启发我们如何设计计算机
  • 模块化使得计算机可以按需组合
    • 程序要(高效地)计算 -> (支持指令集的)图灵机 [TRM] {必选}
    • 程序要输入输出 -> 冯诺依曼机 [IOE] {B阶段, 南大数电实验}
    • 想依次运行多个程序 -> 批处理系统 [CTE] {A阶段}
    • 想并发运行多个程序 -> 分时多任务 [VME] {S阶段, 南大PA}
    • 想并行运行多个程序 -> 多处理器系统 [MPE] {南大oslab}
  • 思想和RISC-V模块化的指令集扩展类似
  • 先完成, 后完美 - 先让处理器运行更多程序, 再逐步优化
    • 实现流水线后能跑哪些程序? 如何更好地测试?

AM的意义(2)

AM = 按照计算机发展史将计算机功能抽象模块化裸机运行时环境

 

  1. 裸机运行时环境 - 软硬协同地揭示程序与计算机的关系
  • 还记得只支持两条RISC-V指令的自制运行时环境吗?
    • AM就是它的完整版
  • 计算机的功能迭代过程
    • (在CPU中)实现功能 -> (在AM中)提供运行时环境 -> (在应用层)运行程序
    • (在CPU中)实现更强大的功能 -> (在AM中)提供更丰富的运行时环境 -> (在应用层)运行更复杂的程序

AM的意义(3)

AM = 按照计算机发展史将计算机功能抽象模块化裸机运行时环境

 

  1. 抽象 - 支持各种计算机和程序, 打通系统方向各课程实验的关键
  • 在AM上开发程序, 就可以在各种支持AM的计算机上运行
    • cputest, coremark, microbench, litenes, FCEUX, 甚至是OS及其软件栈
  • 为计算机实现AM, 就可以运行各种基于AM开发的程序
    • x86-nemu, riscv32-sodor, mips32-qemu, riscv64-npc, 甚至是Linux native
  • 在AM上开发的各种程序, 都可以无缝迁移到各种支持AM的计算机
    • 真实案例: 国科大的系统结构实验用LoongArch, 包老师希望同学们接入 “一生一芯”, 怎么办?
      • 实现一个LoongArch的AM, 就能跑仙剑!

抽象带来的其他好处

  1. 裸机程序更容易开发
    • 无需关心硬件细节, 几乎和hosted运行时环境一样
  2. 操作系统更容易理解
    • 传统的操作系统课程为了讲系统启动, 需要讲一大堆复杂概念
      • x86: 保护模式, CR0, 段描述符, 段选择符, GDT, IDT, 门描述符, TR, TSS, CR3, 页表…
      • RISC-V: 看看BBL的代码, 就知道也不简单
    • 有了AM: 操作系统就是一个从main()开始执行的C程序
      • 直接从操作系统的核心概念开始讲(jyy的OS课)
  3. 全系统更容易调试
    • Linux native的实现是正确的, 调好软件再到硬件上运行
    • 基于AM的trace/model checking/formal verification

AM代码导读

代码导读

  1. Makefile
    • 老规矩, 先看trace
  2. TRM
  3. klib - 机器无关的运行时环境
    • 为方便程序开发, 提供glibc的常用功能, 如printf(), strcpy()

AM的八卦

课程实验集成是各大高校计算机系统教学的梦想

  • 南京大学也经历了这个过程
    • 2008年: 06级学长设计了第一版组成原理实验
    • 2010年: 07级学长设计了第一版操作系统实验
    • 2012年: 08级学长设计了第一版编译原理实验
    • 2014年: 10级学长设计了第一版系统基础实验
  • 2017年1月和jyy讨论: 该有的都有了, 要不搞个大的?
    • 自己写个CPU, 上面跑自制OS, 上面跑自制编译器编译出的程序
    • 这个程序可以是NEMU模拟器, 上面又可以跑自制OS… (递归了)
  • 听上去很酷, 但最大的两个挑战是
    1. 每个实验用的ISA不同, 怎么解决?
      • 组成原理/编译原理用MIPS, 操作系统/系统基础用x86
    2. 全系统有bug, 怎么调试?

现在有很多大佬成功单挑这一目标

例如P大硕士的本科毕设(看时间应该是)成果(点此膜拜)

我们更希望提出可传承的实验方案, 帮助更多同学实现这一目标

jyy首次提出AM, 同时解决两大挑战

  • 2017年3月10日的计算机系统综合实验课上, jyy给大家介绍AM的初步想法
    • 通过抽象层把ISA和OS解耦
  • 3月17日, jyy给大家介绍AM的第一版API

南京大学在2017年提出的若干有影响力的教学成果

  1. 提出AM
  2. NEMU加入DiffTest(后面1~2次课进一步介绍)
  3. 第一版Project-N教学生态系统
Project-N组件 说明
Navy-apps 应用程序集
NCC NJU C Compiler, C编译器
Newlib 嵌入式C库(从官方版本移植)
Nanos/Nanos-lite NanJU OS, 操作系统/简化版
Nexus-AM(旧名称) 抽象计算机, 裸机运行时环境
NEMU NJU EMUlator, 全系统模拟器
NPC NJU Personal Computer, SoC
NOOP NJU Out-of-Order Processor, 处理器

影响了 “一生一芯”(1/2/3), 香山(1/2), 龙芯杯(2)的流程

2018年龙芯杯南京大学作品

  • 通过NEMU与NOOP进行DiffTest
  • 成功在乱序发射乱序执行处理器上运行自制OS
  • 并通过自制GUI界面分时运行仙剑/马里奥/x86-NEMU
  • 展示全自制计算机系统生态(C库和应用程序除外)

第三期 “一生一芯”陈同学也partially实现了这个目标

一些很酷的想法

  • 给RT-Thread加一个用AM实现的BSP(Board Support Package)
  • 将xv6移植到AM
    • jyy课题组正在开展
  • 给Linux加一个用AM实现的arch
    • 也许不完整的CPU也能partially运行Linux
    • 需要舍弃一些现代功能, 还要考虑如何整合Linux驱动和AM的IOE
    • 拍脑袋想的, 但很酷!

 

  • 把verilator编译的香山移植到AM, 可以在单周期CPU上跑仿真香山!
    • 编译的C++很复杂, 用来测试大家的指令实现是一个不错的选择
    • 不过这些C++依赖的运行时环境比较复杂, 我们找了替代方案
      • 但负责移植的陈同学还在🕊🕊🕊

总结

AM = 按照计算机发展史将计算机功能抽象模块化裸机运行时环境

  • 按计算机发展史模块化 - 从需求的角度启发我们如何设计计算机
  • 裸机运行时环境 - 软硬协同地揭示程序与计算机的关系
  • 抽象 - 支持各种计算机和程序, 打通系统方向各课程实验的关键