Проектирование цифровых систем на языках описания аппаратуры/Лекция 11
- Заголовок
- Верификация VHDL описаний цифровых систем. Генерация псевдослучайных тестов и функциональное покрытие
- Автор
- Ланкевич Ю.Ю.
- Нижний колонтитул
- Проектирование цифровых систем на языках описания аппаратуры/Лекция 11
- Дополнительный нижний колонтитул
- Ланкевич Ю.Ю., 12:55, 20 октября 2020
Слайд:Структура тестирующей программы
Тестирующие программы, реализующие процесс тестирования VHDL модели, не имеют входных и выходных портов.
Архитектурное тело тестирующей программы обычно состоит из трех либо четырех частей.
- Декларативная часть. В декларативной части архитектурного тела указывается тестируемый компонент (интерфейс VHDL модели проектируемого устройства либо системы), а также задаются сигналы, являющиеся входными и выходными портами устройства.
- Вызов модели. В исполняемом разделе архитектурного тела, т. е. после ключевого слова Begin, записывается оператор port map для вызова модели устройства.
- Задание входных воздействий. В третьей части в исполняемом разделе архитектурного тела записываются входные наборы, подаваемые на вход тестируемого компонента, либо программным образом обеспечивается генерация входных наборов по тем или иным правилам.
- Сравнение полученных реакций модели с ожидаемыми реакциями. Если тестирующая программа записала полученные реакции модели в виде файла, то данный файл может быть сравнен с файлом ожидаемых реакций, который обычно считается эталонным.
Заметим, что проектировщик может внести ошибку и в файл ожидаемых реакций, поэтому в результате несовпадений файлов, возможно, придется исправлять как модель, так и файл ожидаемых реакций.
Слайд:Пример схемы для верификации
Далее будут представлены различные варианты организации тестирующих программ для одной и той же модели двухразрядного умножителя mult_2.
1 library ieee; 2 use ieee.std_logic_1164.all; 3 4 entity mult_2 is 5 port ( 6 s1, s0, r1, r0 : in std_logic; 7 t3, t2, t1, t0 : out std_logic); 8 end mult_2; 9 architecture struct of mult_2 is 10 component add1 11 port ( b1, b2 : in std_logic; 12 c1, s1 : out std_logic); 13 end component; 14 signal p1, p2, p3, p4 : std_logic; 15 begin 16 t0 <= r0 and s0; 17 p1 <= r1 and s0; 18 p2 <= r0 and s1; 19 p4 <= r1 and s1; 20 circ1 : add1 21 port map (b1=>p1, b2=>p2, c1=>p3, s1=>t1); 22 circ2 : add1 23 port map (b1=>p3, b2=>p4, c1=>t3, s1=> 2); 24 end struct;
VHDL модель полусумматора add1.
1 library ieee; 2 use ieee.std_logic_1164.all; 3 entity add1 is 4 port( b1, b2 : in std_logic; 5 c1, s1 : out std_logic); 6 end add1; 7 architecture beh of add1 is 8 begin 9 s1 <= (b1 and (not b2)) or ((not b1) and b2); 10 c1 <= b1 and b2; 11 end beh;
Слайд:Задание тестирующих наборов в тестирующей программе
Самая простая тестирующая программа содержит первые три части. На рисунке показаны четыре входных набора, которые должны поочередно подаваться в качестве входных воздействий для функциональной модели двухразрядного умножителя mult_2.

Слайд:Задание тестирующих наборов в тестирующей программе
1 library ieee; 2 use ieee.std_logic_1164.all; 3 entity mult_2_tb1 is 4 end; 5 architecture tb of mult_2_tb1 is 6 component mult_2 is 7 port( 8 s1, s0, r1, r0 : in std_logic; 9 t3, t2, t1, t0 : out std_logic); 10 end component; 11 signal s1, s0, r1, r0 : std_logic; 12 signal t3, t2, t1, t0 : std_logic; 13 begin 14 DUT : mult_2 port map (s1, s0, r1, r0, t3, t2, t1, t0); 15 s1 <= '0', 16 '1' after 20 ns, 17 '1' after 40 ns, 18 '0' after 60 ns; 19 s0 <= '1', 20 '0' after 20 ns, 21 '1' after 40 ns, 22 '0' after 60 ns; 23 r1 <= '0', 23 '0' after 20 ns, 24 '1' after 40 ns, 25 '0' after 60 ns; 26 r0 <= '1', 27 '1' after 20 ns, 28 '1' after 40 ns, 29 '1' after 60 ns; 30 end tb;
Слайд:Генерация тестирующих наборов
Достаточно часто, особенно при тестировании блоков комбинационной логики, возникает потребность в генерации всевозможных тестовых наборов – n-разрядных двоичных векторов. Число всех различных n-разрядных двоичных векторов равно 2n , поэтому такая генерация возможна дляограниченного числа n, например, для n≤30.

В тестирующей программе генерация наборов осуществляется согласно возрастанию их десятичного эквивалента, проще говоря, наборы перечисляются, как в левой части таблицы истинности, задающей булеву функцию либо систему булевых функций. Генерация всевозможных 2n наборов (в двоичном коде) согласно возрастанию десятичного эквивалента осуществляется в бесконечном цикле, т. е. после набора 1111 генерируется набор 0000, затем 0001, и т. д. Поэтому для такой программы не рекомендуется выполнять в системе моделирования команду RUN ALL.
Слайд:Генерация тестирующих наборов
1 library ieee; 2 use ieee.std_logic_1164.all; 3 entity mult_2_tb3 is 4 end; 5 architecture tb of mult_2_tb3 is 6 component mult_2 is 7 port (s1, s0, r1, r0 : in std_logic; 8 t3, t2, t1, t0 : out std_logic); 9 end component; 10 signal s1, s0, r1, r0 : std_logic := '0'; 11 signal t3, t2, t1, t0 : std_logic; 12 begin 13 DUT : mult_2 port map (s1, s0, r1, r0, t3, t2, t1,t0); 14 -- не выполнять RUN ALL 15 s1 <= not s1 after 160 ns; 16 s0 <= not s0 after 80 ns; 17 r1 <= not r1 after 40 ns; 18 r0 <= not r0 after 20 ns; 19 end tb;
Слайд:Моделирование с проверкой ожидаемых реакций
Тестирующая программа реализует процесс тестирования, представленный на рисунке ниже.

Файл IN.TST представляет собой тест, т. е. наборы входных сигналов, подаваемых на входы тестируемой системы. Файл AWAIT_OUT.TST представляет собой выходные реакции. Каждой строке (входному набору) соответствует строка с тем же номером в файле ожидаемых реакций. Файл OUT.TST представляет собой реакции системы. Данный файл формируется при моделировании.
Слайд:Моделирование с проверкой ожидаемых реакций
Алгоритм тестирования следующий:
- из файла IN.TST в цикле считывается строка за строкой;
- считывание строки текстового файла осуществляется функцией readline;
- Затем функция read превращает символы в тип std_logic_vector.
- считанная строка “помещается” в переменную data, потом данные входные воздействия передаются в вектор din, который командой
(s1, s0, r1, r0) <= din;
распределяет разряды на вход модели mult_2.
В каждой итерации цикла считывается также строка из файла AWAIT_OUT.TST.
Каждая строка – это вектор (ожидаемая реакция системы).
Потом идет ожидание 20 ns – это важно, чтобы подождать и записать затем модельную реакцию схемы в файл OUT.TST.
Функция write преобразует std_logic_vector в строку символов, функция writeline записывает строку в файл.
Затем идет сравнение строк ожидаемой и действительной реакций.
Если есть ошибка, то наращивается значение сигнала errors, подсчитывающего число несовпадающих модельных и ожидаемых реакций.
Если ошибок нет, то выдается сообщение "Done!".
Если ожидаемая реакция не совпадает с модельной, то выдается номер ошибки и время моделирования (такт), в которое ошибка произошла.
Рекомендуется выполнить моделирование с правильным и неправильным файлом ожидаемых реакций.
Заметим, что ошибки могут быть и в файле ожидаемых реакций.
Слайд:Моделирование с проверкой ожидаемых реакций
1 library ieee; 2 use ieee.std_logic_1164.all; 3 use ieee.numeric_std.all; 4 use std.textio.all; 5 use ieee.std_logic_textio.all; 6 architecture tb2 of mult_2_tb7 is 7 component mult_2 is 8 port ( 9 s1, s0, r1, r0 : in std_logic; 10 t3, t2, t1, t0 : out std_logic); 11 end component; 12 signal s1, s0, r1, r0 : std_logic; 13 signal t3, t2, t1, t0 : std_logic; 14 signal din, dout : std_logic_vector (3 downto 0); 15 signal errors : natural := 0; 16 begin 17 DUT : mult_2 port map (s1, s0, r1, r0, t3, t2, t1, t0); 18 19 (s1, s0, r1, r0) <= din; 20 dout <= (t3, t2, t1, t0); 21 p1 : process 22 file fin, fout, fexpected : text; 23 variable l : line; 24 variable data, data_expected : std_logic_vector (3 downto 0); 25 begin 26 file_open(fin, "IN.TST", read_mode); 27 file_open(fexpected, "AWAIT_OUT.TST", read_mode); 28 file_open(fout, "OUT.TST", write_mode); 29 while not (endfile(fin)) loop 30 readline(fin, l); 31 read(l, data); 32 din <= data; 33 readline(fexpected, l); 34 read(l, data_expected); 35 wait for 20 ns; 36 data := dout; 37 write(l, data); 38 writeline(fout, l); 39 if (data /= data_expected) then 40 assert (false) report "ERRORS =" & to_string(errors) & " !" severity failure; 41 errors <= errors + 1; 42 end if; 43 end loop; 44 file_close(fout); 45 file_close(fin); 46 file_close(fexpected); 47 err : assert (errors = 0) report "ERRORS =" & to_string(errors) & " !" severity failure; 48 finish: assert (errors /= 0) report "Done!" severity warning; 49 wait; 50 end process p1; 51 end architecture tb2;
Слайд:Генерация псевдослучайных тестов и функциональное покрытие
В данном разделе рассматривается настраиваемая генерация псевдослучайных тестов и функциональное покрытие.
Настраиваемая генерация псевдослучайных тестов позволяет обнаруживать случайные ошибки в проектах в отличие от проблемно-ориентированных или прямых тестов.
Функциональное покрытие (functional coverage) предназначено для измерения того, какая часть функций проекта была проверена во время выполнения моделирования.
В методологии OS-VVM функциональное покрытие осуществляется сбором значений переменных и сигналов VHDL проекта при выполнении моделирования.
Методология OS-VVM базируется на VHDL пакетах CoveragePkg и RandomPkg стандарта VHDL’2008.
Рассмотрим подход к генерации псевдослучайных тестовых векторов на примере тестирования простейшего VHDL проекта цифровой системы – умножителя mult, предназначенного для перемножения целых положительных чисел a, b, заданных в двоичном коде. Точнее говоря, каждое из этих чисел задается в виде
a, b : in std_logic_vector (4 downto 1);
Слайд:Пример схемы на алгоритмическом уровне
Умножитель описан на алгоритмическом уровне: с помощью функции to_integer, находящейся в пакете numeric_std, входящем в стандартную библиотеку ieee, осуществляется переход к численным значениям входных векторов, затем полученные числа перемножаются, после чего осуществляется преобразование произведения e в выходной вектор d.
1 library ieee; 2 use ieee.std_logic_1164.all; 3 use ieee.numeric_std.all; 4 entity mult is 5 port ( a, b : in std_logic_vector (4 downto 1); 6 d : out std_logic_vector (8 downto 1)); 7 end mult; 8 architecture functional of mult is 9 signal e : integer range 0 to 225; 10 begin 11 p0 : process(a, b) 12 variable a_int, b_int : integer range 0 to 15; 13 begin 14 a_int := to_integer(unsigned(a)); 15 b_int := to_integer(unsigned(b)); 16 e <= a_int * b_int; 17 end process; 18 d <= std_logic_vector(to_unsigned(e, 8)); 19 end functional;
Слайд:Тестирующая программа на основе генерации псевдослучайных наборов
1 library ieee; 2 use ieee.std_logic_1164.all; 3 use ieee.math_real.all; 4 use ieee.numeric_std.all; 5 entity tstb is 6 end; 7 architecture beh of tstb is 8 component mult 9 port ( a, b : in std_logic_vector (4 downto 1); 10 d : out std_logic_vector (8 downto 1)); 11 end component; 12 signal a, b : std_logic_vector (4 downto 1); 13 signal d : std_logic_vector (8 downto 1); 14 begin 15 p0 : mult 16 port map (a => a, b => b, d => d); 17 RandomGenProc1 : process 18 variable RndValA, RndValB : real; 19 -- Random value 20 variable RndA, RndB : integer; 21 variable SeedA1 : positive := 7; 22 -- initialize seeds 23 variable SeedA2 : positive := 1; 24 variable SeedB1 : positive := 4; 25 -- initialize seeds 26 variable SeedB2 : positive := 2; 27 begin 28 for i in 1 to 100 loop 29 -- randomize 0.0 to 1.0 30 uniform(SeedA1, SeedA2, RndValA); 31 -- randomize 0.0 to 1.0 32 uniform(SeedB1, SeedB2, RndValB); 33 RndA := integer(trunc(RndValA*16.0)); 34 -- scale to 0 to 15 35 RndB := integer(trunc(RndValB*16.0)); 36 -- scale to 0 to 15 37 a <= std_logic_vector(to_unsigned(RndA, 4)); 38 b <= std_logic_vector(to_unsigned(RndB, 4)); 39 wait for 10 ns; 40 end loop; 41 wait ; 42 end process; 43 end;
Слайд:Тестирующая программа на основе генерации псевдослучайных наборов
Тестирующая программа tstb написана для организации тестовых воздействий, подаваемых на вход компонента mult. Данный компонент предназначен для перемножения целых положительных чисел a и b, заданных четырехразрядными векторами, результатом является восьмиразрядный вектор, представляющий числа из диапазона 0, 225. Такой диапазон получается из-за того, что числа a, b принимают значения из диапазона 0, 15. Процедура uniform(SeedA1, SeedA2, RndValA); -- генерация числа a пакета math_real позволяет получить псевдослучайное вещественное число RndValA по начальным значениям SeedA1, SeedA2 генератора. Сосредоточим основное внимание на подготовке тестирующих векторов a, b. В процессе RandomGenProc1 выполняются две процедуры ieee.math_real.uniform (из пакета math_real библиотеки ieee) для генерации двух псевдослучайных вещественных чисел RndValA, RndValB из диапазона (0.0, 1.0) (не включая граничных значений 0.0 и 1.0), данные числа затем будут преобразованы в 4-разрядные векторы (числа) a, b. Для генерации числа a процедуре uniform требуется задать начальные значения, например SeedA1, SeedA2, результатом ее выполнения будет псевдослучайное число RanValA. Функция ieee.math_real.trunc позволяет произвести округление вниз (отбрасывание дробной части), затем выполняется переход к целому числу RndA, которое с помощью функции to_unsigned пакета numeric_std преобразуется в вектор с требуемым числом (4) разрядов. Аналогичным образом все повторяется для получения второго псевдослучайного числа b. Такие пары псевдослучайных чисел a, b генерируются в цикле. Длину цикла for можно изменять. В рассматриваемом примере некоторые комбинации (например, от 226 до 255) выходов компонента mult никогда не могут появиться, а ведь они могут идти на вход другого функционального блока в «большом» проекте. Аналогично и для входных значений – в каких-то проектах некоторые входные комбинации могут быть запрещенными, за ними целесообразно следить при тестировании, отслеживать их появление – они могут свидетельствовать об ошибке в каких-то функциональных блоках. Рассмотрим достоинства и недостатки используемых средств генерации входных векторов, далее будем говорить о псевдослучайных числах a, b. Во-первых, в данной тестирующей программе генератором чисел можно управлять, изменяя только начальные значения seed1, seed2. Во-вторых, нужно дополнительно написать довольно сложные программы, если требуется
- генерировать последовательности псевдослучайных чисел, подчиняющиеся требуемым законам распределения вероятностей их появления, например нормальному закону с заданными параметрами;
- исключать значения некоторых чисел из определенных диапазонов;
- перебирать все значения каждого из чисел a, b;
- перебирать все варианты пар значений чисел a, b;
- получать все комбинации значений выходов тестируемого компонента.
Проблемы и соответствующие VHDL программы комбинаторно усложняются, если тестируемый компонент имеет десяток (и более) различных входных и выходных векторов.