AnyMath 文档
Notebook

从AnyMath定点模型生成Verilog

考虑从四个AnyMath定点模型生成Verilog。 在计算面积和周长时,我们将显示位深度,溢出和寄存器添加的影响。 然后我们将使用测试台检查生成的模块。

定点算术

类型 设置符号、位深度和小数位数。 值存储为 . 对于有符号类型,范围为

让我们比较fixdt(1,16,8)和fixdt(1,6,2)格式。 模型使用底舍入,饱和被禁用:溢出导致值的循环转换。

模型描述

前三个模型计算梯形的中线、周长和面积:

套装 , , , , ;在检查案件时,我们也使用 .

**model_old—-fixdt格式的计算(1,16,8)。
**model_new—-fixdt格式(1,6,2)的相同计算。
*model_delay是fixdt(1,16,8)和单位延迟格式在平方输出。
*model_triangle-单独计算 由 , .

模型使用添加、增益和产品库。 选择TargetHardware=Verilog进行生成。

准备工作

模型和测试台位于示例文件夹中。 生成的代码保存到/user内部的临时文件夹中。 我们将准备启动和验证功能。

In [ ]:
using Main: engee, plot, plot!, histogram, hline!, savefig, Animation, frame, gif
root = @__DIR__
generated = mktempdir("/user"; prefix=".verilog_demo_")
println("生成的文件: ", generated)

function run_demo_model(root::String, name::String)
    path = joinpath(root, name * ".engee")
    @assert isfile(path) "Нет модели: $path"
    model = engee.load(path; force=true)
    try
        return engee.run(model; verbose=false)
    finally
        engee.close(model; force=true)
    end
end

function logged_values(result, signal)
    return Float64.((collect(result[signal])).value)
end

for file in ("model_old.engee", "model_new.engee", "model_delay.engee",
             "model_triangle.engee", "tb_trapezoid_delay.v", "tb_triangle.v")
    @assert isfile(joinpath(root, file)) "Нет исходного файла: $file"
end
println("找到初始模型和测试台")
Исходные модели и тестбенчи найдены

让我们检查四个模型和两个测试台的文件的可用性。

第1步。 位深度和溢出

让我们运行两个梯形模型。 有了足够的位深度,我们期望周长为19,面积为24。

In [ ]:
old_sim = run_demo_model(root, "model_old")
new_sim = run_demo_model(root, "model_new")
old_P = logged_values(old_sim, "P")
new_P = logged_values(new_sim, "P")
old_S = logged_values(old_sim, "S")
new_S = logged_values(new_sim, "S")
@assert !isempty(old_P) && !isempty(new_P)
@assert isapprox(old_P[end], 19.0; atol=1/256)
println("P: 16 бит = $(old_P[end]), 6 бит = $(new_P[end])")
println("S: 16 бит = $(old_S[end]), 6 бит = $(new_S[end])")
P: 16 бит = 19.0, 6 бит = 3.0
S: 16 бит = 24.0, 6 бит = -8.0

在fixdt(1,16,8)格式中,我们得到P=19和S=24。 在fixdt(1,6,2)格式中,基数12的总和超出范围并变为-4。 因此,m=-2,P=3和S=-8。

让我们生成一个Verilog并检查端口位深度:分别为16位和6位。

In [ ]:
old_dir = joinpath(generated, "01_trapezoid_16bit")
new_dir = joinpath(generated, "02_trapezoid_6bit")
mkpath(old_dir)
mkpath(new_dir)
engee.generate_code(joinpath(root, "model_old.engee"), old_dir,
                    subsystem_name="trapeze_calculator")
engee.generate_code(joinpath(root, "model_new.engee"), new_dir,
                    subsystem_name="trapeze_calculator")
old_v = joinpath(old_dir, "model_old_trapeze_calculator.v")
new_v = joinpath(new_dir, "model_new_trapeze_calculator.v")
@assert isfile(old_v) && isfile(new_v)
@assert occursin(r"input\s+\[15:0\]", read(old_v, String))
@assert occursin(r"input\s+\[5:0\]", read(new_v, String))
println("RTL:确认16位和6位输入")
RTL: 16-битные и 6-битные входы подтверждены

在生成的代码中,通过移位来实现增益,并且通过与期望结果位的选择相乘来实现乘积。 信号的大小反映了模型中的固定点格式。

第二步。 登记及时钟检查

让我们用单位延迟开始模型。 在第一步中,面积应为零,然后等于上一步的结果。

In [ ]:
delay_sim = run_demo_model(root, "model_delay")
delay_P = logged_values(delay_sim, "P")
@assert length(delay_P) >= 5 && length(old_P) >= 5
@assert isapprox(delay_P[1], 0.0; atol=1/256)
@assert all(isapprox.(delay_P[2:5], old_P[1:4]; atol=1/256))
println("P无延迟: ", old_P[1:5])
println("P有延迟: ", delay_P[1:5])
P без задержки: [19.0, 19.0, 19.0, 19.0, 19.0]
P с задержкой: [0.0, 19.0, 19.0, 19.0, 19.0]

仿真结果一步确认了区域延迟。

我们将生成一个带有寄存器的模块,并在Icarus Verilog中执行时钟检查。 测试台检查复位和两个高度值的结果。 运行需要iverilog和vvp。

In [ ]:
delay_dir = joinpath(generated, "03_trapezoid_delay")
mkpath(delay_dir)
engee.generate_code(joinpath(root, "model_delay.engee"), delay_dir,
                    subsystem_name="trapeze_calculator")
delay_v = joinpath(delay_dir, "model_delay_trapeze_calculator.v")
@assert isfile(delay_v)
delay_rtl = read(delay_v, String)
@assert occursin(r"\breg\b", delay_rtl)
@assert occursin("posedge clock", delay_rtl)

delay_tb = joinpath(root, "tb_trapezoid_delay.v")
delay_exe = joinpath(delay_dir, "sim_delay")
run(`iverilog -o $delay_exe $delay_tb $delay_v`)
delay_output = read(`vvp $delay_exe`, String)
@assert occursin("h=4: m=6.00 P=19.00 S=24.00", delay_output)
@assert occursin("h=5: m=6.00 P=19.00 S=30.00", delay_output)
println(delay_output)
h=4: m=6.00 P=19.00 S=24.00
h=5: m=6.00 P=19.00 S=30.00

试验台确认m=6,P=19,以及正方形24和30。 单元延迟转换为在时钟信号边缘更新的寄存器。

第三步。 独立计算模块

在一个单独的模型中,我们计算a=8和h=5处三角形的面积。 预期结果为20。

In [ ]:
triangle_sim = run_demo_model(root, "model_triangle")
triangle_S = logged_values(triangle_sim, "S")
@assert !isempty(triangle_S)
println("AnyMath模型的S值: ", triangle_S)
Значения S модели Engee: [20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0]

模型输出S=20。

让我们生成S_triangle模块并检查三组输入数据。 预计区域为10,10.5和30。

In [ ]:
triangle_dir = joinpath(generated, "04_triangle")
mkpath(triangle_dir)
engee.generate_code(joinpath(root, "model_triangle.engee"), triangle_dir,
                    subsystem_name="S_triangle")
triangle_v = joinpath(triangle_dir, "model_triangle_S_triangle.v")
@assert isfile(triangle_v)
triangle_rtl = read(triangle_v, String)
@assert occursin("module model_triangle_S_triangle", triangle_rtl)

triangle_tb = joinpath(root, "tb_triangle.v")
triangle_exe = joinpath(triangle_dir, "sim_triangle")
run(`iverilog -o $triangle_exe $triangle_tb $triangle_v`)
triangle_output = read(`vvp $triangle_exe`, String)
for expected in ("a=5, h=4: S=10.00", "a=7, h=3: S=10.50",
                 "a=10, h=6: S=30.00")
    @assert occursin(expected, triangle_output) "Не совпал RTL-результат: $expected"
end
println(triangle_output)
a=5, h=4: S=10.00
a=7, h=3: S=10.50
a=10, h=6: S=30.00

所有三个检查都符合预期值。 产品数字的选择考虑到格式的小数部分和除以二。

如果模型仍然打开,我们将关闭它们。

In [ ]:
for name in ("model_old", "model_new", "model_delay", "model_triangle")
    if name in [m.name for m in engee.get_all_models()]
        engee.close(name; force = true)
    end
end
"Verilog demo passed; four models and two RTL testbenches verified"

结论

生成的Verilog保留了模型计算的特性:位深度、溢出和一个时钟延迟。 Icarus Verilog中的检查确认了所考虑的计算模块的预期结果。