fpga-systems-magazine

Отслеживаем успехи верификации в Obsidian

Главная » Статьи » Направление » Верификация
barkovian
09.12.2022 17:31
1142
1
0.0

Главный вопрос жизни, Вселенной и всего такого

"Мы уже приехали?" -- так можно сформулировать главный вопрос верификации. И позвольте вас остановить, прежде чем вы неразборчиво пробурчите что-то про функциональное и кодовое покрытие. Это лишь один элемент мозаики. Глядя в отчёт покрытия, нельзя сделать никаких выводов о покрытии требований.

А как их покрыть? Написать тесты для этого функционала, группы покрытия и чекеры.

Когда требование считается покрытым? Когда его тесты проходят, группы закрываются, чекеры работают.

Как отследить прогресс от беспорядочной кучи до стройного списка полностью покрытых требований? А вот здесь уже всё не так однозначно.

Контроль хода верификации

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

Моё решение:

  • Бесплатное.
  • Лёгкое в установке.
  • Позволяет совместную работу.
  • Позволяет расширение скриптами.
  • Не вызывает попаболи (я надеюсь).
  • Кроссплатформенное.
  • Было собрано на коленке за два вечера, поэтому не ждите чудес.

Обсидиан следящий

Обзор фич

Узрите, Obsidian, приложение для создания заметок в формате Markdown, молоток с возможностью расширения до микроскопа.

Для примера, рассмотрим верификацию котика. Всё верно, котика.

Таблицу на изображении ниже я буду называть привычным для меня термином VPTD, Verification Progress Tracking Document. В ней перечислены все требования и чем и как они покрыты.

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

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

Для упрощения поиска проблемных мест есть страница Dashboard.

В первом списке отображаются требования без связанного теста или группы. Второй список показывает требования, у которых тесты есть, но не все из них проходят. Таблица приводит все падающие тесты и их ошибки. Последний список собирает все todo из всех тестов.

Реализация

Страницы в Obsidian это Markdown файлы, хранящиеся локально на комьютере. С одной стороны отсутствие необходимости стучаться на сервер упрощает скриптовую обработку, с другой - позволяет самим выбрать способ общего доступа, будь то сетевой диск, облачное хранилище вроде Dropbox или система контроля версий.

Требования

Посмотрим как выглядит страница требования на примере "Jump vertically from standing position".

## Covered by
### Testcases
- [[Vertical Jump testcase]]

### Covergroups
- [[Jump direction covergroup]]

#requirement 

Всё, что она содержит, это ссылки на тесты и группы. Конечно, никто не запрещает добавить описание требования, картинки и таблицы - весь Markdown к вашим услугам.

Так выглядит рендер страницы:

Для упрощения создания типовых страниц можно воспользоваться расширением Templater и использовать шаблоны. Так выглядит шаблон для требования:

## Covered by
### Testcases
- 

### Covergroups
- 

#requirement 

Тесты

Переходя по ссылке к "Vertical Jump testcase", видим следующий файл:

---
sv_name: vert_jump_test
Status: FAIL
Error: The cat falls to sleep mid-jump
---

## Description
Test the cat's jumping ability, from any position.

## Test steps

## TODO
- [ ] Add options for initial position.
#testcase 

Здесь появилось что-то новенькое. Текст, отделённый тремя дефисами называется YAML frontmatter и представляет собой произвольный набор ключ-значение. Это поможет нам автоматически получать информацию о тесте. Обратите внимание на TODO. Он был отображён на странице Dashboard. Поле sv_name призвано помочь внешнему скрипту обновлять информацию по результатам регрессий.

Группы покрытия

Взглянем на "Walk speed covergroup"

---
cg_name: walk_speed_cg
cp_names: fw_speed_cp, bw_speed_cp
coverage: 15
---

## Coverpoints:
```dataview
list
from #coverpoint 
where contains(this.cp_names, cp_name)
```
#covergroup 

Здесь мы снова видим YAML frontmatter. Поле cg_name есть название соответствующей группы покрытия в тестбенче. Поле cp_names служит аналогичной цели.

Ниже находится блок кода. Dataview - это расширение Obsidian для исполнения поисковых запросов и динамического отображения результатов на странице. Именно Dataview рисовал содержимое страниц VPTD и Dashboard. Синтаксис запросов схож с SQL. На этой странице мы ищем среди всех страниц с тегом covergrpoint такие, чьё значение поля cp_name присутствует среди значений поля cp_names данной таблицы. Таким образом, для связи группы с её точками покрытия достаточно перечислить их имена в YAML полях, а Dataview сам отобразит ссылки на страницы. Никакой специфичной для данной страницы информации запрос не содержит, поэтому может быть вынесен в шаблон.

Результат рендера.

Страница точки покрытия выглядит аналогичным образом.

---
cp_name: fw_speed_cp
coverage: 46
---

Belongs to the covergroup:
```dataview
list
from #covergroup 
where contains(cp_names, this.cp_name)
```

#coverpoint

Запрос вывернут наизнанку, чтобы отобразить родительскую группу.

Сложные запросы в Dashboard и VPTD

Разберём реализацию запросов в Dashboard.

Поиск непокрытых требований:

```dataview
list
from #requirement and -"Templates"
where (!contains(file.outlinks.file.tags, "testcase")) or
      (!contains(file.outlinks.file.tags, "covergroup") and !contains(file.outlinks.file.tags, "coverpoint"))
```

Первым делом убираем из поиска папку с шаблонами с помощью -"Templates". Функция contains нам уже встречалась, а что есть file.outlinks.file.tags?

  • file это неявная переменная, хранящая все файлы из from;
  • outlinks - свойство файла, хранящее все исходящие ссылки;
  • чтобы получить файл по ссылке, используем свойство file;
  • наконец, получаем список тегов файла из свойства tags.

Ознакомиться со списком всех внутренних переменных можно в документации Dataview

Чтобы получить список требований с падающими тестами, нужно обратиться к полю Status на странице теста. Напомню, что это поле содержится в YAML frontmatter, и именно к через это поле мы доберёмся до статуса.


```dataview
list
from #requirement and -"Templates"
where contains(file.outlinks.file.frontmatter.Status, "FAIL")
```

Чтобы построить таблицу всех падающих тестов и их ошибок, также нужно фильтровать тесты по полю Status. Однако в данном случае поле принадлежит страницам, которые запрос получает из from, поэтому можно обойтись без обращения к file и frontmatter.

```dataview
table Status as "Status", Error as "Error"
from #testcase and -"Templates"
where Status != "PASS"
```

Это наш первый запрос, который строит не список, а таблицу. Вместо list мы указываем table и поля, из значений которых будут формироваться столбцы.

Чтобы сделать список всех todo, вместо list/table пишем task.

```dataview
task 
from #testcase 
where !completed
group by file.link
```

По умолчанию Dataview скидывает все todo в один список. Чтобы понимать, какая задача принадлежит какой странице, мы просим добавить группировку по ссылке на файл-источник.

Наконец, финал шоу уродцев. Разберём запрос для таблицы VPTD по кусочкам.

```dataview
table without id
 file.link as Requirement,
 filter(file.outlinks, (t) => contains(t.file.tags, "testcase")) as Testcases,
 map(filter(file.outlinks, (t) => contains(t.file.tags, "testcase")).Status, (x) => choice(x = "PASS", "✅", "❌")) as Status,
 filter(file.outlinks, (t) => contains(t.file.tags, "covergroup") or contains(t.file.tags, "coverpoint")) as Covergroups,
 filter(file.outlinks, (t) => contains(t.file.tags, "covergroup") or contains(t.file.tags, "coverpoint")).coverage as Coverage
from #requirement and -"Templates"
```

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

table without id
file.link as Requirement,

Мы уже видели, как получить список всех связанных с требованием тестов, однако тогда мы фильтровали требования по признаку "нет теста". Однако для VPTD нам нужно не фильтровать, а отображать тесты в отдельном столбце. Для этого мы прибегнем к помощи встроенной функции filter, которая, как вы и подумали, принимает список и предикат и возвращает отфильтрованный по этому предикату список.

filter(file.outlinks, (t) => contains(t.file.tags, "testcase")) as Testcases,

Чтобы сделать таблицу чуточку приятнее взгляду, будем отображать статус тестов не словами, а значками ✅ и ❌. Для этого из результата фильтра нужно взять поле Status и воспользоваться другой встроенной функцией, map. Она применяет функцию, переданную во втором аргументе, к каждому элементу списка из первого аргумента.

map(filter(file.outlinks, (t) => contains(t.file.tags, "testcase")).Status, (x) => choice(x = "PASS", "✅", "❌")) as Status,

Группы покрытия получаем аналогично тестам, однако будем выводить не только группы, но и отдельные точки.

filter(file.outlinks, (t) => contains(t.file.tags, "covergroup") or contains(t.file.tags, "coverpoint")) as Covergroups

Чтобы получить процент покрытия, просто обращаемся к полю coverage.

filter(file.outlinks, (t) => contains(t.file.tags, "covergroup") or contains(t.file.tags, "coverpoint")).coverage as Coverage

Что осталось за бортом

Описанное выше - это ни в коей мере не законченное решение, а всего лишь концепт. Здесь есть как недоработки, так и просто оставленные без внимания вопросы. Коснёмся некоторых из них.

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

У меня нет решения для задачи покрытия составных требований, то есть состоящих из нескольких под-требований. Такое требование будет считаться покрытым, если покрыты все дочерние требования, однако вложенность требований может быть произвольной. Это требует рекурсивных запросов, которые Dataview не поддерживает. Однако Dataview поддерживает исполнение кода JavaScript и, в теории, так решить задачу всё-таки можно. Но пускай это делает кто-то другой.

В начале я говорил, что требование должно быть покрыто тестами, группами покрытия и чекерами, но в дальнейшем про чекеры не вспоминал. Дело в том, что

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

Obsidian может строить графы связности страниц. Отображение можно настраивать различными способами. Возможный результат приведён ниже.


Я не придумал, как можно извлечь пользу из такого графа, поэтому упоминаю его лишь вскользь.

Хотя мы не касались документации на тестбенч и DUT, её вполне можно писать здесь же, в Obsidian. Единственная проблема, которая приходит мне в голову, это создание сложных таблиц. Синтаксис Markdown позволяет описывать простые, но если вам нужно объединять произвольные ячейки, то готовьтесь к боли и разочарованию.

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

Как использовать

  1. Установите Obsidian.
  2. Склонируйте пример.

Бонус: покрытие чекеров

Раз уж я говорил, что это важно, нужно объяснить почему.

Покрытие чекера - это функциональное покрытие двух событий: чекер сработал и чекер обнаружил ошибку. Первое событие покрывать обязательно, второе -- если есть тесты на инжекцию соответствующей ошибки.

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

Как работать с таким покрытием зависит от того, как вы пишете чекеры, и выходит за рамки статьи.

Заключение

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

 Мотивировать автора     

  Поддержать FPGA комьюнити     

Оставить комментарий/отзыв

1142
1
0.0

Всего комментариев : 1
avatar
1 krendkrend • 14:29, 14.12.2022
Большое спасибо за статью!
avatar

FPGA-Systems – это живое, постоянно обновляемое и растущее сообщество.
Хочешь быть в курсе всех новостей и актуальных событий в области?
Подпишись на рассылку

ePN