Зачем язык Verilog программисту микроконтроллеров

в 13:24, , рубрики: testbench, Verilog, Железо, микроконтроллер, Программинг микроконтроллеров, тестирование, метки: , , ,

image

Несколько раз начинал писать эту статью и бросал. Бросал потому, что тема, как мне кажется, несколько спорная. Изобретенный мною велосипед может кому-то показаться смешным и нелепым и вообще не совсем корректным. Тем не менее…

Вообще, мне кажется, что в области разработки электронных устройств существует как бы несколько мало пересекающихся миров. Например, существует разработка устройств на базе микроконтроллеров и параллельно существует разработка устройств на базе ПЛИС. Принципы работы этим микросхем принципиально отличаются и точно так же отличаются принципы и методы разработки, используемые языки программирования и отладки. Конечно, выбор элементной базы сильно зависит от поставленной задачи. Однако и так понятно, что эти миры, мир микроконтроллеров и мир ПЛИСов почти не пересекаются. Может быть на стыке технологий что-то есть?

Сам я, в общем, больше предпочитаю ПЛИС и даже участвую в блоге о ПЛИС, однако, недавно наша компания взялась за разработку устройства на базе микроконтроллера STM32. Собственно основные проблемы, которые мы встретили были не совсем технические, а скорее организационные.

Дело в том, что несмотря на заключенный договор о разработке устройства и несмотря на наличие более-менее согласованного ТЗ на устройство так получилось, что каждую неделю заказчик приходил с новыми идеями, требованиями, мыслями и пожеланиями. Можно было бы конечно, послать их куда подальше, но мы решили, что постараемся быть терпеливыми и все же будем выполнять проект.

Самая главная организационная проблема – поступивших запросов на изменение алгоритма управления контроллера было очень много и зачастую они были очень противоречивы. Причем заказчик сам не был уверен, как оно должно работать и даже не очень понимал, как он будет все это проверять. Точнее будет сказать так. Есть менеджеры которые хотят иметь некий контроллер в свой агрегат. Какие конкретно функции должны быть у контроллера и агрегата сами менеджеры точно не знают и требования должен был сформировать единственный инженер, который и должен был проверить работоспособность устройства. Своей методики проверки работоспособности контроллера у инженера нет, так как нет опыта работы с электронными устройствами.

Вот такой пример. В режиме ожидания, когда контроллер просто ждет нажатия кнопки «Пуск», наш контроллер должен раз в сутки включать насос, прокачивающий жидкость, на 10 секунд. Это типа защита от закисания сальников насоса. Когда я спрашиваю; «вы как будете эту функцию проверять» – отвечают, что «никак». Лично я в шоке. Мы, конечно, стараемся писать программы «без ошибок», но думаю принимающая сторона то же как-то должна делить ответственность, проводить свои испытания и т.д…

Далее перейду собственно к технической стороне дела.

Мы решили, что нам нужны свои какие-то тесты программного обеспечения микроконтроллера.

Обычно в мире ПО есть такое понятие юнит-тестирование. И в принципе, эта методика в какой-то мере подходит и для программ микроконтроллера. В настоящее время программы для микроконтроллеров зачастую пишутся на обычном языке Си, так что с юнит тестами, в общем, проблем быть не должно. Хотя… не все можно легко проверить юнит тестами в микроконтроллере.

Все что связано с программированием внутренних аппаратных устройств вроде таймеров, каналов DMA, последовательных портов, прерываний и так далее – с этим есть проблемы. Тут для отладки в пору брать осциллограф и смотреть какие сигналы микроконтроллера получаются на входах-выходах. И вообще померить сколько по времени идет обработка прерывания – дело не лишнее. И это правильно.

Есть еще один нюанс с юнит тестами. Как мне кажется, обычное юнит-тестирование ПО ориентированно на данные. Ну то есть обычно программа юнит-тест подает разные предполагаемые данные на вход проверяемой функции и далее проверяет, что обработанные выходные данные соответствуют требованиям. Все.

Для электронного устройства, которое выступает как обработчик входных сигналов принципиально важно понятие «течение времени». Ну то есть требования обычно такие: при возникновении нештатной ситуации и срабатывании аварийного датчика A последовательно с интервалом в N1 секунд выключить исполнительные устройства B, C, а устройство D выключить не позже N2 секунд но не раньше N3 секунд, Что-то вроде этого.

Понятно, что для проверки алгоритма управления программными средствами хорошо бы использовать какой-то симулятор сигналов с учетом течения времени. И такие средства есть в арсенале… у разработчиков систем на ПЛИС.

Разработчик электронных устройств на базе ПЛИС как правило использует язык описания аппаратуры Verilog или VHDL. При этом, кроме кода для ПЛИС пишутся так называемые testbench – это и есть что-то вроде unit-test для обычного программиста на Си.

Я использую Verilog HDL.
Программу тестбенч пишет сам программист и при этом старается симулировать все возможные входные воздействия на микросхему ПЛИС. Выходные сигналы анализируются самим тестбенчем и проверяются в соответствии с ожидаемыми.
При этом, сам симулятор Verilog следит за течением времени в системе.

Рассмотрим простой и совершенно абстрактный пример, что делает программист ПЛИС.
Например, вот модуль, который описывает простой двоичный счетчик с асинхронным сбросом (sample.v):

module sample(
	input wire reset,
	input wire clk,
	output reg [3:0]cnt
);

always @(posedge clk or posedge reset)
  if(reset)
	cnt <= 0;
  else
	cnt <= cnt + 1;

endmodule

Думаю такой код поймет любой программист, даже и не знающий языка Verilog HDL. Есть два входных сигнала reset и clk. И есть выходной четырехбитный сигнал [3:0]cnt. Всегда по фронту тактового сигнала значение в счетчике увеличивается. И всегда при появлении единицы на reset счетчик обнуляется.

Этот модуль синтезируемый, то есть его планируется откомпилировать и зашить в ПЛИС.

Теперь, например, программист хочет проверить работоспособность своего модуля. Он пишет программу на Verilog, тестбенч, который будет имитировать входные сигналы для микросхемы (testbench.v):

`timescale 1ms / 1 ms

module testbench();

reg tb_rst, tb_clk;
wire [3:0]value;

always
	#5 tb_clk = ~tb_clk;

initial
begin
	$dumpfile("waves.vcd");
	$dumpvars(0,testbench);

	$display("starting testbench!!!!");
	tb_rst = 1;
	tb_clk = 0;
	#10;
	tb_rst = 0;
	
	#73;
	tb_rst = 1;
	#11;
	tb_rst = 0;

	#134;
	tb_rst = 1;
	#57;
	tb_rst = 0;
	#200;
	$display("finished OK!");
	$finish;
end

sample my_sample_inst(
	.reset(tb_rst),
	.clk(tb_clk),
	.cnt( value )
);

wire fail;
assign fail = (tb_rst & value!=0 );

endmodule

Этот модуль не синтезируемый, его нельзя откомпилировать и зашить в ПЛИС, но он нужен для симуляции проекта. Обратите внимание на строки

sample my_sample_inst(
	.reset(tb_rst),
	.clk(tb_clk),
	.cnt( value )
);

Это в тестбенч вставлен исследуемый экземпляр модуля sample. Получается вот так:

image

Очень возможно, что у нас есть критерий работоспособности проекта. Нам нужно, чтобы во время сигнала reset выходные сигналы счетчика были всегда в нуле. То есть мы можем в тестбенче определить сигнал ошибки:

wire fail;
assign fail = (tb_rst & value!=0 );

Этот сигнал можно программно мониторить или просто посмотреть глазами на выходных временных диаграммах.

Я часто использую простой свободный симулятор VerilogHDL IcarusVerilog. Его просто установить и работать с ним. Компилирую и запускаю симулятор:

image

Мой тестбенч благодаря строкам программы $dumpfile(«waves.vcd»); и $dumpvars(0,testbench); создает файл с временными диаграммами waves.vcd. И эти временные диаграммы можно посмотреть с помощью другого замечательного свободного инструмента GtkWave:

image

Таким образом, самое простое, что может сделать программист ПЛИС – это написать к тестируемому модулю тестбенч и сгенерировать файлы получившихся временных диаграмм и рассматривать их, смотреть правильный ли отклик идет от ПЛИС.

Теперь расскажу, как можно похожую технологию тестирования применить к микроконтроллерам.

Если обратите внимание еще раз на Verilog тестбенч, то заметите там некоторые системные функции вроде $display(..).

Так вот. Оказывается, для симулятора Verilog HDL можно самому дописывать нужные нам системные функции на языке Си. А раз появляется место для языка Си, то появляется место, где код Verilog тестбенча может взаимодействовать с Сишным кодом для микроконтроллера.

Вообще, интерфейс симулятора Verilog к языку Си называется Verilog Procedural Interface (VPI). Рассказывать про него можно долго, это большая отдельная тема. Можно больше почитать, например, вот здесь.

Я же хочу просто схематично проиллюстрировать, как это может быть использовано.
Проект для микроконтроллера может состоять из многих файлов. Наша задача отделить собственно алгоритм управления от аппаратных особенностей конкретного микроконтроллера.

Предположим, проект для микроконтроллера состоит из файлов:

Main.c
Dma.c
Serial.c
Interrupts.c
….
Algorithm.c

Обработчики прерываний от входных линий микроконтроллера описаны в файле Interrupts.c. Они представляют из себя что-то вроде этого:

void EXTI9_5_IRQHandler(void)
{
  Int val;
  disableGlobalInterrupts();
  EXTI_ClearITPendingBit(EXTI_Line6);
  val = GPIO_ReadInputDataBit(MY_PORT, MY_SIGNAL);
       Algo_set_value( val );
  enableGlobalInterrupts();
}

Когда сигнал на входной линии микроконтроллера меняется, то происходит прерывание, в нем производится чтение значения на линии и это значение передается функцией Algo_set_val() алгоритму обработки. Весь алгоритм управления описан в файле Algorithm.c.

Файл Algorithm.c одновременно участвует в проекте для микроконтроллера и в проекте для VPI модуля Verilog.

image

Таким образом, разрабатывая «алгоритм управления» мы в основном компилируем VPI модуль вместе с Algorithm.c для Verilog симулятора. Вызывая в тестбенче Verilog нами определенную новую системную функцию $int() мы имитируем возникновение прерывания микроконтроллера в некоторый момент времени. Точно так же, внутренние переменные алгоритма можно читать и передавать тестбенчу Verilog с помощью новой нами определенной системной функции $getpin(..). Получается, что наш Verilog тестбенч может имитировать входные воздействия и течение времени для алгоритма управления микроконтроллера.

Примечательно, что мы получаем в свое распоряжение временные диаграммы входных воздействий и откликов. Их можно показать заказчику с целью ознакомления – для их просмотра используется программа GtkWave. Заказчик своими глазами сможет увидеть, как входные воздействия от различных датчиков будут откликаться его алгоритмом управления. Заказчик должен увидеть все возможные комбинации воздействий от входных сигналов и все отклики «черного ящика» под названием микроконтроллер.

По крайней мере, на временных диаграммах можно увидеть, как виртуально включается насос на 10 секунд каждые сутки…

Последний этап – перестаем компилировать VPI модуль для Verilog симулятора (фиолетовый блок) и начинаем компилировать проект микроконтроллера (оранжевый блок).

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

На всякий случай еще раз замечу, что с помощью нашего Verilog тестбенча мы конечно не проверяем правильность программирования регистров периферийных устройств вроде DMA или таймеров или GPIO. Мы тестируем только «алгоритм управления». На мой взгляд и это очень и очень важно.

Ну вот как-то так.

Автор: nckma

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js