从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内部的临时文件夹中。 我们将准备启动和验证功能。
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。
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])")
在fixdt(1,16,8)格式中,我们得到P=19和S=24。 在fixdt(1,6,2)格式中,基数12的总和超出范围并变为-4。 因此,m=-2,P=3和S=-8。
让我们生成一个Verilog并检查端口位深度:分别为16位和6位。
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位输入")
在生成的代码中,通过移位来实现增益,并且通过与期望结果位的选择相乘来实现乘积。 信号的大小反映了模型中的固定点格式。
第二步。 登记及时钟检查
让我们用单位延迟开始模型。 在第一步中,面积应为零,然后等于上一步的结果。
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])
仿真结果一步确认了区域延迟。
我们将生成一个带有寄存器的模块,并在Icarus Verilog中执行时钟检查。 测试台检查复位和两个高度值的结果。 运行需要iverilog和vvp。
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)
试验台确认m=6,P=19,以及正方形24和30。 单元延迟转换为在时钟信号边缘更新的寄存器。
第三步。 独立计算模块
在一个单独的模型中,我们计算a=8和h=5处三角形的面积。 预期结果为20。
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=20。
让我们生成S_triangle模块并检查三组输入数据。 预计区域为10,10.5和30。
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)
所有三个检查都符合预期值。 产品数字的选择考虑到格式的小数部分和除以二。
如果模型仍然打开,我们将关闭它们。
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中的检查确认了所考虑的计算模块的预期结果。