spike基础机制及运行
Spike机制
Spike 仿真器模拟 RISC-V 处理器的指令执行,而宿主机是运行仿真器的物理计算机。tohost
和 fromhost
寄存器的通信机制允许目标系统(仿真中的 RISC-V 处理器)与宿主机之间进行数据交换,主要作用包括:
- 输入/输出(I/O)处理:例如,当仿真程序需要与外部设备(如控制台、磁盘等)交互时,这些通信机制允许目标系统通过宿主机的资源来执行这些操作。
- 系统调用(Syscalls):当仿真程序执行系统调用(如文件操作、内存分配等)时,
tohost
和fromhost
机制将系统调用的请求从目标系统传递到宿主机,由宿主机模拟相应的系统调用,并返回结果。 - 异常与中断处理:仿真过程中,宿主机负责处理一些来自目标系统的异常情况或中断信号,通过
tohost
发送中断请求,宿主机可以模拟响应这些事件。
tohost
和 fromhost
寄存器的工作机制:
tohost
寄存器:目标系统将数据写入tohost
寄存器,通常用于发送命令或请求给宿主机。例如,仿真程序可能请求宿主机进行某种I/O操作或触发系统调用。fromhost
寄存器:宿主机通过fromhost
寄存器将响应数据传递回目标系统,例如返回系统调用的结果或提供输入数据。
通信机制未启用时
- 如果
tohost_addr
为零,通信未启用。在这种情况下,仿真器会简单地执行目标系统的指令而不与宿主机进行任何交互。这意味着仿真器处于一个纯粹的“裸机模式”(bare-metal mode),运行的是不需要宿主机协助的程序。 - 在这个模式下,仿真器通过不断调用
idle()
函数执行指令,直到接收到退出信号。在这种情况下,仿真程序无法与外部系统(宿主机)进行交互,所以适合用于一些不依赖 I/O 的测试程序或无操作系统的裸机程序。
Spike的运行
Spike的程序启动逻辑位于spike.cc
文件的主函数(main()
)中。包括以下关键部分:
- 参数解析:解析输入参数并根据这些参数设置仿真器的配置。
- 初始化内存和处理器:根据配置初始化仿真的内存和处理器核心。
- 加载程序:将内核和初始RAM盘加载到仿真内存中。
- 启动仿真:通过
sim_t::run()
进入主仿真循环,开始指令的执行。
通过sim_t::run()
调用htif_t::run()
的主运行循环(它是仿真器和宿主机之间的接口主循环。htif_t::run()
会不断调用 sim_t::idle()
函数来推进仿真的进程。)
1 |
|
sim_t::idle()
负责推进仿真器的时间状态,通常通过让仿真中的处理器核心执行指令。它可以处理中断、定时器事件等,模拟系统的实际行为。
1 |
|
step(INTERLEAVE)
:idle()
函数调用 sim_t::step()
函数来推进仿真器的执行。INTERLEAVE
是仿真器每次执行的指令数量,它是一个预定义的值或根据仿真配置设定。
sim_t::step()
函数是仿真器的主执行循环,它遍历每个处理器核心,并调用每个核心的 processor_t::step()
函数来执行指令:
1 |
|
processor_t::step(size_t n)
是 Spike 中用于实现指令取指(fetch)、解码(decode)、执行(execute)循环的核心函数。它负责让每个处理器核心(hart)执行 n
条指令,同时处理调试模式、陷阱、触发器和中断等情况。
调试模式检查
1 |
|
在执行指令之前,Spike 会检查是否存在调试模式请求。如果有调试中断或调试模式标志被设置,处理器会进入调试模式。
主执行循环
基本状态初始化
1 |
|
instret
记录已经执行的指令数量。pc
是当前的程序计数器,指向当前要执行的指令的地址。_mmu
指向内存管理单元(MMU),用于加载和存储指令。state.prv_changed
和state.v_changed
用于跟踪特权级(privilege level)和矢量化(vectorization)的变化。
取指、解码和执行
慢速路径(slow path)
1 |
|
- 慢速路径:如果进入了“慢速路径”(即需要特殊处理的情况,如单步调试、触发器检测等),每条指令都会通过
mmu->load_insn(pc)
从内存中加载,并通过execute_insn_logged()
执行。 - 单步执行:在慢速路径下,指令会逐条取出、解码并执行,同时处理调试触发器和中断。
快速路径(fast path)
1 |
|
- 快速路径:在不需要特殊处理的情况下,Spike 使用指令缓存(icache)加速执行流程。处理器从缓存中读取指令,并通过
execute_insn_fast()
执行指令。如果缓存匹配,则直接执行下一个指令块,减少频繁的内存访问。 - 跳转指令:如果遇到分支跳转或者缓存不匹配的情况,指令缓存中的执行流会中断,并重新从新地址获取指令。
中断和陷阱处理
在指令执行过程中,可能会发生各种异常、中断或者调试陷阱:
中断处理
1 |
|
take_pending_interrupt()
:检查并处理待处理的中断。如果检测到中断,将触发中断处理流程。
陷阱处理
1 |
|
- 如果在指令执行过程中发生陷阱(如非法指令、内存访问异常等),Spike 会捕获陷阱,并调用
take_trap()
处理陷阱。
触发器匹配
1 |
|
- 如果检测到触发器匹配事件,Spike 会执行相应的触发器操作(如中断、陷阱等)。
等待中断
1 |
|
- 当执行
WFI
(等待中断)指令时,处理器会进入等待状态,并等待中断发生。此时会退出循环,并标记为in_wfi
状态。
指令计数和周期计数
1 |
|
- Spike 模拟了处理器的性能计数器。在每次指令执行后,
minstret
(已完成指令计数器)和mcycle
(周期计数器)会根据执行的指令数量进行递增。
spike基础机制及运行
http://willimt.com/2024/08/02/模拟器/spike基础机制及运行/