QPSK+冷杉Verilog
此示例显示了与FIR滤波器组合的QPSK调制器模型。 该示例的目的是演示代码生成器的功能,以及使用嵌入在Engee环境中的Verilog模拟器验证此代码。
该模型具有模块化结构,由三个基本块组成:
-
Gen_data-简单地生成从[0,0]到[1,1]的位组合
-
QPSK_modulator-将位序列转换为复数符号(在此实现中不考虑载波相移)。
- FIR是一个具有有限脉冲响应的数字滤波器,它将每个输出值计算为最后11个输入样本的加权和。
现在让我们继续启动模型和代码生成。
In [ ]:
function run_model( name_model)
Path = (@__DIR__) * "/" * name_model * ".engee"
if name_model in [m.name for m in engee.get_all_models()] # Проверка условия загрузки модели в ядро
model = engee.open( name_model ) # Открыть модель
model_output = engee.run( model, verbose=true ); # Запустить модель
else
model = engee.load( Path, force=true ) # Загрузить модель
model_output = engee.run( model, verbose=true ); # Запустить модель
engee.close( name_model, force=true ); # Закрыть модель
end
sleep(0.1)
return model_output
end
run_model("QPSK+FIR_verilog") # Запуск модели.
Out[0]:
In [ ]:
Re = collect(simout["QPSK+FIR_verilog/FIR.re"]).value
Im = collect(simout["QPSK+FIR_verilog/FIR.im"]).value
plot(Re, label="Re")
plot!(Im, label="Im")
Out[0]:
In [ ]:
collect(simout["QPSK+FIR_verilog/FIR.re"]).value
Out[0]:
这些图表将有助于我们与Verilog代码的工作进行比较。
现在,我们将从模型块生成代码,并以与模型输入相同的方式描述测试模块。
In [ ]:
engee.generate_code(
"$(@__DIR__)/test_codgen_ic.engee",
"$(@__DIR__)/prj",
subsystem_name="QPSK_modulator"
)
engee.generate_code(
"$(@__DIR__)/test_codgen_ic.engee",
"$(@__DIR__)/prj",
subsystem_name="fir-1"
)
Tb模块是一个测试平台,通过馈送测试数据并记录结果来验证QPSK调制器和FIR滤波器模块的操作。
测试台提供位的循环序列到输入[11, 00, 10, 01], 将调制器(QPSK_Re,QPSK_Im)和滤波器(FIR_Re,FIR_Im)的输出值写入文件output_data。txt和它在控制台中可视化它们,并生成波形测试文件_codgen_ic。用于时间图分析的vcd。
In [ ]:
filename = "$(@__DIR__)/prj/tb.v"
try
if !isfile(filename)
println("Файл $filename не найден!")
return
end
println("Содержимое файла $filename:")
println("="^50)
content = read(filename, String)
println(content)
println("="^50)
println("Конец файла")
catch e
println("Ошибка при чтении файла: ", e)
end
现在让我们运行模拟。
In [ ]:
# Компиляция
run(`iverilog -o sim tb.v QPSKFIR_verilog_FIR.v QPSKFIR_verilog_QPSK_modulator.v`)
# Запуск симуляции
run(`vvp sim`)
可以看到,根据仿真结果,生成了2个txt和vcd文件。
TXT是一个带有数据表(时间戳,信号值)的文本文件。
VCD(值变化转储)是时间图的二进制文件,用于GTKWave和其他分析仪中信号的可视化调试。
让我们尝试执行VCD解析。
In [ ]:
function vcd_to_txt(vcd_filename; output_txt="simple_output.txt")
println("Упрощенная конвертация VCD в TXT: ", vcd_filename)
lines = readlines(vcd_filename)
signals = Dict{String, Vector{Tuple{Float64, Any}}}()
current_time = 0.0
for line in lines
line = strip(line)
isempty(line) && continue
if startswith(line, "\$var")
parts = split(line)
if length(parts) >= 4
signal_id = parts[3]
signal_name = parts[4]
signals[signal_name] = []
end
elseif startswith(line, "#")
current_time = parse(Float64, line[2:end])
elseif length(line) >= 2
value_char = line[1:1]
signal_id = line[2:end]
value = if value_char == "0"
0
elseif value_char == "1"
1
else
0
end
for (name, values) in signals
if occursin(signal_id, name) || signal_id == string(hash(name))[1:min(3, end)]
push!(values, (current_time, value))
break
end
end
end
end
open(output_txt, "w") do io
header = "Time\t" * join(keys(signals), "\t")
println(io, header)
all_times = Set{Float64}()
for values in values(signals)
for (time, _) in values
push!(all_times, time)
end
end
sorted_times = sort(collect(all_times))
for time in sorted_times
row = string(time)
for signal_name in keys(signals)
value = 0
for (t, v) in signals[signal_name]
if t == time
value = v
break
elseif t < time
value = v
end
end
row *= "\t" * string(value)
end
println(io, row)
end
end
println("Упрощенная конвертация завершена: ", output_txt)
end
vcd_to_txt("test_codgen_ic.vcd", output_txt="simple_result.txt")
正如我们所看到的,这是可能的,但不是很方便和快速,再加上我们没有拉出字段的名称。
所以让我们使用第二个选项并分析TXT。
In [ ]:
using DataFrames
using DelimitedFiles
using Plots
data = readdlm("output_data.txt", '\t', skipstart=1) # Пропускаем заголовок
cycle = data[:, 1]
qpsk_re = data[:, 4] # QPSK_Re в 4-й колонке
qpsk_im = data[:, 5] # QPSK_Im в 5-й колонке
fir_re = data[:, 7] # FIR_Re в 7-й колонке
fir_im = data[:, 8] # FIR_Im в 8-й колонке
p = plot(layout=(2, 1), size=(800, 600))
plot!(p[1], cycle, qpsk_re, label="QPSK Real", linewidth=2, color=:blue)
plot!(p[1], cycle, qpsk_im, label="QPSK Imag", linewidth=2, color=:red)
title!(p[1], "QPSK Signal")
plot!(p[2], cycle, fir_re, label="FIR Real", linewidth=2, color=:green)
plot!(p[2], cycle, fir_im, label="FIR Imag", linewidth=2, color=:orange)
title!(p[2], "FIR Filter Output")
Out[0]:
验证结果确认测试用例的行为与预期模型匹配。 微小的差异只能通过不同类型的数据来解释。 模块正常工作:QPSK生成字符,FIR滤波器处理它们。
结论
在这个例子中,我们已经弄清楚了如何可视化Verilog代码模拟测试,并且还生成了通信系统传输路径的简单模型。