Сообщество Engee

Icarus Verilog и медианный фильтр

Автор
avatar-yurevyurev
Notebook

Усредняющий фильтр Verilog

Усредняющий фильтр — один из видов цифровых фильтров, широко используемый в цифровой обработке сигналов и изображений для уменьшения уровня шума. Медианный фильтр является нелинейным КИХ-фильтром. В данном примере мы рассмотрим его упрощённую реализацию и посмотрим насколько корректно она работает, а также выполним генерацию кода из модели и проверим корректность работы сгенерированного кода при помощи icarus verilog, на рисунке ниже показана сама модель реализованного фильтра для окна фильтрации равного 10.

image.png

Далее определим функцию запуска модели и проведём тестирование модели и запись результатов её работы.

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

Далее запустим данную модель.

In [ ]:
run_model("Averaging_filter") # Запуск модели.
Building...
Progress 0%
Progress 100%
Progress 100%
Out[0]:
SimulationResult(
    "filter_out" => WorkspaceArray{Fixed{1, 10, 2, Int16}}("Averaging_filter/filter_out")
,
    "count" => WorkspaceArray{Fixed{1, 10, 2, Int16}}("Averaging_filter/count")

)
In [ ]:
out_model = collect(simout["Averaging_filter/filter_out"]).value
count = collect(simout["Averaging_filter/count"]).value

plot(count, label="Вход фильтра")
plot!(out_model, label="Выход фильтра")
Out[0]:

Как мы видим модель работает корректно, значения счётчика усредняются.

Генерация кода

Теперь перейдём к генерации кода из блока счётчика и фильтра из полученных модулей в последствии мы будем собирать итоговый проект.

In [ ]:
engee.generate_code(
"$(@__DIR__)/Averaging_filter.engee",
"$(@__DIR__)/Averaging_prj",
subsystem_name="cnt"
)
engee.generate_code(
"$(@__DIR__)/Averaging_filter.engee",
"$(@__DIR__)/Averaging_prj",
subsystem_name="Averaging_filter"
)

Icarus Verilog

Далее для тестирования мы реализуем TestBench и запустим его в Icarus Verilog — это свободный (GPL) симулятор Verilog для Linux и других UNIX-подобных систем. Это полнофункциональный компилятор и симулятор языка Verilog HDL (IEEE-1364).

Основное назначение:

  • Верификация цифровых схем перед синтезом
  • Тестирование и отладка Verilog-кода

Ключевые возможности:

  • Компиляция Verilog в исполняемый код (iverilog)
  • Симуляция с помощью виртуальной машины (vvp)
  • Поддержка большей части стандарта Verilog-2005
  • Генерация VCD-файлов для анализа в GTKWave

Основные инструменты:

  1. iverilog — компилятор (преобразует .v файлы в байт-код)
  2. vvp — симулятор (исполняет скомпилированный байт-код)
  3. gtkwave — вьюер временных диаграмм (отдельная программа)

Типичный workflow:

# Компиляция
iverilog -o sim design.v testbench.v
# Симуляция
vvp sim
# Или с генерацией VCD для визуализации
vvp sim -lxt2
gtkwave waveform.vcd
In [ ]:
filename = "$(@__DIR__)/Averaging_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
Содержимое файла /user/start/examples/codegen/averaging_filter_verilog/Averaging_prj/tb.v:
==================================================
module tb;
  reg clock;
  reg reset;
  wire [9:0] cnt_out;
  wire [9:0] filter_out;
  real real_value;
  Averaging_filter_cnt u_cnt (
    .clock(clock),
    .reset(reset),
    .io_Out1(cnt_out)
  );
  Averaging_filter_Averaging_filter u_filter (
    .clock(clock),
    .reset(reset),
    .io_Count(cnt_out),
    .io_Out1(filter_out)
  );
  always @(*) begin
    real_value = $itor($signed(filter_out)) / 4.0;
  end
  always #5 clock = ~clock;
  initial begin
    clock = 0;
    reset = 1;
    $display("№\tЗначение");
    $display("--\t-------");
    #15 reset = 0;
  end
  integer output_count = 0;
  always @(posedge clock) begin
    if (!reset) begin
      output_count = output_count + 1;
      $display("%d\t%0.2f", output_count, real_value);
      if (output_count >= 8) begin
        $finish;
      end
    end
  end
endmodule
==================================================
Конец файла

Этот код представляет собой тестбенч (testbench) на Verilog для тестирования двух модулей: Averaging_filter_cnt (счетчик) и Averaging_filter_Averaging_filter (фильтр).

Тестбенч генерирует тактовый сигнал, сбрасывает модули, затем запускает их работу и выводит первые 8 значений выхода фильтра в формате таблицы с преобразованием fixed-point (формат {1,10,2}) в вещественные числа делением на 4, останавливая симуляцию после вывода 8-го значения.

In [ ]:
run(`cd $(@__DIR__)/Averaging_prj`)
# Компиляция
run(`iverilog -o sim tb.v Averaging_filter_cnt.v Averaging_filter_Averaging_filter.v`)
# Запуск симуляции
run(`vvp sim`)
№	Значение
--	-------
          1	-3.00
          2	-6.00
          3	-8.75
          4	-11.25
          5	12.25
          6	9.75
          7	7.50
          8	5.25
Out[0]:
Process(`vvp sim`, ProcessExited(0))
In [ ]:
# Вывод заголовка таблицы
println("№\tЗначение")
println("--\t-------")

# Вывод только первых 8 значений
for i in 1:8
    println("$i\t$(out_model[i])")
end
№	Значение
--	-------
1	-3.0
2	-5.75
3	-8.5
4	-11.25
5	12.25
6	9.75
7	7.5
8	5.25

Как мы можем видеть результаты практически полностью совпали, но проблема в том, что модуль Averaging_filter_Averaging_filter имеет внутренние регистры (UnitDelay_state и т.д.), которые требуют времени для инициализации и заполнения конвейера. Первые несколько выходных значений могут быть некорректными или нестабильными, пока фильтр не заполнится данными.

Вывод

В данном примере мы разобрали возможности верификации Verilog кода при помощи встроенного симулятора в Engee, такой подход позволяет нам не просто получить код, но и не покидая среду убедиться в его работоспособности.