【一生一芯 / PA】AM 核心文件逐行解析(上):从 start.S 到 gpu.c

本文按依赖顺序,逐行解析 AM 的 10 个核心文件。

代码路径:~/ysyx-workbench/abstract-machine


目录

  1. am/src/riscv/nemu/start.S
  2. am/src/platform/nemu/trm.c
  3. am/src/platform/nemu/include/nemu.h
  4. am/src/riscv/riscv.h
  5. am/include/am.h
  6. am/include/amdev.h
  7. am/src/platform/nemu/ioe/ioe.c
  8. am/src/platform/nemu/ioe/timer.c
  9. am/src/platform/nemu/ioe/input.c
  10. am/src/platform/nemu/ioe/gpu.c

1. start.S

复制代码
.section entry, "ax"
.globl _start
.type _start, @function

_start:
  mv s0, zero
  la sp, _stack_pointer
  call _trm_init

.size _start, . - _start
行 解释
.section entry, "ax" 放入可执行段 entry,链接脚本会把它放在 0x80000000
.globl _start 声明全局符号
.type _start, @function 标记为函数
_start: 入口标签
mv s0, zero 帧指针清零
la sp, _stack_pointer 设置栈指针
call _trm_init 调用 _trm_init,并把返回地址存入 ra
.size ... 记录符号大小

2. trm.c

复制代码
#include <am.h>
#include <nemu.h>

extern char _heap_start;
int main(const char *args);

Area heap = RANGE(&_heap_start, PMEM_END);

static const char mainargs[MAINARGS_MAX_LEN] =
    TOSTRING(MAINARGS_PLACEHOLDER);

static Context *event_handler(Event ev, Context *ctx) {
  switch (ev.event) {
    case EVENT_YIELD: break;
    case EVENT_SYSCALL:
      if (ctx->GPRx == 0) halt(ctx->GPR1);
      break;
    case EVENT_IRQ_TIMER: break;
    default: panic("Unhandled event"); break;
  }
  return ctx;
}

void putch(char ch) { outb(SERIAL_PORT, ch); }

void halt(int code) { nemu_trap(code); while (1); }

void _trm_init() {
  cte_init(event_handler);
  int ret = main(mainargs);
  halt(ret);
}
内容 解释
Area heap 定义堆 [_heap_start, PMEM_END)
mainargs 保存传给 main 的参数
event_handler 异常/中断处理函数
putch 输出字符,写 0xa00003f8
halt 通过 ebreak 停机
_trm_init 注册 CTE,启动 main

3. nemu.h

复制代码
#if defined(__riscv)
# define nemu_trap(code) asm volatile("mv a0, %0; ebreak" : :"r"(code))
#endif

#define DEVICE_BASE 0xa0000000

#define SERIAL_PORT     (DEVICE_BASE + 0x00003f8)
#define KBD_ADDR        (DEVICE_BASE + 0x0000060)
#define RTC_ADDR        (DEVICE_BASE + 0x0000048)
#define VGACTL_ADDR     (DEVICE_BASE + 0x0000100)
#define AUDIO_ADDR      (DEVICE_BASE + 0x0000200)
#define DISK_ADDR       (DEVICE_BASE + 0x0000300)
#define FB_ADDR         (MMIO_BASE   + 0x1000000)

作用:

  • nemu_trap:定义停机指令
  • 各设备 MMIO 地址
  • PMEM_END:物理内存结束地址
  • NEMU_PADDR_SPACE:合法地址空间

4. riscv.h

复制代码
static inline uint8_t  inb(uintptr_t addr) { return *(volatile uint8_t  *)addr; }
static inline uint32_t inl(uintptr_t addr) { return *(volatile uint32_t *)addr; }

static inline void outb(uintptr_t addr, uint8_t data) { *(volatile uint8_t *)addr = data; }
static inline void outl(uintptr_t addr, uint32_t data) { *(volatile uint32_t *)addr = data; }

作用:

  • inb/inw/inl:读 1/2/4 字节
  • outb/outw/outl:写 1/2/4 字节
  • PTE_*:页表权限位
  • MODE_U/S/M:特权模式
  • MSTATUS_*:状态位

volatile 保证每次读写都真正执行,不被编译器优化。


5. am.h

核心接口:

复制代码
typedef struct { void *start, *end; } Area;

typedef struct {
  enum {
    EVENT_NULL = 0,
    EVENT_YIELD, EVENT_SYSCALL, EVENT_PAGEFAULT, EVENT_ERROR,
    EVENT_IRQ_TIMER, EVENT_IRQ_IODEV,
  } event;
  uintptr_t cause, ref;
  const char *msg;
} Event;

// TRM
void putch(char ch);
void halt(int code);

// IOE
void ioe_read(int reg, void *buf);
void ioe_write(int reg, void *buf);

// CTE
bool cte_init(...);
void yield(void);
Context *kcontext(...);

// VME / MPE
...

作用:

  • 定义 Area、Event、AddrSpace
  • 声明 TRM/IOE/CTE/VME/MPE 五层接口

6. amdev.h

核心宏:

复制代码
#define AM_DEVREG(id, reg, perm, ...) \
  enum { AM_##reg = (id) }; \
  typedef struct { __VA_ARGS__; } AM_##reg##_T;

AM_DEVREG(6, TIMER_UPTIME, RD, uint64_t us);

展开后:

复制代码
enum { AM_TIMER_UPTIME = 6 };
typedef struct { uint64_t us; } AM_TIMER_UPTIME_T;

作用:

  • 定义所有设备寄存器编号和结构体
  • 供 io_read/io_write 使用

7. ioe.c

复制代码
static void *lut[128] = {
  [AM_TIMER_CONFIG] = __am_timer_config,
  [AM_TIMER_RTC   ] = __am_timer_rtc,
  [AM_TIMER_UPTIME] = __am_timer_uptime,
  ...
};

bool ioe_init() {
  for (int i = 0; i < LENGTH(lut); i++)
    if (!lut[i]) lut[i] = fail;
  __am_gpu_init();
  __am_timer_init();
  __am_audio_init();
  return true;
}

void ioe_read(int reg, void *buf) {
  ((handler_t)lut[reg])(buf);
}

作用:

  • lut:设备编号 → 处理函数
  • ioe_init:填充空项、初始化设备
  • ioe_read/ioe_write:查表分发

8. timer.c

复制代码
void __am_timer_init() {
  outl(RTC_ADDR, 0);
  outl(RTC_ADDR + 4, 0);
}

void __am_timer_uptime(AM_TIMER_UPTIME_T *uptime) {
  uptime->us = inl(RTC_ADDR + 4);
  uptime->us <<= 32;
  uptime->us += inl(RTC_ADDR);
}

void __am_timer_rtc(AM_TIMER_RTC_T *rtc) {
  rtc->year = 1900; // 目前写死
}

作用:

  • 初始化定时器
  • 读取 64 位 uptime
  • 读取 RTC 日历(当前写死)

9. input.c

复制代码
#define KEYDOWN_MASK 0x8000

void __am_input_keybrd(AM_INPUT_KEYBRD_T *kbd) {
  uint32_t kc = inl(KBD_ADDR);
  kbd->keydown = kc & KEYDOWN_MASK ? true : false;
  kbd->keycode = kc & ~KEYDOWN_MASK;
}

作用:

  • 读 0xa0000060
  • 拆出 keydown 和 keycode

10. gpu.c

复制代码
void __am_gpu_init() {
  uint32_t screen_wh = inl(VGACTL_ADDR);
  screen_w = screen_wh >> 16;
  screen_h = screen_wh & 0xffff;
}

void __am_gpu_fbdraw(AM_GPU_FBDRAW_T *ctl) {
  uint32_t *fb = (uint32_t *)(uintptr_t)FB_ADDR;

  for (int i = 0; i < h; i++) {
    uint32_t *dst = fb + (y + i) * screen_w + x;
    uint32_t *src = pixels + i * w;
    for (int j = 0; j < w; j++) dst[j] = src[j];
  }

  if (ctl->sync) outl(SYNC_ADDR, 1);
}

作用:

  • 读取屏幕宽高
  • 把像素写入帧缓冲
  • 刷新屏幕

总结

这 10 个文件组成了 AM 的最小可运行环境:

复制代码
start.S  -> 启动
trm.c    -> TRM
nemu.h   -> 硬件约定
riscv.h  -> MMIO 读写
am.h     -> 总接口
amdev.h  -> 设备定义
ioe.c    -> 设备分发
timer.c  -> 定时器
input.c  -> 键盘
gpu.c    -> 显卡

这就是 AM 核心文件逐行解析的上半部分。

相关推荐
三十而立洋1 小时前
知枝 · MindSprout:可以直接将 markdown 转换为思维导图的 vscode 插件
前端·visual studio code
兔子零10241 小时前
架构师最危险的能力:用正确的技术,做出错误的架构决策
前端·javascript·后端
Bug收容所1 小时前
Vue+js Day2
前端·javascript·vue.js
Tairitsu_H2 小时前
[动态规划#1] 子数组 | 最大子数组和 | 环形子数组的最大和 | 乘积最大子数组 | 乘积为正数的最长子数组
算法·动态规划
掘金酱2 小时前
带着作品来,坐下来聊|掘金 X-lab · Coffee Talk 邀请你来见一面
前端·javascript·后端
凯瑟琳.奥古斯特2 小时前
动态调优IoU提升OCR文本合并精度
开发语言·python·算法
布莱克6052 小时前
A* 算法详解:从原理到实现
开发语言·c++·算法
段一凡-华北理工大学3 小时前
高炉炼铁机器视觉与智能识别十八讲~系列文章15:可靠性篇:防误报、容错与识别系统的鲁棒性设计
人工智能·算法·机器学习·高炉大模型·高炉机器视觉·高炉炼铁模型开发
CoovallyAIHub3 小时前
用 AI 做 PPT 被辞退,法院判公司赔:员工贴进对话框的数据,到底谁负责?
前端·agent