Engee 文档
Notebook

4-FSK 调制器的 Verilog 生成

下面我们将了解在Engee中如何使用 4-FSK(频移键控)。

频率调制是一种通过改变信号频率来编码信息的调制方式。4-FSK(四电平频移键控)是 DMR(数字移动无线电)中使用的调制类型,也是 PMR(专业移动无线电)系统的最佳选择。

我们还将根据该模型生成 Verilog 代码,并在 Vivado 中验证其是否有效。

Verilog 是一种用于开发电子系统的硬件描述语言。

在不同抽象层次的模拟、数字和混合电子系统的设计、验证和实施中都需要 Verilog。

让我们声明辅助函数

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.01)
    return model_output
end
Out[0]:
run_model (generic function with 1 method)

分析模型

我们研究了该模型的两个变体。一种是通过开关实现标准选择逻辑。第二个变体使用数学公式实现。在开发系统时,为了优化速度或资源方面的性能,这种方法经常被用来改变算法,使其无法识别。

以下是据以开发模型的表格。

In [ ]:
symbol = [-3, -1, 1, 3]
bits = [[0, 0], [0, 1], [1, 0], [1, 1]]

println(join(["bits: $bit, symbol: $f" for (f, bit) in zip(symbol, bits)], "\n"))
bits: [0, 0], symbol: -3
bits: [0, 1], symbol: -1
bits: [1, 0], symbol: 1
bits: [1, 1], symbol: 3

下面的截图显示了所开发的模型。 image.png

源码块 4-FSK。 image.png

我们可以看到,使用该公式实现的程序块比原始程序块的逻辑少得多。此外,它的所有乘法块都使用 2 乘法,因此,在生成代码时,这种逻辑将表示移位一位。

image.png

我们还可以注意到,与使用 Int8 的原始代码块相比,本例使用的数据类型更短。

让我们简单谈谈定点数据类型。这种数据类型由 fi(X,1,16,5)命令指定,其中从左到右的参数是

  1. 数值; 符号(1-符号,0-无符号); 3; 字的全位大小; 4; 小数部分的大小。

接下来,我们来看一个简单的例子。

In [ ]:
x = fi(7.5, 1, 7, 5)
y = fi(7.5, 1, 7, 3)
println("x: $x")
println("y: $y")
x: 1.96875
y: 7.5

我们可以看到,在第一种情况下,数字7.5溢出。

In [ ]:
x+y
Out[0]:
fi(9.46875, 1, 10, 5)

我们还可以看到,在将这两个数字相加时,为它们分配的内存超过了最初分配的内存。

检查模型的性能

现在,让我们来分析一下两种实现方式之间的一致性。首先,让我们运行模型。

In [ ]:
bit_1 = 1; bit_2 = 1;
println("Inp_bit: $([bit_1, bit_2])")

println()
@time run_model("FSK_V") # Запуск модели.
Inp_bit: [1, 1]

Building...
Progress 0%
Progress 7%
Progress 16%
Progress 26%
Progress 34%
Progress 43%
Progress 49%
Progress 59%
Progress 66%
Progress 72%
Progress 80%
Progress 86%
Progress 96%
Progress 100%
Progress 100%
  3.472636 seconds (81.98 k allocations: 6.529 MiB)
Out[0]:
SimulationResult(
    "4-FSK modulator math.Symbol" => WorkspaceArray{Fixed{1, 4, 0, Int8}}("FSK_V/4-FSK modulator math.Symbol")

)

现在让我们比较一下结果。我们可以看到,两个结果都与初始表格一致。

In [ ]:
Symbol_math = collect(Symbol_sim).value[end]
println("Symbol_math: $Symbol_math")
Symbol_switch = collect(Symbol_sim_switch).value[end]
println("Symbol_switch: $Symbol_switch")
Symbol_math: -1.0
Symbol_switch: -1

为了验证最终项目,我们可以用公式的形式来表示我们要生成代码的模块。让我们确保公式与模型完全一致。

In [ ]:
Symbol_ref = 2 * (2 * bit_1 + bit_2) - 3
println("Symbol_ref: $Symbol_ref")
println("Symbol_sim: $Symbol_math")
Symbol_ref: -1
Symbol_sim: -1.0

让我们从 4-FSK 调制器模块开始生成代码

让我们从代码生成命令开始。下面将介绍如何使用代码生成器。

In [ ]:
? engee.generate_code
Out[0]:
engee.generate_code(path/to/modelname.engee::String, path/to/output_dir::String; subsystem_name=subsystem_path::String, subsystem_id=subsystem_id::String, target::String, jl_path::String)

Генерирует код на указанном языке для модели или подсистемы.

Аргументы

  • path/to/modelname.engee::String: абсолютный или относительный путь к модели из которой генерируется код. В качестве аргумента может выступать объект модели (объект типа model, полученный функцией engee.gcm).
  • path/to/output_dir::String: абсолютный или относительный путь к директории, в которую сохранится сгенерированный код. Если директории output_dir не существует — она будет создана автоматически.
  • subsystem_name=path/to/subsystem::String: полный путь к атомарной подсистеме из которой генерируется код.
  • subsystem_id=subsystem_id::String: уникальный идентификатор атомарной подсистемы из которой генерируется код.
  • target::String: указание языка для генерации кода. Поддерживаемые языки — Си (по умолчанию) или Verilog.
  • jl_path::String: абсолютный или относительный путь к файлу .jl, содержащему шаблон для генерации кода.

Примеры

engee.generate_code("/user/newmodel_1.engee", "/user/newmodel_1/Subsystem") # генерация кода для подсистемы

engee.generate_code("/user/newmodel_1.engee", "/user/newmodel_1/codegen_output") # генерация через абсолютный путь к модели

engee.generate_code("newmodel_1.engee", "newmodel_1/codegen_output") # генерация через относительный путь к модели

m = engee.gcm()  # получение текущей открытой модели
engee.generate_code(m, "/user/newmodel_1/codegen_output")

现在,让我们在模型中设置目标平台。

image.png

让我们执行代码生成。由于目标平台已在模型设置中明确设置,因此我们不需要目标字符串。

In [ ]:
engee.generate_code(
"$(@__DIR__)/FSK_V.engee",
"$(@__DIR__)/V_Code",
subsystem_name="4-FSK modulator math",
# target="verilog"
)
[ Info: Generated code and artifacts: /user/my_projects/Demo/A_In_Work/Seminar/FSK/V_Code

使用 Vivado

现在,让我们在 Vivado 中测试获得的代码并下载获得的文件。

image.png

让我们创建一个空项目。

image.png

让我们添加生成的文件。

image.png

让我们为项目定义目标平台。

image.png

现在,我们可以看看我们项目的最终原理图了。结果非常简单。

image.png

让我们来综合并实施该项目。我们可以看到,时序没有定义。这是因为我们程序块的输入端口是空的,没有任何输入。

image.png

我们也可以通过观察模拟结果来验证这一点。所有输入和输出均未定义。

image.png

让我们通过将输入端口设置为常量来解决这个问题,并为我们的代码块添加流水线功能。

image.png

image.png

现在让我们重复模拟。

image_2.png

模拟的结果似乎并不正确,但让我们逐点分析一下生成的逻辑,前提是输入一对位[0,1]。让我们先用我们开发的公式分析一下结果。

In [ ]:
Symbol_ref = 2*(2*0+1)-3 #[0,1]
println("Ожидаемый результат: $Symbol_ref")
Ожидаемый результат: -1

现在,让我们继续编写代码:


(io_Symbol = {{1'h0, io_Bit_1, 1'h0} + {2'h0, io_Bit_2}, 1'h0} - 4'h3)


1.{1'h0, 0, 1'h0}: 000。 2.{2'h0, 1}: 001 3.{0,0,0} + {0,0,1}: 001 4.{001, 0}: 0010 5.4'h3: 0011


现在我们来看看答案。如果我们进行位相减,结果是[1111]. 1.0010 - 0011 = -1 2.取模:1:0001。 3.Invert the bits: 0001: 1110. 4.添加 1: 1110 + 1: 1111*.

根据以上论述,我们可以说,我们简化的 4-FSK 调制器的实现是正确的。

结论

在本示例中,我们分析了在 Engee 中生成和验证 Verilog 代码的可能性,我们已经看到这种 FPGA 系统开发方法的适用性和相关性。此外,由于能够即时编辑和测试模型,它还能大大加快开发过程。