Проектирование цифровых систем на языках описания аппаратуры/Лекция 12
- Заголовок
- Введение в OS-VVM и функциональное покрытие (functional coverage).
- Автор
- Ланкевич Ю.Ю.
- Нижний колонтитул
- Проектирование цифровых систем на языках описания аппаратуры/Лекция 12
- Дополнительный нижний колонтитул
- Ланкевич Ю.Ю., 04:20, 2 ноября 2020
Слайд:Введение
Функциональное покрытие является одной из метрик, показывающей какая часть спецификации проверена. Качество результатов покрытия зависит от плана тестирования, т. к. 100% функциональное покрытие означает, что все функции, заданные в плане тестирования, выполнены во время моделирования. В отличии от покрытия кода, которое является функцией системы моделирования и может быть полностью автоматизировано, функциональное покрытие требует тщательной разработки тестового окружения и тестов в соответствии со спецификацией, а также анализа результатов моделирования, что не может быть полностью автоматизировано. Для осуществления функционального покрытия разработаны специальные функции в различных специализированных языках верификации, таких как SystemVerilog, е и др.

Проблема верификации цифровых устройств привела к появлению методологии OS-VVM (Open Source VHDL Verification Methodology). OS-VVM – это методология написания «интеллектуальных» тестирующих программ с использованием языка VHDL. Данная методология позволяет реализовать функциональное покрытие и управляемую генерацию псевдослучайных тестов, что используется при верификации цифровых функциональных блоков. https://www.synthworks.com/downloads/
Методология OS-VVM основывается на использовании специализированных VHDL пакетов RandomPkg и CoveragePkg при разработке тестирующих программ. Функции пакетов базируются на использовании защищенного типа protected, который появился в стандарте VHDL’2002.
Слайд:Защищенный тип protected
Защищенный тип (protected) базируется на концепции, похожей на классы в объектно-ориентированном подходе, известном из других языков программирования. Тип protected позволяет объединить данные и операции, выполняемые над ними, в один объект (инкапсуляция), таким образом, скрываются детали реализации типов данных от пользователей.
Полное определение типа protected состоит из двух частей: декларации и тела (body) типа. Объявления в декларативной части типа могут включать декларации подпрограмм (процедур и функций), спецификации атрибутов и конструкции подключения, использующие ключевое слово use. Тела подпрограмм объявляются в теле типа protected. Подпрограммы, описанные при декларации типа protected, называются методами. Ниже приведён пример декларации защищенного типа COUNTER_TYPE:
type COUNTER_TYPE is protected procedure Set (num : integer); procedure Inc; impure function get return integer; end protected COUNTER_TYPE;
Элементы, объявленные внутри тела типа protected, не доступны для использования вне этого типа. Таким образом, единственный способ доступа к этим элементам, это использование методов, объявленных при декларации типа. Единственным ограничением для методов является то, что формальные параметры методов не могут быть типа access или file.
Тело типа protected задаёт детали реализации данного типа, в теле типа могут быть описаны: декларации и тела подпрограмм, пакетов; декларации типов, подтипов, констант, переменных, файлов и alias (переименований); декларации атрибутов, спецификации и др. Пример тела защищенного типа COUNTER_TYPE:
type COUNTER_TYPE is protected body variable count : integer := 0; procedure Set ( num : integer) is begin count := num ; end procedure Set; procedure Inc is begin count := count + 1 ; end procedure Inc; impure function Get return integer is begin return count; end function Get; end protected body COUNTER_TYPE;
Только локальные либо общие (shared) переменные могут быть типа protected. Передача значения одной переменной типа protected другой переменной не допускается. Как следствие, переменная защищенного типа не должна иметь присвоения начального значения при декларации. Аналогичным образом, операторы отношений (например, равенства (“=”) и неравенства (“/=”)) не могут использоваться для переменных защищенного типа.
Для того чтобы вызвать методы (функции, процедуры) защищенного типа, нужно указать имя переменной и имя метода, разделенные точкой, например, если переменная объявлена как:
type COUNTER_TYPE is protected shared variable Cnt : COUNTER_TYPE;
тогда она может использоваться так:
if (Cnt.Get = 6) then . .
В выражении выше вызывается метод Get переменной Cnt, который возвращает значение внутренней переменной count.
Использование защищённых типов в OS-VVM защищает пользователя от довольно сложных структур и подпрограмм, поддерживающих генерацию псевдослучайных тестов и функциональное покрытие.
Слайд:Пакет RandomPkg
Пакет RandomPkg предоставляет подпрограммы и методы защищённого (protected) типа для упрощения псевдослучайной генерации чисел. В пакете декларируется защищенный тип RandomPType, который включает в себя начальное значение (seed) псевдослучайного генератора и набор функций для генерации случайных чисел в различных форматах и диапазонах. Генерация псевдослучайных чисел с использованием типа RandomPType проходит в три этапа: декларация переменной данного типа, настройка генератора (в простейшем случае задание начального значения seed) и получение псевдослучайного числа, как показано в следующем примере.
-- декларация переменной RV variable RV : RandomPType; ... -- задание начального значения seed RV.InitSeed(RV’instance_name); X <= RV.RandInt(1, 10); -- получение -- псевдослучайного числа в диапазоне [1, 10]
Метод RandInt является перегруженным, т.е. для него описано несколько реализаций с различным набором входных аргументов, как показано в примере.
RndGenProc : process -- защищённый тип из RandomPkg variable RV : RandomPType ; variable D : integer ; begin -- Задание значения seeds RV.InitSeed(RV'instance_name); -- Получение значения -- из диапазона [0, 255] D := RV.RandInt(0, 255); . . . -- Получение значения в -- диапазоне [1, 9], исключая -- значения 2, 4, 6, 8 D := RV.RandInt(1, 9, (2, 4, 6, 8)); . . . -- Получение значения из -- набора чисел 1, 3, 7, 9. D := RV.RandInt( (1, 3, 7, 9) ); . . . -- Получение значения из -- набора 1, 3, 7, 9, исключая -- значения 3, 7 D:=RV.RandInt((1, 3, 7, 9), (3, 7));
В приведенном примере дополнительные круглые скобки используются для задания аргумента, имеющего тип integer_vector и представляющего собой массив чисел. В пакете RandomPkg описан также ряд функций генерации псевдослучайных чисел, имеющих упрощенный вызов (сокращенный набор указываемых при вызове входных аргументов).
Функции генерации псевдослучайных чисел доступны не только для целых чисел, но и для векторных типов std_logic_vector (метод RandSlv), unsigned (RandUnsigned) и signed (RandSigned). При этом значения параметров по-прежнему задаются как целые числа (integer), но при вызове этих функций нужно указать еще дополнительный параметр – разрядность генерируемого вектора.
. . . variable DataSlv : std_logic_vector(7 downto 0); begin . . . -- Получение значения -- из диапазона [0, 255] DataSlv := RV.RandSlv(0, 255, 8);
Имя функции | Входные параметры | Return | Описание | ||||
---|---|---|---|---|---|---|---|
Min | Max | A | Exclude | Size | |||
RandReal | – | – | – | – | – | real | Возвращает псевдослучайное число из диапазона [0.0, 1.0] |
RandSigned | natural | Signed | Возвращает вектор размерности Size из диапазона [0, 2**Size-1] | ||||
RandUnsigned | Unsigned | ||||||
RandSlv | std_logic_vector | ||||||
RandReal | – | real | – | – | – | real | Возвращает псевдослучайное число из диапазона [0.0, Max] |
RandInt | integer | integer | |||||
RandSigned | natural | Signed | Возвращает вектор размерности Size из диапазона [0.0, Max] | ||||
RandUnsigned | natural | Unsigned | |||||
RandSlv | std_logic_vector | ||||||
RandReal | Real | Real | – | – | – | Real | Возвращает псевдослучайное число из диапазона [Min, Max] |
RandInt | integer | integer | integer | ||||
RandSigned | natural | Signed | Генерация вектора размерностью Size в диапазоне значений [Min, Max] | ||||
RandUnsigned | natural | natural | Unsigned | ||||
RandSlv | std_logic_vector | ||||||
RandInt | integer | integer | – | integer_vector | – | integer | Генерация числа в диапазоне [Min, Max] исключая список Exclude |
RandSigned | natural | Signed | Генерация вектора размерностью Size в диапазоне [Min, Max] исключая список Exclude | ||||
RandUnsigned | natural | natural | Unsigned | ||||
RandSlv | std_logic_vector | ||||||
RandInt | – | – | integer_vector | – | – | integer | Генерация случ. числа из списка чисел A |
RandSigned | natural | Signed | Генерация случ. вектора размерностью Size из списка чисел A | ||||
RandUnsigned | Unsigned | ||||||
RandSlv | std_logic_vector | ||||||
RandInt | – | – | integer_vector | integer_vector | – | integer | Генерация случ. числа из списка чисел A исключая список Exclude |
RandSigned | natural | Signed | Генерация случ. вектора размерностью Size из списка чисел A исключая список Exclude | ||||
RandUnsigned | Unsigned | ||||||
RandSlv | std_logic_vector |
По умолчанию, функции генерации возвращают псевдослучайные числа, подчиняющиеся равномерному закону распределения (реализованному с помощью процедуры uniform пакета math_real из библиотеки IEEE). В пакете реализованы другие законы распределения: FAVOR_SMALL (распределение с преобладанием малых значений), FAVOR_BIG (распределение с преобладанием больших значений), NORMAL (нормальный закон распределения, закон Гаусса), POISSON (распределение Пуассона). Для задания закона распределения используется следующий тип:
type RandomDistType is (NONE, UNIFORM, FAVOR_SMALL, FAVOR_BIG, NORMAL, POISSON);
RV.SetRandomParm(NORMAL, 5.0, 2.0);
В этом случае все функции генерации псевдослучайных чисел (Rand*) будут возвращать числа, подчиняющиеся нормальному распределению. В случае, если нужно генерировать только целые псевдослучайные числа, можно использовать перегруженные функции Uniform, FavorSmall, FavorBig, Normal, Poisson, которые возвращают псевдослучайные значения, подчиняющихся соответствующим законам распределения, независимо от закона распределения, заданного по умолчанию.
Кроме стандартных законов распределения случайной величины в пакете существует возможность взвешенной генерации – так называют генерацию по произвольному закону распределения с помощью указания списка требуемых значений целых чисел и их вероятностей появления (весов). Для взвешенной генерации псевдослучайных чисел в типе RandomPType есть две группы перегруженных функций DistVal* и Dist*. При вызове функций группы DistVal* в качестве аргумента задаётся массив пар чисел (число, вес). При вызове функций из другой группы Dist* задаётся массив (integer_vector) весов. Например, функция DistValInt вызывается с массивом пар значений.
Data := RV.DistValInt( ((1, 7), (3, 2), (5, 1)) );
Первый элемент в паре это значение, а второй – его вес. Частота, с которой каждое значение будет возникать, зависит от вероятности, которая определяется по формуле [вес/(сумма всех весов)]. В приведённом примере в результате многократного вызова метода DistValInt появление числа 1 будет с вероятностью 7/10 или 70%, числа 3 – 20%, а числа 5 – 10%.
Функция DistInt является упрощённой версией DistValInt, в которой задаются только веса. Числа генерируются в диапазоне от 0 до N – 1, где N –количество заданных весов. Например, результат многократного вызова
variable RV : RandomPType ; . . . DataInt := RV.DistInt((7, 2, 1)) ;
функции DistInt будет следующим: вероятность выпадения числа 0 будет 70%, числа 1 – 20%, а числа 2 – 10%.
Имя функции | Входные параметры | Return | Описание | |||
---|---|---|---|---|---|---|
Weight | A | Exclude | Size | |||
DistInt | integer_vector | – | – | – | integer | Возвращает псевдослучайное число (или вектор размерностью Size) из диапазона [0, N-1] на основе заданного массива весов Weight, где N число элементов в массиве Weight |
DistSlv | natural | std_logic_vector | ||||
DistUnsigned | natural | unsigned | ||||
DistSigned | natural | signed | ||||
DistInt | integer_vector | – | integer_vector | – | integer | Distribution with just weights and with exclude values |
DistSlv | natural | std_logic_vector | ||||
DistUnsigned | unsigned | |||||
DistSigned | signed | |||||
DistValInt | – | DistType | – | – | integer | Distribution with weight and value |
DistValSlv | natural | std_logic_vector | ||||
DistValUnsigned | unsigned | |||||
DistValSigned | signed | |||||
DistValInt | – | DistType | integer_vector | – | integer | Distribution with weight and value and with exclude values |
DistValSlv | natural | std_logic_vector | ||||
DistValUnsigned | unsigned | |||||
DistValSigned | signed |
Слайд:Использование RandomPkg
Каждый результат генератора псевдослучайных чисел производится функцией и результат может быть использован непосредственно в выражении. Таким образом, можно рандомизировать задержку, которая лежит между 3 и 10 периодами синхросигнала.
wait for RV.RandInt(3, 10) * tperiod_Clk - tpd ; wait until Clk = '1' ;
Значения могут также использоватся непосредственно внутри оператора выбора CASE. Следующий пример использует функцию DistInt для генерации выбора первого блока с вероятностью 70%, второго 20 %, и третьего 10%.
variable RV : RandomPType ; . . . StimGen : while TestActive loop -- Repeat until done case RV.DistInt( (7, 2, 1) ) is when 0 => -- Normal Handling -- 70% . . . when 1 => -- Error Case 1 -- 20% . . . when 2 => -- Error Case 2 -- 10% . . . when others => report "DistInt" severity failure ; -- Signal bug in DistInt end case ; end loop ;
Следующий фрагмент кода генерирует транзакции для записи DMA_WORD_COUNT, DMA_ADDR_HI, и DMA_ADDR_LO в случайном порядке, которые запускается каждый раз в различный момент. Последовательность заканчивает с записью в DMA_CTRL. Когда вызывается DistInt с весом 0, соответствующее значение не будет сгенерировано. Следовательно инициализируя все веса в 1 и затем устанавливая их в 0, когда выбираются, каждый блок оператора выбора происходит единожды. Цикл "for loop" проходит три раза чтобы разрешить каждой тракзакции сработаь.
variable RV : RandomPType ; . . . Wt0 := 1; Wt1 := 1; Wt2 := 1; -- Initial Weights for i in 1 to 3 loop -- Loop 1x per transaction case RV.DistInt( (Wt0, Wt1, Wt2) ) is -- Select transaction when 0 => -- Transaction 0 CpuWrite(CpuRec, DMA_WORD_COUNT, DmaWcIn); Wt0 := 0 ; -- remove from randomization when 1 => -- Transaction 1 CpuWrite(CpuRec, DMA_ADDR_HI, DmaAddrHiIn); Wt1 := 0 ; -- remove from randomization when 2 => -- Transaction 2 CpuWrite(CpuRec, DMA_ADDR_LO, DmaAddrLoIn); Wt2 := 0 ; -- remove from randomization when others => report "DistInt" severity failure ; end case ; end loop ; CpuWrite(CpuRec, DMA_CTRL, START_DMA or DmaCycle);
Следующий пример использует список исключений, чтобы не повторить последнего значения. Следует обратить внимание, что когда приходит целое значение integer в параметре integer_vector, множество успользующее поимённое назначение используется для обозначения одного элемента массива. Обратите внимание, что в течении первого выполнения этого процесса LastDataInt имеет значение integer'left (очень малое значение) которое лежит вне диапазона от 0 до 255, и в результате не имеет влияния на генерацию псевдослучайных чисел.
RandomGenProc : process variable RV : RandomPType ; variable DataInt, LastDataInt : integer ; begin . . . DataInt := RV.RandInt(0, 255, (0 => LastDataInt)) ; LastDataInt := DataInt; . . .
Следующий фрагмент кода использует лист исключений, чтобы избежать появления четырёх предыдущих значений.
RandProc : process variable RV : RandomPtype ; variable DataInt : integer ; variable Prev4DataInt : integer_vector(3 downto 0) := (others => integer'low) ; begin . . . DataInt := RV.RandInt(0, 100, Prev4DataInt) ; Prev4DataInt := Prev4DataInt(2 downto 0) & DataInt ; . . .
Слайд: Функциональное покрытие. Пример ручного отслеживания размера пакета
Функциональное покрытие может быть собрано с любого кода. В качестве первого примера, просто напишем код.
В пакетной передаче (такой как Ethernet), наиболее интересные эффекты возникают когда размер передачи равно или близко к минимальному или максимальному размеру передачи. Важно, чтобы происходили передачи среднего размера, но чтобы их было не слишком много. Для этого примера, предположим, что нас интересуют следующие пересылки следующих размеров или диапазонов: 1, 2, 3, 4–127, 128–252, 253, 254, 255. Эти размеры передач взяты из плана тестирования.
Нужно также определиться в какой момент собирать данные покрытия. В следующем коде используется положительный фронт синхросигнала Clk при установленном флаге TransactionDone в 1.
signal Bin : integer_vector(1 to 8) ; . . . process begin wait until rising_edge(Clk) and TransactionDone = '1' ; case to_integer(unsigned(ActualData)) is when 1 => Bin(1) <= Bin(1) + 1 ; when 2 => Bin(2) <= Bin(2) + 1 ; when 3 => Bin(3) <= Bin(3) + 1 ; when 4 to 127 => Bin(4) <= Bin(4) + 1 ; when 128 to 252 => Bin(5) <= Bin(5) + 1 ; when 253 => Bin(6) <= Bin(6) + 1 ; when 254 => Bin(7) <= Bin(7) + 1 ; when 255 => Bin(8) <= Bin(8) + 1 ; when others => end case ; end process ;
Любое покрытие может быть описано таким образом. Однако, это является слишком трудоёмким и узконаправленным. Можно было бы сделать небольшое улучшение этого описания путём переноса кода в процедуру. Это поможет с локальным повторным использованием, но по прежнему не будет встроенных функций (таких как отчет или сохранение базы). Пакет CoveragePkg и синтаксис языка предназначены исключительно для упрощения этой возможности.
Слайд:Пакет CoveragePkg
Пакет CoveragePkg включает описание новых типов данных и функций, которые позволяют создавать корзины для точек покрытия и перекрестного покрытия, собирать контролируемые значения переменных (сигналов), проверять полноту покрытия, выводить отчет о результатах покрытия. Но наиболее важной функцией пакета CoveragePkg является возможность организации интеллектуального покрытия (intelligent coverage), под которым понимается выбор псевдослучайного значения (или набора значений для перекрестного покрытия) из диапазона непокрытых значений.
В методологии OS-VVM функциональное покрытие осуществляется сбором значений переменных и сигналов VHDL проекта при выполнении моделирования.
Для организации покрытия в VHDL-программе необходимо создать переменную защищенного типа CovPType и описать модель покрытия путем задания необходимых корзин.
Далее в соответствии с планом тестирования с помощью метода ICover осуществляется сбор значений переменных или сигналов в заданные моменты времени (или по определенным событиям).
Полученные данные о покрытии можно использовать для управления псевдослучайной генерацией тестов при использовании метода RandCovPoint.
Метод IsCovered позволяет проверить достигнуто ли полное покрытие всех корзин.
С помощью методов WriteCovDb и ReadCovDb можно сохранить и загрузить базу данных о покрытии, что позволяет осуществить объединение данных о покрытии по нескольким запускам моделирования, которые могут выполняться параллельно.
Далее рассмотрим этап создания корзин более подробно.
Основные этапы работы с функциями пакета CoveragePkg:
Подключение пакета |
library osvvm; |
Декларация объекта покрытия |
shared variable CovX, CovXY : CovPType; |
Генерация корзин |
GenBin(0, 7); — 8 корзин, 1 значение в каждой |
GenBin(0, 255, 16); — 16 корзин одинакового размера} | |
Создание точек покрытия, либо перекрестного покрытия |
CovX.AddBins(GenBin(0, 31, 8)); CovX.AddBins(GenBin(32, 47, 1)); CovXY.AddCross(GenBin(0, 7), GenBin(0, 7)); |
Выборка (сбор) значений |
CovX.ICover(X); |
Проверка полноты покрытия |
if CovX.IsCovered then |
Оценка непокрытой области |
NotCov := CovX.CountCovHoles; |
Генерация псевдослучайных тестов |
X := CovX.RandCovPoint; — выбор непокрытых значений |
Вывод отчета |
CovX.WriteBin; — отчет может быть достаточно большим |
Сохранение базы данных |
CovX.WriteCovDb(“covdb.txt”, OpenKind => WRITE_MODE); |
Ниже описан пример описания функционального покрытия с использованием пакета CoveragePkg (аналогичный рассмотренному раннее)
architecture Test1 of tb is shared variable CovBin1 : CovPType ; -- Coverage Object begin TestProc : process begin -- Model the coverage CovBin1.AddBins(GenBin(1, 3 )); -- bins 1, 2, 3 CovBin1.AddBins(GenBin( 4, 252, 2)) ; -- bins 4 to 127 and 128 to 252 CovBin1.AddBins(GenBin(253, 255 )) ; -- 253, 254, 255 -- Accumulating coverage using clock based sampling loop wait until rising_edge(Clk) and nReset = '1' ; CovBin1.ICover(to_integer(unsigned(RxData_slv))) ; end loop ; end process ; ReportCov : process begin wait until rising_edge(Clk) and Bin1.IsCovered ; CovBin1.WriteBin ; wait ; end process ;
Слайд:Создание корзин (модели покрытия)
Функциональное покрытие осуществляется распределением значений переменных проекта по заранее определённым корзинам (bins) – диапазонам значений, которые имеют специальное назначение в проекте. Для корзин, использующих только одну переменную, создается структура данных, которую называют элементом покрытия (coverage item) или точкой покрытия (coverage point). По корзинам перекрёстного покрытия (cross coverage bins) распределяются пары (тройки и т.д.) значений двух либо нескольких переменных. Для корзин, использующих две и более переменные, создается структура данных, называемая элементом перекрёстного покрытия (cross coverage item).
В пакете CoveragePkg для корзины определена запись CovBinBaseType со следующими полями:
- BinVal – массив, включающий минимальное и максимальное значения интервала собираемых корзиной значений;
- Count – текущее (при моделировании) число попаданий в корзину значений из диапазона BinVal;
- AtLeast – цель покрытия задаёт число попаданий, при котором корзина будет считаться покрытой;
- Weight – вес – это целое (не равное нулю) положительное число, по которому вычисляется вероятность выбора корзины при использовании метода RandCovPoint;
- Action – действие (корзина может быть запрещенной, игнорируемой или рабочей), для рабочей корзины считается число попаданий.
Тип CovBinType представляет собой массив записей CovBinBaseType. Создание корзин (тип CovBinType) осуществляется вызовом функции GenBin. Метод AddBins используется для последовательного добавления созданных корзин в структуру данных покрытия – переменную, заданную в защищенном типе CovPType.
Функция GenBin является перегруженной и может вызываться с числом целочисленных аргументов от одного до пяти:
- AtLeast – цель покрытия,
- Weight – вес,
- Min – нижняя граница диапазона,
- Max – верхняя граница диапазона,
- NumBin – число корзин, на которое нужно разделить диапазон [Min, Max].
Функция GenBin возвращает тип CovBinType. Например, следующая команда создает три корзины с диапазонами собираемых значений [1, 2], [3, 4], [5, 6], устанавливает для каждой корзины цель покрытия равную 3, а метод AddBins добавляет корзины в структуру данных покрытия.
-- AtLeast Min Max NumBin CovX.AddBins( GenBin( 3, 1, 6, 3) );
При вызове функции GenBin с двумя аргументами (Min, Max) будет создано N корзин, где N = Max – Min + 1. Следующие три вызова функции GenBin являются эквивалентными и создают одну корзину с диапазоном собираемых значений [5, 5], значение цели покрытия и веса для данной корзины устанавливаются равными 1.
CovX.AddBins( GenBin(5) ); CovX.AddBins( GenBin(5, 5) ); CovX.AddBins( GenBin(5, 5, 1) );
Каждый раз, когда вызывается метод AddBins, новые корзины добавляются после уже созданных корзин. Метод AddBins является перегруженным и позволяет задавать цель покрытия и вес для добавляемых корзин. Используя раздельные вызовы AddBins, каждая корзина может иметь различные цель покрытия и/или вес псевдослучайного тестирования.
При создании корзин, иногда необходимо обозначить их так, чтобы при попадании значений в них реагировать как на ошибки или игнорировать и не считать их. Перегруженные функции IllegalBin и IgnoreBin используются для создания запрещённых и игнорируемых корзин и могут вызываться с одним, двумя либо тремя аргументами. При вызове функций IllegalBin и IgnoreBin с тремя аргументами задаются: минимальное (Min) и максимальное (Max) значения диапазона, и число корзин (NumBin) на которое разбивается заданный диапазон.
-- 3 запрещённые корзины: [1,3], [4,6], [7,9] CovX.AddBins( IllegalBin(1, 9, 3) ); -- 1 запрещенная корзина [1,9] CovX.AddBins( IllegalBin(1, 9, 1) ); -- 3 игнорируемые корзины: [1,1], [2,2], [3,3] CovX.AddBins( IgnoreBin ( 1, 3, 3) );
Вызов функций IgnoreBin и IllegalBin с двумя параметрами (Min, Max) приведёт к созданию одной корзины с диапазоном [Min, Max]. При вызове данных функций с одним параметром создаётся одна корзина с единичным диапазоном собираемых значений.
Так как все функции GenBin, IllegalBin и IgnoreBin возвращают значение типа CovBinType, их результаты могут быть объединены с помощью оператора конкатенации. В приведенном ниже примере будут добавлены шесть допустимых корзин [0, 0], [1, 1], [2, 5], [6, 9], [14, 14], [15, 15], одна игнорируемая корзина [10, 13], а все остальные корзины попадают в запрещённую корзину.
CovX.AddBins( GenBin(0,1) & GenBin(2,9,2) & GenBin(14,15) & IgnoreBin(10, 13) & ALL_ILLEGAL);
Кроме приведенной константы ALL_ILLEGAL, означающей запрещенную корзину с диапазоном значений [integer’left, integer’right], в пакете CoveragePkg декларируются следующие константы: ALL_BIN и ALL_COUNT – являются синонимами и обозначают рабочую корзину [integer’left, integer’right], ALL_IGNORE – игнорируемая корзина [integer’left, integer’right], ZERO_BIN – рабочая корзина [0, 0], ONE_BIN – рабочая корзина [1, 1]. Порядок добавления корзин в структуру данных покрытия имеет значение, т.к. по умолчанию установлен режим COUNT_FIRST, при котором собираемое значение распределяется в первую подходящую корзину. Чтобы задать режим, при котором собираемое значение распределяется по всем подходящим корзинам, необходимо вызвать метод SetCountMode с параметром COUNT_ALL.
Метод AddCross используется для добавления перекрестных корзин в структуру данных покрытия. При вызове метода AddCross, также как AddBins, можно передавать дополнительные параметры, задающие цель покрытия и вес псевдослучайного тестирования.
CovXY.AddCross( GenBin(0,3), GenBin(0,3) );
Метод AddCross создаёт векторное произведение корзин, заданных в качестве входных аргументов. Каждый вызов GenBin(0,3) создаёт четыре корзины: 0, 1, 2, 3. В результате чего AddCross создаёт 16 корзин со следующими парами: (0,0), (0,1), (0,2), (0,3), (1,0), (1,1), (1,2), (1,3), … , (3,0), (3,1), (3,2), (3,3). В приведённом примере используется перекрестное покрытие между двумя элементами, но метод AddCross поддерживает пересечение до 20 элементов.
Метод ICover используется для сбора покрытия. Для точек покрытия, метод ICover принимает значение integer. Для перекрёстного покрытия, метод ICover принимает значение integer_vector. Примеры вызова метода приведены ниже. Для обозначения типа integer_vector требуется вторая пара круглых скобок.
CovX.ICover( X ); CovXY.ICover( (X, Y) );
Процедуры WriteBin и WriteCovHoles используются для вывода отчета о покрытии.
procedure WriteBin ; procedure WriteBin (FileName : string; OpenKind : File_Open_Kind := APPEND_MODE) ; procedure WriteCovHoles ( PercentCov : real := 100.0 ) ; procedure WriteCovHoles ( FileName : string; PercentCov : real := 100.0 ; OpenKind : File_Open_Kind := APPEND_MODE );
Метод WriteBin печатает (выводит) результаты покрытия с выводом одной корзины на строку. Есть две версии. Первая не имеет аргументов и выводит в стандартный поток OUTPUT. Это показано ниже. Следует обратить внимание, что корзины, отмеченные как игнорируемые не выводятся WriteBin, а корзины отмеченные как запрещённые выводятся только если они имеют не нулевые значения счетчика (счёта).
ReportCov : process begin wait until rising_edge(Clk) and CovX.IsCovered ; CovX.WriteBin ; wait ; end process ;
Другая версия метода принимает два аргумента. Первый аргумент FileName задаёт имя файла (тип string). Второй аргумент задаёт значение OpenKind (to file_open) и принимает одно из значений WRITE_MODE или APPEND_MODE. Аргумент OpenKind инициализируется в значение APPEND_MODE.
-- FileName, OpenKind CovBin1.WriteBin ("Test1.txt", WRITE_MODE);
Метод WriteCovHoles выводит результаты покрытия, которые ниже параметра PercentCov. Корзины, помеченные как запрещённые или игнорируемые, не выводятся методом WriteCovHoles. Параметр ProcentCov инициализируется в 100% и обычно таким и остается.
CovX.WriteCovHoles ;
Другая версия WriteCovHoles задаёт FileName, PercentCov, и OpenKind в похожем стиле метода WriteBin. Аргумент инициализируется в APPEND_MODE. Это показано ниже.
-- FileName, PercentCov, OpenKind CovX.WriteCovHoles("Test1.txt", 100.0, APPEND_MODE);
Методы SetName и SetItemName используютя для печати первой и второй строки заголовка для методов WriteBin и WriteCovHoles. SetName предназначается для ссылки на имя или цель корзины покрытия. SetItemName предназначена для вывода столбцов заголовка для корзин покрытия. Каждая из них также использует их строковые параметры для инициализации внутреннего начального значения(seed) для псевдослучайного генератора чисел.
Слайд:Использование пакетов
В примере демонстрируется использование пакетов RandomPkg и CoveragePkg. Заданы два сигнала целочисленного типа, принимающие значения из диапазонов [0, 3] и [0, 4] соответственно, необходимо сгенерировать все возможные пары значений не менее двух раз. В программе декларируются две пары сигналов X, Y и A, B. Для генерации значений X, Y используется метод RandInt из пакета RandomPkg, а для A, B – метод RandCovPoint из пакета CoveragePkg для проведения интеллектуального покрытия.
library ieee; library osvvm; use osvvm.RandomPkg.all; use osvvm.CoveragePkg.all; entity example is end entity example; architecture beh of example is signal X, Y, A, B : natural; shared variable CrossXY, CrossAB : CovPType; begin p1: process is variable RndX : RandomPtype; variable RndY : RandomPtype; begin CrossXY.AddCross(2,GenBin(0,3),GenBin(0,4)); RndX.InitSeed(RndX'instance_name); RndY.InitSeed(RndY'instance_name); l1: while not CrossXY.IsCovered loop X <= RndX.RandInt(0,3); Y <= RndY.RandInt(4); wait for 10 ns; CrossXY.ICover((X, Y)); end loop; CrossXY.WriteBin; wait; end process p1; p2: process is begin CrossAB.AddCross(2,GenBin(0,3),GenBin(0,4)); l1: while not CrossAB.IsCovered loop (A, B) <= CrossAB.RandCovPoint; wait for 10 ns; CrossAB.ICover((A, B)); end loop; CrossAB.WriteBin; wait; end process p2; end architecture beh;
Для каждой из пар сигналов (X, Y и A, B) собирается перекрестное покрытие с помощью общих переменных CrossXY и CrossAB.
В результате моделирования для покрытия всех пар значений X, Y дважды потребовалось 105 циклов, а для покрытия A, B – 40 циклов.
В таблицах ниже приведены результаты покрытия пары X, Y и пары A, B.
Видно, что при использовании псевдослучайного генератора с линейным законом распределения (uniform) распределение пар значений X, Y не равномерно.
Пара чисел (2, 3) генерировалась 10 раз, а пара (0, 1) – 2 раза.
|
|
Таким образом, использование интеллектуального покрытия в тестирующей программе позволяет значительно сократить необходимое число генерируемых псевдослучайных тестов для полного покрытия, что приводит к уменьшению времени моделирования.
Слайд:Пример простого тестбенча с рандомизацией на базе ieee.math_real
library ieee; use ieee.std_logic_1164.all; use ieee.math_real.all; use ieee.numeric_std.all; entity tstb is end; architecture BEHAVIOR of tstb is component sxema port ( a, b : in std_logic_vector (4 downto 1); d : out std_logic_vector (8 downto 1)); end component; signal a, b : std_logic_vector (4 downto 1); signal d : std_logic_vector (8 downto 1); begin p0 : sxema port map ( a => a, b => b, d => d); RandomGenProc1 : process variable RandomVal_1, RandomVal_2 : real; -- Random value variable DataSent_1, DataSent_2 : integer; variable seed1_a : positive := 7; -- initialize seeds variable seed2_a : positive := 1; variable seed1_b : positive := 4; -- initialize seeds variable seed2_b : positive := 2; begin for i in 1 to 100 loop uniform(seed1_a, seed2_a, RandomVal_1); -- randomize 0.0 to 1.0 uniform(seed1_b, seed2_b, RandomVal_2); -- randomize 0.0 to 1.0 DataSent_1 := integer(trunc(RandomVal_1*15.0)); -- scale to 0 to 15 DataSent_2 := integer(trunc(RandomVal_2*15.0)); -- scale to 0 to 15 a <= std_logic_vector(to_unsigned(DataSent_1, 4)); b <= std_logic_vector(to_unsigned(DataSent_2, 4)); wait for 50 ns; end loop; wait ; end process; end;
Слайд:Пример OS-VVM тесбенча с рандомизацией на базе пакета osvvm.RandomPkg (нормальное распределение)
library ieee; library osvvm; use ieee.std_logic_1164.all; use ieee.math_real.all; use ieee.numeric_std.all; use osvvm.RandomPkg.all; use osvvm.CoveragePkg.all; architecture tb of tstb is componen sxema port (a, b : in std_logic_vector (4 downto 1); d : out std_logic_vector (8 downto 1)); end component; signal a, b : std_logic_vector (4 downto 1); signal d : std_logic_vector (8 downto 1); shared variable RndA, RndB : RandomPType; shared variable CovA, CovB, CovD : CovPType ; begin p0 : sxema port map ( a => a, b => b, d => d); RandomGenProc1 : process -- variable RndVal1, RndVal_2 : real; -- Random value -- variable DataSent1, DataSent2 : integer; variable SeedA1 : positive := 7; -- initialize seeds variable SeedA2 : positive := 1; variable SeedB1 : positive := 4; -- initialize seeds variable SeedB2 : positive := 2; begin -- инициализация генератора псевдослучайных чисел RndA.InitSeed(IV => (SeedA1,SeedA2)); RndB.InitSeed(IV => (SeedB1,SeedB2)); RndA.SetRandomParm ( Distribution => NORMAL, Mean => 5.0, Deviation => 2.0 ) ; RndB.SetRandomParm ( Distribution => NORMAL, Mean => 10.0, Deviation => 2.0 ) ; -- создание корзин для a, b, d CovA.AddBins(GenBin(0,15)); CovB.AddBins(GenBin(0,15)); CovD.AddBins(GenBin(0,225)); for i in 1 to 100 loop a <= RndA.RandSlv(0,15,4); b <= RndB.RandSlv(0,15,4); --a <= std_logic_vector(to_unsigned(DataSent_1, 4)); --b <= std_logic_vector(to_unsigned(DataSent_2, 4)); wait for 10 ns; CovA.ICover(to_integer(unsigned(a))); CovB.ICover(to_integer(unsigned(b))); CovD.ICover(to_integer(unsigned(d))); end loop; CovA.WriteBin; CovB.WriteBin; CovD.WriteBin; -- CovA.DumpBin; -- CovA.WriteOrderCount; CovA.WriteCovDb ("CovA.db", OpenKind => WRITE_MODE ); CovB.WriteCovDb ("CovB.db", OpenKind => WRITE_MODE ); CovD.WriteCovDb ("CovD.db", OpenKind => WRITE_MODE ); wait ; end process; end architecture tb;
Слайд:Пример OS-VVM тесбенча с рандомизацией на базе пакета osvvm.RandomPkg (равномерное распределение)
Случайным образом генерит числа A и B до тех пор пока не переберёт все возможные входные значения требуется 102 цикла генерации
library ieee; library osvvm; use ieee.std_logic_1164.all; use ieee.math_real.all; use ieee.numeric_std.all; use osvvm.RandomPkg.all; use osvvm.CoveragePkg.all; architecture tb2 of tstb is component sxema port ( a, b : in std_logic_vector (4 downto 1); d : out std_logic_vector (8 downto 1)); end component; signal a, b : std_logic_vector (4 downto 1); signal d : std_logic_vector (8 downto 1); shared variable RndA, RndB : RandomPType; shared variable CovA, CovB, CovD : CovPType ; begin p0 : sxema port map ( a => a, b => b, d => d); RandomGenProc1 : process -- variable RndVal1, RndVal_2 : real; -- Random value -- variable DataSent1, DataSent2 : integer; variable SeedA1 : positive := 7; -- initialize seeds variable SeedA2 : positive := 1; variable SeedB1 : positive := 4; -- initialize seeds variable SeedB2 : positive := 2; variable i : natural := 0; begin -- инициализация генератора псевдослучайных чисел RndA.InitSeed(IV => (SeedA1,SeedA2)); RndB.InitSeed(IV => (SeedB1,SeedB2)); -- RndA.SetRandomParm ( -- Distribution => NORMAL, -- Mean => 5.0, -- Deviation => 2.0 ) ; -- RndB.SetRandomParm ( -- Distribution => NORMAL, -- Mean => 10.0, -- Deviation => 2.0 ) ; -- создание корзин для a, b, d CovA.AddBins(GenBin(0,15)); CovB.AddBins(GenBin(0,15)); CovD.AddBins(GenBin(0,225)); MainCovLoop: while not (CovA.IsCovered and CovB.IsCovered) loop -- MainCovLoop: while not (CovD.IsCovered) loop a <= RndA.RandSlv(0,15,4); b <= RndB.RandSlv(0,15,4); --a <= std_logic_vector(to_unsigned(DataSent_1, 4)); --b <= std_logic_vector(to_unsigned(DataSent_2, 4)); wait for 10 ns; CovA.ICover(to_integer(unsigned(a))); CovB.ICover(to_integer(unsigned(b))); CovD.ICover(to_integer(unsigned(d))); i := i+1; if i=1000000 then exit; end if; end loop; CovA.WriteBin; CovB.WriteBin; CovD.WriteBin; -- CovA.DumpBin; -- CovA.WriteOrderCount; CovA.WriteCovDb ("CovA.db", OpenKind => WRITE_MODE ); CovB.WriteCovDb ("CovB.db", OpenKind => WRITE_MODE ); CovD.WriteCovDb ("CovD.db", OpenKind => WRITE_MODE ); wait ; end process; end architecture tb2;
Слайд:Пример OS-VVM тесбенча с рандомизацией на базе пакета osvvm.CoveragePkg
Случайным образом генерит входные A и B с помощью пакета покрытия, т.е. интеллектуальный тестбенч. Для покрытия всех состояний требуется 16 циклов
library ieee; library osvvm; use ieee.std_logic_1164.all; use ieee.math_real.all; use ieee.numeric_std.all; use osvvm.RandomPkg.all; use osvvm.CoveragePkg.all; architecture tb3 of tstb is component sxema port ( a, b : in std_logic_vector (4 downto 1); d : out std_logic_vector (8 downto 1)); end component; signal a, b : std_logic_vector (4 downto 1); signal d : std_logic_vector (8 downto 1); -- shared variable RndA, RndB : RandomPType; shared variable CovA, CovB, CovD : CovPType ; begin p0 : sxema port map ( a => a, b => b, d => d); RandomGenProc1 : process -- variable RndVal1, RndVal_2 : real; -- Random value variable DataA, DataB,tmp : integer; -- variable SeedA1 : positive := 7; -- initialize seeds -- variable SeedA2 : positive := 1; -- variable SeedB1 : positive := 4; -- initialize seeds -- variable SeedB2 : positive := 2; variable i : natural := 0; begin -- инициализация генератора псевдослучайных чисел -- RndA.InitSeed(IV => (SeedA1,SeedA2)); -- RndB.InitSeed(IV => (SeedB1,SeedB2)); -- CovA.InitSeed(IV => (SeedA1,SeedA2)); -- CovB.InitSeed(IV => (SeedB1,SeedB2)); CovA.InitSeed(CovA'instance_name); CovB.InitSeed(CovB'instance_name); -- RndA.SetRandomParm ( -- Distribution => NORMAL, -- Mean => 5.0, -- Deviation => 2.0 ) ; -- RndB.SetRandomParm ( -- Distribution => NORMAL, -- Mean => 10.0, -- Deviation => 2.0 ) ; -- создание корзин для a, b, d CovA.AddBins(GenBin(0,15)); CovB.AddBins(GenBin(0,15)); CovD.AddBins(GenBin(0,225)); MainCovLoop: while not (CovA.IsCovered and CovB.IsCovered) loop -- MainCovLoop: while not (CovD.IsCovered) loop -- a <= RndA.RandSlv(0,15,4); -- b <= RndB.RandSlv(0,15,4); (DataA,tmp) := CovA.RandCovPoint ; (DataB,tmp) := CovB.RandCovPoint ; a <= std_logic_vector(to_unsigned(DataA, 4)); b <= std_logic_vector(to_unsigned(DataB, 4)); wait for 10 ns; CovA.ICover(to_integer(unsigned(a))); CovB.ICover(to_integer(unsigned(b))); CovD.ICover(to_integer(unsigned(d))); i := i+1; if i=1000000 then exit; end if; end loop; wait for 10 ns; CovA.WriteBin; CovB.WriteBin; CovD.WriteBin; -- CovA.DumpBin; -- CovA.WriteOrderCount; CovA.WriteCovDb ("CovA.db", OpenKind => WRITE_MODE ); CovB.WriteCovDb ("CovB.db", OpenKind => WRITE_MODE ); CovD.WriteCovDb ("CovD.db", OpenKind => WRITE_MODE ); wait ; end process; end architecture tb3;
Слайд:Пример OS-VVM тесбенча с рандомизацией на базе пакета osvvm.RandomPkg и перекрестным покрытием
Случайным образом генерит входные A и B с помощью пакета RandomPkg тестбенч для перебора всех пар, проверка перекрёстного покрытия. Для покрытия всех пар состояний требуется 2258 циклов, покрытие A достигается за 103 циклов, покрытие B достигается за 68 циклов
library ieee; library osvvm; use ieee.std_logic_1164.all; use ieee.math_real.all; use ieee.numeric_std.all; use osvvm.RandomPkg.all; use osvvm.CoveragePkg.all; architecture tb4a of tstb is component sxema port ( a, b : in std_logic_vector (4 downto 1); d : out std_logic_vector (8 downto 1)); end component; signal a, b : std_logic_vector (4 downto 1); signal d : std_logic_vector (8 downto 1); shared variable RndA, RndB : RandomPType; shared variable CovA, CovB, CovD : CovPType ; shared variable CovCrossAB : CovPType ; begin p0 : sxema port map ( a => a, b => b, d => d); RandomGenProc1 : process -- variable RndVal1, RndVal_2 : real; -- Random value variable DataA, DataB,tmp : integer; variable SeedA1 : positive := 7; -- initialize seeds variable SeedA2 : positive := 1; variable SeedB1 : positive := 4; -- initialize seeds variable SeedB2 : positive := 2; variable i : natural := 0; variable CovACovered,CovBCovered : natural := 0; begin -- инициализация генератора псевдослучайных чисел RndA.InitSeed(IV => (SeedA1,SeedA2)); RndB.InitSeed(IV => (SeedB1,SeedB2)); -- CovA.InitSeed(IV => (SeedA1,SeedA2)); -- CovB.InitSeed(IV => (SeedB1,SeedB2)); -- CovA.InitSeed(CovA'instance_name); -- CovB.InitSeed(CovB'instance_name); -- CovCrossAB.InitSeed(CovCrossAB'instance_name); -- RndA.SetRandomParm ( -- Distribution => NORMAL, -- Mean => 5.0, -- Deviation => 2.0 ) ; -- RndB.SetRandomParm ( -- Distribution => NORMAL, -- Mean => 10.0, -- Deviation => 2.0 ) ; -- создание корзин для a, b, d CovA.AddBins(GenBin(0,15)); CovB.AddBins(GenBin(0,15)); CovD.AddBins(GenBin(0,225)); CovCrossAB.AddCross( 2, GenBin(0,15), GenBin(0,15) ); -- MainCovLoop: while not (CovA.IsCovered and CovB.IsCovered) loop MainCovLoop: while not (CovCrossAB.IsCovered) loop a <= RndA.RandSlv(0,15,4); b <= RndB.RandSlv(0,15,4); -- (DataA,DataB) := CovCrossAB.RandCovPoint ; -- a <= std_logic_vector(to_unsigned(DataA, 4)); -- b <= std_logic_vector(to_unsigned(DataB, 4)); wait for 10 ns; CovA.ICover(to_integer(unsigned(a))); CovB.ICover(to_integer(unsigned(b))); CovCrossAB.ICover( (to_integer(unsigned(a)) , to_integer(unsigned(b))) ); CovD.ICover(to_integer(unsigned(d))); i := i+1; if i=1000000 then exit; end if; if (CovA.IsCovered and CovACovered=0) then CovACovered := i; end if; if (CovB.IsCovered and CovBCovered=0) then CovBCovered := i; end if; end loop; wait for 10 ns; CovA.WriteBin; CovB.WriteBin; CovD.WriteBin; CovCrossAB.WriteBin; -- CovA.DumpBin; -- CovA.WriteOrderCount; CovA.WriteCovDb ("CovA.db", OpenKind => WRITE_MODE ); CovB.WriteCovDb ("CovB.db", OpenKind => WRITE_MODE ); CovD.WriteCovDb ("CovD.db", OpenKind => WRITE_MODE ); CovCrossAB.WriteCovDb ("CovCrossAB.db", OpenKind => WRITE_MODE ); wait ; end process; end architecture tb4a;
Слайд:Пример OS-VVM тесбенча с рандомизацией на базе пакета osvvm.CoveragePkg и перекрестным покрытием
Случайным образом генерит входные A и B с помощью пакета покрытия, используя перекрёстное покрытие, т.е. интеллектуальный тестбенч для перебора всех пар. Для покрытия всех пар состояний требуется 512 циклов, покрытие A достигается за 49 циклов, покрытие B достикается за 44 циклов
library ieee; library osvvm; use ieee.std_logic_1164.all; use ieee.math_real.all; use ieee.numeric_std.all; use osvvm.RandomPkg.all; use osvvm.CoveragePkg.all; architecture tb4 of tstb is component sxema port ( a, b : in std_logic_vector (4 downto 1); d : out std_logic_vector (8 downto 1)); end component; signal a, b : std_logic_vector (4 downto 1); signal d : std_logic_vector (8 downto 1); -- shared variable RndA, RndB : RandomPType; shared variable CovA, CovB, CovD : CovPType ; shared variable CovCrossAB : CovPType ; begin p0 : sxema port map ( a => a, b => b, d => d); RandomGenProc1 : process -- variable RndVal1, RndVal_2 : real; -- Random value variable DataA, DataB,tmp : integer; -- variable SeedA1 : positive := 7; -- initialize seeds -- variable SeedA2 : positive := 1; -- variable SeedB1 : positive := 4; -- initialize seeds -- variable SeedB2 : positive := 2; variable i : natural := 0; variable CovACovered,CovBCovered : natural := 0; begin -- инициализация генератора псевдослучайных чисел -- RndA.InitSeed(IV => (SeedA1,SeedA2)); -- RndB.InitSeed(IV => (SeedB1,SeedB2)); -- CovA.InitSeed(IV => (SeedA1,SeedA2)); -- CovB.InitSeed(IV => (SeedB1,SeedB2)); CovA.InitSeed(CovA'instance_name); CovB.InitSeed(CovB'instance_name); CovCrossAB.InitSeed(CovCrossAB'instance_name); -- RndA.SetRandomParm ( -- Distribution => NORMAL, -- Mean => 5.0, -- Deviation => 2.0 ) ; -- RndB.SetRandomParm ( -- Distribution => NORMAL, -- Mean => 10.0, -- Deviation => 2.0 ) ; -- создание корзин для a, b, d CovA.AddBins(GenBin(0,15)); CovB.AddBins(GenBin(0,15)); CovD.AddBins(GenBin(0,225)); CovCrossAB.AddCross( 2, GenBin(0,15), GenBin(0,15) ); -- MainCovLoop: while not (CovA.IsCovered and CovB.IsCovered) loop MainCovLoop: while not (CovCrossAB.IsCovered) loop -- a <= RndA.RandSlv(0,15,4); -- b <= RndB.RandSlv(0,15,4); (DataA,DataB) := CovCrossAB.RandCovPoint ; a <= std_logic_vector(to_unsigned(DataA, 4)); b <= std_logic_vector(to_unsigned(DataB, 4)); wait for 10 ns; CovA.ICover(to_integer(unsigned(a))); CovB.ICover(to_integer(unsigned(b))); CovCrossAB.ICover( (to_integer(unsigned(a)) , to_integer(unsigned(b))) ); CovD.ICover(to_integer(unsigned(d))); i := i+1; if i=1000000 then exit; end if; if (CovA.IsCovered and CovACovered=0) then CovACovered := i; end if; if (CovB.IsCovered and CovBCovered=0) then CovBCovered := i; end if; end loop; wait for 10 ns; CovA.WriteBin; CovB.WriteBin; CovD.WriteBin; CovCrossAB.WriteBin; -- CovA.DumpBin; -- CovA.WriteOrderCount; CovA.WriteCovDb ("CovA.db", OpenKind => WRITE_MODE ); CovB.WriteCovDb ("CovB.db", OpenKind => WRITE_MODE ); CovD.WriteCovDb ("CovD.db", OpenKind => WRITE_MODE ); CovCrossAB.WriteCovDb ("CovCrossAB.db", OpenKind => WRITE_MODE ); wait ; end process; end architecture tb4;