Причина агонии студентов во время интервью, или популярно о моделях интерфейсов шины

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

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

Сейчас я интервьирую кандидатов которые приходят на позиции в RTL design / проектировщики микросхем на уровне регистровых передач. Но 5 лет назад я интервьировал студентов и других инженеров на позиции в DV / Design Verification / верификаторы блоков микросхем.

Моим стандартным вопросом было написать маркером на доске псевдокод для упрощенного драйвера модели шины (Bus Functional Model - BFM) для протокола AXI. На этом вопросе у ~80% кандидатов наступала агония - они как ужи на сковородке пытались натянуть сову на глобус - приспособить решение для последовательной шины а-ля APB, которое они прочитали в каком-нибудь тьюториале - к шине AXI, которая во-первых конвейерная, а во-вторых, допускает внеочередные ответы на запросы чтения с разными идентификаторами.

Аналогия из другой области: представьте, что кто-то пытается обходить дерево или решить "ханойские башни" - не зная концепций рекурсии и стека. Или написать GUI интерфейс, не зная концепции cобытийно-ориентированной архитектуры.

Причина не в том, что студенты глупые, а в том, что: 1) в вузах верификации не учат (даже в Беркли и Стенфорде) и 2) цель авторов тьюториалов - не научить методам верификации, а научить синтаксису языков (SystemVerilog) и возможностям библиотек (UVM). Поэтому при попытке решить проблему студенты начинают танцевать кругами, заводят fork-и для потоков, семафоры, получают частные случаи, которые глючат. Или начинают накручивать объектно-ориентированное программирование, код внутри интерфейсов, ожидая что время интервью пройдет, хотя проблема в сердце этого кодового гарнира остается.

При этом у задачи существует просто и надежное, но несколько контр-итуитивное решение. Базовую идею я впервые увидел еще в 1990-е годы, когда я работал в Mentor Graphics (сейчас Siemens EDA) в проекте с Nokia и Ericsson, которые тогда проектировали ранние массовые сотовые телефоны. Потом я эксплуатировал тот же подход при работе в Denali (сейчас Cadence), причем клиентами моих BFM-ов были Apple, Broadcom, Xilinx. Затем делал то же самое для клиентов MIPS (PMC Sierra). У некоего японского инженера в Камакуре, которые использовал мою BFM в проекте "зеленого суперкомпьютера", следы этого решения даже просочились в BFM для open-source RISC-V проекта.

Но начнем с начала. Модель интерфейса шины (Bus Functional Model, BFM) - это программа, которая переводит транзакции в последовательности изменений сигналов, а также последовательности из менений сигналов - в транзакции. Транзакция - это высокоуровневый объект, который может передаваться по интерфейсу один или несколько тактов, возможно пересекаясь по времени с другими транзакциями (pipelining, interleave).

Транзакция может быть структурой или объектом класса в SystemVerilog или С++. Например, простая транзакция для минимального подмножества AXI (без burst, mask, locked итд) выглядит так:

class axi_transaction;

    rand op_t    op;     // чтение или запись
    rand addr_t  addr;   // адрес
    rand data_t  data;   // данные

    rand id_t    id;            // идентификатор чтобы понять к какому
                                // адресу на шине относятся данные

    rand delay_t addr_delay;    // задержка в тактах для адреса
    rand delay_t data_delay;    // задержка в тактах для данных

    bit          data_is_set;   // Флаги которые устанавливаются
    bit          addr_is_sent;  // при обработке транзакции
    bit          data_is_sent;

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

Здесь и далее я буду ссылаться на пример упрощенного Verification IP, который я написал для образовательных семинаров и которое будет в частности использоваться для занятия Школы Синтеза Цифровых Схем в субботу 1 апреля, с полудня по московскому времени (занятия проходят на 12 площадках от Питера до Томска). Вот сайт Школы и телеграм‑канал.

Так вот. Уже лет 20 писатели тьюториалов по SystemVerilog и BFM переписывают друг у друга один и тот же код примеров, который условно говоря выглядит так:

task run ()

   while (не конец)
   begin
       получить следующую транзакцию из теста

       выставить на шину ее адрес
       если запись, то выставить данные для записи

       @ (posedge clock) перейти на следущий такт

       while (не готово)
           @ (posedge clock) перейти на следущий такт

       если чтение, получить данные чтения с шины
       закончить транзакцию
   end
endtask

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

такт 1 : адрес чтения 1
такт 2 : данные 1
такт 3: адрес чтения 2
такт 4: данные 2
такт 5: адрес чтения 3
такт 6: данные 3:

Проблема в том, что в AXI (и не только AXI, но и в AHB, OCP и других конвейерных шинах, применяемых внутри систем на кристалле) следущая транзакция может начаться, когда еще не прочитано подтверждение за предыдущую.

Например:

такт 1 : адрес и данные записи 1
такт 2 : адрес и данные записи 2 начинаются еще до считывания мастером подтверждения (response valid) записи 1 от слейва
такт 3: адрес и данные записи 3 начинаются еще до считывания мастером подтверждения (response valid) записи 2 от слейва:

Хуже того: мастер может выдать сначала такт за тактом адрес1-адрес2-адрес3, а потом данные для этих трех адресов данные1-данные2-данные3:

Тут студент на собеседовании говорит: "нет проблем, давайте заведем три треда" и пишет что-нибудь типа:

   fork
       // Поток 1
       begin
          получить следующую транзакцию-1 из теста
          выставить на шину ее адрес-1
          repeat (n) @ (posedge clock) пропустить n тактов
          выставить на шину ее данные-1
       end

       // Поток 2
       begin
          получить следующую транзакцию-2 из теста
          выставить на шину ее адрес-2
          repeat (n) @ (posedge clock) пропустить n тактов
          выставить на шину ее данные-2
       end
       ....

Тут сразу следует от меня вопрос: "а как гарантировать, что данные от транзакции 1 на окажутся на шине в том же такте, что и данные от транзакции 2"? Студент предлагает использовать семафоры.

Тогда я говорю: "а вы в курсе, что данные в AXI могут прийти перед адресом?" Например такт за тактом данные1-данные2-данные3, а потом адрес1-адрес2-адрес3:

Студент начинает еще усложнять. Тогда я говорю: "и вообще на шине может быть одновременно 16 транзакций, причем ответы от запросов на чтение могут приходить не в том порядке, в котором посылались запросы. Вы так и будете 16 тредов писать и семафорами их синхронизировать?"

Одновременно на это накладывается проблема с valid-ready, но опустим ее в этом посте, так как я про не уже писал в Что делать, когда выпускник топ-10 мирового вуза не может спроектировать блок сложения A+B.

Мой подход выглядит так: не пытаемся последовательно проходить одну транзакцию в одном треде который работает несколько тактов. Вместо этого заводим кучу очередей, между которыми перебрасываем транзакции. И работаем с этими очередями внутри одного такта, причем задом наперед: сначала обрабатываем ответы слейва, потом незавершенные передачи мастера, только в конце устанавливаем на шины адреса и данных новые транзакции если шины не заняты. Никаких семафоров, тредов (треды используются в интерфейсе к драйверу, но не в драйвере). Это вам не RTOS, а хардверный симулятор, не надо путать!

В одной компании я работал с юным инженером из юго-западной Европы, который долго сопротивлялся, не верил что так может работать, потом его пробило "ой, а что, так можно?"

Итак, заводим очереди транзакций:

  // очередь транзакций, полученных от пользователя
  axi_transaction send_queue [$];   

  // очередь транзакций, передаваемых пользователю
  axi_transaction receive_queue [$];

  // очередь транзакций, ждущих подтверждения ready
  // для valid адреса чтения
  axi_transaction rd_addr_queue [$];

  // очередь транзакций, ждущих подтверждения ready
  // для valid адреса записи
  axi_transaction wr_addr_queue [$];

  // очередь транзакций, ждущих подтверждения ready
  // для valid данных для записи
  axi_transaction wr_data_queue [$];

  // Массив (индексируемый id) очередей
  // ждущих прибытия данных чтения от слейва
  axi_transaction rd_data_array [n_ids][$];

  // Очередь транзакций ждущих подтверждения записи от слейва
  axi_transaction wr_resp_queue [$];

Далее создаем обработчик ровно одного такта ("always @ (posedge clock)", в котором делаем следущее:

  1. Проверяем, принят ли мастером ответ от слейва по поводу чтения ("if (rvalid & rready)"). Если да, перебрасываем головную транзакцию очереди rd_data_array [read id] в хвост очереди ответов пользователю. При этом записывам в транзакцию полученные данные.

  2. Проверяем, принят ли мастером ответ от слейва по поводу записи ("if (bvalid & bready)"). Если да, перебрасываем головную транзакцию очереди wr_resp_queue в хвост очереди ответов пользователю.

  3. Проверяем, подтвердил ли слейв получение адреса чтения ("if (arvalid & arready)"). Если да, перебрасываем головную транзакцию очереди адресов чтения rd_addr_queue в хвост очереди rd_data_array [read id].

  4. Проверяем, подтвердил ли слейв получение адреса записи ("if (awvalid & awready)"). Если да, помечаем головную транзакцию очереди адресов записи wr_addr_queue флагом address_is_sent и выкидываем ее из очереди. При этом, если она еще не помечена флаом data_is_sent, помещаем ее в хвост очереди wr_resp_queue.

  5. Проверяем, подтвердил ли слейв получение данных записи ("if (wvalid & wready)"). Если да, помечаем головную транзакцию очереди данных для записи wr_data_queue флагом data_is_sent и выкидываем ее из очереди. При этом, если она еще не помечена флаом address_is_sent, помещаем ее в хвост очереди wr_resp_queue.

  6. Получаем новые транзакции из очереди transmit_queue и заносим их, в зависимости от типа, в хвосты очередей rd_addr_queue, wr_addr_queue и wr_data_queue.

  7. Начинаем выставлять транзакции на шины (наконец-то!) Если очередь адресов чтения rd_addr_queue непуста и при этом в головной транзакции нет задержки в количестве тактов, выставляем адрес на шину адресов чтения. Иначе ставим arvalid=0. Если очередь непуста и есть задержка в количестве тактов, уменьшаем задержку.

  8. Для адресов записи аналогично (7).

  9. Для данных для записи аналогично (7).

Вот и все. Выглядит незамысловато, но такими моделями шин торговала дюжина компаний, а компания по Verification IP под названием Denali была продана Cadence-у в 2010 году ровно за столько же ($315 миллионов долларов), за сколько Cadence-у же в 2005 году была продана компания Verisity, которая сделала язык "e".

Писание BFM-ов - это хороший хлеб, так как у кучи людей такое программирование вызывает взрыв мозга, при этом если его понять, то все очень просто. Их можно даже писать из Долгопрудного на американский рынок, хотя один такой случай расследовало ФБР.

И еще раз напомню, что остаток примера будет разбираться преподавателем заленоградского МИЭТ Сергеем Чусовым в эту субботу 1 апреля на Школе Синтеза Цифровых Схем, подробности в телеграм‑канале.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Какая часть вам понравилась?
0% Про мучения американских студентов 0
0% Про продажу американской компании-производителя verification IP за $315M 0
0% Про расследование российского производителя BFM из Догопрудного американским ФБР 0
100% Детали подхода 1
0% У меня есть лучший подход и я описал его в комментариях! 0
Проголосовал 1 пользователь. Воздержавшихся нет.
Источник: https://habr.com/ru/post/726022/


Интересные статьи

Интересные статьи

Современная программа школьного образования гораздо отличается от той, по которой учился я. По крайней мере, речь идёт про математику, так как именно эта наука была и есть для меня центральной. Порой ...
Рады приветствовать Вас на корпоративной странице компании «Юнидата». В последнее время имя нашей компании все чаще стало звучать на «Хабре», что сподвигло нас создать свой корпоратив...
Статистический подход к объяснению ошибочных дедлайнов в инженерных проектах Кем бы вы ни были — джуниором, сениором, менеджером проекта или менеджером верхнего звена с двадцатилетним ...
Сейчас я работаю над проектом для одного клиента. Речь идёт о сайте из сферы электронной коммерции, поэтому меня очень сильно интересуют некоторые аспекты производительности. Для начала это — раз...
Эта статья посвящена одному из способов сделать в 1с-Битрикс форму в всплывающем окне. Достоинства метода: - можно использовать любые формы 1с-Битрикс, которые выводятся компонентом. Например, добавле...