转换成Verilog和VHDL代码
导言
在某些限制之下,MyHDL支持将MyHDL代码自动转换为Verilog或VHDL代码。此特性提供了从MyHDL到基于Verilog或VHDL的标准设计环境的路径。
本章介绍了转换的概念。具体的例子可以在附带的章节转换例子中找到。
解决方案说明
要实现可转换,硬件描述必须满足某些限制,即定义为可转换子集。这在可转换子集中有详细描述。
可以使用MyHDL库中的函数toVerilog或toVHDL将可转换设计转换为Verilog或VHDL中的等效模型。
当设计用于实现时,使用第三方综合工具将Verilog或VHDL模型编译为ASIC或FPGA的实现。有了这一步,就有了从Python中的硬件描述到FPGA或ASIC实现的自动路径。
转换不是从源文件开始的,而是从Python解释器详细阐述的实例化设计开始的。转换器使用Python探查器来跟踪解释器的操作,并推断设计结构和名称空间。然后,它有选择地编译源代码片段,以便进行额外的分析和转换。
特征
精细化后转换
精细化elaborate是指对硬件描述的初始处理,以实现可供仿真或综合的设计实例的表示。具体而言,在此步骤中处理结构参数和构造。在MyHDL中,Python解释器本身用于精细化。使用详细的设计实例作为参数构造仿真对象。同样,转换工作在一个详细的设计实例上。因此,尽可能多地使用Python解释器。
## 任意复杂结构
由于转换工作在详细的设计实例上,因此任何建模约束仅适用于设计结构的叶元素,即协同工作生成器。换句话说,对设计结构的描述没有任何限制:Python的全部功能可以用于此目的。此外,设计层次可以任意深入。
## 生成器映射到Verilog或VHDL结构
转换器分析每个生成器的代码,并将其映射到目标HDL中的等效结构。对于Verilog,它将生成器映射到always块、连续赋值assign或initial块。对于VHDL,它将把它们映射到process语句或并发信号赋值。
从信号使用情况推断模块端口
在MyHDL中,端口的输入或输出方向没有显式声明。转换器调查设计层次结构中的信号使用情况,以推断信号是用作输入、输出还是用作内部信号。
接口可转换
接口:具有多个信号对象作为其属性的对象。转换器通过名称扩展和变形来支持这一点。
函数调用映射到Verilog或VHDL子程序。
转换器分析函数调用和函数代码。每个函数被映射到目标HDL中的一个适当的子程序:Verilog中的一个函数或任务,VHDL中的一个函数或过程。为了支持Python函数的全部功能,每个Python函数调用都会生成一个唯一的子程序。
if-Then-Else结构可以映射到case语句
Python不提供case语句。然而,转换器识别IF-THEN-ELSE结构,其中变量与枚举类型的项进行顺序比较,并使用适当的综合属性将这样的结构映射到Verilog或VHDLcase语句。
枚举类型编码方案的选择。
MyHDL中的枚举函数返回枚举类型。此函数接受一个额外的参数编码,该参数编码指定实现中所需的编码:二进制、一个热的或一个冷的。转换器为指定的编码生成适当的代码。
内存
某些综合工具可以将Verilog存储器或VHDL阵列映射到RAM结构。为了支持这一有趣的特性,转换器将信号列表映射到Verilog存储器或VHDL阵列。
ROM存储器
一些综合工具可以从CASE语句中推断出ROM。转换器根据更高级别的描述自动扩展为case语句。ROM访问是通过索引成整数的元组,在一行中进行描述的。
有符号算法
在MyHDL中,处理负数并不重要:只需使用一个intbv对象,并对其值进行适当的约束。相反,Verilog和VHDL在无符号表示和有符号表示之间都起到了不同的作用。要使用负值,用户必须显式声明带符号的变量。但是,当有符号和无符号操作数混合在一个表达式中时,事情可能会变得棘手。
在Verilog中,当有符号和无符号操作数混合时,所有操作数都被解释为无符号。显然,这会导致意想不到的结果。设计人员必须添加符号扩展和类型转换来解决这个问题。
在VHDL中,有符号和无符号的混合通常是不起作用的。设计器必须通过添加大小调整和类型转换手动匹配操作数。
在MyHDL中,这些问题不存在,因为intbv对象只是作为(受约束的)整数工作。此外,该转换器实现了Verilog和VHDL语言所需的繁琐任务的自动化。它根据intbv对象的值约束使用有符号或无符号类型,并自动执行所需的符号扩展、大小调整和类型转换。
用户定义代码
如果需要,用户可以绕过转换过程,并描述要插入的用户定义代码。
可转换子集
导言
不出所料,并不是所有的MyHDL代码都可以转换。虽然限制是重要的,但可转换子集比RTL综合子集广泛得多,而RTL综合子集是一种工业标准。换句话说,按照RTL综合规则编写的MyHDL代码,应该始终是可转换的。但是,也可以为不可综合的模型或测试台编写可转换代码。
当遇到无法转换的构造时,转换器尝试发出明确的错误消息。
回想一下,任何限制只适用于精细化后的设计。在实践中,这意味着它们只应用于生成器的代码,即MyHDL设计中的叶功能块。
编码方式
对可转换代码的一个自然限制是,它应该以MyHDL风格编写:协作生成器、通过信号进行通信以及以敏感的方式指定恢复条件。
对于纯建模,如何创建生成器并不重要。但是,在可转换代码中,应该使用MyHDL Decorator中的一个来创建它们:实例instance、总是always_seq或always_comb。
支持的类型
最重要的限制是对象类型。只能转换有限数量的类型。Python int和long对象映射到Verilog或VHDL整数。所有其他支持的类型都需要具有定义的位宽度。支持的类型是Python bool类型、MyHDL intbv类型和函数枚举返回的MyHDL枚举类型。
必须构造intbv对象,以便可以推断位宽度。这可以通过指定最小值和最大值来完成,例如如下所示:
index = intbv(0, min=MIN, max=MAX)
Verilog转换器支持可以采用负值的intbv对象。
或者,也可以从intbv对象获取切片,如下所示:
index = intbv(0)[N:]
例如Slice返回一个新的intbv对象,其最小值为0,最大值为2*N。
除了上面描述的标量类型之外,转换器还支持许多基于元组和List的类型。下表总结了MyHDL类型的映射。
MyHDL type | VHDL type | Notes | Verilog type | Notes |
---|---|---|---|---|
int | integer | integer | ||
bool | std_logic | (1) | reg | |
intbv with min >= 0 | unsigned | (2) | reg | |
intbv with min < 0 | signed | (2) | reg signed | |
enum | dedicated enumeration type | reg | ||
tuple of int | mapped to case statement | (3) | mapped to case statement | (3) |
list of bool | array of std_logic | reg | (5) | |
list of intbv with min >= 0 | array of unsigned | (4) | reg | (4)(5) |
list of intbv with min < 0 | array of signed | (4) | reg signed | (4)(5) |
注:(1) VHDLSTD_LOGIC类型在标准VHDL软件包IEEE.std_Logic_1164中定义。
(2) 使用的VHDLunsignedandsignedtype是来自标准VHDL包IEEE.NUMERIC_STD的类型。
(3) int的MyHDL元组用于ROM推理,只能以一种非常特殊的方式使用:对元组的索引操
作应该是赋值的RHS。
(4) 所有列表成员应具有相同的值约束。
(5) 列表映射到Verilog存储类型。
该表适用于MyHDL变量。 转换器还支持使用bool、intbv或enum对象作为底层类型的MyHDL信号。对于VHDL,它们被映射到具有上表中指定的底层类型的VHDL信号。Verilog没有信号的概念。对于Verilog,MyHDL信号被映射到Verilog寄存器reg,如上表所示,或者映射到Verilog导线wire,具体取决于信号的使用情况。
如果底层信号类型为bool或intbv,转换器支持MyHDL信号列表。它们可以映射到具有在表中指定的VHDL类型的VHDL信号,或者映射到Verilog存储器。然而,信号列表并不总是映射到相应的VHDL或Verilog对象。有关更多信息,请参见信号列表的转换。
支持的语句
以下是Verilog转换器支持的语句列表,可能带有限制或使用说明。
assert
Python中的Assert语句如下所示:
assert test_expression
如果test_expression是可转换的,则可以对其进行转换。
break
continue
def
for
唯一支持的迭代方案是通过内置函数范围或MyHDL函数下降范围返回的整数序列进行迭代。不支持可选的Else子句。
if
完全支持if、elif和Else子句。pass
Print
具有多个参数的打印语句:
print arg1, arg2, ...
是支持的。但是,这些参数是有限制的。首先,它们应该是下列形式之一:
arg
formatstring % arg
formatstring % (arg1, arg2, ...)
其中arg是一个bool、int、intbv、enum或这些类型的信号。
与Python一样,格式化字符串包含普通字符和转换说明符。但是,唯一支持的转换说明符是%s和%d。因此,不支持对正和宽度规范。
不支持不带换行符的打印:
print arg1 ,
raise
此语句映射到以错误消息结束仿真的语句。
return
yield
用于指定灵敏度列表。所产生的表达式可以是信号、由MyHDL函数Signal.posedge或Signal.negedge指定的信号边缘,也可以是信号和边缘规范的元组。它也可以是延迟对象。
while
不支持可选的Else子句
支持的内置函数
下面是转换器支持的内置函数的列表。
bool
布尔函数可用于将对象显式地类型化为其布尔解释。
len
对于Signal和intbv对象,函数len返回位宽度。
int
此函数可用于将对象显式地类型化为其整数解释。
文件串
转换器以Python文档字符串的形式传播注释。
在Python中,Docstring通常用来以标准的方式记录某些对象。这样的“正式”文档字符串被放入转换后的输出中的适当位置。转换器支持顶级模块和生成器的正式文档字符串。
在生成器中,“非官方”文档字符串也会被传播。这些字符串(按惯例用三重引号)可以出现在语句之间的任何位置。
Python解析器忽略常规的Python注释,并且它们不存在于解析树中。因此,它们不会被传播。使用docstring,您可以一种优雅的方式指定哪些注释应该传播,哪些不应该传播。
信号列表的转换
信号列表在许多方面都很有用。例如,它们使创建重复结构变得容易。另一个应用程序是内存行为的描述。
转换器的输出是非层次化的。这意味着所有信号都是在VHDL或Verilog(作为VHDL信号,或Verilog规则和连线)的顶层声明的。但是,作为MyHDL设计层次结构中某个级别的列表成员的一些信号可以用作较低级别的普通信号。对于这样的信号,必须选择是声明Verilog内存还是VHDL数组,还是声明多个普通信号名称。
如果可能,最好是纯信号声明,因为Verilog存储器和数组在使用和工具支持方面有一些限制。如果在生成器代码之外严格使用列表语法,这是可能的,例如,当使用信号列表来描述结构时。
相反,在某些生成器中使用列表语法时,将声明Verilog内存或VHDL数组。典型的例子是RAM存储器的描述。
接口转换
复杂的设计通常有许多信号传递到不同的层次结构中。通常,许多信号在逻辑上属于同一类信号。这可以由一个接口来建模:一个具有多个信号对象作为其属性的对象。将信号分组到接口中可简化代码、提高效率并减少错误。
该转换器支持使用分层名称扩展和名称更改的接口。
赋值问题
Python中的名称指定。
Python中的名称赋值与许多其他语言中的名称赋值是一个不同的概念。这一点对于Python中的有效建模非常重要,对于可综合的MyHDL代码更是如此。因此,这里对这些问题进行了明确的讨论。
请考虑以下名称赋值:
a = 4
a = ``a string''
a = False
在许多语言中,其含义是现有变量a获得许多不同的值。在Python中,这样的变量概念并不存在。相反,赋值只是创建一个名称到某个对象的新绑定,该绑定将替换以前的任何绑定。因此,在本例中,名称a按顺序绑定了许多不同的对象。
转换器必须研究MyHDL代码中的名称赋值和使用,并将名称映射到Verilog或VHDL对象。为此,它尝试推断分配给名称的每个表达式的类型以及可能的位宽。
如果可以确定分配中使用了一致的类型和位宽度,则可以支持同一名称的多个分配。这可以用于布尔表达式、数字表达式和枚举类型的文字。
在其他情况下,创建对象时应使用单个赋值。然后通过修改现有对象来实现后续的值更改。此技术应用于信号和intbv对象。
信号赋值
MyHDL中的信号赋值是使用属性赋值来实现的。因此,值更改是通过修改现有对象来建模的。转换器研究信号对象以推断相应Verilog或VHDL对象的类型和位宽。
intbv对象
intbv类型很可能是MyHDL中可综合建模的主要工具。intbv实例的行为类似于一个(可变的)整数,其各个位都可以访问和修改。此外,还可以约束其值集。除了错误检查,这使得推断位宽度成为可能,这是实现所必需的。
如前所述,不可能使用名称赋值来修改intbv对象的值。在接下来的部分中,我们将展示如何实现这一点。考虑:
a = intbv(0)[8:]
这是一个初始值为0且位宽为8的intbv对象。要将其值更改为5,可以使用切片指定:
a[8:] = 5
也可以通过保留未指定的位宽来实现这一点,这意味着更改“所有”位:
a[:] = 5
通常,新的值将取决于旧的值。例如,要使用上述技术递增intbv,请执行以下操作:
a[:] = a + 1
Python还提供了增强的赋值操作符,该操作符可用于实现就地操作。这些是由intbv对象和转换器支持的,因此增量也可以执行以下操作:
a += 1
从转换中排除代码
对于某些任务(如调试),插入不应转换的任意Python代码可能很有用。
转换器通过忽略 if debug 测试中嵌入的所有代码来支持此功能。不考虑 debug 变量的值。
用户定义代码
MyHDL提供了一种在转换过程中包含用户定义代码的方法。有一些特殊的函数属性被转换器理解,但被仿真器忽略。这些属性分别是Verilog的verilog_code和VHDL的vhdl_code。它们的操作就像一个特殊的返回值。当在MyHDL函数中定义时,转换器将使用它们的值而不是常规的返回值。实际上,在这一点上,它将停止转换。
verilog_code或vhdl_code的值应该是Python模板字符串。模板字符串支持基于$的替换。
$name表示法可用于引用字符串上下文中的变量名。转换器将替换字符串中的适当值,然后插入它而不是常规转换的输出。
用户定义的代码还有一个问题。通常,转换器会推断输入、内部信号和输出。它还检测非驱动和多驱动信号。要做到这一点,它假设信号不是默认驱动的。然后,它处理代码以找出哪些信号是从哪里驱动的。
正确的信号使用推断不能用户定义的代码来完成。在没有用户帮助的情况下,这将导致在推理过程中出现警告或错误,或者导致无效代码的编译错误。用户可以通过设置从用户定义的代码中驱动或读取的信号的Driven或Read属性来解决此问题。默认情况下,这些属性为false。受驱动属性的允许“true”值为True、‘wire’和‘reg’。后两个值指定用户定义的Verilog代码如何在Verilog中驱动信号。要决定使用哪个值,请考虑插入用户定义的代码后如何在Verilog中声明信号。
有关用户定义代码的示例,请参见用户定义代码。
模板变换
注:此部分仅与VHDL相关。
VHDL和Verilog在确定信号边缘灵敏度的方法上存在差异。在Verilog中,边缘说明符可以直接在灵敏度列表中使用。在VHDL中,这是不可能的:只能在灵敏度列表中使用信号。要检查边缘,可以在代码中使用rising_Edge()或falling_Edge()函数。
MyHDL遵循Verilog方案在灵敏度列表中指定边缘。因此,在将这些代码映射到VHDL时,需要将其转换为等价的VHDL。这是一个重要的问题,因为它影响所有推断顺序逻辑的可综合模板。
我们将用一些例子来说明这一特性。以下是D触发器的MyHDL代码:
@always(clk.posedge)
def logic():
q.next = d
将其转换为VHDL,如下所示:
DFF_LOGIC: process (clk) is
begin
if rising_edge(clk) then
q <= d;
end if;
end process DFF_LOGIC;
转换器可以处理更一般的情况。例如,以下是具有异步设置、异步重置和设置优先于重置的D触发器的MyHDL代码:
@always(clk.posedge, set.negedge, rst.negedge)
def logic():
if set == 0:
q.next = 1
elif rst == 0:
q.next = 0
else:
q.next = d
将其转换为VHDL,如下所示:
DFFSR_LOGIC: process (clk, set, rst) is
begin
if (set = '0') then
q <= '1';
elsif (rst = '0') then
q <= '0';
elsif rising_edge(clk) then
q <= d;
end if;
end process DFFSR_LOGIC;
所有具有实际效用的案件都可以用这种方式处理。但是,也有一些情况不能转化为等价的VHDL语言。转换器将检测这些情况并给出错误。
转换输出的联合仿真验证
注: 本节仅与Verilog相关。
为了验证转换后的Verilog输出,可以使用协同仿真。为了简化这项工作,转换器还生成了一个测试平台,该测试平台使得使用Verilog协同仿真界面仿真Verilog设计成为可能。这允许使用与MyHDL代码相同的测试平台来验证Verilog代码。
试验平台的改装
转换后,显然需要验证VHDL或Verilog代码是否正确工作。对于Verilog,我们可以使用前面讨论的联合仿真。但是,VHDL目前不支持协同仿真。
另一种方法是对测试平台本身进行转换,这样测试平台和设计都可以在HDL仿真器中运行。当然,这不是一个完全通用的解决方案,因为可以转换的代码类型有重要的限制。因此,问题是转换限制是否允许开发足够复杂的测试台。在本节中,我们将对此提出一些见解。
最重要的限制是可以使用的类型,如本章前面所讨论的。但是,“可换子集”比“综合子集”更宽。我们将介绍一些测试工作台感兴趣的非综合特性。
while循环
而循环可用于高级控制结构。
raise声明
Rise语句可以在错误情况下停止仿真。
delay对象
延迟建模对于测试台架是必不可少的。
print语句
可以使用Print语句进行简单调试。
assert语句
最初,Assert语句只打算在代码中插入调试断言。最近,有一种趋势是使用它们来编写自检单元测试,由单元测试框架(如py.test)控制。特别是,它们是为MyHDL设计编写自检测试台的强大方法。由于Assert语句是可转换的,MyHDL中的整个单元测试套件可以转换为Verilog和VHDL中的等效测试套件。
此外,可以使用与可综合代码相同的技术来控制复杂性。具体地说,生成器外部的任何代码都是在精细化elaborate过程中执行的,因此在转换过程中不会被考虑。例如,此功能可用于设置常数或预期结果的复杂计算。此外,可以使用int的元组来保存将映射到Verilog和VHDL中的case语句的值表。
方法学说明
第一步仿真
在Python哲学中,运行时规则。除了语法错误之外,Python编译器不会尝试检测很多错误,考虑到Python的超动态特性,这无论如何都是一项几乎不可能完成的任务。要验证Python程序,应该运行它,最好使用单元测试来验证每个特性。
在将MyHDL描述转换为Verilog时,应该使用相同的原理:首先确保仿真运行良好。尽管转换器检查了许多事情并试图发出明确的错误消息,但是除非仿真运行正常,否则不能保证它执行了有意义的工作。
处理层次
回想一下,转换发生在精细化elaborate之后。结果是转换后的输出是非分层的。在许多情况下,这不是一个问题。转换的目的是通过使用Verilog和VHDL作为“后端”格式提供到传统设计流程的路径。层次结构与人类是相当相关的,但对工具却不是那么重要。
但是,在某些情况下,层次结构是可取的。例如,如果转换测试工作台,则可能希望将其代码与测试中的设计分开。换句话说,转换应该在测试实例下的设计停止,并插入一个实例化。
有一种解决方法可以通过少量的额外工作来实现这一点。解决方法是定义由被测试设计的实例化组成的用户定义代码。正如在用户定义代码中所讨论的,当转换器看到钩子时,它将停止转换并插入实例化。当然,您也需要转换测试中的设计本身。因此,您应该使用一个控制是否定义挂钩的标志,并根据所需的转换对其进行设置。