Verilog (HDL) 代码生成
除了 C-代码生成,Verilog 代码也可以从 Engee 中有限的程序块中生成。 Verilog 是一种流行的硬件描述语言(HDL),用于ASIC和FPGA的设计和测试。生成的代码可合成为网表,用于 ASIC 光刻或 FPGA 固件创建。
生成 Verilog 代码的过程与生成 C 代码类似。
您也可以使用generate_code 函数,指定 Verilog 为目标语言。
Verilog 代码生成器的功能
支持的数据类型:
-
宽度不超过 128 位的整数类型,包括非标准大小(不仅是二度);
-
具有定点和正分数长度的符号类型。
还可提供
-
从虚拟子系统生成代码;
-
自定义模板(参见根据自定义模板生成代码 );
-
生成的代码验证(参见验证)。
示例
基本算法在子系统(块子系统 )中实现:
由于算法是在子系统中实现的,因此将从中生成 Verilog 代码。为此,请在设置窗口 中将 Verilog 设置为目标平台,然后在终端执行命令:
engee.generate_code("pid_fixed.engee", "pid_fixed_code", subsystem_name="SubSystem", target="verilog")
此处:
-
pid_fixed.engee
- 模型名称; -
pid_fixed_code
- 生成 Verilog 代码的文件夹; -
subsystem_name="子系统"--表示生成代码的子系统;
-
target="verilog"-指定代码生成器的语言。
您也可以使用
|
执行命令后,文件 pid_fixed.v 将出现在文件浏览器 中,其中包含以下 Verilog 代码:
module pid_fixed_SubSystem(
input clock,
reset,
input [15:0] io_setpoint,
io_feedback,
output [15:0] io_command
);
reg [15:0] UnitDelay_state;
wire [15:0] _AddAccum_T = io_setpoint - io_feedback;
wire [41:0] _Gain_2_new_T_3 = {{26{_AddAccum_T[15]}}, _AddAccum_T} * 42'h148000;
wire [29:0] _Gain_new_T_1 = {{14{_AddAccum_T[15]}}, _AddAccum_T} * 30'h6000;
wire [15:0] _Add_1Accum_T = {_Gain_2_new_T_3[41:27], 1'h0} + UnitDelay_state;
always @(posedge clock) begin
if (reset)
UnitDelay_state <= 16'h0;
else
UnitDelay_state <= _Add_1Accum_T;
end // always @(posedge)
assign io_command = _Gain_new_T_1[29:14] + {_Add_1Accum_T[15], _Add_1Accum_T[15:1]};
endmodule
生成的代码具有以下特点:
-
总是生成 "时钟 "和 "复位 "信号;
-
同时使用顺序逻辑和组合逻辑,但不支持组合循环;
-
"复位 "总是同步和高电平有效。
验证
验证涉及使用 C 功能 块创建一个验证模型,其仿真结果应与具有相同输入数据的原始模型的结果相匹配。
与 C 代码生成一样,您可以在 "代码生成 "选项卡的设置窗口中启用 "生成 C 功能块 "选项。在这种情况下,除了 Verilog 文件(.v)外,包含生成的 Verilog 代码的文件夹还将包含以下内容:
-
.jl 脚本;
-
包含以下辅助文件的 obj_dir 文件夹:
文件 pid_fixed_Subsystem_verification.jl 包含一个命令控制语言脚本。要获取验证模型,需要执行该文件。执行方法有两种:
运行脚本后,将生成一个模型 {model_name}_verification.engee
。它包括
-
原始模型(或子系统)的输入和输出块;
-
*C 功能*块
-
信号类型转换辅助块(如果模型使用定点类型)。
简化后,C Function 块包含从源模型生成的 Verilog 代码。因此可以
传统的通用处理器无法直接执行用于综合的 Verilog RTL 代码。不过,使用 Verilator等仿真器可以做到这一点。该工具可将 Verilog 代码转换为等效的行为 C++ 代码,运行后可对结果进行比较。 生成的 C++ 代码被打包在一个包含控制仿真界面的库中,而辅助文件则放在 obj_dir 文件夹中(如前所述)。然后,验证模型中的 C Function 块将使用该库进行操作。 |
Verilog 内部工作原理
对于高级用户,如 HDL 代码生成的模板开发人员,了解 Verilog 生成的步骤非常重要。简化后,流程如下:
-
翻译为 Chisel - 代码生成器将输入模型翻译为 Chisel 语言的代码。Chisel 是一种内置于 Scala 的高级硬件描述语言。它提供了简化硬件设计的抽象,使您能够充分利用 Scala 的设计能力;
-
转换为 FIRRTL - Chisel 公开高级设计并转换为 FIRRTL(Flexible Intermediate Representation for RTL)。在此步骤中
-
转换为 Verilog - 使用 CIRCT(firtool 工具)将 FIRRTL 转换为最终的 Verilog 代码。
如何获取 Chisel 代码?
默认情况下,第一步生成的 Chisel 代码不会保存在代码文件夹中。不过,它对调试或开发很有用。要获取该文件,请使用 programmatic-management,向 generate_code
命令传递 target="chisel"
参数。例如
engee.generate_code(engee.gcm(), "pid_fixed_code", target="chisel", subsystem_name="SubSystem")
执行 Chisel 命令后,代码将保存在指定文件夹中,可用于后续工作:
扩展名为 .scala 的文件包含 Chisel 代码:
Chisel 代码示例
//> using scala "2.13.14"
//> using dep "org.chipsalliance::chisel:6.5.0"
//> using plugin "org.chipsalliance:::chisel-plugin:6.5.0"
//> using options "-unchecked", "-deprecation", "-feature", "-language:reflectiveCalls", "-Xcheckinit", "-Xfatal-warnings", "-Wdead-code"
import chisel3._
import circt.stage.ChiselStage
import fixedpoint._
class pid_fixed_SubSystem extends Module {
val io = IO(new Bundle{
val setpoint = Input(FixedPoint(16.W,14.BP)) /* /setpoint */
val feedback = Input(FixedPoint(16.W,14.BP)) /* /feedback */
val command = Output(FixedPoint(16.W,13.BP)) /* /command */
})
val Add = Wire(FixedPoint(16.W,14.BP))
val AddAccum = Wire(FixedPoint(16.W,14.BP))
val AddCast0iosetpoint = Wire(FixedPoint(16.W,14.BP))
val AddCast1iofeedback = Wire(FixedPoint(16.W,14.BP))
val UnitDelay = Wire(FixedPoint(16.W,14.BP))
val Gain_2 = Wire(FixedPoint(16.W,13.BP))
val Gain = Wire(FixedPoint(16.W,13.BP))
val Add_1 = Wire(FixedPoint(16.W,14.BP))
val Add_1Accum = Wire(FixedPoint(16.W,14.BP))
val Add_1Cast0Gain_2 = Wire(FixedPoint(16.W,14.BP))
val Add_1Cast1UnitDelay = Wire(FixedPoint(16.W,14.BP))
val Add_2 = Wire(FixedPoint(16.W,13.BP))
val Add_2Accum = Wire(FixedPoint(16.W,13.BP))
val Add_2Cast0Gain = Wire(FixedPoint(16.W,13.BP))
val Add_2Cast1Add_1 = Wire(FixedPoint(16.W,13.BP))
val UnitDelay_state = RegInit({ val _init = Wire(FixedPoint(16.W,14.BP)); _init := 0.0.F(16.W,14.BP); _init })
/* Output for UnitDelay: /Unit Delay */
UnitDelay := UnitDelay_state
/* Sum: /Add incorporates:
* Inport: /setpoint
* Inport: /feedback
*/
AddCast0iosetpoint := io.setpoint
AddCast1iofeedback := io.feedback
AddAccum := AddCast0iosetpoint - AddCast1iofeedback
Add := AddAccum
/* Gain: /Gain-2 incorporates:
* Sum: /Add
*/
Gain_2 := 0.02.F(16.W,13.BP) * Add
/* Gain: /Gain incorporates:
* Sum: /Add
*/
Gain := 3.0.F(16.W,13.BP) * Add
/* Sum: /Add-1 incorporates:
* Gain: /Gain-2
* UnitDelay: /Unit Delay
*/
Add_1Cast0Gain_2 := Gain_2
Add_1Cast1UnitDelay := UnitDelay
Add_1Accum := Add_1Cast0Gain_2 + Add_1Cast1UnitDelay
Add_1 := Add_1Accum
/* Sum: /Add-2 incorporates:
* Gain: /Gain
* Sum: /Add-1
*/
Add_2Cast0Gain := Gain
Add_2Cast1Add_1 := Add_1
Add_2Accum := Add_2Cast0Gain + Add_2Cast1Add_1
Add_2 := Add_2Accum
/* Outport: /command incorporates:
* Sum: /Add-2
*/
io.command := Add_2
/* Update for UnitDelay: /Unit Delay */
UnitDelay_state := Add_1
}
object pid_fixed_SubSystemDriver extends App {
ChiselStage.emitSystemVerilogFile(
new pid_fixed_SubSystem,
firtoolOpts = Array("--disable-all-randomization", "--strip-debug-info",
"--lowering-options=disallowLocalVariables"))
}
只有选择 Verilog 作为目标平台,才能运行 .jl 脚本生成的验证模型。如果未生成 obj_dir 文件夹,验证模型将无法运行。 |
代码模板在第一步生成时就已暴露,因此 HDL 模板的创建应主要在 Chisel 中完成,必要时使用 Julia 的内置控制结构。