Python 简单硬件仿真

说明

这篇文章使用 Python 的语法实现一个简单的硬件仿真器。只能通过实例化模块构建电路,Verilog 等 HDL 中的高级语法都用不了。

Nand 是与非门,只需使用它,任何逻辑门都可以搭建。所以,我们决定这个硬件仿真器中,与非门是最基础的元件,其它模块通过实例化它构建电路。

目标:实现这样的语法:

from hardware_sim_simple import *  # 要实现的模块


class Xor(Module):  # 每个模块都要继承自 Module
    inputs = ('a', 1), ('b', 1)
    outputs = ('out', 1),

    def build(self):
        # 输入
        a = self.a
        b = self.b
        # 输出
        out = self.out
        # 内部导线
        wire0 = Wire()
        wire1 = Wire()
        wire2 = Wire()
        # 构造
        Nand(a=a, b=b, out=wire0)  # Nand 是基础门,无需搭建
        Nand(a=a, b=wire0, out=wire1)
        Nand(a=b, b=wire0, out=wire2)
        Nand(a=wire1, b=wire2, out=out)

这段代码定义了一个异或门的结构。它的输入由 inputs 类变量指出:输入有两个,一个是 a,位宽是 1;另一个是 b,位宽也是 1。输出由 outputs 指出(为了使它成为一个元组,需要在末尾有一个逗号。如果在 Module.__init_subclass__ 中写了类型转换的代码,则末尾的逗号不需要,但这里没有):输出有一个,是 out,位宽是 1。

build 方法用于构建电路,每个模块初始化时会调用它。

self.aself.bself.out 用于获取该模块的输入 a、 b 和输出 out,将它们赋值给同名变量可以节省打字时间。Wire() 用于生成一条导线,这里生成了 wire0 - wire2,用于承载内部数据。“构造”部分是模块的核心部分(之前的只是固定搭配),描述了电路内部的连接。实例化了 4 个与非门。实例化的方式是 <模块名>(<端口 1>=<导线 1>, <端口 2>=<导线 2>, ..., <端口 n>=<导线 n> 的关键字传参方式。以第一个与非门为例,例化它的语句是:

Nand(a=a, b=b, out=wire0)

这表示,实例化一个与非门,把它的 a 输入连到导线 a(异或门的一个输入),b 输入连到 b(异或门的另一个输入),out 输出连到导线 wire0(内部导线)。这条语句的电路图是:

       +-------+
a --+  |  nand |
    +--+a      |
       |    out+------- wire0
    +--+b      |
b --+  |       |
       +-------+

测试这个模块,使用这样的方法:

def testbench():
    def next_and_show():
        next_step(test)  # 使数据传递一步
        print(out.read())  # out.read() 读取导线 out 上的信息

    a = Wire()
    b = Wire()
    out = Wire()
    test = Xor(a=a, b=b, out=out)  # 要测试的模块
    values = '01'
    for a_ in values:
        for b_ in values:
            a.write(a_)  # 把 a_ 写入导线 a
            b.write(b_)
            next_and_show()  # 因为这个 Xor 有三级门延迟,所以需要三个
            next_and_show()
            next_and_show()
            print(f'end: a={
     a_}, b={
     b_}, out={
     out.read()}')


if __name__ == '__main__':
    testbench()

与多数事件驱动的 HDL 不同,这个需要手动驱动。这样优点是可以更清楚地了解模块的门延迟,缺点显而易见。

实现

下面,探究如何实现。应该实现 Module、 Nand(继承自 Module)、 Wire 三个类和 next_step() 函数。

实现 Wire 类

我们首先实现 Wire 类。这个类应该实现的方法已经基本明了了,但是还应该有一个方法,在多位总线上使用。这个方法就是索引和切片(均为 __getitem__),用于这样的代码:

Xor(a=a[0], b=b[0], out=out[0])
# 假如 Xor16 是输入输出位宽都为 16 位的异或门
Xor(a=a[0:16], b=b[16:32], out=out)
# 注意和 Verilog 不同,低位在前,高位在后,使用 Python 的切片表示法

所以,这个方法的返回值也应该是导线。向这根子导线写入时,父导线的值也会被改变。

我们还需要一个 width 方法,方便计算导线宽度。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值