如何在Python中模拟硬件?

3 投票
3 回答
2325 浏览
提问于 2025-04-18 02:27

这个标题有点让人困惑,但我的任务是用高级语言写一个四阶段的MIPS处理器(取指、译码、执行、写回),我最熟悉的语言可能是Python。在面对缓存和停顿这些困难之前,我还没开始尝试,如何把本来应该并行运行的代码变成顺序执行的呢?请看这个代码片段。

pc = 0x0
IFinput, IDinput, EXinput, WBinput = None, None, None, None
while True:
    IFinput = self.memory.getInstruction(pc)
    if not IFinput: #No more instructions
        break

    self.IF.giveInput(IFinput)
    self.ID.giveInput(IDinput)
    self.EX.giveInput(EXinput)
    self.WB.giveInput(WBinput)

    instruction += 0x4
    clock += 1

    IDinput = self.IF.getOutput(clock)
    EXinput = self.ID.getOutput(clock)
    WBinput = self.EX.getOutput(clock)
    result  = self.WB.getOutput(clock)
result.printToFile()

我想把输入和输出分成两个不同的阶段,这样我就不会在“时钟加1”命令执行之前“作弊”硬件,提前得到输出。这样实现对吗?有没有适合这个任务的Python库?谢谢。

3 个回答

1

你快到了。函数的名字有点让人困惑——我更喜欢用 capture()update()memory.getIstruction() 这个功能肯定应该放在 IF.capture() 里面(IF的工作就是获取指令,对吧?)。而PC的更新也应该属于IF阶段。

在现实中,不会出现“没有更多指令”的情况,所以这个条件应该去掉。正确的方式是让EX阶段抛出一个异常(比如,遇到一个来自非法范围的保留指令;记住,其他合法的异常也可能会被抛出)。

总的来说,作为一个起点看起来不错。

1

考虑一些专门用途的编程语言,比如 VHDL 或 Verilog;也可以结合或扩展一些工具,比如 PyHVLCocotb

不过,如果你必须从头开始创建一个纯 Python 的实现,可以考虑使用高层硬件语言中的相同概念。特别是,可以考虑一种 反应式 设计。Verilog 和 VHDL 都支持这种设计理念——也就是说,当输入发生变化,比如时钟信号变化时,会驱动行为和新的输出状态。

每个“反应式回调”只需接收输入状态,并在与其他组件隔离的情况下输出特定的输出状态。依赖关系则仅通过状态变化和与变化相关的反应触发来建立。

在触发器/保护条件中可能会用到的东西包括:

  • 线路或数据变化状态
  • 信号事件
  • 周期计数(例如,最小值、之后、随机)
1

评论里有人建议使用一些专用语言,比如VHDL之类的,但我觉得对于一些简单的练习来说,速度可能不太重要,使用Python完全没问题。

你的代码看起来差不多没问题,我只是搞不懂为什么你的getOutput函数需要clock作为输入,因为一个模块的功能应该是和时间无关的,只跟它的输入有关。我觉得你只需要做的就是为你处理器里的每个寄存器做两个版本,一个表示它们的当前状态,另一个表示点击一次后状态。所有模拟你硬件的函数应该只使用“当前”的寄存器作为输入,然后把它们的输出保存到“下一个”的寄存器里。然后在循环结束时,把所有的下一个寄存器的值复制到当前寄存器里,然后再重新开始。大概是这样的:

pc = 0
IF2ID_cur, ID2EX_cur, EX2WB_cur = 0 # values of registers after a reset

while True:
    instruction = memory[pc]
    IF2ID_nxt = simulate_IF(instruction)
    ID2EX_nxt = simulate_ID(IF2ID_cur)
    EX2WB_nxt = simulate_EX(ID2EX_cur)
    result =    simulate_WB(EX2WB_cur)

    pc += 1
    IF2ID_cur, ID2EX_cur, EX2WB_cur = IF2ID_nxt, ID2EX_nxt, EX2WB_nxt

注意,这样一来,所有的“模拟”函数只使用当前寄存器的值,而不使用下一个时钟周期的值。因此,你可以改变它们执行的顺序而不影响结果,就好像它们都是并行运行的一样。

撰写回答