MyHDL中文手册(五)——结构化建模

介绍

硬件描述需要支持模块实例化和层次结构的概念。在MyHDL中,实例被递归地定义为一系列的实例或生成器。层次结构是通过在更高级别的函数中定义实例并返回它们来建模的。递归体现于,在本级block内部定义函数(always)、生成器(instance)并返回,在上一级block中调用本级block并再次返回。
下面是基本用法示例的示意图。

from myhdl import block
@block
def top(...):
    ...
    instance_1 = module_1(...)
    instance_2 = module_2(...)
    ...
    instance_n = module_n(...)
    ...
    return instance_1, instance_2, ... , instance_n

注意,MyHDL使用传统的过程方法来建模结构。这使得对更复杂的情况进行建模变得非常简单。

有条件例化

为了建模条件实例化,我们可以在参数控制下选择返回的实例。(这比`define方式决定例化结果要简洁、易于管理)例如:

from myhdl import block

SLOW, MEDIUM, FAST = range(3)

@block
def top(..., speed=SLOW):
    ...
    def slowAndSmall():
       ...
    ...
    def fastAndLarge():
       ...
    if speed == SLOW:
        return slowAndSmall()
    elif speed == FAST:
        return fastAndLarge()
    else:
        raise NotImplementedError

实例和信号的列表

Python列表很容易创建。我们可以使用它们来建模实例列表。

假设我们有一个顶层模块,它实例化单个channel子模块,如下所示:

from myhdl import block, Signal

@block
def top(...):

    din = Signal(0)
    dout = Signal(0)
    clk = Signal(bool(0))
    reset = Signal(bool(0))

    channel_inst = channel(dout, din, clk, reset)

    return channel_inst

如果我们想要支持任意数量的通道,我们可以使用信号列表和实例列表,如下所示:


    from myhdl import block, Signal

@block
def top(..., n=8):

    din = [Signal(0) for i in range(n)]
    dout = [Signal(0) for in range(n)]
    clk = Signal(bool(0))
    reset = Signal(bool(0))
    channel_inst = [None for i in range(n)]

    for i in range(n):
        channel_inst[i] = channel(dout[i], din[i], clk, reset)

    return channel_inst

信号列表和位向量之间的转换

from myhdl import block, Signal

@block
def top(..., n=8):

    din = [Signal(0) for i in range(n)]
    dout = [Signal(0) for in range(n)]
    clk = Signal(bool(0))
    reset = Signal(bool(0))
    channel_inst = [None for i in range(n)]

    for i in range(n):
        channel_inst[i] = channel(dout[i], din[i], clk, reset)

    return channel_inst

与VHDL和Verilog等HDL相比,MyHDL信号对结构建模的灵活性较差。例如,对信号进行切片将返回当前值的一部分。对于行为代码来说,这是很好的。但是,它意味着您不能在结构描述中使用例如切片。换句话说,信号切片不能用作信号。

在MyHDL中,您可以通过一个称为影子信号的概念来解决这种情况。阴影信号是由其他信号构造出来的,并自动跟踪它们的值变化。例如,a_SliceSignal跟踪另一个信号的索引或片的值。同样,ConcatSignal也会跟踪多个信号的值作为连接。

例如,假设我们有一个系统,其中N个请求程序需要仲裁。每个请求者都有一个请求输出和一个许可输入。要在系统中连接它们,我们可以使用信号列表。例如,可以按照以下方式构造请求信号列表:

request_list = [Signal(bool()) for i in range(M)]

假设仲裁器模块可用,其实例化如下:

arb = arbiter(grant_vector, request_vector, clock, reset)

输入信号request_vector是一个位向量,可以断言它的任何位元。grant_vector是一个输出位向量,只有一个断言的位,或者没有。这种模块通常基于位向量,因为它们在RTL代码中易于处理。在MyHDL中,位向量使用intbv类型建模。
我们需要一种将信号列表“连接”到位向量的方法,反之亦然。当然,我们可以用显式代码做到这一点,但影子信号可以自动做到这一点。例如,我们可以构造一个请求向量作为ConcatSignal对象:

request_vector = ConcatSignal(*reversed(request_list)

请注意,我们首先反转列表。之所以这样做,是因为链表的索引范围是intbv位向量范围的倒数。通过反向操作,这些指数对应于相同的位。

对于grant_vector向量,存在逆问题。其定义如下:

grant_vector = Signal(intbv(0)[M:])

为了构造一个自动连接到位向量的信号列表,我们可以使用Signal 调用接口来构造_SliceSignal对象:

grant_list = [grant_vector(i) for i in range(M)]

请注意用于此类型切片的圆括号。此外,可能没有必要显式地构造此列表。您可以简单地在实例化中使用grant_vector(i)。

要决定何时使用正常信号或阴影信号,请考虑数据流。使用正常信号连接到输出。使用阴影信号对这些信号进行变换,以便将它们用作输入。

推断实例列表

在MyHDL中,实例必须由顶级函数显式返回。可以方便地自动组装实例列表。为此,MyHDL提供了函数实例。使用本节中的第一个示例,如下所示:

from myhdl import block, instances

@block
def top(...):
    ...
    instance_1 = module_1(...)
    instance_2 = module_2(...)
    ...
    instance_n = module_n(...)
    ...
    return instances()

函数实例使用内省来检查由调用函数定义的局部变量的类型。所有符合实例定义的变量都组装在一个列表中,然后返回该列表。

猜你喜欢

转载自blog.csdn.net/zt5169/article/details/84144768