Engee documentation
Notebook

Scrambler

In this example the possibilities of verification of generated code using C-functions in Engee are considered on the example of a scrambler model.

A scrambler is a device or algorithm that performs a reversible transformation of a digital stream to give it the properties of a random sequence without changing the bit rate. This example uses scrambling with a pseudo-random binary sequence (PRBS) generated by an LFSR of length 15 with an initial value of 100101010000000.

Next, we consider the implemented scrambling and descrambling scheme. It and bitwise comparison of the operation of this algorithm are shown in the figure below.

image_3.png

Let's run this model using the run function we described and save the bit error results.

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]

As we can see, the algorithm works correctly. Now let's move on to the settings of the code generator. The screenshot below shows the following points. The target platform is selected as Verilog, as well as comments in the generated code are disabled (this is done to visually simplify its readability), and C Function generation is enabled to verify the generated code. image.png

Now let's run the generation of the code and validation model.

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

Let's analyse the generated files. image.png As we can see, apart from the verilog code file, the folder also contains:

  1. executable .so file
  2. library file .h
  3. .jl code file

Let's start with the last one. This file allows us to generate a model containing a C function for comparison with the original model.

# 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_descrambler_init();"""""
output_code = """
Scrambler_descrambler_Scrambler_descrambler_descrambler_eval(In1,
                                                &Out1);
"""
terminate_code = """
Scrambler_descrambler_Scrambler_descrambler_descrambler_final();""""

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

Let's run this file and get the C function block, the operation of which we will later compare with the original model.

image_2.png

According to the results of execution of the generated .jl file, we have a block that accesses the library and the executable file. Now let's build several models based on this block. Let's start with a model similar to the initial one.

image_3.png

Let's run this model and compare the execution time and overall behaviour of the model.

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]

As we can see, the model runs faster than the original model and, judging by the BER, is identical to the original model. Now let's compare these blocks in the overall model. image.png

Let's run this model and compare the error.

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

As we can see from the results, the blocks are identical and it means that the code generation is correct.

Conclusion

In this example, we have demonstrated a code generation verification tool and successfully generated verilog code of the scrambler model.

image.png The code itself is shown below.

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

Blocks used in example