如何在Python中模拟硬件?
这个标题有点让人困惑,但我的任务是用高级语言写一个四阶段的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 个回答
你快到了。函数的名字有点让人困惑——我更喜欢用 capture()
和 update()
。memory.getIstruction()
这个功能肯定应该放在 IF.capture()
里面(IF的工作就是获取指令,对吧?)。而PC的更新也应该属于IF阶段。
在现实中,不会出现“没有更多指令”的情况,所以这个条件应该去掉。正确的方式是让EX阶段抛出一个异常(比如,遇到一个来自非法范围的保留指令;记住,其他合法的异常也可能会被抛出)。
总的来说,作为一个起点看起来不错。
考虑一些专门用途的编程语言,比如 VHDL 或 Verilog;也可以结合或扩展一些工具,比如 PyHVL 或 Cocotb。
不过,如果你必须从头开始创建一个纯 Python 的实现,可以考虑使用高层硬件语言中的相同概念。特别是,可以考虑一种 反应式 设计。Verilog 和 VHDL 都支持这种设计理念——也就是说,当输入发生变化,比如时钟信号变化时,会驱动行为和新的输出状态。
每个“反应式回调”只需接收输入状态,并在与其他组件隔离的情况下输出特定的输出状态。依赖关系则仅通过状态变化和与变化相关的反应触发来建立。
在触发器/保护条件中可能会用到的东西包括:
- 线路或数据变化状态
- 信号事件
- 周期计数(例如,最小值、之后、随机)
评论里有人建议使用一些专用语言,比如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
注意,这样一来,所有的“模拟”函数只使用当前寄存器的值,而不使用下一个时钟周期的值。因此,你可以改变它们执行的顺序而不影响结果,就好像它们都是并行运行的一样。