Документация Engee
Notebook

Скремблер

В данном примере рассмотрены возможности верификации сгенерированного кода при помощи C-функций в Engee на примере модели скремблера.

Скремблер — это устройство или алгоритм, выполняющий обратимое преобразование цифрового потока для придания ему свойств случайной последовательности без изменения скорости передачи. В данном примере используется скремблирование с псевдослучайной бинарной последовательностью (PRBS), генерируемой LFSR длины 15 с начальным значением 100101010000000.

Далее рассмотрим реализованную схему скремблирования и дескремблирования. Она и битовое сравнение работы данного алгоритма показаны на рисунке ниже.

image_3.png

Запустим данную модель при помощи описанной нами функции запуска и сохраним результаты битовой ошибки.

In [ ]:
function run_model(name_model)
    Path = string(@__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
    return model_output
end
Out[0]:
run_model (generic function with 1 method)
In [ ]:
@time run_model("Scrambler_descrambler")
BER = simout["Scrambler_descrambler/Error Rate Calculation.Output_1"];
BER = collect(BER)
BER[end-3:end,:]
Building...
Progress 0%
Progress 18%
Progress 100%
Progress 100%
 10.704403 seconds (527.95 k allocations: 38.180 MiB, 5.61% compilation time)
Out[0]:
4×2 DataFrame
Rowtimevalue
Float64Array…
19.97[0.0, 0.0, 998.0]
29.98[0.0, 0.0, 999.0]
39.99[0.0, 0.0, 1000.0]
410.0[0.0, 0.0, 1001.0]

Как мы видим, алгоритм работает корректно. Теперь перейдём к настройкам генератора кода. На скриншоте ниже видны следующие моменты. Целевая платформа выбрана как Verilog, а также отключены коментарии в сгенерирированном коде (это сделано для визуального упрощения его читабельности), и включена генерация C Function для верификации сгенерированного кода. image.png

Теперь запустим генерацию кода и валидационной модели.

In [ ]:
engee.generate_code(
"$(@__DIR__)/Scrambler_descrambler.engee",
"$(@__DIR__)/V_Code",
subsystem_name="Scrambler_descrambler"
)
[ Info: Generated code and artifacts: /user/start/examples/codegen/scrambler_Verilog/V_Code

Проведём анализ сгенерированных файлов. image.png Как мы можем заметить, кроме файла с кодом verilog в папке также присутствуют:

  1. исполняемый фал .so
  2. библиотечный файл .h
  3. файл кода .jl

Начнём с последнего. Данный файл позволяет нам сгенерировать модель, содержащую C function для сравнения с оригинальной моделью.

# This is a verification script for the Scrambler_descrambler_Scrambler_descrambler generated code.
# Run this code in order to create a verification model with a CFunction block inside that behaves like the original model.

# CFunction variables
start_code = """
Scrambler_descrambler_Scrambler_descrambler_init();"""
output_code = """
Scrambler_descrambler_Scrambler_descrambler_eval(In1,
                                               &Out1);
"""
terminate_code = """
Scrambler_descrambler_Scrambler_descrambler_final();"""

# Close
try engee.close("Scrambler_descrambler_Scrambler_descrambler_verification";force=true) catch end
# Create
engee.create("Scrambler_descrambler_Scrambler_descrambler_verification")
# Add Blocks
engee.add_block("/Basic/Ports & Subsystems/In1", "Scrambler_descrambler_Scrambler_descrambler_verification/In1")
engee.add_block("/Basic/User-Defined Functions/C Function", "Scrambler_descrambler_Scrambler_descrambler_verification/")
engee.add_block("/Basic/Ports & Subsystems/Out1", "Scrambler_descrambler_Scrambler_descrambler_verification/Out1")
# Edit block parameters
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/In1", "PortDimensions" => "()")
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/In1", "SignalType" => "real")
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/In1", "OutDataTypeStr" => "Bool")
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "OutputCode" => output_code, "StartCode" => start_code, "TerminateCode" => terminate_code)
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "Inputs" => 1)
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "Outputs" => 1)
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "InputPort1Label" => "In1")
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "InputPort1VarName" => "In1")
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "InputPort1Type" => "bool")
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "InputPort1Size" => "()")
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "SampleTime" => "0.01")
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "Parameters" => 0)
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "OutputPort1Label" => "Out1")
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "OutputPort1VarName" => "Out1")
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "OutputPort1Type" => "bool")
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "OutputPort1Size" => "()")
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "IncludeDirectories" => "/user/my_projects/Demo/Work/scrambler_Verilog/V_Code/obj_dir")
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "LibraryDirectories" => "/user/my_projects/Demo/Work/scrambler_Verilog/V_Code/obj_dir")
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "Headers" => "Scrambler_descrambler_Scrambler_descrambler_cfn_api.h ")
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification/C Function", "Libraries" => "libScrambler_descrambler_Scrambler_descrambler.so ")
# Edit model parameters
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification", "FixedStep" => 0.01)
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification", "StartTime" => 0.0)
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification", "StopTime" => 10)
engee.set_param!("Scrambler_descrambler_Scrambler_descrambler_verification", "EnableMultiTasking" => false)
# Add lines
engee.add_line("Scrambler_descrambler_Scrambler_descrambler_verification", "In1/1", "C Function/1")
engee.add_line("Scrambler_descrambler_Scrambler_descrambler_verification", "C Function/1", "Out1/1")
# Save
engee.save("Scrambler_descrambler_Scrambler_descrambler_verification.engee"; force=true)

Запустим этот файл и получим блок C function, работу которого впоследствии сравним с оригинальной моделью.

image_2.png

По результатам выполнения сгенерированного файла .jl мы получили блок, который обращается к библиотеке и исполняемому файлу. Теперь соберём на основе этого блока несколько моделей. Начнём с модели, аналогичной исходной.

image_3.png

Запустим эту модель и сравним время выполнения и общее поведения модели.

In [ ]:
@time run_model("Scrambler_descrambler_verification")
BER = simout["Scrambler_descrambler_verification/Error Rate Calculation.Output_1"];
BER = collect(BER)
BER[end-3:end,:]
Building...
Progress 0%
Progress 18%
Progress 99%
Progress 100%
  5.424469 seconds (205.50 k allocations: 15.873 MiB)
Out[0]:
4×2 DataFrame
Rowtimevalue
Float64Array…
19.97[0.0, 0.0, 998.0]
29.98[0.0, 0.0, 999.0]
39.99[0.0, 0.0, 1000.0]
410.0[0.0, 0.0, 1001.0]

Как мы можем заметить, модель работает быстрее исходной модели и, судя по BER, идентична исходной модели. Теперь давайте сравним эти блоки в общей модели. image.png

Запустим эту модель и сравним погрешность.

In [ ]:
run_model("Scrambler_add_verification")
err = simout["Scrambler_add_verification/err"];
println("Кол-во ошибок: $(sum(collect(err).value))")
Building...
Progress 0%
Progress 22%
Progress 100%
Progress 100%
Кол-во ошибок: 0

Как мы можем видеть из результатов, блоки идентичны, и это означает, что генерация кода выполнена корректно.

Вывод

В данном примере мы продемонстрировали инструмент верификации генерации кода, а также успешно сгенерировали verilog-код модели скремблера.

image.png Сам код представлен ниже.

module Scrambler_descrambler_Scrambler_descrambler(
input  clock,
       reset,
       io_In1,
output io_Out1
);

reg  UnitDelay_13_state;
reg  UnitDelay_2_state;
reg  UnitDelay_13_1_state;
reg  UnitDelay_2_1_state;
reg  UnitDelay_14_state;
reg  UnitDelay_14_1_state;
reg  UnitDelay_3_state;
reg  UnitDelay_3_1_state;
reg  UnitDelay_4_state;
reg  UnitDelay_4_1_state;
reg  UnitDelay_state;
reg  UnitDelay_1_state;
reg  UnitDelay_5_state;
reg  UnitDelay_5_1_state;
reg  UnitDelay_6_state;
reg  UnitDelay_1_1_state;
reg  UnitDelay_1_2_state;
reg  UnitDelay_6_1_state;
reg  UnitDelay_7_state;
reg  UnitDelay_7_1_state;
reg  UnitDelay_8_state;
reg  UnitDelay_8_1_state;
reg  UnitDelay_9_state;
reg  UnitDelay_9_1_state;
reg  UnitDelay_10_state;
reg  UnitDelay_10_1_state;
reg  UnitDelay_11_state;
reg  UnitDelay_11_1_state;
reg  UnitDelay_12_state;
reg  UnitDelay_12_1_state;
wire LogicalOperator = UnitDelay_13_state ^ UnitDelay_14_state;
wire LogicalOperator_1 = UnitDelay_13_1_state ^ UnitDelay_14_1_state;
always @(posedge clock) begin
  if (reset) begin
    UnitDelay_13_state <= 1'h0;
    UnitDelay_2_state <= 1'h0;
    UnitDelay_13_1_state <= 1'h0;
    UnitDelay_2_1_state <= 1'h0;
    UnitDelay_14_state <= 1'h0;
    UnitDelay_14_1_state <= 1'h0;
    UnitDelay_3_state <= 1'h0;
    UnitDelay_3_1_state <= 1'h0;
    UnitDelay_4_state <= 1'h0;
    UnitDelay_4_1_state <= 1'h0;
    UnitDelay_state <= 1'h0;
    UnitDelay_1_state <= 1'h0;
    UnitDelay_5_state <= 1'h0;
    UnitDelay_5_1_state <= 1'h0;
    UnitDelay_6_state <= 1'h0;
    UnitDelay_1_1_state <= 1'h0;
    UnitDelay_1_2_state <= 1'h0;
    UnitDelay_6_1_state <= 1'h0;
    UnitDelay_7_state <= 1'h0;
    UnitDelay_7_1_state <= 1'h0;
    UnitDelay_8_state <= 1'h0;
    UnitDelay_8_1_state <= 1'h0;
    UnitDelay_9_state <= 1'h0;
    UnitDelay_9_1_state <= 1'h0;
    UnitDelay_10_state <= 1'h0;
    UnitDelay_10_1_state <= 1'h0;
    UnitDelay_11_state <= 1'h0;
    UnitDelay_11_1_state <= 1'h0;
    UnitDelay_12_state <= 1'h0;
    UnitDelay_12_1_state <= 1'h0;
  end
  else begin
    UnitDelay_13_state <= UnitDelay_12_state;
    UnitDelay_2_state <= UnitDelay_1_1_state;
    UnitDelay_13_1_state <= UnitDelay_12_1_state;
    UnitDelay_2_1_state <= UnitDelay_1_2_state;
    UnitDelay_14_state <= UnitDelay_13_state;
    UnitDelay_14_1_state <= UnitDelay_13_1_state;
    UnitDelay_3_state <= UnitDelay_2_1_state;
    UnitDelay_3_1_state <= UnitDelay_2_state;
    UnitDelay_4_state <= UnitDelay_3_state;
    UnitDelay_4_1_state <= UnitDelay_3_1_state;
    UnitDelay_state <= LogicalOperator;
    UnitDelay_1_state <= LogicalOperator_1;
    UnitDelay_5_state <= UnitDelay_4_1_state;
    UnitDelay_5_1_state <= UnitDelay_4_state;
    UnitDelay_6_state <= UnitDelay_5_1_state;
    UnitDelay_1_1_state <= UnitDelay_1_state;
    UnitDelay_1_2_state <= UnitDelay_state;
    UnitDelay_6_1_state <= UnitDelay_5_state;
    UnitDelay_7_state <= UnitDelay_6_state;
    UnitDelay_7_1_state <= UnitDelay_6_1_state;
    UnitDelay_8_state <= UnitDelay_7_1_state;
    UnitDelay_8_1_state <= UnitDelay_7_state;
    UnitDelay_9_state <= UnitDelay_8_1_state;
    UnitDelay_9_1_state <= UnitDelay_8_state;
    UnitDelay_10_state <= UnitDelay_9_state;
    UnitDelay_10_1_state <= UnitDelay_9_1_state;
    UnitDelay_11_state <= UnitDelay_10_state;
    UnitDelay_11_1_state <= UnitDelay_10_1_state;
    UnitDelay_12_state <= UnitDelay_11_state;
    UnitDelay_12_1_state <= UnitDelay_11_1_state;
  end
end // always @(posedge)
assign io_Out1 = LogicalOperator_1 ^ LogicalOperator ^ io_In1;
endmodule

Блоки, использованные в примере