Русский
Русский
English
Статистика
Реклама

Verilog

Использование UDB в микроконтроллерах PSOC 4 и 5LP Infineon (Cypress) для управления светодиодами WS2812

21.04.2021 22:10:18 | Автор: admin

Светодиоды типа WS2812 очень популярны, ими удобно управлять, передавая по одному проводу команды для сотен светодиодов. Они имеют, с одной стороны, очень простой протокол, а с другой стороны, в микроконтроллерах нет аппаратных интерфейсов для этого протокола и его приходится формировать программно управляя выводом микроконтроллера. В этой статье я расскажу, как с помощью UDB микроконтроллеров серии PSOC 4 и PSOC 5LP компании Infineon сделать периферийный модуль для управления этими светодиодами.

Компонент UDB позволяет создавать достаточно сложные аппаратные модули. Данные модули можно проектировать несколькими разными способами: нарисовать схему, нарисовать блок-схему машины состояний, а также, описать с помощью языка высокого уровня Verilog. Я буду использовать язык Verilog, так как он позволяет воспользоваться всеми возможностями UDB.

На данном рисунке показана блок-схема микроконтроллера PSOC 5LP Family.

На рисунке виден блок Universal Digital Block Array, который связан через внутренние шины Digital Interconnect и System Bus со всеми компонентами микроконтроллера.

Для того чтобы начать разработку, необходимо скачать и установить PSOC Creator. Эта IDE абсолютно бесплатна, работает как с компилятором GCC, так и Keil.

После установки и запуска вы увидите стартовую страницу.

Нажимаем File->New->Project и попадаем в окно создания нового проекта.

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

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

Следует заметить, что микроконтроллеры PSOC 4 и PSOC 5LP могут работать в диапазоне напряжений питания от 1.7 до 5.5 вольт. Таким образом, мы сможем напрямую подключить микроконтроллер к светодиоду WS2812 без дополнительного буфера.

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

Жмем Next>, в следующем окне надо выбрать имя проекта, место его расположения и имя рабочего пространства.

Нажимаем кнопку Finish и попадаем на закладку схемы проекта.

На панели слева, двойным щелчком, можно открыть файл main.c. Мастер создания нового проекта уже наполнил минимально необходимым содержимым этот файл.

Нажав SHIFT+F6 можно скомпилировать проект, компиляция должна пройти без ошибок.

Для создания нового компонента на вкладке слева жмем закладку Components.

Затем щелкаем правой клавишей на строчке Project и выбираем пункт меню Add Component Item.

Первое, что нам нужно создать, это символ компонента. Поэтому выбираем пункт Symbol Wizard, прописываем имя компонента, Cypress рекомендует включать номер версии в имя компонента таким образом Имя_компонента_v1_0, цифры 1_0 в дальнейшем можно изменять в зависимости от версии компонента.

Нажимаем Create New и попадаем в мастер создания символа компонента, где создаем четыре вывода: вход тактового сигнала, асинхронного сброса, выход для управления светодиодами и выход сигнала прерывания или DMA, который будет устанавливаться в 1 при опустошении буфера FIFO. Выводимые данные мы будем записывать в оперативную память, поэтому на схеме нет выводов для входных данных.

Жмем OK и попадаем на вкладку с созданным символом. Щелкаем на пустом месте листа правой клавишей мыши и выбираем пункт PROPERTIES.

Здесь нам нужно ввести два имени Doc.ApiPrefix и Doc.DefaultInstanceName.

Жмем OK, затем снова щелкаем правой клавишей мыши на пустом месте листа символа и выбираем пункт меню Generate Verilog:

Тут просто нажимаем кнопку Generate.

Двойным щелчком открываем Verilog файл.

Затем выбираем пункт меню Tools->DataPath Config Tool.

В DataPath Config Tool открываем только что созданный Verilog файл.

Теперь нам нужно добавить DataPath в наш Verilog файл. Выбираем пункт меню Edit->New DataPath.

В этом окне нам нужно ввести имя Datapath и выбрать разрядность DataPath, 8 бит нам будет достаточно.

Мастер создал Datapath, заполненный значениями по умолчанию:

Область 1 это 8 команд, которые может выполнять DataPath. Область 2 это маски. Область 3 настройка режима работы элементов Datapath.

Настал момент, когда нам надо разобраться с тем, как устроен и работает этот самый DataPath. UDB состоит: из блока тактовых сигналов, асинхронного сброса, регистра Status для чтения состояния и регистра Control для записи управляющих сигналов UDB.

Также в него входят две PLD матрицы на 12 входов и 4 выхода.

И сердцем UDB является DataPath. Можно сказать, что это очень примитивный микроконтроллер. У него есть: ALU, память программ из восьми ячеек, четыре регистра общего назначения, два буфера глубиной четыре байта организованных как FIFO, сдвиговый регистр, две маски, а также два блока сравнения и два блока проверки на равенство 0x00 и 0xFF регистров A0, A1. Следует также добавить, что эти блоки можно объединять и организовывать 16, 24, 32 битную обработку данных. Микроконтроллер может писать и читать, как в регистры A0,A1,D0,D1, так и в FIFO.

В datasheet к светодиоду WS2812 указаны следующие требования к временных характеристикам сигнала.

Старший бит посылается первымСтарший бит посылается первым

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

Рисуем блок схему для нашего модуля состоящую из 8 состояний:

Выбираем длительность такта 200ns, такой интервал будет удовлетворять временным ограничениям указанным в datasheet WS2812B. Длительность импульса сброса в таком случае составит 255*0.2s= 51s. Заполним необходимые поля в DataPath Configuration Tool.

Первая команда это состояние ожидания записи данных в FIFO. Одновременно мы обнуляем операцией Исключающее ИЛИ регистр A1. Функция выбрана XOR, источник A и B регистр A1, запись регистра A1 выбрана из ALU.

На вторую команду мы переходим по сигналу FIFO empty == 0. То есть в FIFO поступили данные, в этот же момент мы устанавливаем выходной сигнал в 0. Во второй команде мы увеличиваем на единицу A1 до тех пор, пока он не примет значение 0xFF. После этого переходим к команде три, устанавливая выходной сигнал в 1.

В команде три мы загружаем байт из FIFO в A0 и загружаем счетчик бит из D1 в A1, и переходим к команде четыре.

В команде четыре и пять DataPath ничего не делает. В команде шесть мы делаем декремент счетчику бит и переходим к команде семь, установив выход в 1. Надо понимать, что когда мы устанавливаем выходной сигнал или флаг, данные изменятся только в следующем такте.

В команде семь происходит ветвление по трем адресам. Если счетчик бит A1 != 0, то мы переходим к команде восемь, чтобы сформировать еще один такт выходного сигнала 1. Если A1 == 0 и FIFO пуст, переходим к первой команде ожидания поступления данных в FIFO. Если FIFO не пуст, то переходим к команде три, загрузка данных из FIFO в A0 и счетчика бит A1 из D1.

Нам также надо включить маску 0 и присвоить ей значение 0x80. При проверке на равенство регистра A0 с маской 0x80, регистру D0 равному 0x80 это даст значение передаваемого бита. FIFO у нас по умолчанию сконфигурированы на ввод данных в DataPath.

В завершении пропишем значения по умолчанию для регистров D0 (маска старшего бита) = 8'h80 и D1 (счетчик бит) = 0'h08, используя пункт меню View->Initial Register Values.

Сохраняем (Ctrl+S) и закрываем Datapath Configuration Tool. Нам осталось написать логику работы машины состояний в Verilog файле. Сначала присвоим сигналам модуля имена используемых нами регистров и флагов.

udb8(        /*  input                   */  .reset(rst),//Входной сигнал сброса        /*  input                   */  .clk(clk),//Входной тактовый сигнал        /*  input   [02:00]         */  .cs_addr(state),//Регистр адреса команды DataPath        /*  input                   */  .route_si(1'b0),        /*  input                   */  .route_ci(1'b0),        /*  input                   */  .f0_load(1'b0),        /*  input                   */  .f1_load(1'b0),        /*  input                   */  .d0_load(1'b0),        /*  input                   */  .d1_load(1'b0),        /*  output                  */  .ce0(send_bit),//Значение отправляемого бита        /*  output                  */  .cl0(),        /*  output                  */  .z0(),        /*  output                  */  .ff0(),        /*  output                  */  .ce1(),        /*  output                  */  .cl1(),        /*  output                  */  .z1(z_count_bit),//Флаг равенству 0 счетчика бит        /*  output                  */  .ff1(end_ws2812_reset),//Флаг равенству 0xFF регистра A1        /*  output                  */  .ov_msb(),        /*  output                  */  .co_msb(),        /*  output                  */  .cmsb(),        /*  output                  */  .so(),        /*  output                  */  .f0_bus_stat(),        /*  output                  */  .f0_blk_stat(fifo_empty),//Флаг пустого FIFO        /*  output                  */  .f1_bus_stat(),        /*  output                  */  .f1_blk_stat());

Первые два сигнала это вход асинхронного сброса и вход тактового сигнала.

Следующий это трех-битный вход адреса памяти команд DataPath. Мы присваиваем этому входу регистр состояний state. Далее идут выходные сигналы UDB: ce0 это значение выводимого бита. При создании конфигурации DataPath мы включили маску 0 и присвоили ей значение 0x80, получается операция send_bit=(A0 & 0x80==D0) 1 : 0;

Флаг z1, проверяет на равенство 0 регистра A1, это у нас счетчик отправляемых бит, присваиваем ему сигнал z_count_bit.

Следующий флаг ff1, он устанавливается в 1 при равенстве регистра A1 - 0xFF. Этот флаг мы используем при формировании импульса сброса для WS2812, присваиваем ему имя сигнала end_ws2812_reset.

И последний флаг f0_blk_stat, он устанавливается в 1, когда FIFO 0 пуст. Присваиваем ему имя сигнала fifo_empty.

Нам осталось объявить используемые регистры и флаги, и прописать машину состояний.

    localparam IDLE = 3'h0;    localparam WS2812_RESET = 3'h1;    localparam LOAD_A0 = 3'h2;    localparam CHECK_BIT = 3'h3;    localparam SEND_BIT = 3'h4;    localparam DEC_BIT_CNT = 3'h5;    localparam SHIFT_DATA = 3'h6;    localparam NOP = 3'h7;    reg [2:0]state;//Адрес команды    reg out;//Регистр выходного сигнала    reg send_tic;//Регистр дополнительного такта    wire fifo_empty;//Флаг пустого FIFO    wire send_bit;//значение отправляемого бита    wire end_ws2812_reset;//Флаг равенства 0xFF регистра A1    wire z_count_bit;//Флаг равенства 0 счетчика бит    assign irq=fifo_empty;//Присваиваем выходу прерывания флаг пустого FIFO    assign ws2812=out;//Присваиваем выходной сигнал регистру выходного сигналаalways @(posedge clk or posedge rst )//beginif (rst)begin       // Асинхронный сбросstate <= IDLE;            out<=1'b1;endelsebegincase (state)IDLE://Ожидание поступления данных в FIFObeginif(fifo_empty==1'b0)beginstate <= WS2812_RESET;//Если данные в FIFO поступили,                     out<=1'b0;//переходим к команде формирования импульса сброса, выходной сигнал в 0endelsebegin                    out<=1'b1;//Если данных в FIFO нет, выходной сигнал в 1                endendWS2812_RESET://Формирование импульса сбросаbegin                if(end_ws2812_reset)//Ждем равенства 0xFF регистра A1                begin                    state <= LOAD_A0;//Если A1=0xFF, переходим к команде загрузки данных из FIFO                    out<=1'b1;//Выход в 1                endendLOAD_A0://Загрузка байта из FIFObegin                state <= CHECK_BIT;//Переходим к команде проверки значения выводимого битаendCHECK_BIT://Команда проверки значения выводимого битаbegin                send_tic <= 1'b0;//Обнуляем регистр дополнительного такта                state <= SEND_BIT;//Переходим к команде отправки бита данных                if(send_bit==1'b0)                begin                    out <= 1'b0;//Если выводимый бит 0, устанавливаем выход в 0                endendSEND_BIT://Команда отправки бита данныхbegin                if(send_tic)//Если дополнительный такт уже был                begin                    state <= DEC_BIT_CNT;//Переходим к команде декремента счетчика бит                    out <= 1'b0;//Устанавливаем выходной сигнал в 0                end                else                begin                    send_tic <= 1'b1;//Если дополнительного такта не было, устанавливаем флаг дополнительного такта                endendDEC_BIT_CNT://Команда декремента счетчика битbegin                state <= SHIFT_DATA;//Переходим к команде сдвига выводимого байта влевоendSHIFT_DATA://Команда сдвига выводимого байта влевоbegin                out<=1'b1;//Выходной сигнал в 1                if(z_count_bit)//Если счетчик выведенных бит равен 0                begin                    if(fifo_empty == 1'b0)//Если FIFO не пуст                    begin                        state <= LOAD_A0;//Переходим к загрузке нового байта                    end                    else                    begin                        state <= IDLE;//Если пуст переходим в режим ожидания прихода данных в FIFO                    end                end                else                begin                    state <= NOP;//Если счетчик бит не равен 0, переходим к формированию дополнительного такта                endendNOP://Команда дополнительного тактаbegin                state <= CHECK_BIT;//Переходим к команде проверки значения выводимого битаendendcaseendend

Железную часть мы закончили, осталось написать небольшое API, чтобы микроконтроллер мог взаимодействовать с нашим модулем. Сохраняем и закрываем Vetilog файл.

Создадим заголовочный файл, щелкаем правой клавишей мыши на имени нашего компонента и выбираем Add Component Item.

Ищем API Header File, вписываем имя файла и нажимаем Create.

Двойным щелчком по имени файла на панели слева открываем файл и добавляем в него следующие строки.

#include "cytypes.h"#define `$INSTANCE_NAME`_SHIFT_MASK 0x80#define `$INSTANCE_NAME`_NUM_SHIFT_BITS 8#define `$INSTANCE_NAME`_FIFO_LEVELS 4#define `$INSTANCE_NAME`_bit_cnt (*(reg8 *)`$INSTANCE_NAME`_udb8_u0__D1_REG)#define `$INSTANCE_NAME`_shift_mask (*(reg8 *)`$INSTANCE_NAME`_udb8_u0__D0_REG)#define `$INSTANCE_NAME`_data_fifo (*(reg8 *)`$INSTANCE_NAME`_udb8_u0__F0_REG)#define `$INSTANCE_NAME`_actl (*(reg8 *)`$INSTANCE_NAME`_udb8_u0__DP_AUX_CTL_REG)void `$INSTANCE_NAME`_Start(void);

Здесь следует разъяснить, что такое $INSTANCE_NAME. Это то, что мы вводили в свойство Doc.DefaultINstanceName при создании символа компонента. В нашем случае, будет автоматически формироваться имя ws2812_1_bit_cnt, где 1 после ws2812 это автоматическая нумерация компонентов на схеме. И да, на схеме это имя можно поменять на любое другое. Хитрую кавычку можно ввести так: <ALT>+<96>. Имя регистра, генерируемого при компиляции, состоит из трёх частей, $INSTANCE_NAME это имя компонента, udb8 это имя DataPath, которое мы указали при создании нового DataPath и u0__D1_REG это имя регистра. В случае ошибки, можно посмотреть эти имена в файле cyfitter.h после компиляции.

Функцию Start можно было не создавать, так как после сброса микроконтроллера в регистры D0 и D1, загружаются значения по умолчанию. Также мы сможем в программе, в любой момент, записать необходимые данные в регистр простой записью:

ws2812_1_bit_cnt=8;

Запись байта в FIFO выглядит так:

ws2812_1_data_fifo=0xAA;

Но мы сделаем это с заделом на будущее. Снова щелкаем правой клавишей мыши на имени компонента и выбираем Add Component Item. Затем выбираем API C File, вводим имя и нажимаем кнопку Create.

Двойным щелчком на имени файла, на панели слева открываем созданный файл и вводим в него следующий текст.

#include "`$INSTANCE_NAME`.h"void `$INSTANCE_NAME`_Start(void){    `$INSTANCE_NAME`_shift_mask=`$INSTANCE_NAME`_SHIFT_MASK;    `$INSTANCE_NAME`_bit_cnt=`$INSTANCE_NAME`_NUM_SHIFT_BITS;}

Нажимаем Ctrl+Shift+S, чтобы сохранить все изменения. Теперь мы можем добавить созданный компонент на схему проекта. Переключаемся на вкладку Source слева.

Двойным щелчком по файлу TopDesign.cysh открываем схему проекта, справа мы видим набор поставляемых с Psoc Creator компонентов.

Справа щелкаем на вкладку Default, и видим только что созданный нами компонент.

Перетаскиваем его на схему.

Щелкаем справа на вкладку Cypress, затем выбираем в папке Systems компонент Clock и перетаскиваем его на схему так, чтобы квадратик вывода clk компонента ws2812_1 совпал с квадратиком вывода компонента Clock_1, тогда выводы соединяться.

Щелкаем правой клавишей на компоненте Clock_1 и в выпавшем меню выбираем пункт Configure. В этом же меню можно выбрать пункт Open Datasheet и посмотреть datasheet на этот компонент.

Вписываем частоту 5MHz.

Затем в папке Digital библиотеки компонентов берем компонент Ligic Low и перетаскиваем его на схему, и размещаем рядом с выводом rst компонента ws2812_1. Затем нажимаем клавишу W и соединяем вывод rst и Logic Low проводом.

Из папки Ports and Pins берем компонент Digital Output Pin и подсоединяем его к выводу ws2812 нашего компонента, затем щелкаем правой клавишей по компоненту Pin_1, выбираем пункт меню Configure и изменяем имя компонента с Pin_1 на ws2812_port.

Присвоим сигнал ws2812_port порту P1(7), для этого, двойным щелчком по строчке Pins в папке Design Wide Resourse, открываем вкладку назначения выводов и на панели справа, выбираем Port - P1(7).

На вкладке Clocks, двойным щелчком на строчке IMO, открываем страничку конфигурации тактовых частот.

И устанавливаем частоту IMO - 3MHz, так как при такой частоте точность составляет 1%. Частоту PLL ставим 79MHz. На 80MHz IDE будет ругаться, так как с учетом отклонений, частота будет превышать предельно допустимые 80MHz для данного микроконтроллера.

Так как пост уже затянулся, автоматическую загрузку FIFO компонента через DMA я делать не буду, будем отправлять данные по прерыванию. Следовательно из папки System добавляем на схему компонент Interrupt.

Вызываем меню Configure для компонента isr_1, и даем ему имя isr_ws2812.

Нам еще понадобится компонент Timer, возьмём его в папке Digital->Function. К выводу interrupt подключим еще один компонент Interrupt, который переименуем в isr_timer. Также удалим у компонента Timer тактовый генератор и подключим тактовый вход таймера к компоненту Clock_1, переместив предварительно компонент Clock_1 чуть влево.

Теперь вызовем меню Configure для компонента Timer_1, выберем 16 битный режим, поставим период 14999 для прерывания каждые 3ms и поставим галочку напротив прерывания по переполнению. Почему я выбрал период обновления 3ms? Пересылка одного байта занимает 8*1,2s=9,6s всего 100 светодиодов по 3 байта на точку, получаем 2880s+51s на импульс сброса. Получаем 2931s, следовательно, 3ms нам вполне достаточно.

Как видно, компонент таймер может быть создан на базе аппаратного таймера или синтезирован на базе UDB блоков, мы оставим Fixed Function реализацию.

И нам осталось написать небольшой Hello Habr, кстати, нажав Shift +F6 можно скомпилировать проект, компиляция должна завершиться без ошибок.

Демонстрационная программа формирует бегущую строку на экране 10 на 10 светодиодов, подключенных последовательно Z способом снизу вверх, слева направо (со стороны проводов).

Кадры формируются по сигналу прерывания модуля Timer_1, картинка рисуется в предварительном буфере и по флагу окончания передачи данные копируются в основной буфер. Для увеличения скорости, копирование идет 32 битными словами.

int main(void){    ws2812_struct.buffer_ptr=ws2812_struct.buffer;    fillScreen(0);    CyGlobalIntEnable; /* Enable global interrupts. */    Timer_1_Start();    ws2812_1_Start();    isr_ws2812_StartEx(WS2812_HANDLER);    isr_timer_StartEx(TIMER333HZ_HANDLER);        uint16 delay=0;    for(;;)    {        if((delay++)==20)        {            delay=0;            scrollStr(text, sizeof(text), color , BACKGROUND);        }        while(ws2812_struct.wait_tx==0);        ws2812_struct.wait_tx=0;        copyBuffer((uint32*)ws2812_struct.draw_buffer,(uint32*)ws2812_struct.buffer,sizeof(ws2812_struct.buffer)/4);                }}

В заключении приведу screenshot измерителя ресурсов для данного проекта:

Мы израсходовали 4,4% ресурсов UDB. Это означает, что в данном микроконтроллере мы сможем разместить около 20 модулей ws2812. В микроконтроллерах PSOC 4200, например, CY8C4245PVI-482 скорей всего удастся поместить три таких модуля.

И в заключении осциллограммы снятые на работающем макете и небольшое видео.

Импульс СбросаИмпульс СбросаПередача 1 и 0Передача 1 и 0

Проект размещен на Github.

Подробнее..

Начинаем работу с Zynq 7000. Пособие для начинающих

29.05.2021 18:19:27 | Автор: admin

Совсем недавно мне в руки попался один из вариантов отладочной платы с SoC Zynq XC7Z020. Поискав в Интернете материалы, а-ля how-to, и попробовав накидать свой минимальный проект обнаружил, что есть целый ряд подводных камней. Именно об этом я и хотел бы рассказать в статье. Кому интересно - добро пожаловать под кат.

Важно! Перед началом повествования, хотелось бы заранее оговориться, что основная цель которую я преследую при написании этой статьи - показать любителям, с чего можно начать, при изучении отладочных плат на базе Zynq. Я не являюсь профессиональным разработчиком под ПЛИС и SoC Zynq и могу допускать какие-либо ошибки в использовании терминологии, использовать не самые оптимальные пути решения задач, etc. Но любая конструктивная и аргументированная критика только приветствуется. Что ж, поехали

Что за отладка такая? Покажи-расскажи...

Мне очень давно хотелось поиграться с SoC Zynq, но никак не доходили руки. Но в очередной раз погуглив - увидел, что за вполне вменяемый ценник продаётся отладка с Zynq на борту, от компании QMTech, называется она Bajie Board. Выпускается отладка в нескольких вариантах с разными вариантами SoC Zynq. Я выбрал для себя вариант на XC7Z020 и тут же ее заказал, через пару недель она у меня уже была в руках.

После распаковки я был приятно удивлен, комплект поставки порадовал. Это была сама отладочная плата, блок питания на 5В/2А, mini-USB кабель и microSD Flash-карта SanDisk на 16Гб с уже залитым на нее Linux. То есть, сразу после получения вы можете подключить к плате питание, воткнуть USB-шнурок, открыть Putty и получить в свое распоряжение полноценный mini-компьютер с Embedded Linux. О Linux для Zynq, я думаю, расскажу в другой статье, поэтому едем дальше...

Итак, рассматривая плату и попутно документацию на нее я увидел относительно не плохой набор всякого-разного:

  • SoC: XC7Z020-1CLG400C

  • (datasheet:https://www.xilinx.com/support/documentation/data_sheets/ds190-Zynq-7000-Overview.pdf);

  • Осциллятор на 33,333 МГц;

  • Оперативная память DDR3 на 512 Мб от компании Micron, MT41K256M16TW-107:P;

  • Встроенный слот micro SD;

  • Источник питания для FPGA TPS563201 с широким диапазоном входных напряжений (от 4.5V до 17V, 3A);

  • Один 50-пиновый и две Digilent PMOD совместимых, гребёнки с пинами, с шагом в 2,54 мм. для пользовательских кейсов (как заверяет производитель, все проводники до пинов выровнены по длине);

  • Кнопка для логического сброса процессорной системы (PS);

  • Гигабитный RGMII Ethernet-контроллер Realtek RTL8211E-VL, подключенный к PS;

  • Два пользовательских светодиода, один подключен к программируемой логике (PL) и другой подключен к процессорной системе (PS);

  • Встроенный HDMI-совместимый интерфейс дисплея TI TPD12S016;

  • Гребёнка для подключения JTAG-отладчика;

Для большинства задач начального уровня такого количества всего будет прям за глаза.

Единственное, что меня опечалило и про что я напрочь забыл, когда покупал плату - это то что для работы с платой необходим JTAG-программатор. Но я его быстро нашел в Москве и мне его привезли курьером буквально в этот же день.

Установка необходимого набора ПО для разработки

Итак, прежде чем начать работу с платой мне было необходимо установить ПО Xilinx Vivado и Xilinx SDK. Насколько я понимаю, грубо говоря, Vivado используется для конфигурации аппаратной части используемой платы и для работы с программируемой логикой. А Xilinx SDK (ныне именуется Vitis) используется для создания кода непосредственно для процессорной системы.

Поскольку бОльшая часть примеров из документации и репозитория производителя и разнообразных примеров из роликов на YouTube делались в версии Vivado 2019.1 (видимо из-за того, что это последняя версия поддерживающая работу с Xilinx SDK) - я установил именно её, а не последнюю доступную 2020.2.

Все программные продукты необходимые для работы с Xilinx Zynq - можно взять на официальном сайте Xilinx, тут. Сразу же спешу обратить внимание, что те из вас, кто захочет установить самую новую версию Vivado - нужно скачивать версию 2020.2, а не 2020.3 т.к. последняя поддерживает только Versal SoC, и не поддерживает Zynq.

В моём случае, т.к. я работаю в операционной системе Linux - я перешел в меню Vivado Archive - 2019.1 и нажал на кнопку скачивания по ссылке Vivado HLx 2019.1: WebPACK and Editions - Linux Self Extracting Web Installer в списке Vivado Design Suite - HLx Editions - 2019.1. Для пользователей Windows - выбирайте Windows Self Extracting Web Installer.

После скачивания открываем инсталлятор, установив права на исполнение:

chmod +x ~/Downloads/Xilinx_Vivado_SDK_Web_2019.1_0524_1430_Lin64.bin~/Downloads/Xilinx_Vivado_SDK_Web_2019.1_0524_1430_Lin64.bin 

Вся установка состоит из набора стандартных шагов.

  1. Вводим авторизационные данные, которые мы указывали при регистрации;

  2. Принимаем условия лицензионных соглашений;

  3. Выбираем Vivado HL WebPACK;

  4. Удостоверяемся в том, что выбран SoC Zynq в списке предложенного оборудования.

  5. Далее программа скачает порядка 16Гб всякого-разного, установит это и на Рабочем столе появятся иконки нужных нам приложений.

После установки Vivado необходимо установить драйвер для JTAG-программатора. В Linux это делается так:

cd Xilinx2019.1/Vivado/2019.1/data/xicom/cable_drivers/lin64/install_script/install_drivers/sudo ./install_drivers 

Подключаем все 6 пинов от JTAG-программатора в соответствии с шелкографией на плате. И проверяем установлены ли драйвера и определяется ли наша отладочная плата:

cd ~/Xilinx2019.1/Vivado/2019.1/bin./xsdb xsdb% connect -host localhost   xsdb% jtag targets                                                                                                                                                             1  Platform Cable USB 13724327082b01     2  arm_dap (idcode 4ba00477 irlen 4)     3  xc7z020 (idcode 23727093 irlen 6 fpga)

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

Hello, world или Баяны подъехали

Не будем отходить от традиции и попробуем поморгать LED-иком который подключен к программируемой логике.

Запускаем Vivado и создаем новый проект. Нажимаем File - Project - New

Откроется мастер создания нового проекта, нажимаем Next > и пишем название нашего проекта PL-Blink.

Выбираем RTL Project и ставим галочку у пункта Do not specify sources at this time.

Далее в списке ищем наш процессор xc7z020clg400-1.

Жмём на кнопку Finish.

Перед нами открывается главное окно программы Vivado и мы можем приступать к реализации намеченной нами цели!

Первым делом, мы добавим необходимые указания программе, на то, какие физические ножки мы задействуем в нашей моргающей вакханалии =)

Находим меню Sources и нажимаем кнопку Add Sources.

Откроется мастер импорта и нам нужно выбрать Add or create constraints.

В следующем меню нажимаем Create file и пишем название нашему файлу physical_constr. Именно в этом файле мы опишем какие ножки и в каком режиме должны работать.

Нажимаем кнопку Finish и в дереве Sourсes ищем только что созданный нами файл и открываем его:

Обратимся к схеме, которую любезно предоставил нам производитель и найдем какая ножка отвечает за тактирование, а какая за наш светодиод.Бегло поискав, я отметил для себя, что из Ethernet-контроллера RTL8211E-VL выведен опорный тактовый сигнал с его внутреннего PLL, частотой в 125МГц и заведен в ножку H16 (IO_L13P_T2_MRCC_35).Так почему бы нам его и не задействовать в нашем примере? =)

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

Тут же, рядом, на ножке H17 (IO_L13N_T2_MRCC_35) расположен светодиод, которым мы будем моргать.

Итак. Открыв наш constraints-файл запишем в него следующие строки:

# User LED and Clockset_property IOSTANDARD LVCMOS33 [get_ports led_h17_d4]set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]set_property PACKAGE_PIN H17 [get_ports led_h17_d4]set_property PACKAGE_PIN H16 [get_ports sys_clk]

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

В квадратных скобках, после get_ports необходимо указать логическое имя ножки (на ваше усмотрение). Имена стоит придумать осмысленно, потому что мы его будем использовать в Verilog-коде.

Кстати, подробнее о Physical Constraints можно почитать тут в главе 8.

Добавим в наш проект таким же образом Design Source. Находим меню Sources и нажимаем кнопку Add Sources.

Откроется мастер импорта и нам нужно выбрать Add or create design sources. Далее нажимаем Create File, смотрим, что выбран язык Verilog. Нажимаем ОК и Finish.

В следующем меню всё оставляем без изменений и нажимаем ОК и Yes.

Открываем созданный файл и видим небольшую заготовку:

Здесь вместо предложенного кода пишем наш Verilog-код и прокомментируем что значит каждая из строк:

// Директива компилятора, которая определяет единицу времени и точность для моделирования Verilog.// В целом, не очень интересный пункт для нас.`timescale 1ns / 1ps // Определяем стандартный блок-модуль (как класс в С++)module pl_blink(input sys_clk, output led_h17_d4);    // Задаем регистр для хранения записи о текущем состоянии светодиодаreg r_led; // Задаем регистр для хранения значения счётчика, использующегося в задержкеreg [31:0] counter;// Тут мы задаем действия которые должны быть выполнены при старте программыinitial begin    counter <= 32'b0;//  Обнуляем счётчик    r_led <= 1'b0;//  Делаем запись о состоянии светодиодаend// Тут описываем поведенческий блок, который будет реагировать на ниспадающий фронт тактовой частотыalways@(posedge sys_clk)begin    counter <= counter + 1'b1;// Увеличиваем счетчик        if(counter > 12000000)// Если счетчик больше некоторого условного значения    begin        r_led <= !r_led;// Инвертируем запись о значении состоянии светодиода        counter <= 32'b0;// Сбрасываем счетчик    end       endassign led_h17_d4 = r_led;          // Присваиваем текущее состояние ножке (условно)           endmodule

Нажимаем сочетание клавиш Ctrl + S чтобы сохранить код. Смотрим, не подсвечены ли где возможные ошибки. Если нет - то можем приступить к синтезированию, имплементации и генерации бинарного файла который мы потом зальем в нашу плату Zynq и будем наблюдать за морганием светодиода.

Нажимаем кнопку Run Synthesis и дожидаемся завершения синтеза. После окончания программа нам скажет, что синтез успешно завершен и мы можем переходить к следующему шагу:

Выбираем Run implementation и дожидаемся окончания. После выбираем пункт Generate Bitstream для запуска финального этапа:

Тут так же дожидаемся сигнала о том, что всё прошло успешно, выбираем Open Hardware Manager и можем приступать к заливке результата компиляции в нашу плату:

В открывшемся меню Hardware Manager нажимаем кнопку Auto connect, дожидаемся когда произойдет успешное соединение и откроется меню со списком устройств:

В меню слева или через нажатие правой кнопкой по xc7z020_1 в меню Hardware нажимаем пункт Program Device.

В следующем окне убеждаемся, что правильно указан путь к bitstream-файлу и нажимаем кнопку Program.

Программа заливается на нашу плату

И через мгновение на плате загорается светодиод D2, который сообщает нам, что FPGA DONE и в другом конце платы мы видим весело моргающий светодиод. =)

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

Подробнее..

Из песочницы Генерация клока в ПЛИС на примитивах

05.07.2020 14:08:58 | Автор: admin
Читая даташиты на ПЛИС, можно находить таблички об их рабочих частотах

Хотя нет, история начинается еще с 2015 года, когда я познакомился с ПЛИС. В своих первых простеньких работах я формировал нужный мне клок из счетчика и запитывал от него всю логику(естественно при условии что клок мне нужен медленнее чем подавался на ПЛИС, например UART и SPI). Естественно за такое меня гоняли, но у меня была простая отмазка но ведь работает же!, и действительно все работало. С тех пор у меня в голове закралась мысль а откуда вообще можно взять тактирующий сигнал?.

Вариантов источников взять клок не много. Либо взять из некого ClockWizard основанный на PLL или MMCM, либо сформировать из счетчика, либо сразу с ножки так сказать single ended. А что, если взять тактовый сигнал сформированный примитивом ПЛИС?

В рамках этой статьи я решил рассмотреть три варианта: мультиплексор(MUXF7), таблица истинности(LUT1) и замкнуть ножки ПЛИС сами на себя.

В случае с мультиплексором выход подаем на управляющий сигнал, а входные сигналы притягиваем к 0 и 1.

image

В случае с LUT замыкаем выход на вход и задаем инвертирующую таблицу истинности. При подаче 1 выдавать ноль, а при 0 выдавать единицу.

image

В случае с GPIO там все просто, выходному сигналу присваиваем инверсию входного:
assign s2 = ~s1;

Цель эксперимента: сгенерировать частоту тремя способами и замерить ее.
Измерять частоту будем за счет счетчиков. Будет 4 счетчика: три на каждый вариант и один счетчик базовый, относительно которого все будет считаться. А смотреть эти счетчики будем через ChipScope.

А вот собственно весь код модуля:
module gen_clk(    input clk_base,    input s1, //gpio    output s2 //gpio    );//счетчик на входных-выходных контактахassign s2 = ~s1;wire clk_gpio = s1;reg [31:0] cnt_gpio = 0; (* MARK_DEBUG="true" *) reg [31:0] cnt_gpio_buf = 0;always@(posedge clk_gpio)begin     if(cnt_gpio[2:0]==3'd0) cnt_gpio_buf<=cnt_gpio;     cnt_gpio <= cnt_gpio + 1'b1;end//счетчик на мультиплексореwire clk_mux;MUXF7 MUXF7_inst(    .O(clk_mux),    .I0(1'b1),    .I1(1'b0),    .S(clk_mux));reg [31:0] cnt_mux = 0; (* MARK_DEBUG="true" *) reg [31:0] cnt_mux_buf = 0;always@(posedge clk_mux)begin     if(cnt_mux[2:0]==3'd0) cnt_mux_buf<=cnt_mux;     cnt_mux <= cnt_mux + 1'b1;end//счетчик на одном лутеwire clk_lut;LUT1#(    .INIT(2'b01))LUT1_inst(    .O(clk_lut),    .I0(clk_lut));reg [31:0] cnt_lut = 0; (* MARK_DEBUG="true" *) reg [31:0] cnt_lut_buf = 0;always@(posedge clk_lut)begin     if(cnt_lut[2:0]==3'd0) cnt_lut_buf<=cnt_lut;     cnt_lut <= cnt_lut + 1'b1;end//базовый счетчик относительно которого будем считать     (* MARK_DEBUG="true" *) reg [31:0] cnt_base = 'd0;        always@(posedge clk_base)begin    cnt_base <= cnt_base + 1'b1;end       endmodule


Вот схематик проекта. В круг обведены примитивы, а стрелками указаны сигнал которые будут внесены в ChipScope для анализа частоты:

image


Практическая часть

В моем распоряжении есть три платы:

  1. KC705 Evaluation Kit

    image

  2. ML507 Evaluation Kit

    image

  3. Китайская плата Spartan-6 XC6SLX16

    image

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


И так теперь собственно результаты


Kintex-7:

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

Базовый счетчик тактируется на 200 МГц, поэтому посчитать частоту клока сгенерированного на луте не сложно, во сколько раз больше дельта счетчика лута дельты базового счетчика за одно и тоже время, во столько раз больше его частота. В данном случае: получается частота генерируемая лутом 381.55 МГц.

image

Теперь к проекту добавим мультиплексор, и по аналогии как с одним лутом посчитаем частоту для него, ну и для лута (должно ведь что-то поменяться).

image

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

  • Частота на мультиплексоре: 5953.89 МГц
  • Частота на луте(изменилась): 379.98 МГц

Ну и на конец добавим к проекту замкнутую петлю из пары GPIO. На плате KC705 есть SMA разъемы J13 и J14. Вот их то я и замкнул проводником длиной примерно 10 см. В результате:

  • Частота на GPIO: 90.59 МГц
  • Частота на мультиплексоре: 12994.13 МГц
  • Частота на луте: 380.18 МГц

Заменим, эксперимента ради, проводник на более длинный, у меня имеется провод в два раза длиннее. В итоге частота упала до 85.29 МГц.

На данном этапе эксперимента можно отметить, что частота работы примитивов в ПЛИС не одинаковая. В случае, когда был только один лут то синтезатор выбрал самый быстрый лут и строил вокруг него схему, затем когда добавился мультиплексор синтезатор попытался найти ту супер позицию где и лут и мультиплексор работают максимально быстро, а это уже другие элемента и частоты уже медленнее. Когда добавились внешние пины то весь проект на кристалле в принципе передислоцировался к этим ножкам и проект стал синтезироваться на близ лежащих элементах, по какой-то причине в том месте частоты лута и мультиплексора заметно выросли, но не стоит забывать что на фоне всего этого к проекту подключён ChipScope глубиной 1024 и шиной данных от 64 до 128(от проекта к проекту меняется). Теперь перейдем к следующей плате.

Virtex-5:

Я не стал проходить весь путь что прошел с предыдущей платой, сразу добавил все 3 варианта генерации клока и посмотрел в ChipScope что получилось.

image

На рисунке видны две метки Х и О. А так же их значения в столбцах, формат чисел беззнаковый десятичный. Стоит отметить, что базовый счетчик теперь считает на частоте 100 МГц. И так результат:

  • Частота на GPIO: 96.34 МГц
  • Частота на мультиплексоре: 614.41 МГц
  • Частота на луте: 5761.1 МГц

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

А теперь последний вариант с китайской платой.

Spartan-6:

В ChipScope появилось два базовых счетчика, на самом деле это один и тот же счетчик просто не хотел перенастраивать ChipScope. В данном проекте базовый счетчик тактируется на частоте 50 МГц.

image

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

  • Частота на GPIO: 51.77 МГц
  • Частота на мультиплексоре: 3 490 504 МГц
  • Частота на луте: не получилось собрать

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

Заключение

В целом, получилось сгенерировать тактовые сигналы на различных примитивах, а так же получилось увидеть(на примере Kintex-7), что примитивы имеют разную задержку работы в зависимости от расположения. От себя хочу добавить, что не считаю проведённый эксперимент полностью корректным, например не рассчитывалась разрядность счетчиков, не учитывался перенос сигнала из разных клоковых доменов(хотя я сделал чтобы сигнал в буфере держался несколько тактов), сам ChipScope в идеале нужно убрать и найти другой способ анализировать генерируемую частоту.

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

  • set_property ALLOW_COMBINATORIAL_LOOPS TRUE [get_nets -of_objects [get_cells gen_clk_inst/LUT1_inst]]
  • NET s1 CLOCK_DEDICATED_ROUTE = FALSE;

Подробнее..

Молодожены, которых Интел привез в Калифорнию, дали интервью в лабнике от ВШЭ МИЭМ. Как повторить их достижение

30.08.2020 12:21:42 | Автор: admin


Я уже рассказывал про молодоженов Владислава и Елены Шаршиных, который вместе с их коллегой Андреем Папушиным компания Intel привезла в Калифорнию за серебряную победу на конкурсе InnovateFPGA. Их интервью есть в недавно вышедшем лабнике Цифровой синтез. Этот лабник мы собираемся использовать на семинаре для школьников и младших студентов, который пройдет 15-17 сентября на выставке ChipEXPO в Сколково. Если вы собираетесть повторить (или превысить) достижение Шаршиных и Папушина, или просто стать FPGA или ASIC designer-ом, не выезжая в Санта-Клару, то участие в семинаре, или даже просто просмотр начальных инструкций в этом посте поможет вам начать.

Многие из зарегистрировавшихся на семинар уже получили FPGA платы (их раздачей занимается образовательное отделение РОСНАНО) за прохождение теоретического пререквизита. Теперь было бы очень желательно, если бы участники семинара заранее установили Intel FPGA Quartus (или, если кто-нибудь не любит Intel / Altera, то Xilinx Vivado), и запустил на нем хотя-бы примитивный тест. Даже не мигание LED, а вообще один логический элемент XOR. Если все это сделают заранее, то на семинаре мы будем обсуждать не тривиальные проблемы типа у меня драйвер для USB Blaster не находится, а что-нибудь поинтереснее, например что спрашивают на интервью на позицию RTL Logic Designer-а в NVidia, AMD, Apple и другие компании.

Итак, прежде всего у семинара есть организация на гитхабе. Вы можете клонировать отуда репозиторию ce2020labs и использовать файлы из директории before для проверки вашей платы, установки Quartus или Vivado, драйверов и решении проблемм с программатором. В директории есть файлы для семи плат: пять плат с Intel FPGA (два варианта OMDAZZ / rzrd, два варианта ZEOWAA и Terasic DE10-Lite), и две платы с Xilinx (Basys3 и Nexys4).

Если вы не хотите пользоваться GitHub-ом, вы можете просто скачать zip отсюда. Все эти файлы будут работать и с версиями Quartus и Vivado под Windows, и под Linux.



Для установки Quartus-а вы можете посмотреть детальное видео от Сергея Иванца, декана факультета электронных и информационных технологий Черниговского национального технологического университета:



Видео:



И видео по синтезу простейшего примера из описания на языке Verilog:



С детальным описанием что к чему:





Единственная проблема в этом видео не очень хорошо раскрыто использование программатора (и проблемы с его драйверами). Поэтому вам может оказаться полезным посмотреть другое видео, тоже от Сергея, где он описывает использование USB Blaster после редактора схематики. Тут надо сказать, что schematic entry вышло из употребления проектировщиков цифровой микроэлектроники в начале 1990-х (все перешли на проектирование с помощью языков описания аппаратуры Verilog и VHDL), но Сергей все-таки решил показать это чисто чтобы начинающий заниматься цифровым проектированием студент сразу интуитивно понял, что мы проектируем схемы, а не пишем программы, хоть код на верилоге и похож на код на языке программирования.

Вот видео с schematic entry, в конце которого расписано использование программатора:



До, во время и после семинара на ChipEXPO в Сколково мы будем продолжать выкладывать разные материалы, но чтобы посмотреть все (в том числе например мини-лекцию, как организованы команды по разработке чипов в Silicon Valley) лучше зарегистрироваться.
Подробнее..

Прокачиваем скрипты симуляции HDL с помощью Python и PyTest

17.01.2021 20:13:46 | Автор: admin

Все делают это. Ну ладно, не все, но большинство. Пишут скрипты, чтобы симулировать свои проекты на Verilog, SystemVerilog и VHDL. Однако, написание и поддержка таких скриптов часто бывает довольно непроста для типично используемых Bash/Makefile/Tcl. Особенно, если необходимо не только открывать GUI для одного тестбенча и смотреть в диаграммы, но и запускать пачки параметризированных тестов для различных блоков, контролировать результат, параллелизировать их выполнение и т.д. Оказалось, что всё это можно закрыть довольно прозрачным и легко поддерживаемым кодом на Python, что мне даже обидно становится от того, как я страдал ранее и сколько странного bash-кода родил.

Конечно, я не первый кто задумывается о подобном. Уже даже существует целый фреймворк VUnit. Однако, как показывает практика и опросы в профильных чатах, такие фреймворки используются нечасто. Вероятно потому, что они предъявляют требования к внутренней структуре самих тестбенчей, с чем наверное можно мириться только на новых проектах, без обширной кодовой базы. Ну и вообще, куда ж без таких вещей как "у нас так не принято" и "not invented here".

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

Задачи

Попробую перечислить большинство задач, которые так или иначе возникают в процессе разработки и эволюции HDL проекта. От более простых к более сложным:

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

  • Одиночный запуск без GUI. То же самое, но без временных диаграмм, оценить результат по выводу в консоль.

  • Параметризированный запуск. Запуск симуляции с GUI или без, но с параметрами (дефайнами), передаваемыми в скрипт из консоли.

  • Запуск с пре-/постпроцессингом. Например, для теста должны быть подготовлены данные. Или сам тест порождает данные, которые должны быть проверены вне HDL.

  • Массовый запуск. Прогнать симуляцию всех существующих тестбенчей.

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

  • Параллельные запуски. Тесты могут легко идти минуты/часы и последовательное исполнение может занимать слишком много времени. Скрипты должны позволять параллельное исполнение нескольких тестбенчей.

  • Поддержка нескольких симуляторов. Сделать так, чтобы всё вышеперечисленное работало в разных симуляторах.

  • Поддержка CI. Массовый запуск должен сочетаться в том числе с выбранной стратегией CI (прогон всех тестов после каждого пуша, "ночные сборки" и т.д.).

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

Идея

Все симуляторы запускаются примерно одинаково в большинстве случаев:

  • собираем список всех исходников (опционально делим на несколько списков по языку);

  • собираем список всех директорий для поиска исходников (нужно для include);

  • собираем список всех дефайнов;

  • сообщаем имя библиотеки, куда всё будем компилировать (или нескольких);

  • сообщаем имя верхнего модуля (обычно это имя тестбенча);

  • передаём это всё симулятору в виде ключей, файлов со списками и т.д.

А что если написать модуль на Python, в котором обернуть нужные симуляторы в один класс Simulator, вынести общие вещи в атрибуты и реализовать метод run(), который запустит симуляцию с помощью выбранного симулятора? В целом, именно это я и сделал для Icarus Verilog, Modelsim и Vivado Simulator, используя модуль subprocessпод капотом. Также я добавил класс CliArgs, основанный на модуле argparse, чтобы иметь возможность управлять запуском из консоли. Ну и написал некоторое количество вспомогательных функций, которые пригодятся в процессе. Получился файл sim.py.

Фактически, я постарался свести всё к тому, что в новом проекте нужно всего-лишь закинуть этот файл, создать рядом еще один скрипт на Python, импортировать необходимое из sim.py и начать работу.

Тестовый проект

Для демонстрации я вытянул модуль пошагового вычисления квадратного корня из одного старого проекта, чтобы тестовый дизайн был хоть чуточку сложнее счётчика или сумматора. Код основан на публикации An FPGA Implementation of a Fixed-Point Square Root Operation.

Репозиторий проекта pyhdlsim на GitHub.

Иерархия проекта проста:

$ tree -a -I .git. .github    workflows # Github Actions        icarus-test.yml # запуск всех тестов в Icarus Verilog после каждого пуша на github        modelsim-test.yml # запуск всех тестов в Modelsim после каждого пуша на github .gitignore LICENSE.txt README.md sim # скрипты для запуска симуляции    conftest.py    sim.py    test_sqrt.py src # исходники     beh # поведенчесие описания и модели        sqrt.py     rtl # синтезируемый HDL код        sqrt.v     tb # HDL код тестбенчей         tb_sqrt.sv

Сам тестбенч tb_sqrt.sv тоже довольно примитивен: подготавливается массив входных значений, вычисляются "идеальные" значения с помощью $sqrt(), входные значения проталкиваются в модуль корня, выходные значения сохраняются в массив, происходит сравнение ожидаемых значений и фактических.

В принципе, краткое описание есть в самом репозитории и на этом можно закругляться, однако, думаю что будет гораздо нагляднее, если показать весь путь написания тестового окружения (будем считать что весь HDL и файл sim.py уже написаны). Всё действо будет происходить внутри папки sim. Осторожно, букв много впереди ожидает.

Одиночный запуск

Создадим файл test_sqrt.py для запуска тестбенча.

#!/usr/bin/env python3from sim import Simulatorsim = Simulator(name='icarus', gui=True, cwd='work')sim.incdirs += ["../src/tb", "../src/rtl", sim.cwd]sim.sources += ["../src/rtl/sqrt.v", "../src/tb/tb_sqrt.sv"]sim.top = "tb_sqrt"sim.setup()sim.run()

Тест будем прогонять в Icarus с открытием GTKWave для просмотра диаграмм. Пути до исходников задаются относительно самого скрипта. Задавать директории поиска инклудов для данного проекта не обязательно, и сделано лишь для демонстрации. Чтобы не загрязнять директорию со скриптами - с помощью sim.setup() будет создана рабочая папка work (а если она существовала, то она будет удалена и создана заново) внутри которой симулятор и будет запущен (sim.run()).

Делаем скрипт исполняемым и запускаем:

chmod +x test_sqrt.py./test_sqrt.py

Симуляция должна пройти успешно и должно появиться окно GTKWave.

Одиночный запуск без GUI

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

#!/usr/bin/env python3from sim import Simulator, CliArgsdef test(tmpdir, defines, simtool, gui):    sim = Simulator(name=simtool, gui=gui, cwd=tmpdir)    sim.incdirs += ["../src/tb", "../src/rtl", sim.cwd]    sim.sources += ["../src/rtl/sqrt.v", "../src/tb/tb_sqrt.sv"]    sim.defines += defines    sim.top = "tb_sqrt"    sim.setup()    sim.run()if __name__ == '__main__':    # run script with key -h to see help    args = CliArgs(default_test="test").parse()    test(tmpdir='work', simtool=args.simtool, gui=args.gui, defines=args.defines)

Посмотрим что нам доступно:

$ ./test_sqrt.py -husage: test_sqrt.py [-h] [-t <name>] [-s <name>] [-b] [-d <def> [<def> ...]]optional arguments:  -h, --help            show this help message and exit  -t <name>             test <name>; default is 'test'  -s <name>             simulation tool <name>; default is 'icarus'  -b                    enable batch mode (no GUI)  -d <def> [<def> ...]  define <name>; option can be used multiple times

Теперь мы можем запустить тест в консольном режиме:

$ ./test_sqrt.py -bRun Icarus (cwd=/space/projects/pyhdlsim/simtmp/work)TOP_NAME=tb_sqrt SIMiverilog -I /space/projects/pyhdlsim/src/tb -I /space/projects/pyhdlsim/src/rtl -I /space/projects/pyhdlsim/simtmp/work -D TOP_NAME=tb_sqrt -D SIM -g2005-sv -s tb_sqrt -o worklib.vvp /space/projects/pyhdlsim/src/rtl/sqrt.v /space/projects/pyhdlsim/src/tb/tb_sqrt.svvvp worklib.vvp -lxt2LXT2 info: dumpfile dump.vcd opened for output.Test started. Will push 8 words to DUT.!@# TEST PASSED #@!

Или запустить в другом симуляторе:

# как в консоли./test_sqrt.py -s modelsim -b# так и с GUI./test_sqrt.py -s modelsim

Параметризированный запуск

Также теперь можно контролировать дефайны из консоли, и, например, увеличить количество подаваемых данных:

$ ./test_sqrt.py -b -d ITER_N=42Run Icarus (cwd=/space/projects/pyhdlsim/simtmp/work)TOP_NAME=tb_sqrt SIMiverilog -I /space/projects/pyhdlsim/src/tb -I /space/projects/pyhdlsim/src/rtl -I /space/projects/pyhdlsim/simtmp/work -D TOP_NAME=tb_sqrt -D SIM -g2005-sv -s tb_sqrt -o worklib.vvp /space/projects/pyhdlsim/src/rtl/sqrt.v /space/projects/pyhdlsim/src/tb/tb_sqrt.svvvp worklib.vvp -lxt2LXT2 info: dumpfile dump.vcd opened for output.Test started. Will push 42 words to DUT.!@# TEST PASSED #@!

Запуск с пре-/постпроцессингом

Часто бывает так, что сгенерировать данные для теста невозможно внутри тестбенча и должны быть применены внешние генераторы. Сделаем еще один тест, где будем сверять работу модуля на Verilog с идеальной моделью, написанной на Python. Алгоритм работы уже был представлен выше - просто перепишем его на Python, не забывая проверить что он на самом деле работает. Результатом будет файл src/beh/sqrt.py. Оттуда нам нужна будет лишь одна функция nrsqrt().

Переименуем старый тест, который ориентируется на данные, полученные внутри тестбенча, в test_sv. И создадим новый test_py, который будет готовить данные с помощью функции nrsqrt().

#!/usr/bin/env python3from sim import Simulator, CliArgs, path_join, write_memfileimport randomimport syssys.path.append('../src/beh')from sqrt import nrsqrtdef create_sim(cwd, simtool, gui, defines):    sim = Simulator(name=simtool, gui=gui, cwd=cwd)    sim.incdirs += ["../src/tb", "../src/rtl", cwd]    sim.sources += ["../src/rtl/sqrt.v", "../src/tb/tb_sqrt.sv"]    sim.defines += defines    sim.top = "tb_sqrt"    return simdef test_sv(tmpdir, defines, simtool, gui):    sim = create_sim(tmpdir, simtool, gui, defines)    sim.setup()    sim.run()def test_py(tmpdir, defines, simtool, gui=False, pytest_run=True):    # prepare simulator    sim = create_sim(tmpdir, simtool, gui, defines)    sim.setup()    # prepare model data    try:        din_width = int(sim.get_define('DIN_W'))    except TypeError:        din_width = 32    iterations = 100    stimuli = [random.randrange(2 ** din_width) for _ in range(iterations)]    golden = [nrsqrt(d, din_width) for d in stimuli]    write_memfile(path_join(tmpdir, 'stimuli.mem'), stimuli)    write_memfile(path_join(tmpdir, 'golden.mem'), golden)    sim.defines += ['ITER_N=%d' % iterations]    sim.defines += ['PYMODEL', 'PYMODEL_STIMULI="stimuli.mem"', 'PYMODEL_GOLDEN="golden.mem"']    # run simulation    sim.run()if __name__ == '__main__':    args = CliArgs(default_test="test_sv").parse()    try:        globals()[args.test](tmpdir='work', simtool=args.simtool, gui=args.gui, defines=args.defines)    except KeyError:        print("There is no test with name '%s'!" % args.test)

Теперь, когда тестов несколько, можно воспользоваться ключом выбора теста:

# аргумент должен совпадать с именем функции./test_sqrt.py -t test_py

Аналогичным образом можно организовать и постпроцессинг внутри запускающего скрипта при желании.

Массовый запуск

Сейчас у нас уже есть 2 теста, а ведь завтра может быть и 202, а значит уже можно переживать о том, что нужен способ как их все прогнать за раз. И вот тут на сцене появляется pytest.

Нано-ликбез по pytest.

  • Начиная с директории запуска, pytest рекурсивно ищёт всё начинающееся на test* и исполняет: модули, функции, классы, методы.

  • Тест считается выполненным, если не возникло исключений (типичным является использование assert для контроля).

  • Для формирования тестового окружения используются фикстуры (fixtures). Например, что подставлять в тест test_a(a) в качестве аргумента при выполнении как раз определяется фикстурой.

  • Можно создать дополнительный файл conftest.py, в котором разместить код для более тонкого контроля выполнения тестов и их окружения.

Типичные сценарии запуска:

  • pytest - рекурсивный поиск и исполнение всех тестов, начиная с текущей директории;

  • pytest -v - выполнить тесты и показать болеe подробную информацию о ходе выполнении тестов;

  • pytest -rP - выполнить тесты и показать вывод в stdout тех тестов, что завершились успешно;

  • pytest test_sqrt.py::test_sv - выполнить указанный тест.

Для того чтобы адаптировать текущий скрипт под pytest нужно совсем немного. Импортируем сам pytest. Добавим пару фикстур для таких аргументов теста как simtool и defines. Значение, возвращаемое фикстурами будет использовано в качестве аргумента во всех тестах. Два других аргумента gui и pytest_run снабжаем значениями по умолчанию. Фактически их тоже можно было сделать фикстурами, но т.к. для запуска pytest они не должны принимать никакое другое значение, то сделал так.

Да, кстати, появился аргумент pytest_run который сообщает о том, выполняется ли сейчас pytest, или просто тест запущен отдельно из консоли.

С аргументом tmpdir я схитрил - это имя стандартной фикстуры, которая возвращает путь к временной папке, уникальной для каждого теста. Т.е. сами тесты будут прогонятся где-то во временных директориях и не засорять содержимое sim.

Имена тестов тоже изменять не нужно - они будут найдены pytest, т.к. имеют префикс test_.

Решение о том, прошел тест или нет принимается по состоянию аттрибута is_passed симулятора. Он возвращает истину, если увидел ключевую фразу !@# TEST PASSED #@! в stdout. Очевидно, если компиляция была неудачной, или тест завершился с ошибкой, то этой фразы выведено не будет. Это самый простой способ оценить результат, но возможности для его кастомизации здесь ограничены лишь фантазией. Можно получить stdout через sim.stdout и искать там что угодно.

#!/usr/bin/env python3import pytestfrom sim import Simulator, CliArgs, path_join, write_memfileimport randomimport syssys.path.append('../src/beh')from sqrt import nrsqrt@pytest.fixture()def defines():    return []@pytest.fixturedef simtool():    return 'icarus'def create_sim(cwd, simtool, gui, defines):    sim = Simulator(name=simtool, gui=gui, cwd=cwd, passed_marker='!@# TEST PASSED #@!')    sim.incdirs += ["../src/tb", "../src/rtl", cwd]    sim.sources += ["../src/rtl/sqrt.v", "../src/tb/tb_sqrt.sv"]    sim.defines += defines    sim.top = "tb_sqrt"    return simdef test_sv(tmpdir, defines, simtool, gui=False, pytest_run=True):    sim = create_sim(tmpdir, simtool, gui, defines)    sim.setup()    sim.run()    if pytest_run:        assert sim.is_passeddef test_py(tmpdir, defines, simtool, gui=False, pytest_run=True):    # prepare simulator    sim = create_sim(tmpdir, simtool, gui, defines)    sim.setup()    # prepare model data    try:        din_width = int(sim.get_define('DIN_W'))    except TypeError:        din_width = 32    iterations = 100    stimuli = [random.randrange(2 ** din_width) for _ in range(iterations)]    golden = [nrsqrt(d, din_width) for d in stimuli]    write_memfile(path_join(tmpdir, 'stimuli.mem'), stimuli)    write_memfile(path_join(tmpdir, 'golden.mem'), golden)    sim.defines += ['ITER_N=%d' % iterations]    sim.defines += ['PYMODEL', 'PYMODEL_STIMULI="stimuli.mem"', 'PYMODEL_GOLDEN="golden.mem"']    # run simulation    sim.run()    if pytest_run:        assert sim.is_passedif __name__ == '__main__':    args = CliArgs(default_test="test_sv").parse()    try:        globals()[args.test](tmpdir='work', simtool=args.simtool, gui=args.gui, defines=args.defines, pytest_run=False)    except KeyError:        print("There is no test with name '%s'!" % args.test)

Прогоним все тесты несколько раз:

$ pytest========== test session starts ===========platform linux -- Python 3.8.5, pytest-6.2.1, py-1.10.0, pluggy-0.13.1rootdir: /space/projects/misc/habr-publications/pyhdlsim/pyhdlsim/simtmpplugins: xdist-2.2.0, forked-1.3.0collected 2 itemstest_sqrt.py ..                    [100%]=========== 2 passed in 0.08s ============$ pytest -v========== test session starts ===========platform linux -- Python 3.8.5, pytest-6.2.1, py-1.10.0, pluggy-0.13.1 -- /usr/bin/python3cachedir: .pytest_cacherootdir: /space/projects/misc/habr-publications/pyhdlsim/pyhdlsim/simtmpplugins: xdist-2.2.0, forked-1.3.0collected 2 itemstest_sqrt.py::test_sv PASSED       [ 50%]test_sqrt.py::test_py PASSED       [100%]=========== 2 passed in 0.08s ============

Массовый параметризированный запуск

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

Модификация будет минимальной, нужно лишь обновить фикстуру defines:

# заменим это@pytest.fixture()def defines():    return []# на это@pytest.fixture(params=[[], ['DIN_W=16'], ['DIN_W=18'], ['DIN_W=25'], ['DIN_W=32']])def defines(request):    return request.param

Теперь фикстура может принимать одно из 5 значений. Запустим тесты:

$ pytest -v================== test session starts ==================platform linux -- Python 3.8.5, pytest-6.2.1, py-1.10.0, pluggy-0.13.1 -- /usr/bin/python3cachedir: .pytest_cacherootdir: /space/projects/misc/habr-publications/pyhdlsim/pyhdlsim/simtmpplugins: xdist-2.2.0, forked-1.3.0collected 10 itemstest_sqrt.py::test_sv[defines0] PASSED            [ 10%]test_sqrt.py::test_sv[defines1] PASSED            [ 20%]test_sqrt.py::test_sv[defines2] PASSED            [ 30%]test_sqrt.py::test_sv[defines3] PASSED            [ 40%]test_sqrt.py::test_sv[defines4] PASSED            [ 50%]test_sqrt.py::test_py[defines0] PASSED            [ 60%]test_sqrt.py::test_py[defines1] PASSED            [ 70%]test_sqrt.py::test_py[defines2] PASSED            [ 80%]test_sqrt.py::test_py[defines3] PASSED            [ 90%]test_sqrt.py::test_py[defines4] PASSED            [100%]================== 10 passed in 0.28s ===================

Как видим, теперь каждый тест запустился по 5 раз с разным дефайном.

Параллельные запуски

Тут тоже всё довольно просто и работает почти из коробки. Ставим один плагин:

python3 -m pip install pytest-xdist

И теперь можем запускать тесты в несколько параллельных потоков, например, в 4:

# можно также использовать значение auto, pytest задействует все доступные ядраpytest -n 4

Для того, чтобы проверить работу этого механизма добавим еще один длительный тест:

def test_slow(tmpdir, defines, simtool, gui=False, pytest_run=True):    sim = create_sim(tmpdir, simtool, gui, defines)    sim.defines += ['ITER_N=500000']    sim.setup()    sim.run()    if pytest_run:        assert sim.is_passed

Запустим последовательное и параллельное исполнение (тестов теперь стало 3*5=15):

$ pytest=================== test session starts ====================platform linux -- Python 3.8.5, pytest-6.2.1, py-1.10.0, pluggy-0.13.1rootdir: /space/projects/misc/habr-publications/pyhdlsim/pyhdlsim/simtmpplugins: xdist-2.2.0, forked-1.3.0collected 15 itemstest_sqrt.py ...............                         [100%]============== 15 passed in 242.74s (0:04:02) ==============$ pytest -n auto=================== test session starts ====================platform linux -- Python 3.8.5, pytest-6.2.1, py-1.10.0, pluggy-0.13.1rootdir: /space/projects/misc/habr-publications/pyhdlsim/pyhdlsim/simtmpplugins: xdist-2.2.0, forked-1.3.0gw0 [15] / gw1 [15] / gw2 [15] / gw3 [15]...............                                      [100%]============== 15 passed in 145.66s (0:02:25) ==============

Результат, как говорится, видно невооруженным взглядом.

Поддержка нескольких симуляторов

Ранее уже было показано, что при выполнении теста без pytest можно было выбрать симулятор с помощью ключа -s. Теперь же добавим выбор симулятора для pytest. Очевидно, нужно что-то сделать с фикстурой simtool.

Тут нам пригодится знание о существовании файла conftest.py, необходимого для кастомизации запусков pytest. Создадим такой файл рядом с sim.py и добавим туда следующий код:

def pytest_addoption(parser):    parser.addoption("--sim", action="store", default="icarus")

В файле теста test_sqrt.py обновим фикстуру simtool:

@pytest.fixturedef simtool(pytestconfig):    return pytestconfig.getoption("sim")

Теперь можно прогнать все тесты в другом симуляторе:

pytest --sim modelsim -n auto

Поддержка CI. Github Actions + (Modelsim | Icarus)

Ну и бонусом будет часть о непрерывной интеграции (CI). В репозиторий добавлены два файла .github/workflows/icarus-test.yml и .github/workflows/modelsim-test.yml. Это так называемые Github Actions - по определенному событию будет выполнено их содержимое внутри виртуального окружения, предоставляемого Github. В данном случае, после каждого пуша будут прогнаны все тесты в двух симуляторах.

В Icarus Verilog:

- name: Install dependencies  run: |    python -m pip install --upgrade pip    pip install pytest pytest-xdist    sudo apt-get install iverilog- name: Test code  working-directory: ./sim  run: |    pytest -n auto

И в Modelsim Intel Starter Pack:

- name: Install dependencies  run: |    python -m pip install --upgrade pip      pip install pytest pytest-xdist      sudo dpkg --add-architecture i386      sudo apt update      sudo apt install -y libc6:i386 libxtst6:i386 libncurses5:i386 libxft2:i386 libstdc++6:i386 libc6-dev-i386 lib32z1 libqt5xml5 liblzma-dev    wget https://download.altera.com/akdlm/software/acdsinst/20.1std/711/ib_installers/ModelSimSetup-20.1.0.711-linux.run        chmod +x ModelSimSetup-20.1.0.711-linux.run    ./ModelSimSetup-20.1.0.711-linux.run --mode unattended --accept_eula 1 --installdir $HOME/ModelSim-20.1.0 --unattendedmodeui none    echo "$HOME/ModelSim-20.1.0/modelsim_ase/bin" >> $GITHUB_PATH- name: Test code  working-directory: ./sim  run: |    pytest -n auto --sim modelsim

Тут кстати очень порадовала последняя версия Modelsim. Они наконец-то починили её! Каждый кто хоть раз устанавливал его на Ubuntu/Fedora поймёт о чём я (вот, например, инструкция для Quartus+Modelsim 19.1 и Fedora 29).

Ну и сравнение времени выполнения после очередного пуша в репозиторий:

Даже не смотря на то, что скачивание 1.3GB установочника Modelsim и его распаковка занимают некоторое время (которое тем не менее, очень мало!), он оказывается в итоге ещё и быстрее моментально развертываемого Icarus.

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

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

Все финальные версии скриптов лежат в репозитории pyhdlsim на GitHub.

Подробнее..
Категории: Python , Fpga , Verilog , Vhdl , Modelsim , Pytest , Vivado , Simulation , Systemverilog , Icarus

Как начать путь к работе по проектированию электроники FPGA космического корабля Blue Origin

30.07.2020 10:07:11 | Автор: admin


Вы хотите узнать, как получить работу по проектированию электроники космического корабля? Мне надавно пришло предложение поинтервьироваться на позицию FPGA designer для Blue Origin (см. выше). Лично мне такая позиция не нужна (у меня уже есть позиция ASIC designer-а в другой компании), но я отметил, что технические требования к претендентам в Blue Origin точно совпадают с содержанием семинара для школьников и младших студентов, который пройдет 15-17 сентября на выставке ChipEXPO в Сколково, с поддержкой от РОСНАНО. Хотя разумеется на семинаре мы коснемся технологий Verilog и FPGA только на самом начальном уровне: базовые концепции и простые, но уже интересные, примеры. Чтобы устроится после этого в Blue Origin, вам все-же потребуется несколько лет учебы и работы.

Из-за короновируса семинар будет удаленный, поэтому принять участие смогут не только школьники и студенты Москвы, но и всей России, Украины, Казахстана, Калифорнии и других стран и регионов. Физически проводить лекции и удаленно помогать участникам будут преподаватели и инженеры МИЭТ, ВШЭ МИЭМ, МФТИ, Черниговского Политеха, Самарского университета, IVA Technologies и fpga-systems.ru.

Для участия сначала, еще до семинара, нужно пройти три части теоретического курса от РОСНАНО, под общим названием Как работают создатели умных наночипов: От транзистора до микросхемы, Логическая сторона цифровой схемотехники, Физическая сторона цифровой схемотехники. Этот курс необходим, чтобы вы понимали, что вы делаете, по время практического семинара. По получению сертификата окончания теоретического онлайн-курса, вы можете зайти в офис РОСНАНО в Москве и получить бесплатную плату для практического семинара (если они останутся, преимущество имеют школьники). С этой платой вы можете работать дома, до, во время и после семинара в Сколково.

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

Инструкция как получить плату есть на сайте STEMford. STEMford это образовательные программы для школьников от eNano, дочернего предприятия Фонда инфраструктурных образовательных программ РОСНАНО. Вот их офис, куда нужно будет зайти:



До практического занития вам нужно будет установить на ваш компьютер среду Intel Quartus Prime Lite Edition. Инструкция как это сделать, есть в бесплатном фрагменте книги Цифровой синтез: практический курс под общей редакцией А. Ю. Романова, Ю. В. Панчула. ДМК Пресс, 2020.

Вообще Цифровой синтез полезная книжка для семинара, там описываются упражнения, которые будут с некоторыми вариациями представлены и на семинаре, хотя например вместо учебного процессора schoolMIPS будет использоваться более новомодный schoolRISCV:



В конце книжки Цифровой синтез есть довольно занятное интервью пары молодоженов Владислава и Елены Шаршиных, который вместе с их коллегой Андреем Папушиным компания Intel привезла в Калифорнию за серебряную победу на конкурсе InnovateFPGA:



Вот одна из плат, которая будет использоваться на семинаре. Если плат на вас не хватит, или если вы не школьник, вы можете заказать плату на AliExpress и в других местах: 1, 2, 3, 4:



Что будет на семинаре? Вот части программы:

15 сентября. Из чего строится современная цифровая схема.
Модератор дня: Александр Михайлович Силантьев, преподаватель Национального исследовательского университета Московский институт электронной техники (МИЭТ).

15.00. Открытие мероприятия, приветствие от организаторов.
15.15-15.30. Мини-лекция: От Клода Шеннона до Apple iPhone: как появилось проектирование цифровых схем и как оно выглядит в современных компаниях.
Юрий Владимирович Панчул, проектировщик сетевых микросхем и микропроцессорных ядер. Саннивейл, Калифорния.

15.30-16.00. Лекция: Комбинационная логика и ее описание на языке Verilog. Теоретический материал переплетается с демонстрацией синтеза для ПЛИС/FPGA в среде Intel Quartus Prime Lite Edition. Александр Михайлович Силантьев.

16.00-16.30. Упражнение с логическими элементами
И/ИЛИ/НЕ/ИСКЛЮЧАЮЩЕЕ-ИЛИ, входы которых подсоединены к кнопкам, а выходы к светодиодам платы c ПЛИС.
16.30-17.00. Упражнение с выводом буквы на семисегментный индикатор.
17.00-17.30. Лекция: Последовательностная логика, которая вводит в схемы память и повторения.
17.30-18.00. Упражнение со сдвиговым регистром.
18.00-19.00. Упражнение для плат ZEOWAA и OMDAZZ с Intel FPGA Cyclone IV: Комбинируем сдвиговый регистр и вывод на семисегментный индикатор буквы: получаем вывод на многоразрядный динамический семисегментный индикатор слова (например имени ученика). Упражнение для платы Terasic DE10-Lite с Intel FPGA MAX10: Комбинируем сдвиговый регистр и вывод букв на статический семисегментный индикатор: получаем вывод бегущей строки (например имени ученика).
19.00-21.00. Дополнительные упражнения и индивидуальные проекты учеников, с помощью от студентов и аспирантов микроэлектроники от участвующих университетов: МИЭТ, ВШЭ МИЭМ, Черниговского НТУ,
Самарского Университета.


Первый день основан на опыте проведения прошлогоднего семинара в Москве, который описан в статье на Хабре:



Второй день основан на опыте проведения летней школы в Зеленограде в прошлом году и семинарах в Самаре:

16 сентября. Приемы и примеры цифрового проектирования на уровне регистровых передач.
Модератор дня: Сергей Анатольевич Иванец, декан факультета электронных и информационных технологий, Черниговский национальный технологический университет, Украина.

15.00-15.15. Мини-лекция: Как из простых схем строить сложные: параллельность, конвейерность и конечные автоматы.
Юрий Владимирович Панчул.

15.15-15.30. Предисловие к примеру игры: рассказ про генерацию графики на VGA.
Сергей Анатольевич Иванец.

15.30-16.00. Упражнение с рисованием на экране разноцветных квадратов и других статических изображений.

16.00-16.30. Презентация примера графической игры с параллельно вычисляемыми спрайтами и конечными автоматами для сценария игры. Демонстрация запуска игры на плате Digilent Basys3 с Xilinx FPGA Artix-7. Обсуждение модификации игры с помощью добавления новых спрайтов и изменения сценария.
Михаил Коробков, fpga-systems.ru.

16.30-17.00. Упражнение с запуском игры на платах ZEOWAA, OMDAZZ и Terasic DE10-Lite.
Сергей Анатольевич Иванец.

17.00-17.30. Лекция: Использование Linear Feedback Shift Registers (LFSR) для передачи данных и генераторов случайных чисел. Сравнение Verilog и VHDL на основе кода LFSR Фибоначчи и Галуа. Демонстрация использования LFSR для генерации изображения на экране VGA движущегося звездного неба из случайных звезд.
Илья Александрович Кудрявцев, декан Факультета электроники и приборостроения Самарского Университета.

17.30-18.00. Упражнение с запуском примера движущегося звездного неба на платах ZEOWAA, OMDAZZ и Terasic DE10-Lite.
Сергей Анатольевич Иванец.

18.00-19.00. Более подробная лекция про моделирование и использование генераторов случайных чисел для глубоко заинтересовавшихся.
Илья Александрович Кудрявцев.

18.00-21.00. Дополнительные упражнения и индивидуальные проекты учеников по изменению игры, в том числе с помощью добавления в нее фона из движущегося звездного неба. С помощью от студентов и аспирантов микроэлектроники от участвующих университетов.






Третий день будет основан на опыте ликбеза по процессорам от Станислава Жельнио и Александра Романова. Только раньше они разработали и использовали учебный процессор schoolMIPS, а теперь мы будем использовать schoolRISCV. Архитектура RISC-V очень похожа на MIPS и другие RISC-архитектуры (SPARC, ARM, POWER итд), но очищена от их костылей, которые имели смысл на простых процессорах, но мешают на сложных (регистровые окна в SPARC, которые обессмысливаются в софтвере с большим стеком; branch delay slots в MIPS до Rev6, которые хороши на простом статическом конвейере с последовательной выборкой инстркций, но превращаются в головную боль в динамическом конвейере итд).

Так как в России есть минимум три компании, которые проектируют процессоры на архитектуре RISC-V, и это хороший кандидат на будущий high-end российский встроеный и может быть даже десктопный и серверный процессор, и так как RISC-V уверенно заменяет MIPS в качестве рабочей лошадки для архитектурного и микроархитектурного экспериментирования в мировых университетах, то школьников лучше учить на нем.

17 сентября. Первый шаг в архитектуру и микроархитектуру современных процессоров.
Модератор дня Александр Юрьевич Романов, к.т.н., доцент Московского института электроники и математики им. А.Н. Тихонова (МИЭМ), Национальный исследовательский университет Высшая школа экономики (НИУ ВШЭ).

15.00-15.15. Мини-лекция: От ENIAC и МЭСМ, через IBM/360 и Cray-1 до Intel, ARM и RISC-V: как появились, эволюционизировали и к чему пришли программируемые процессоры общего назначения.
Юрий Владимирович Панчул.

15.15-16.30. Архитектура: вид процессора с точки зрения программиста. Лекция об ассемблере RISC-V с одновременными упражнениями на симуляторе процессора на уровне инструкций.
Никита Поляков, проектировщик микропроцессоров и преподаватель Московского физико-технического института.

16.30-17.30. Микроархитектура: вид процессора с точки зрения схемотехника. Лекция по аппаратной организации процессора schoolRISCV, с вариантами одноцикловой и конвейерной микроархитектуры. Демонстрация синтеза процессора и запуск его на платах.
Станислав Жельнио, разработчик микросхем в IVA Technologies.

17.30-17.30. Упражнение по добавлению в процессор инструкции и верификации с помощью программного теста. Измерение максимальной тактовой частоты получившегося варианта процессора.
Станислав Жельнио.

17.30-18.00. Заключительная лекция: Следущие шаги в освоении проектирования цифровых схем, а также применение этих знаний для проектирования встроенных систем и ускорителей вычислений искуственного интеллекта.
Александр Юрьевич Романов.

18.00-21.00. Дополнительные упражнения и индивидуальные проекты учеников по изменению процессора и интеграции его с периферийными устройствами. С помощью от студентов и аспирантов микроэлектроники от участвующих университетов.


Вот Станислав Жельнио обучает школьников прошлым летом в Зеленограде:







До встречи на семинаре на виртуальном ChipEXPO в Сколково! Мы постараемся провести там часть мероприятия и вживую, силами преподавателей МИЭТ, ВШЭ МИЭМ и МФТИ, но если вирус не позволит, проведем распределенно.
Подробнее..

Моделируем поведение Quartus-проекта на Verilog в среде ModelSim

29.07.2020 16:12:53 | Автор: admin
В прошлой статье мы сделали достаточно сложный модуль. Разумеется, я вставил в тело статьи уже отлаженный результат. Но мне показалось, что достаточно странно, когда автор говорит делай, как я, но при этом не показывает очень важного процесса. Давайте я покажу, как вообще проводится отладка системы путём моделирования. Причём в следующей статье будут содержаться сведения, которые ещё неделю назад не знал даже я. Но, чтобы перейти к ним, надо разобраться с базовыми принципами. Итак. Давайте рассмотрим, как быстро подготовить и не менее быстро запустить процесс моделирования в среде ModelSim.



Предыдущие статьи цикла
  1. Разработка простейшей прошивки для ПЛИС, установленной в Redd, и отладка на примере теста памяти
  2. Разработка простейшей прошивки для ПЛИС, установленной в Redd. Часть 2. Программный код
  3. Разработка собственного ядра для встраивания в процессорную систему на базе ПЛИС
  4. Разработка программ для центрального процессора Redd на примере доступа к ПЛИС
  5. Первые опыты использования потокового протокола на примере связи ЦП и процессора в ПЛИС комплекса Redd
  6. Веселая Квартусель, или как процессор докатился до такой жизни
  7. Методы оптимизации кода для Redd. Часть 1: влияние кэша
  8. Методы оптимизации кода для Redd. Часть 2: некэшируемая память и параллельная работа шин
  9. Экстенсивная оптимизация кода: замена генератора тактовой частоты для повышения быстродействия системы
  10. Доступ к шинам комплекса Redd, реализованным на контроллерах FTDI
  11. Работа с нестандартными шинами комплекса Redd
  12. Практика в работе с нестандартными шинами комплекса Redd
  13. Проброс USB-портов из Windows 10 для удалённой работы
  14. Использование процессорной системы Nios II без процессорного ядра Nios II
  15. Практическая работа с ПЛИС в комплекте Redd. Осваиваем DMA для шины Avalon-ST и коммутацию между шинами Avalon-MM
  16. Разработка простейшего логического анализатора на базе комплекса Redd
  17. Разработка логического анализатора на базе Redd проверяем его работу на практике
  18. Делаем голову шинного USB-анализатора на базе комплекса Redd


Как работает обычная программа для ЭВМ? Имеется некая внешняя среда (монитор и клавиатура с мышкой самые типичные представители этой самой среды). Программа с ними взаимодействует. При отладке можно производить настоящие воздействия от внешней среды, а можно эмулировать их. У нас тестеры часто пишут всякие скрипты, которые как раз эмулируют внешние воздействия. После чего запускаются анализаторы логов, которые проверяют, чтобы ответы в среду уходили верные.

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

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

И здесь возникает вопрос о внешней среде. Как сымитировать её? Нам на помощь приходят модели. На языке Verilog (как и VHDL, и других похожих) вполне можно описать поведение чего угодно. Делаем мы систему, которая работает с микросхемой ULPI Значит, чтобы проверить её работу, на том конце должно быть что-то, что ведёт себя именно, как ULPI. То есть, модель ULPI. Но этого мало. Наш блок реагирует на команды от шины ALAVON_MM. Именно эта шина заставляет блок жить. Поэтому надо ещё добавить модель шины AVALON_MM, причём эта модель должна быть активной. Именно она будет подавать тестовые воздействия.



В конечном итоге мы должны сделать именно такую систему. И тогда мы сможем снимать временные диаграммы сигналов на всех её шинах и даже внутри любых её модулей. Если будет возникать ошибка, мы сможем устанавливать точки останова и изучать срезы системы, чтобы найти врага. Хотя, лично я эти точки останова обычно не ставлю, чаще всего хватает анализа временных диаграмм. Дело в том, что сигналы можно смотреть не только интерфейсные, а любые внутренние. Вытянув десяток-другой внутренних сигналов на график, обычно можно догадаться, что в логике реализовано не так.

Цель сегодняшней статьи не рассказать о том, что такое моделирование вообще (это долгая история), а показать, как это моделирование провести быстрее всего. И рассмотрим мы это не на боевой задаче, а на простом примере. Сделаем совсем простенькую тестовую систему, чтобы в следующей статье уже понимать, откуда растут ноги у более сложного её варианта, ведь при чтении удобнее не сидеть и недоумевать: Зачем он это делает?, а знать все базовые принципы, из которых уже вытекают усложнения. Кстати, недавно выяснилось, что один мой знакомый хоть и владеет мастерством моделирования, но не знал, что в среду Quartus встроены механизмы, которые позволяют делать это легко и непринуждённо. Он тратил на это намного больше усилий, чем требуется. Так что может, кто-то тоже сейчас узнает для себя что-то новое о возможностях, заложенных в Quartus. Итак, приступаем.

Создание простейшей модели на языке Verilog


Люди делятся на две категории. Те, кто любит создавать всё с нуля руками и те, кто любит делать это, повозив мышкой. Руками создавать всё правильнее. Можно контролировать каждое действие и делать всё заведомо идеально. Но память штука ненадёжная. Если всё время заниматься одним и тем же делом, она держит детали в уме, а если приходится всё время переключаться между языками, через месяц-другой приходится вспоминать, что же там надо сделать. Поэтому работа через вариант повозить мышкой имеет право на существование хотя бы из-за этого. Опять же, если у отлаживаемого модуля десяток-другой интерфейсных сигналов, мне всегда скучно делать рутинную работу по их переобъявлению и пробросу. Поэтому сейчас мы рассмотрим, как сделать модель при помощи мышки. А дальше каждый для себя решит, достаточно ему этого, или стоит переходить на ручную работу.

Итак, мы хотим промоделировать модуль. Что такое промоделировать выходит за рамки нашего цикла, можно на эту тему написать отдельный большой цикл. То есть, в рамках этого раздела, считаем, что вы знакомы с методикой разработки модели. Но дальше надо всё включить в проект Или нет? Как ни странно, для моделирования модуля совершенно не нужно даже создавать собственный проект. Мы можем прицепиться в качестве паразита к любому проекту, не включая в него ничего нового, а только создав тестовый набор, который никак не будет участвовать в основной сборке.

Давайте ради интереса прицепим к нашему ULPI-проекту вот такой забавный модуль на SystemVerilog, написанный мной специально для иллюстрации и не имеющий никакого отношения к разрабатываемому анализатору. Просто некоторое время назад довелось много возиться с вычислением контрольных сумм, вот он в голову и пришёл.
module sum(input         clk,input [7:0]   data,input         we,input         sof,output [15:0] sum);logic [15:0] temp;always @ (posedge clk)begin     if (we)      begin         if (sof)             temp <= data;         else             temp <= temp + data;     endend// В идеале - так//assign sum = (~temp)+1;// Но контролировать проще так:assign sum = temp;endmodule

Видно, что данные в него поступают по шине, очень отдалённо напоминающей AVALON_MM, а выходят просто в параллельном коде.

Положим получившийся файл в каталог с нашим проектом, но не станем включать его в проект в Quartus. Вместо этого создадим тестовый набор специально под него. Для этого выбираем пункт меню Assignments>Settings:



и в появившемся дереве ищем пункт EDA Tools Settings>Simulation:



Кстати, о типе моделирования, выделенном зелёной рамкой. Возможно, кто-то помнит, в первых статьях я говорил, что при создании проекта чисто по привычке выбираю ModelSim Altera? Это было то самое ружьё на сцене, которое рано или поздно должно было выстрелить. Но если при создании проекта тип моделирования не был выбран, его можно выбрать или изменить здесь.

Продолжаем создавать тестовый набор. Переключаем радиокнопку на Compile test bench (кстати, а как этот термин красиво переводится на русский? Я не могу заставить себя писать тестовый стенд, так как не вижу никакого стенда) и нажимаем кнопку Test Benches:



В открывшемся диалоге нажимаем New:



Если делать тестовый набор вручную, то можно заполнить поля за один проход. Но так как мы делаем всё при помощи мышки, то сейчас заполняем только часть полей, а остальные дозаполним позже. В поле Test bench name я вбил слово Parazit (а как ещё назвать тест, который просто паразитирует на проекте?). Слово Parazit под ним заполнилось автоматически. Сейчас мы не будем его менять, но в будущем нам ещё предстоит это сделать. Также при помощи кнопки ... я выбрал файл sum.sv с кодом отлаживаемого сумматора, после чего, при помощи кнопки Add, затолкнул его в список файлов теста. Пока всё. Закрываем диалог



Дальше мы продолжим формирование теста в среде ModelSim. Для этого выбираем пункт меню Tools>Run Simulation Tools>RTL Simulation:



Открывается окно ModelSim. Возможно, будут найдены ошибки в коде Verilog, тогда надо закрывать ModelSim, править ошибки, открывать вновь. Но рано или поздно, перечень ошибок станет чисто организационным. У меня он выглядит так:



Не найдено модуля верхнего уровня. Это нормально. Мы его ещё не создали просто. Поэтому идём в перечне библиотек к work и раскрываем её. Вот он, наш сумматор.



Наводимся на него, нажимаем правую кнопку Мыши и выбираем пункт меню Create Wave. Это в тексте всё так занудно, если бы я снимал видео, весь процесс занимал бы десятки секунд, так что не пугайтесь, а следите за руками дальше. Итак, Create Wave



Интерфейсные сигналы модуля автоматически переехали на график:



Надо назначить значение какого-нибудь из них. Не важно какого, важно назначить. Очень старая среда моделирования Квартуса умела красиво генерить тактовые сигналы. Увы, её давно изъяли из поставки, так как стали прилагать ModelSim, а тут с подобным всё не так красиво. Проку в формировании генератора здесь, я не увидел, поэтому даже показывать не буду. Так что Ну, давайте линию we нулю присвоим. Наводимся на сигнал, нажимаем правую кнопку, выбираем пункт меню Edit>Wave Editor>Create/Modify WaveForm.



В появившемся диалоге выбираем Constant. И время заодно поменяем, скажем, на 100 микросекунд:



Далее указываем значение 0:



Всё, минимально необходимый набор данных мы создали, а остальное проще будет ручками сделать. Экспортируем файл. Для этого выбираем пункт меню File>Export>Waveform:



Выбираем тип файла Verilog Testbench (кстати, очень жаль, что не SystemVerilog, но в будущем можно будет поправить и ручками). Также задаём имя файла. Я назвал его parazit_tb, по принципу а почему бы и нет?.



Всё, ModelSim можно закрывать, времянку при этом сохранять не нужно.

Что делать с моделью дальше


Вот такой кривоватый, но всё-таки готовый Верилоговский файл нам создала система:
`timescale 1ns / 1nsmodule parazit_tb  ;    reg    sof   ;   reg    we   ;   wire  [15:0]  sum   ;   reg  [7:0]  data   ;   reg    clk   ;   sum     DUT  (        .sof (sof ) ,      .we (we ) ,      .sum (sum ) ,      .data (data ) ,      .clk (clk ) ); // "Constant Pattern"// Start Time = 0 ns, End Time = 100 us, Period = 0 ns  initial  begin  end  initial#0 $stop;endmodule

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

Как видим, толку от задания констант, сделанного автогенератором никакого. Но всё-таки, созданы все цепи, подключён модуль, подлежащий тестированию, даже секция initial создана. Давайте облагородим код. Первое выкинем точку останова, удалив строки:
  initial#0 $stop;

Дальше добавим модель тактового генератора (как же мне не хватает замечательного генератора, который делали старинные Квартусы! Там можно было задать частоту в мегагерцах и не думать о пересчёте её в период, а тем более полупериод).
  always   begin      clk = 0;      #5;      clk = 1;      #5;  end

Теперь нам надо послать несколько байт данных. Проще всего это сделать прямо в секции initial, но если я буду прописывать там каждую фазу доступа к шине, код в этой секции станет запутанным. Поэтому я сделаю такую задачку (именно она выступает в роли модели шины):
task SendByte (input reg[7:0] D);    begin        data = D;        we = 1;        @(posedge clk);        #1        we = 0;   endendtask

Ну, и впишу назначение констант и вызов циклов работы с шиной в блок initial. Напоминаю, что запись типа #123 означает ждать 123 единицы времени. У нас это наносекунды. Также напоминаю, что так как присвоения идут последовательно, используем операцию равно, а не стрелка. Итого, имеем следующий основной код тестирования:
Смотреть здесь
  initial  begin     sof = 0;     we = 0;     data = 0;     #13;     // Первый байт кадра     sof = 1;     SendByte (1);     // Остальные байты     sof = 0;     SendByte (5);     SendByte (1);     // А тут мы промоделируем небольшую задержечку     #20;     SendByte (1);  end


Итого, у нас полный код модуля приобрёл такой вид:
Смотреть полный код модуля.
`timescale 1ns / 1nsmodule parazit_tb  ;    reg    sof   ;   reg    we   ;   wire  [15:0]  sum   ;   reg  [7:0]  data   ;   reg    clk   ;   sum     DUT  (        .sof (sof ) ,      .we (we ) ,      .sum (sum ) ,      .data (data ) ,      .clk (clk ) );   always   begin      clk = 0;      #5;      clk = 1;      #5;  endtask SendByte (input reg[7:0] D);    begin        data = D;        we = 1;        @(posedge clk);        #1        we = 0;   endendtask// "Constant Pattern"// Start Time = 0 ns, End Time = 100 us, Period = 0 ns  initial  begin     sof = 0;     we = 0;     data = 0;     #13;     // Первый байт кадра     sof = 1;     SendByte (1);     // Остальные байты     sof = 0;     SendByte (5);     SendByte (1);     // А тут мы промоделируем небольшую задержечку     #20;     SendByte (1);  endendmodule



Завершение подготовки тестового набора


Пришла пора добавить этот текст к тестовому набору. Для этого идём в уже известный нам диалог



Но теперь наш набор не создаём, а выбираем в списке. В будущем список будет расти по мере добавления наборов Выбрав, нажимаем кнопку Edit. Я внёс в настройки три правки:
  1. Добавил файл parazit_tb.v в список.
  2. Так как файле parazit_tb.v модуль верхнего уровня имеет имя parazit_tb (можете убедиться, глянув исходник из предыдущего раздела), я вписал это имя в строку Top level module in test bench.
  3. Я сказал вести моделирование в течение 10 микросекунд, после чего приостановиться. Если что я домоделирую через нажатие кнопок ручного управления.




Итого


Закрываем всё. Снова запускаем ModelSim. Видим, что всё работает верно. Данные приходят и учитываются в сумме. Если же на такте нет данных (we в нуле) сумма не увеличивается.



Как пользоваться самой средой моделирования это тема на несколько статей. Причём скорее в видеоформате. Но в целом мы познакомились с методикой быстрой подготовки и запуска тестов на языке Verilog из среды Quartus.

Теперь, зная, как быстро запустить моделирование, мы можем набросать модели среды для нашей головы USB-анализатора и проверять её работу. При этом мы не запоминали ни одного заклинания ModelSim, так как Квартус позволяет всё настроить при помощи мышки. Все необходимые скрипты он генерит сам и среду ModelSim вызывает тоже сам. Базу для модели нам также создали в автоматическом режиме, хоть её и пришлось затем доработать вручную.

Увы и ах. Один из элементов внешней среды модуль ULPI. Чтобы разработать его модель самостоятельно, надо, во-первых, тщательно разобраться в логике работы той микросхемы. А в предыдущей статье я говорил, что она очень заковыристая. Ну и, во-вторых, надо затратить уйму времени на разработку кода модели. И устранение ошибок в нём Понятно, что проще найти что-то готовое. Но готовую модельку удалось найти только на языке SystemC. Поэтому в следующей статье мы будем учиться моделировать систему с использованием этого языка.
Подробнее..

Моделирование прошивки в среде ModelSim с использованием моделей на языке SystemC

29.09.2020 14:15:47 | Автор: admin
В прошлой статье мы познакомились с процессом моделирования прошивки в среде ModelSim, где и целевой код, и генератор тестовых воздействий написаны на языке Verilog. Жаль, но для решаемой в цикле цели этого недостаточно. Я уже многократно продвигал идею, что разработка для комплекса Redd должна идти с наименьшими трудозатратами. Если модель устройства пишется быстро, её можно написать с нуля. В прошлый раз мы сделали модель шины, по которой писали байты в сумматор. Но ULPI очень сложная вещь. Написать её модель с нуля ой, как не просто. Если можно найти готовую, лучше это сделать. И я нашёл Увы и ах, она оказалась на языке SystemC. Как начать работать с этим языком, мы сейчас и рассмотрим.




Предыдущие статьи цикла
  1. Разработка простейшей прошивки для ПЛИС, установленной в Redd, и отладка на примере теста памяти
  2. Разработка простейшей прошивки для ПЛИС, установленной в Redd. Часть 2. Программный код
  3. Разработка собственного ядра для встраивания в процессорную систему на базе ПЛИС
  4. Разработка программ для центрального процессора Redd на примере доступа к ПЛИС
  5. Первые опыты использования потокового протокола на примере связи ЦП и процессора в ПЛИС комплекса Redd
  6. Веселая Квартусель, или как процессор докатился до такой жизни
  7. Методы оптимизации кода для Redd. Часть 1: влияние кэша
  8. Методы оптимизации кода для Redd. Часть 2: некэшируемая память и параллельная работа шин
  9. Экстенсивная оптимизация кода: замена генератора тактовой частоты для повышения быстродействия системы
  10. Доступ к шинам комплекса Redd, реализованным на контроллерах FTDI
  11. Работа с нестандартными шинами комплекса Redd
  12. Практика в работе с нестандартными шинами комплекса Redd
  13. Проброс USB-портов из Windows 10 для удалённой работы
  14. Использование процессорной системы Nios II без процессорного ядра Nios II
  15. Практическая работа с ПЛИС в комплекте Redd. Осваиваем DMA для шины Avalon-ST и коммутацию между шинами Avalon-MM
  16. Разработка простейшего логического анализатора на базе комплекса Redd
  17. Разработка логического анализатора на базе Redd проверяем его работу на практике
  18. Делаем голову шинного USB-анализатора на базе комплекса Redd
  19. Моделируем поведение Quartus-проекта на Verilog в среде ModelSim


Вообще, эта статья в виде DOC файла появилась ещё в июне. Тогда был написан блок из пяти статей одновременно. Но выгрузить DOC файл на Хабр та ещё задача. Поэтому так вышло, что время именно на неё появилось только сейчас (а ещё две томятся в ожидании). При выгрузке, я заметил, что если не пропитаться духом предыдущих статей, эта выглядит каким-то занудством. Поэтому, если есть такое желание освежите в памяти хотя бы прошлую статью, а лучше эти две (Делаем голову шинного USB-анализатора... и Моделируем поведение Quartus-проекта...).

Введение


Итак, готовая модель, где её взять? Есть проект, решающий точно такую же задачу, что и анализатор, который мы разрабатываем, но имеющий пару особенностей. Первая особенность он для ПЛИС Xilinx. Вторая он совершенно не документирован. Как-то работает. Можно даже купить готовую макетную плату, залить в неё готовый двоичный код И получить какую-то функциональность. Кому нужен прибор любой ценой, может просто пойти по этому пути. Но как его развивать не знает никто. Тот проект лежит тут . В каталоге \ulpi_wrapper\testbench лежит комплект файлов для тестирования подсистемы обёртки вокруг ULPI. Там рекомендуют вести моделирование в среде Icarus Verilog, но я порылся и не нашёл на поверхности путных описаний, как это делать на языке SystemC. Поэтому решил продолжить работу в среде ModelSim. Если бы я знал, чем это кончится Но я не знал. Поэтому начал исследования. По ходу изложения будут показаны как успехи, так и неудачи. Начнём с неудач, чтобы все видели, как не стоит делать.

Неудачная попытка сделать всё в лоб


Сначала я решил взять и прогнать через моделирование готовый пример. Привычным движением руки (а руку мы набивали в прошлой статье), я создал тестовый набор, содержащий файлы на Verilog и SystemC. У меня вышло как-то так:



Запускаю ModelSim и не вижу в группе work ничего, что было бы связано с SystemC. Верилоговский код вижу, а Сишный нет.



Если посмотреть на логи, то видно, что его и не пытались собирать. В чём дело?



Полезная информация про настройку файла *.do


Известно, что для запуска ModelSim используется файл *.do. Но как любитель всё делать мышкой, я никогда не заглядывал ему внутрь. Давайте его поищем и откроем! В каталоге проекта находится только один такой файл. Наверное, это то, что нам нужно.



Открываем его. В начале сборка всяких служебных вещей и файлов, входящих в проект.
Смотреть текст
transcript onif ![file isdirectory verilog_libs] {file mkdir verilog_libs}if ![file isdirectory vhdl_libs] {file mkdir vhdl_libs}vlib verilog_libs/altera_vervmap altera_ver ./verilog_libs/altera_vervlog -vlog01compat -work altera_ver {c:/intelfpga_lite/17.1/quartus/eda/sim_lib/altera_primitives.v}vlib verilog_libs/lpm_vervmap lpm_ver ./verilog_libs/lpm_vervlog -vlog01compat -work lpm_ver {c:/intelfpga_lite/17.1/quartus/eda/sim_lib/220model.v}vlib verilog_libs/sgate_vervmap sgate_ver ./verilog_libs/sgate_vervlog -vlog01compat -work sgate_ver {c:/intelfpga_lite/17.1/quartus/eda/sim_lib/sgate.v}


А вот в конце явно сборка нужных нам вещей, это я сужу по имени файла ulpi_wrapper.v:
vlog -vlog01compat -work work +incdir+C:/Work/UsbHead1/SystemCPlay {C:/Work/UsbHead1/SystemCPlay/ulpi_wrapper.v}vsim -t 1ps -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_lnsim_ver -L cycloneive_ver -L rtl_work -L work -L UsbHead1 -voptargs="+acc"  lalalaadd wave *view structureview signalsrun 10 us

Действительно. Есть сборка Verilog-овского модуля, и нет никаких намёков на сборку модулей на SystemC. Жаль только, что этот DO-файл автоматически создаётся при каждом запуске моделирования, так что просто взять и отредактировать его не получится. Его создаёт очень сложный TCL-скрипт. Править его нет никакого желания. Но после статьи про весёлую квартусель, наверное, понятно, что такая мелочь не повод опускать руки. Наверняка, всё уже есть. Жаль только, что в документации сказано, что вы можете сделать скрипт так, а можете так, и нет никаких намёков на примеры. Ну что ж, давайте выводить всё экспериментальным путём. Создаём файл C:\Work\UsbHead1\SystemCPlay\myrun.do и пытаемся передать ему управление. Сначала пробуем это сделать так:



Основной DO-файл всё равно продолжает вырабатываться, но его концовка становится такой:
vlog -sv -work UsbHead1 +incdir+C:/Work/UsbHead1/UsbHead1/synthesis/submodules {C:/Work/UsbHead1/UsbHead1/synthesis/submodules/UsbHead1_master_0_b2p_adapter.sv}vlog -sv -work UsbHead1 +incdir+C:/Work/UsbHead1/UsbHead1/synthesis/submodules {C:/Work/UsbHead1/UsbHead1/synthesis/submodules/UsbHead1_master_0_timing_adt.sv}vlog -vlog01compat -work work +incdir+C:/Work/UsbHead1/SystemCPlay {C:/Work/UsbHead1/SystemCPlay/ulpi_wrapper.v}vsim -t 1ps -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_lnsim_ver -L cycloneive_ver -L rtl_work -L work -L UsbHead1 -voptargs="+acc"  lalalado C:/Work/UsbHead1/SystemCPlay/myrun.do

Мы видим, что Verilog файл по-прежнему компилится, далее по-прежнему запускается процесс моделирования (правда, я-то это видел при пробных запусках, но теперь могу точно сказать, что команда vsim запускает этот процесс), после чего управление передаётся нашему скрипту. Этот скрипт должен процессом отображения управлять. Но сборкой мы управлять по-прежнему не можем. Если собранных файлов не хватает, система отвалится по ошибке раньше, чем нам дадут что-либо сделать. Ну и отлично, пробуем последний вариант настройки.



И тут начинается самое интересное. Оно настолько важно, что я возьму это в рамочку.

Выбираю скрипт, а он не выбирается. Вхожу в настройку (у меня предыдущий выбранный вариант). Выбираю, а не выбирается. И так хоть до посинения. Пока я это заметил, пока нашёл, как победить вечер убил! Оказалось, если просто выбрать файл, кнопка Apply останется серой. И изменения не будут запомнены. Надо обязательно через правку других параметров диалога добиться, чтобы кнопка Apply почернела! На рисунке выше, она именно чёрная. Если останется серой, изменения не сохранятся, и на использование скрипта всё не перенастроится.


Скрипт всё равно формируется, но его концовка стала более удобной для нас.
vlog -sv -work UsbHead1 +incdir+C:/Work/UsbHead1/UsbHead1/synthesis/submodules {C:/Work/UsbHead1/UsbHead1/synthesis/submodules/UsbHead1_master_0_timing_adt.sv}do "C:/Work/UsbHead1/SystemCPlay/myrun.do"

Наконец-то процесс сборки исходников для проекта полностью отдан нам на откуп! Замечательно! На тот момент я смог найти только документ SystemC Verification with ModelSim, написанный для Xilinx. Но ModelSim, он и в Африке ModelSim. Пользуясь примерами из этого документа и образцами DO-файла, созданного на прошлых опытах, я сделал следующий текст скрипта (не пугайтесь обилию ключей, ниже мы почти все выкинем, абсолютные пути тоже потом заменим на относительные, на этом этапе я просто всё дергал из примеров и автоматически сгенерённых образцов).
vlog -vlog01compat -work work +incdir+C:/Work/UsbHead1/SystemCPlay {C:/Work/UsbHead1/SystemCPlay/ulpi_wrapper.v}vlib sc_worksccom g I C:/intelFPGA_lite/17.1/quartus/cusp/systemc/include work sc_work C:/Work/UsbHead1/SystemCPlay/ulpi_driver.cpp

Барабанная дробь И ModelSim нам заявляет:



Если опустить все неприличные слова, то мне и сказать-то нечего Но такой путь пройден! И где взять другую модель ULPI? Разумеется, я договорился с иностранными знакомыми, профессионально занимающимися серьёзными проектами для ПЛИС. Специально для меня они открыли на выходные удалённый доступ к машине с лицензионным ModelSim. Второй блин также оказался комом: 64-битная версия даже в лицензионном виде не работает с SystemC. Но в конце концов, мне удалось поиграть с 32-битной версией лицензионного ModelSim. Поэтому продолжаем рассказ

Пара слов о документации


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

C:\modeltech_10.2c\docs\pdfdocs документация, включая файлы в формате PDF. Мне понравились файлы modelsim_se_ref.pdf (ModelSim SE Command Reference Manual), modelsim_se_user.pdf (ModelSim SE Users Manual) и modelsim_se_tut.pdf (ModelSim SE Tutorial). По самому языку там мало что есть, но по тому, как подключать файлы и как решать проблемы диалектов вполне.

Дальше, полезный каталог C:\modeltech_10.2c\examples. Там есть примеры готовых файлов *.do и готовых файлов cpp и h. Самый полезный для нас пример C:\modeltech_10.2c\examples\systemc\vlog_sc. В нём показано, как обращаться из Verilog кода к коду на SystemC. Мы, в итоге, пойдём именно этим путём.

В каталоге C:\modeltech_10.2c\include\systemc содержатся исходные коды библиотеки типов языка. Неплохой справочник. Как говорится, на безрыбье и рак рыба.

Из каталогов всё. Теперь название замечательной книги, из которой можно узнать многое как о языке, так и о методике программирования на нём. SystemC From the Ground Up, Second Edition. Авторы David C. Black, Jack Donovan, Bill Bunton, Anna Keist.

Диалекты языка SystemC


Итак. Получив доступ к работающей системе, я радостный собрал проект, согласно ранее созданному скрипту. Он собрался без ошибок! Первая моделька с ГитХаба согласилась работать с нами! Желая прогнать эталонный тест, я добавил в проект файл ulpi_wrapper_tb.cpp из того же каталога и получил массу ошибок. Допустим, ошибку в строке:
m_vpi_handle = vpi_handle_by_name((const char*)name, NULL);
поправить сложно, но ещё можно. Но строка
        // Update systemC TB        if(sc_pending_activity())            sc_start((int)(time_value-m_last_time),SC_NS);

навевала плохие мысли. Функция sc_pending_activity() в библиотеках отсутствует. Имеется функция sc_pending_activity_at_current_time(), но с нею я даже разбираться не стал. Вместо тысячи слов объяснения, приведу дамп:



И файлов с таким текстом (*.exe, *.dll и т. п.) нашлось 44 штуки.

Можно было попытаться всё переписать Но надо ли оно? Напомню, я вообще-то начал всё это, так как хотел воспользоваться всем готовым. Разработать я всё и в бесплатной среде на чистом SystemVerilog могу, если уж тратить кучу времени Я шёл сюда, чтобы время не потратить, а сэкономить! Но на самом деле Главное не забыть, что мы делаем. Мы хотим воспользоваться моделькой шины ULPI. Она собралась. Проблемы возникли при попытке собрать полную тестовую систему из примера А зачем это? Ну не работает полная система, и ладно. Будем осваивать одну модельку, не глядя на работу системы, методом проб и ошибок.

Устраняем непонимание, основанное на диалектах


Итак. Мы будем делать смешанную систему. Модуль с моделью будет написан на языке SystemC, а тестовые воздействия ему и разрабатываемому модулю я буду подавать на языке Verilog. То есть, надо добиться появления модуля ulpi_driver в группе work.

Осматривая примеры файлов *.do из поставки ModelSim, я сильно упростил скрипт, и в итоге, сделал такой:
vlog +../../SystemCPlay {../../MyCores/ULPIhead.sv}sccom -g ../../SystemCPlay/ulpi_driver.cppsccom -link

Ошибок нет, но и модуль в группе не появился. Осматривая файлы примера (напомню, лучший пример, реализующий именно такое смешивание языков есть в каталоге C:\modeltech_10.2c\examples\systemc\vlog_sc), я понял, что в конец файла ulpi_driver.cpp надо добавить строчку:
SC_MODULE_EXPORT(ulpi_driver);

Документация на ModelSim говорит, что это особенности диалекта. И вуаля! Вот он, наш модуль:



Правда, для него меню Create Wave (это меню мы рассматривали в прошлой статье) недоступно. И портов у него нет. Исторически я сначала разбирался с портами, но методически я отложу рассказ про них на потом. Иначе придётся дважды править код. Чтобы этого не делать, сначала проведём небольшую подготовку.

Делаем тактовый генератор


Оказалось, что у модели есть пара отличий от настоящего ULPI. Первое отличие состоит в том, что тактовый сигнал 66 МГц должен вырабатывать чип. А что мы видим в модели?
    sc_in<bool>             clk_i;

Непорядок! Начинаем переделку! Все работы, если не указано иное, ведём в файле ulpi_driver.h.
Заменяем тип порта. Было:
    sc_in<bool>             clk_i;

стало (я ещё и имя порта поменял):
    sc_inout<bool>             clk;

Из книжки я узнал, что настоящий генератор вставляется путём добавления переменной:
    sc_clock oscillator;

Параметры задаём в конструкторе. В итоге, конструктор приобретает вид:
    //-------------------------------------------------------------    // Constructor    //-------------------------------------------------------------    SC_HAS_PROCESS(ulpi_driver);    ulpi_driver(sc_module_name name): sc_module(name),                                      m_tx_fifo(1024),                                       m_rx_fifo(1024),                                      oscillator ("clk66",sc_time(15,SC_NS))    {

Последняя строка как раз для этого. При желании, можно даже уже запустить моделирование, дважды щёлкнуть по модулю usb_driver, дальше вытянуть clk66 на времянку и немного прогнать процесс моделирования. Мы уже видим, как работает генератор:



Не забудем поменять имя тактового сигнала и в месте, где стартует основной поток. Было:
        SC_CTHREAD(drive, clk_i.pos());

Стало:
        SC_CTHREAD(drive, clk.pos());


Внутренние связи заменены. А вот как красиво вывести сигнал наружу, я не нашёл. Возможно, мне просто не хватает квалификации. Но так или иначе, а все попытки вытянуть порт наружу, не увенчались успехом. Всегда что-то мешало. Я даже нашёл на одном форуме обсуждение, где автору нужно было сделать то же самое. Коллектив решил, что можно пробросить только на входные порты. Но нам же надо на выход! Поэтому делаем так.

Добавляем под конструктором функцию потока:
    void clkThread(void)     {       while (true)       {           wait(oscillator.posedge_event());           clk.write (true);           wait(oscillator.negedge_event());           clk.write (false);       }    }

И добавляем ссылку на неё в конструктор класса:
        SC_THREAD(clkThread);

Давайте я покажу текущий район конструктора, чтобы было целостное видение текущего результата:
    SC_HAS_PROCESS(ulpi_driver);    ulpi_driver(sc_module_name name): sc_module(name),                                      m_tx_fifo(1024),                                       m_rx_fifo(1024),                                      oscillator ("clk66",sc_time(15,SC_NS))    {        SC_CTHREAD(drive,clk.pos());        SC_THREAD(clkThread);        m_reg[ULPI_REG_VIDL]    = 0x24;        m_reg[ULPI_REG_VIDH]    = 0x04;        m_reg[ULPI_REG_PIDL]    = 0x04;        m_reg[ULPI_REG_PIDH]    = 0x00;        m_reg[ULPI_REG_FUNC]    = 0x41;        m_reg[ULPI_REG_OTG]     = 0x06;        m_reg[ULPI_REG_SCRATCH] = 0x00;    }    void clkThread(void)     {       while (true)       {           wait(oscillator.posedge_event());           clk.write (true);           wait(oscillator.negedge_event());           clk.write (false);       }    }

Всё. Первая правка завершена.

Делаем двунаправленную шину данных


У ULPI двунаправленная шина данных. А в модели мы видим следующее её описание:
    sc_out <sc_uint<8> >    ulpi_data_o;    sc_in  <sc_uint<8> >    ulpi_data_i;

Непорядок! Сначала мы сделаем заготовку на базе выходной шины, а затем переключим всё на неё. С чего начать? С того, что шина должна уметь переходить в третье состояние, а тип sc_uint<8> работает только с двоичными данными. Нам поможет тип sc_lv<8>. Поэтому меняем объявление шины на:
    sc_inout <sc_lv<8> >    ulpi_data_o;

Теперь переходим в файл ulpi_driver.cpp и там ищем все обращения к шине ulpi_data_o. Интуитивно я понял, что поправить надо только одно место:


То же самое текстом.
void ulpi_driver::drive_input(void){    // Turnaround    ulpi_dir_o.write(false);    ulpi_nxt_o.write(false);    ulpi_data_o.write(0x00);    wait(oscillator.posedge_event());}



Меняем выделенную строку на
    ulpi_data_o.write("ZZZZZZZZ");

Всё. Теперь можно вместо двух строк:
    sc_inout <sc_lv<8> >    ulpi_data_o;    sc_in  <sc_uint<8> >    ulpi_data_i;

написать одну:
    sc_inout <sc_lv<8> >    ulpi_data;

и заменить все ссылки на старые переменные как в h-нике, так и в cpp-шнике на ссылки на переменную ulpi_data.

Добавляем псевдонимы для портов


Итак. После долгих поисков я пришёл к выводу (возможно, ошибочному), что в среде ModelSim просто взять и увидеть порты для отдельно лежащего модуля на SystemC средствами GUI, не судьба. Однако, если этот модуль вставить в тестовую систему, они появятся. Но пока рылся с теорией, нашёл, как красиво задать псевдонимы для имён портов. Итоговый конструктор класса стал выглядеть так:
    SC_HAS_PROCESS(ulpi_driver);    ulpi_driver(sc_module_name name): sc_module(name),                                      m_tx_fifo(1024),                                       m_rx_fifo(1024),                                      oscillator ("clk66",sc_time(15,SC_NS)),                                      rst_i ("rst"),                                           ulpi_data ("data"),                                      ulpi_dir_o ("dir"),                                      ulpi_nxt_o ("nxt"),                                      ulpi_stp_i ("stp")    {        SC_CTHREAD(drive,clk.pos());        SC_THREAD(clkThread);        m_reg[ULPI_REG_VIDL]    = 0x24;        m_reg[ULPI_REG_VIDH]    = 0x04;        m_reg[ULPI_REG_PIDL]    = 0x04;        m_reg[ULPI_REG_PIDH]    = 0x00;        m_reg[ULPI_REG_FUNC]    = 0x41;        m_reg[ULPI_REG_OTG]     = 0x06;        m_reg[ULPI_REG_SCRATCH] = 0x00;    }

Делаем тестовую систему


Ну что же. Сделать всё на автомате, чтобы сразу два отлаживаемых модуля (голова анализатора и модель шины ULPI) сами запрыгнули в тестовый файл, у меня не получилось. Но сделаем хотя бы тест для головы, а потом добавим к нему ULPI. Пользуясь методикой из прошлой статьи, я сделал тестовую систему для файла ULPIhead.sv. Файл я назвал sim1.v и тут же переименовал его в sim1.sv.

После чего ручками добавил туда модуль ulpi_driver. Итоговый скрипт myrun.do выглядит так:
vlog +../../SystemCPlay {../../MyCores/ULPIhead.sv}sccom -g ../../SystemCPlay/ulpi_driver.cppsccom -linkvlog +../../SystemCPlay {../../SystemCPlay/sim1.sv}vsim -voptargs="+acc" sim1

Последняя строка вымученная. Без неё не было портов у Verilog кода. Изменяя параметры оптимизации, мы устраняем эту беду. Её я подсмотрел в том файле *.do, который был создан для моделирования нашей системы в самом начале, когда всё ещё делалось на автомате. Правда, там строка длиннющая. Я просто нашёл тот ключ, который решает проблему, и скопировал его. А так не люблю длинных строк, всё лишнее я выкинул.

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

У меня получился такой тест.
Смотреть текст.
`timescale 1ns / 1nsmodule sim1  ;    reg    ulpi_dir   ;   wire   source_valid   ;   wire    ulpi_stp   ;   reg    ulpi_clk   ;   reg    ulpi_nxt   ;   reg    reset_n   ;   reg    read   ;   reg  [31:0]  writedata   ;   wire    ulpi_rst   ;   reg    clk   ;   wire  [7:0]  source_data   ;   reg    write   ;   wire  [7:0]  ulpi_data   ;   reg    source_ready   ;   reg  [1:0]  address   ;   wire  [31:0]  readdata   ;   always   begin     clk = 1;     #5;     clk = 0;     #5;  end  ULPIhead  DUT    (       .ulpi_dir (ulpi_dir ) ,      .source_valid (source_valid ) ,      .ulpi_stp (ulpi_stp ) ,      .ulpi_clk (ulpi_clk ) ,      .ulpi_nxt (ulpi_nxt ) ,      .reset_n (reset_n ) ,      .read (read ) ,      .writedata (writedata ) ,      .ulpi_rst (ulpi_rst ) ,      .clk (clk ) ,      .source_data (source_data ) ,      .write (write ) ,      .ulpi_data (ulpi_data ) ,      .source_ready (source_ready ) ,      .address (address ) ,      .readdata (readdata ) );   ulpi_driver ULPI  (      .clk (ulpi_clk),      .rst (ulpi_rst),      .data (ulpi_data),      .dir (ulpi_dir),      .nxt (ulpi_nxt),      .stp (ulpi_stp)  );  initial  begin     reset_n  = 1'b0;     source_ready = 1;     writedata = 0;     address = 0;     read = 0;     write = 0;     #20     reset_n  = 1'b1;  endendmodule





Заключение


Худо-бедно, но мы освоили моделирование на языке SystemC с использованием системы ModelSim. Правда, оказалось, что для этого необходимо иметь доступ к лицензионной 32-битной версии. Свободная версия и лицензионная 64-битная версия такой возможности не дают. Как я понял, совершенно бесплатно всё можно сделать в системе Icarus Verilog, но как именно этого достичь, не разобрался. Мне оказалось проще получить доступ к требуемому ModelSim. В следующей статье мы воспользуемся полученными знаниями, чтобы провести моделирование нашей головы.

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

Проблемы методологии проектирования микропроцессорных систем

24.12.2020 16:21:15 | Автор: admin

Применяемая, в настоящее время, для проектирования СБИС, методология с использованием языков описания аппаратуры, обладает общепризнанными недостатками, а именно:

  • Разработка сложных СБИС требует сотни квалифицированных инженеров, несколько лет работы и затрат в миллиарды долларов.

  • До половины времени разработки, уходит на поиск и устранение ошибок в программной модели проектируемого микропроцессора.

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

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

Программы DARPA

В рамках программы Electronics Resurgence Initiative, объявленной DARPA, поставлена задача преодолеть проблемы применяемой методологии проектирования микропроцессорных систем. С этой целью реализуется следующие подпрограммы:

  • CRAFT предполагает создание средств высокоуровневого синтеза СБИС;

  • IDEA направлена на создание автоматического генератора компоновки SoC, многокристальных микросхем, печатных плат;

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

В случае успешного выполнения указанных программ полученные результаты должны быть объединены в перспективных САПР. Предполагается достичь 10 кратного роста в скорости разработки над ручным созданием RTL кода. Кроме того, ставится цель принципиально изменить практику разработки микропроцессорных устройств. Итоговая цель преобразования инженерной практики описана так:

  • Разработка СБИС становится частью инструментария разработчика программного обеспечения.

  • Каждая компания разработчик ПО, получает возможность разрабатывать СБИС.

Описывая ситуацию в отрасли разработки микропроцессорных систем, DARPA говорит о следующих проблемах (на основе Electronics Resurgence Initiative Page 3 Investments Design Thrust). Интеллектуальные системы следующего поколения, в областях искусственного интеллекта, автономных транспортных средств, связи, радиоэлектронной борьбы и радиолокации, потребуют на порядок большей эффективности обработки, чем та, которую предлагает современная коммерческая электроника. Достижение требуемых уровней производительности, потребует разработки очень сложных платформ SoC, использующих самые передовые технологии интегральных схем. В последние годы сложность микросхем быстро росла, произошел взрыв затрат и времени, необходимых для разработки усовершенствованных SoC, печатных плат и многокристальных сборок (SiP). Предлагаемая инициатива создаст новые парадигмы для интеллектуального физического проектирования и уменьшит барьеры для проектирования высокопроизводительных, высокоэффективных специализированных интегральных схем.

Исследовательский подход в программе CRAFT (из доклада Khailany Brucek CRAFT Final на ERI Summit 2019):

  1. Поднять уровень абстракции при разработке аппаратуры:

    a. Использовать языки более высокого уровня, например, C ++ вместо Verilog;

    b. Использовать инструменты высокоуровневого синтеза;

    c. Использовать библиотеки и генераторы, например, MatchLib.

  2. Применение методологии AGILE в разработке СБИС:

    a. Небольшие команды, совместно работая над архитектурой, реализацией, VLSI (Very Large Scale Integration);

    b. Непрерывная интеграция и средства автоматической верификации;

    c. AGILE методы управления проектами;

    d. Круглосуточная генерация из С++ в схему.

Предложен высоко продуктивный подход к разработке цифровых СБИС для создания сложных SoC. Маршрут проектирования включает средства высокоуровневого синтеза, объектно-ориентированные библиотеки синтезируемых компонентов на языках SystemC и С++. И модульный подход к физическому проектированию СБИС основанный на мелкозернистом глобально асинхронном и локально синхронном (GALS) тактировании. Методология проектирования была продемонстрирована на 16-нм тестовом чипе FinFET, предназначенном для машинного обучения и компьютерного зрения. (A Modular Digital VLSI Flow for High-Productivity SoC Design, Khailany et al., DAC 2018)

Spoiler

Рисунок 1. Вариант маршрута проектирования предложенный для программы CRAFT [1].

Программа IDEA состоит из двух этапов. На первом этапе будет создан генератор макетов, для создания автоматизированной платформы проектирования печатных плат, SoC и SiP. На втором этапе должна быть создана программная система, которая на основе функционального описание высокого уровня и заданных ограничений, будет создавать печатные платы и микросхемы правильной конструкции на основе обширной библиотеки коммерческих компонентов (COTS). Примеры компонентов COTS включают в себя коммерчески доступные микросхемы для печатных плат, кристаллы для SiP и IP-модули для SoC. Созданный список цепей, будет поступать на вход генератора макетов, разработанного в первой части программы. Таким образом будет создана сквозная автоматизированная платформа проектирования для печатных плат, SoC и SiP.

Spoiler

Рисунок 2. Схема применение искусственного интеллекта для решения задачи проектирования СБИС, по замыслу программы IDEA [5].

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

Spoiler

Рисунок 3. Концепция маршрута проектирования программы IDEA [5].

В программе POSH предлагается использовать практику разработки ПО с открытым кодом, в области разработки микропроцессорных систем. В документах DARPA говорится следующее. Появление экосистемы программного обеспечения с открытым исходным кодом позволило создать глубокую иерархию программного обеспечения со многими уровнями абстракции, что значительно повысило производительность в проектах разработки сложных программных систем. Модель абстрагирования программного обеспечения позволяет программисту сосредоточить свои усилия в одной конкретной области, игнорируя при этом большое количество деталей, которые в противном случае были бы слишком сложными для обработки. Чтобы создать устойчивую экосистему разработки аппаратуры с открытым исходным кодом, программа POSH создаст технологии, методы и стандарты для эффективной разработки SoC с открытым исходным кодом. Предполагается, что критически важным компонентом этих усилий станет разработка технологий проверки, обеспечивающих доверие к строительным блокам (IP блокам) аппаратного обеспечения с открытым исходным кодом.

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

В рамках обширной экосистемы компонентов с открытым исходным кодом неизвестного качества, существует потребность в технологии, которая могла бы объективно оценить и эффективно дифференцировать высококачественные и низкокачественные блоки IP до включения в проекты SoC. Для широкого распространения также важно, чтобы технология обеспечения доверия на этом уровне требовала от пользователя минимальных (или нулевых) инженерных усилий. Технология формальной гарантии, которая позволяет проводить прямой анализ самого исходного кода, может обеспечить точную оценку качества и помочь стимулировать принятие, более рискованных, компонентов с открытым исходным кодом.

Spoiler

Рисунок 4. Экосистема с открытым исходным кодом для разработки SoC по замыслу программы POSH [4].

Программа POSH предполагает создание необходимых компонентов экосистемы для широкого использования в разработке SoC, блоков с открытым исходным кодом. Главными компонентами экосистемы являются программные средства, обеспечивающие моделирование, верификацию, валидацию, отладку отдельных IP блоков и собранных на их основе SoC.

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

Промежуточные результаты программ, представленные на ERI Summit 2019, подтверждают возможность достижения заявленной цели.

В частности, в рамках программы CRAFT показаны следующие результаты:

  • Снижение трудоёмкости в 8-11 раз для аналоговых и цифровых интегральных схем.

  • Сокращение трудоёмкости в 4.3-5.3 раза при переносе на технологию 16-нм GF, продемонстрированное на цифровых и цифро-аналоговых ASIC.

Однако существует ряд проблем:

  • При обсуждении программы POSH признаётся, что абсолютно безошибочное проектирование невозможно: Качество платформы моделирования, по своей природе, ограничено качеством используемых моделей системы и векторов испытаний, и вполне вероятно, что ряд критических ошибок будет пропущен.

  • Так же отмечается, что для достижения поставленных, программой IDEA, целей потребуется прорыв в алгоритмах машинного обучения и оптимизации. Для чего создаваемая платформа должна использовать прикладные методы машинного обучения для непрерывного развития, по мере появления новых наборов данных (примеров проектирования).

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

  • Кроме того, увеличение уровня абстракции на котором человек описывает проектные решения, создаёт предпосылки для снижение качества проекта по транзисторному бюджету, потребляемой мощности, быстродействию. И в настоящее время нет оснований утверждать, что предлагаемые решения обеспечат незначительное снижение качества, по сравнению с ручным проектированием.

Обратим внимание на методы повышения производительности проектирования, используемые в программах DARPA:

  • При разработке аппаратуры поднять уровень абстракции на котором человек описывает проектируемую систему. Все ниже лежащие уровни абстракции передать автоматическим средствам.

  • Минимизировать участие человека в решении задачи проектирования, за счёт применения средств искусственного интеллекта.

  • Создать условия для повторного использования качественных строительных блоков аппаратного обеспечения.

Парадигма проектирования остаётся прежней использование языков программирования для описания проектируемой аппаратуры.

Описанные программы опираются на базу существующих решений, готовые к использованию компоненты COTS, IP блоки, программные средства с открытым исходным кодом. Однако, опираясь только на готовые компоненты невозможно достичь высшей производительности или минимального энергопотребления. Разработка новых архитектур, максимально сложных SoC и SiP с использованием новых технологий производства, для этих задач остаётся всё та же методология проектирования, которая критикуется в программах DARPA.

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

В декабре 2019 года компания NVIDIA представила перспективную платформу для самоуправляемых автомашин и роботов [6]. Анонсированный процессор NVIDIA Orin появится не ранее 2022 год. Для обеспечения пятого уровня автономности самоуправляемого автомобиля, потребуется сочетать пару процессоров Orin и два не названных, перспективных, дискретных графических процессора NVIDIA [32]. Уровень быстродействия в этом случае составит до 2000 триллионов операций в секунду, при энергопотреблении до 750 Вт.

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

Другим примером могут служить современные программы создания суперкомпьютеров максимальной производительности, которые реализуют Евросоюз, США, Китай, Япония и другие страны. Эти программы, в частности, реализуемые DARPA и DOE, фокусируются на решении следующих задач [25]:

  • Существенный рост производительности при умеренном энергопотреблении;

  • Повышение надежности аппаратуры и программ;

  • Рост продуктивности программирования.

Увеличение реальной производительности на разных типах задач требует создания принципиально новых SoC и SiP с использованием передовых архитектур и технологий производства, не основанных на ранее использовавшихся решениях.

Например, суперкомпьютер Neocortex, создаётся для задач классов DCIGN и RNN это более точное прогнозирование погоды, анализ геномов, поиск новых материалов и разработка новых лекарств. Предположительно, будет введено в эксплуатацию до конца 2020 года [37]. Он объединяет серверы Cerebras CS-1 и HPE SuperDome Flex в единую систему с общей памятью. Сервер Cerebras CS-1 основан на уникальном процессоре Cerebras. На изготовление одного процессора уходит целая 300-мм пластина [2].

Основой для роста сложности микропроцессорных систем, в ближайшем будущем, станет развитие технологии 3D TSV. Например, концепция, предложенная IBM [16], к настоящему моменту, изготовление микроканалов продемонстрировано в кремнии [22].

Spoiler

Рисунок 5. Концепция построения перспективных микропроцессорных систем предложенная IBM [16].

Краткая характеристика современной методологии, ключевые проблемы

К настоящему времени в проектировании сложных микропроцессорных систем сложилась практика, представленная на рисунке 6, в изложении Brucek Khailany (Director of research, ASIC & VLSI NVIDIA Corporation).

Spoiler

Рисунок 6. Использование языков программирования в проектировании СБИС [1].

В процессе проектирования используются различные языки программирования, как специализированные, в частности языки описания аппаратуры (HDL), так и языки высокого уровня общего назначения.

Создаваемое изделие описывается в виде программы на языке описания аппаратуры (HDL). Созданная программа, обычно, представляет собой модель уровня регистровых передач (RTL), которая должна быть преобразована в логическую схему посредством автоматического синтеза (Logic Synthesis). Далее созданную схему необходимо превратить в описание физического изделия. Для чего выполняется последовательность этапов проектирования, которую принято называть физическое проектирование (physical design).

Упрощённая схема маршрута проектирования СБИС, с выделением этапов физического проектирования, представлена на рис. 7.

Spoiler

Рисунок 7. Типовой маршрут проектирования с выделением этапов физического проектирования СБИС [23].

Здесь важно отметить, что инженерная задача разработки вычислительной системы (SiP) разделена на две задачи с разным понятийным аппаратом. Для логического проектирования - математический понятийный аппарат, а для физического проектирования - понятийный аппарат описывающий физические процессы.

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

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

Взаимосвязи понятий отрасли, в виде распространения задаваемых ограничений, на возможные варианты инженерных решений, показаны на рис. 8.

Spoiler

Рисунок 8. Распространение ограничений на варианты реализации, между понятийными компонентами отрасли вычислительной техники.

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

Трудоёмкость проектирования, например, для NVIDIA Xavier оценивается в 8000 человеко-лет [1].

По слова Jensen Huang, на создание NVIDIA V100 потребовалось несколько тысяч инженеров, ушло несколько лет, при примерной стоимости разработки в 3 миллиарда долларов [7].

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

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

Spoiler

Рисунок 9. Стоимость разработки СБИС в зависимости от их сложности [7].

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

Мышление человека в инженерной практике

Изложенный выше подход к созданию методологии проектирования принято называть инструментальным. То есть методология строится на возможностях инструментов, к работе с которыми человек должен адаптироваться. Возможен и противоположный инструментальному подход к созданию методологии проектирования [30].

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

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

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

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

Для каждой отрасли техники (технической системы) исторически сложилась своя последовательность этапов проектирования, т.е. уровней абстракции на каждом из которых рассматривается некоторая часть задачи проектирования. На каждом уровне абстракции (этапе разработки) используется специфичный набор понятий, описывающий решаемую на данном этапе проблему и её решение.

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

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

Методология проектирования и реализующий её инструментарий, определяют порядок проектирования (последовательность этапов) и набор понятий в которых описывается задача и её решение. Таким образом, методология навязывает инженеру способ решения задачи проектирования, набор понятий, форму их представления, а, в итоге, и определённый стиль мышления. Однако, чрезмерный отрыв от иерархии абстракций и понятийного аппарата отрасли (замена или исключение понятий и уровней абстракции) может привести к потере качества решения и провоцирует человека совершать ошибки.

Происходит это в силу особенности работы человеческой психики в процессе решения инженерных задач.

Мозг (память и мышление) оперирует комплексами, объединяющими все известные субъекту знания о некотором объекте и его ассоциативные связи с другими объектами [20]. То есть визуальное и вербальное представление понятий взаимосвязаны. Но восприятие вербальных и визуальных объектов имеют разные физиологические механизмы и объединяются только на уровне высших психических процессов.

Установлено, что в силу личных особенностей мышления, некоторые люди легче оперируют вербальными понятиями, а другие визуальными [33].

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

В частности, в рамках психологии программирования, установлено, что профессиональные программисты обладают, отличными от представителей других профессий, особенностями мышления. В том числе, развитыми вербальными способностями. То есть быстрее и с меньшей умственной нагрузкой осваивают новые языки, в том числе искусственные, и работают с текстами [17, 27].

Длительная профессиональная деятельность может приводить к психологической профессиональной деформации личности, которая выражается, в частности, в формировании стереотипного мышления о проблематике отрасли и методах работы, используемых при решении задач отрасли [15].

Ошибка - неизбежный результат деятельности человека. Вероятность совершения ошибки определяется многими факторами среди которых сам человек, его физическое и психоэмоциональное состояние, уровень знаний и навыков, а также факторы условий, в которых совершается деятельность [30].

Любая работа ума создаёт умственную нагрузку, которая постепенно утомляет нервную систему, что снижает концентрацию и повышает вероятность ошибки. Умственная нагрузка, при решении одной и той же задачи, может различаться в зависимости от условий в которых человек должен эту задачу решать. Например, слепая игра в шахматы (не глядя на доску) или решение ханойской головоломки в уме (без физического перемещения дисков) создают большую умственную нагрузку, чем те же задачи находящиеся в поле зрения [10, 12].

Мышление в процессе решения задачи проектирования представляет собой последовательность интеллектуальных действий.

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

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

Восприятие информации предполагает установление соответствия между формами представления информации (установлены методологией) и известным человеку понятийным аппаратом отрасли. Тогда возникает понимание условий задачи и выполняется поиск решения.

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

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

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

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

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

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

Дополнительная умственная нагрузка определяется особенностями методологии и реализующих её инструментов (т.е. САПР и др.). Например, необходимость учитывать, в процессе проектирования, понятия отрасли, которые в методологии отсутствуют, создаёт дополнительную умственную нагрузку и повышает вероятность ошибок.

Связь между формой представления понятий и скоростью восприятия понятий (условий задачи), установлена экспериментально. Следствием этого стало введение терминов образный интерфейс, когнитивная графика, экологический интерфейс. Создание таких интерфейсов внедрено в инженерной практике, прежде всего в операторской работе по управлению сложными промышленными объектами [29].

В некоторых отраслях исторически сложилось и внедрено в САПР введение процесса решения в поле зрения, что упрощает восприятие понятий (т.е. процессов и взаимосвязей) и поиск решения.

В качестве примера можно привести отрасль авиастроения. Для примера рассмотрим, выполненный не профессионалом, проект тяжёлого конвертоплана созданный в САПР T-FLEX CAD 16 (см. рис. 10). Проект состоящий из более чем 60000 тел был создан одним инженером, исключительно на основе информации из открытых источников и собственного опыта проектирования. На исследование предметной области ушло около 2-х месяцев и ещё примерно 6 месяцев периодической работы на реальное проектирование [31].

Spoiler

Рисунок 10. Проект летательного аппарата в САПР T-FLEX CAD 16.

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

Описание решения ведётся в визуальной среде, где объекты прямо соответствуют понятиям в которых человек мыслит о задаче. Взаимодействие объектов также легко воспринимается и прямо соответствует физическим процессам отрасли. Промежуточные этапы описания и конечное решение доступны для многократного анализа на предмет улучшения и выявления ошибок. Используемые в проектировании САПР и другие программные средства позволяют наблюдать поведение проектируемых систем и физические процессы (см. рис. 11).

Spoiler

Рисунок 11. Визуализация обтекания модели самолёта в виртуальной аэродинамической трубе.

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

В проектировании СБИС точным решением является описание физической структуры изделия, которая будет создана в процессе изготовления СБИС.

В отличии от приведённого примера отрасли авиастроения, отрасль вычислительной техники, имеет существенно иные физические основания и сложно организованную структуру понятийного аппарата (см. рис. 8), который носит более абстрактный характер.

Источники проблем методологии проектирования

Анализ методологии на предмет поиска источников проблем проведён с точки зрения условий, создаваемых методологией, для работы мышления человека.

Конфликт формы представления понятий

Используемая методология проектирования требует от разработчика представить физическое изделие в форме программы. На всех этапах разработки создаваемое изделие описывается средствами языков программирования, то есть алгоритмическими понятиями, в форме программ (см. рис. 6).

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

Требуется всё многообразие не алгоритмических понятий отрасли, представить их алгоритмическим выражением (средствами используемых языков программирования). В то время, как разработка вычислительных систем и СБИС не тождественна программированию, на уровне понятийного аппарата. Понятийный аппарат программирования лишь подмножество понятийного аппарата отрасли вычислительной техники.

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

На практике установлено, что текст, далеко не всегда, лучшая форма представления понятий, в том числе алгоритмических. Примером могут служить медицинские алгоритмы. Практика показала, что их текстовое описание не лучший способ представления, потому, в настоящее время, используются схемы алгоритмов, в частности визуальный язык ДРАКОН [21].

Разрыв целостности маршрута и понятийного аппарата

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

Применительно к существующей технологии производства СБИС выделяют следующие проблемы логического синтеза [23]:

  • Задержка на межсоединениях становится доминирующей, что требует совмещения этапов sizing/physical/logic synthesis. Логический синтез без учета реализации схемы в кремнии неэффективен.

  • Изготовление транзисторов усложняется, что делает критическим методы проверки, рассеиваемая мощность становится определяющим фактором.

Многие задачи физического проектирования NP-трудны решаются при помощи эвристик, которые, обычно, находят почти оптимальные решения [24]. Необходимо заметить, что при использовании эвристик, важным является наличие на выходе фильтра здравого смысла оценки результата человеком. Без развитых средств визуализации, эффективный контроль качества синтеза человеком не возможен.

Для решения этой проблемы, в программе IDEA, предлагается использовать, на этапе логического синтеза и физического проектирования СБИС, обученные модели искусственного интеллекта (см. рис. 2). При этом результаты должны контролироваться человеком (см. рис. 3).

Любой из HDL является, прежде всего, языком программирования и, как следствие, инструментом более высокого уровня абстракции, чем понятийный аппарат этапа схемотехнического проектирования. Кроме того, языки программирования применяются на всех этапах проектирования, как средство описания решения (см. рис. 6). Тем самым подталкивая человека мыслить лишь об алгоритмах.

Ранее основным средством решения задачи на этапе схемотехнического проектирования являлось схемное проектирование. Не только как способ описания решения, но как метод мышления в понятиях этапа схемотехнического проектирования.

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

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

Разрыв маршрута проектирования (последовательного уточнения решения) выражается, в том числе, в трудностях понимания между разными специалистами. Люди, выполняющие решение задачи на разных её этапах, разделены на уровне навыков, то есть им труднее понять друг друга, в силу владения разным понятийным аппаратом.

Требования к личным навыкам

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

Spoiler

Рисунок 12. Описание двухвходового мультиплексора на языке SystemVerilog [13].

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

Работа с текстом с минимальной умственной нагрузкой требует развитых вербальных навыков, которые формируются далеко не у всех взрослых людей [33].

Для формирования развитых вербальных навыков (навыков работы с текстами), необходимо соответствующее обучение. Установлено, что такие навыки формируются у профессиональных программистов [17, 27]. Но далеко не у всех людей, прошедших профессиональное обучение по специальностям программная инженерия (software engineer) или прикладная математика.

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

Вывод

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

Проблема и предлагаемое решение

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

Известно лишь два подхода к построению методологии проектирования инструментальный и человеко-ориентированный (антропоцентрический).

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

Во-первых, инженерное дело изменчиво, как по условиям задачи, так и по способам её решения. Например, в процессе развития технологий полупроводникового производства изменились характеристики технологии производства СБИС (соотношение задержек на вентилях и проводниках, существенно снизились размеры транзисторов и уровень рассеиваемой мощности), что изменило способы решения задачи проектирования. И в ближайшем будущем, технология производства микропроцессорных систем, неизбежно, претерпит существенные изменения.

Во-вторых, прогресс ИИ (машинного и глубокого обучения) достигнут в условиях постоянного контекста задачи, т.е. компоненты задачи и критерии качества решения со временем не меняются. Но в инженерном деле, в частности в проектировании СБИС контекст задачи изменчив. При том, что качество решения задачи проектирования СБИС оценивается по нескольким критериям, и в разных областях применения СБИС значимость критериев различна. И кроме того, для обучения ИИ требуется большое количество примеров, но в области проектирования СБИС таких примеров может оказаться недостаточно.

В связи с этим, некоторые успехи в достижении поставленных в программах DARPA целей, не дают гарантии дальнейшего роста производительности проектирования на основе предлагаемой парадигмы. Прежде всего, применительно к микропроцессорным системам (SoC & SiP) предельных параметров.

Второй подход для создания методологии проектирования, в отрасли вычислительной техники, ранее не применялся.

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

Автор предлагаемого проекта, наблюдал на практике эффективность схемного проектирования при разработке микроархитектуры микропроцессора. В рамках проекта суперкомпьютера Ангара, под руководством Эйсымонта Л.К., автор участвовал в разработке микропроцессора с массовым параллелизмом, где применялся метод схемного проектирования. Разработка микроархитектуры выполнялась одним человеком, обладавшим большим опытом схемотехнического проектирования. Применение схемного проектирования показало существенно меньшее количество ошибок, допускаемых человеком, при разработке микроархитектуры и схемотехнической реализации функций блоков. При этом, описание уже готовых схем, на языке Verilog, приводило к появлению ошибок, отсутствовавших в схемном описании. Для сравнения, работая в INTEL Corp., автор участвовал в разработке микропроцессора Kevet (проект закрыт). В проектировании RTL модели микроархитектуры, относительно простого микропроцессора, участвовало 6 инженеров. Для верификации RTL кода, только на микроархитектурном уровне, было задействовано 20 человек.

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

Ранее упомянутый пример, проект тяжёлого конвертоплана выполненный в САПР T-FLEX CAD 16, интересен тем, что проектирование ведётся в визуальной среде, дающей явную связь между понятиями, в которых мыслит инженер, и объектами, составляющими проектируемую систему.

Другой пример из области ракетостроения. При создании космической системы Энергия-Буран, для повышения производительности программирования, были созданы проблемно-ориентированные языки, основанные на терминах, понятиях и форме представления алгоритмов управления и испытаний, используемых разработчиками корабля. Такой подход позволил повысить производительность программирования и надёжность программного обеспечения. В дальнейшем, на основе когнитивно-эргономического подхода, был создан графический язык ДРАКОН. Применение его для создание различных ракетно-космических систем доказало эффективность идей, положенных в его основу [14, 28, 39].

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

Литература

1. A modular digital VLSI flow for high-productivity SOC design. Brucek Khailany director of research, ASIC & VLSI NVIDIA CORPORATION. ERI Summit 2018.

2. Cerebras процессор для ИИ невероятных размеров и возможностей. Геннадий Детинич. 20.08.2019. https://3dnews.ru/992698 (дата обращения: 11.11.2020)

3. DARPA мечтает радикально упростить проектирование чипов. 04.07.2018 https://3dnews.ru/972103 (дата обращения 07.05.2019)

4. IDEA & POSH program updates. Andreas Olofsson. DARPA MTO program manager. ERI Summit 2019.

5. Intelligent Design of Electronic Assets (IDEA) & Posh Open Source Hardware (POSH) // Andreas Olofsson Program Manager, DARPA/MTO. Proposers Day Mountain View, CA. 9/22/17.

6. NVIDIA Introduces DRIVE AGX Orin Advanced, Software-Defined Platform for Autonomous Machines. Tuesday, December 17, 2019. https://nvidianews.nvidia.com/news/nvidia-introduces-drive-agx-orin-advanced-software-defined-platform-for-autonomous-machines (дата обращения: 11.11.2020)

7. Silicon Compilers - Version 2.0 // Andreas Olofsson Program Manager, DARPA/MTO. International Symposium on Physical Design. March 25-28, Monterey, CA. 2019.

8. Будылина С.М., Смирнов В.М. Физиология сенсорных систем и высшая нервная деятельность: Учеб. пособие для студ. высш. учеб, заведений. М. 2003.

9. Бухтев А. Методы и средства проектирования систем на кристалле. // Chip News 2003. -4.

10. Гигиена труда : учебник / под ред. Н. Ф. Измерова, В. Ф. Кириллова. -2-е изд., перераб. и доп. -М. : ГЭОТАР-Медиа, 2016.

11. Горбунов В., Елизаров Г., Эйсымонт Л. Экзафлопсные суперкомпьютеры: достижения и перспективы. Открытые системы. СУБД 2013. -07 http://www.osp.ru/os/2013/07/13037342/ (дата обращения 05.11.2013)

12. ГОСТ Р ИСО 10075-2011 Эргономические принципы обеспечения адекватности умственной нагрузки. Основные термины и определения.

13. Дональд Томас. Логическое проектирование и верификация систем на SystemVerilog / пер. с анг. А. А. Слинкина, А. С. Камкина, М. М. Чупилко; науч. ред. А. С. Камкин, М. М. Чупилко. М.: ДМК Пресс, 2019.

14. ДРАКОН [электронный ресурс]: Википедия. Свободная энциклопедия. https://ru.wikipedia.org/wiki/ДРАКОН (дата обращения: 11.11.2020)

15. Ильин Е. П. Дифференциальная психология профессиональной деятельности. СПб.: Питер, 2008. 16. Инновационные суперкомпьютерные технологии и проблемы создания отечественной перспективной элементной базы. / Л.К.Эйсымонт, В.С.Горбунов. Доклад на 5-м Московском суперкомпьютерном форуме. 21 октября 2014 года.

17. Исследование способности к усвоению искусственных языков у программистов. Орел Е. А. ОРГАНИЗАЦИОННАЯ ПСИХОЛОГИЯ. Т. 2. 2. 2012.

18. Как устроена универсальная разумная система? Анохин К.В. Наука о поведении: четыре главных вопроса Школа молодых ученых, 7-13 октября 2015. https://scorcher.ru/articles/images/3678/anokhin.pdf (дата обращения 08.09.2019)

19. Канышев В., Шагурин И. Применение языка SystemC и средств разработки на его основе для проектирования Систем на кристалле. // Chip News 2006. -9.

20. Когнитом: разум как физическая и математическая структура. К.В. Анохин. Семинар Социофизика. 27 сентября, 2016.

21. Космический ДРАКОН. Как заброшенный проект "Роскосмоса" подарил язык литовской медицине. Николай Воронин. BBC NEWS Русская служба. 5 августа 2019. https://www.bbc.com/russian/features-48583773 (дата обращения: 11.11.2020)

22. Кремниевый радиатор с микроканалами с жидкостью в 100 раз лучше металлического. 01.03.2019 https://3dnews.ru/983605 (дата обращения: 05.05.2019)

23. Ложкин С.А., Марченко А.М. Математические вопросы синтеза интегральных схем. [электронный ресурс] http://mk.cs.msu.ru/index.php/Математические_модели_и_методы_синтеза_СБИС (дата обращения: 15.10.2013)

24. Марков И. Проектирование интегральных схем: от разбиения графов до временной оптимизации систем. Лекции на факультете ВМК МГУ. 2013.

25. На пути к экзафлопсному суперкомпьютеру: результаты, направления, тенденции. / Горбунов В.С., Эйсымонт Л.К. Доклад на третьем Московском суперкомпьютерном форуме 01.11.2012

26. Общая психология : учебник для вузов / В. В. Нуркова, Н. Б. Березанская. 3-е изд., перераб. и доп. М. : Издательство Юрайт, 2017.

27. Особенности интеллекта профессиональных программистов. Орел Е. А. BECTH. MOCK. УН-ТА. СЕР. 14. ПСИХОЛОГИЯ. 2007. 2.

28. Параджанов В. Д. Как улучшить работу ума: Алгоритмы без программистов это очень просто! М.: Дело, 2001.

29. Представление информации для поддержки когнитивной деятельности человека-оператора. Анохин А. Н. // Актуальные проблемы психологии труда, инженерной психологии и эргономики. Выпуск 6 / Под ред. А. А. Обознова, А. Л. Журавлева. М.: Издательство Институт психологии РАН, 2014.

30. Применение концепций инженерной психологии к профессиям, традиционно не считающимся операторскими. С.А. Дружилов // Электронный журнал Психологическая наука и образование 1 2011.

31. Проект тяжёлого конвертоплана в T-FLEX CAD 16 (более 60000 тел). T-FLEX CAD. 25.11.2019. https://3dtoday.ru/blogs/topsystems/proekt-tyazhelogo-konvertoplana-v-t-flex-cad-16-bolee-60000-tel (дата обращения: 11.11.2020)

32. Процессор NVIDIA Orin шагнёт за пределы 12-нм технологии с помощью Samsung. Алексей Разин. 19.12.2019. https://3dnews.ru/1000054 (дата обращения: 11.11.2020)

33. Психология мышления. Гурова Л.Л. М.: ПЕР СЭ, 2005.

34. Психология программирования: цели, проблемы, перспективы. Рожников В. А. ОБЩЕСТВО: СОЦИОЛОГИЯ, ПСИХОЛОГИЯ, ПЕДАГОГИКА 3 2014.

35. Стасевич К. Человеческий мозг похудел на 14 миллиардов нейронов. 01.03.2012 года. http://compulenta.computerra.ru/archive/neuroscience/664455/ (дата обращения: 15.10.20013)

36. Стрелков Ю.К. Инженерная и профессиональная психология. 2-е изд. - М.: Издательский центр Академия, 2005.

37. Суперкомпьютер Neocortex: 800 тыс. ядер Cerebras для ИИ. Юрий Поздеев. 09.06.2020. https://servernews.ru/1013005 (дата обращения: 11.11.2020)

38. Финский суперкомпьютер получит 200 тысяч ядер благодаря 7-нм CPU AMD EPYC Rome. 15.12.2018 https://servernews.ru/979696 (дата обращения: 07.06.2019)

39. Энергия Буран [электронный ресурс]: Википедия. Свободная энциклопедия. https://ru.wikipedia.org/wiki/Энергия__Буран (дата обращения: 11.11.2020)

Подробнее..

Google бесплатно изготовит чип на техпроцессе 130нм Skywater Апрель-Июнь 2021

23.04.2021 20:10:29 | Автор: admin

Если вы прочитали мою статью, то вы слышали про технологию Skywater 130nm. Google сделала анонс второй программы Multi-project-wafer, и вы можете произвести свою микросхему за бесплатно. С несколькими оговорками

  • Проект должен быть по технологии 130нм Skywater

  • Проект должен быть доступен всем

  • Проект должен находиться под лицензией из списка одобренных

  • Проект должен быть готов к июню 18 числа 2021-го года.

Несмотря на то, что 130нм очень древняя технология почти 20 летней давности, тем не менее с ней можно разработать самые разнообразные и интересные проекты.

Меня поправили в коментариях

130 нм мэйнстрим в части микроконтроллеров, многих видов автомобильных и высоковольтных силовых микросхем, а вовсе не древняя технология. Не надо все мерить по процессорам для песональных компьютеров, современные 130 нм так же похожи на 130 нм из Pentium III, как Tesla 3 похожа на Ford T. И там, и там четыре колеса, на этом сходство заканчивается.

Для изготовления микросхем Google и её партнер efabless выбрали компанию Skywater Technology Foundry, ранее известную под названием Cypress Semiconductor. Производство первых микросхем будет окончено в декабре.

Ранее бывшую собственным производством Cypress и выделенную в независимую компанию. Сама Cypress продолжает существовать в fabless режиме.

Кхм, меня попровляют в личных сообщениях, спасибо @amartology.

Технология имеет слой для полупроводников и 5 металлических слоёв. Заказчики проектов получат 50 экземпляров в корпусе WCLSP 6x10.

Из проектов прошлого года мне понравился вот этот. Это проект передатчика-приёмника для любительских спутников AMSAT.

Для того чтобы вы смогли разработать свою микросхему гугл также выпустила Process Development Kit доступный по ссылке вот тут. Важно сказать, чтобы гугл вашу микросхему вообще одобрил она должна быть собранна по проекту Caravel user project.

Если у вас есть готовый проект написанный на Verilog, его можно собрать используя OpenLANE.

Подробнее..

Перевод SuperRT чип для рейтрейсинга на Super Nintendo

17.12.2020 12:23:08 | Автор: admin
image

В продолжение темы, представляем вашему вниманию перевод оригинала статьи от Бена Картера.

Ссылки на видео по этой статье:


У меня наконец-то появились результаты работы над проектом, которым я занимался в свободное время примерно около года.

Его идея возникла, когда я пытался придумать интересный проект для изучения Verilog и проектирования FPGA. Мне пришла в голову мысль о создании простого трассировщика лучей (частично я вдохновлялся успехами моего до ужаса умного друга, создавшего собственный GPU). Немного позже (наверно, потому, что мой мозг ненавидит меня и наслаждается придумыванием глупых заданий) всё это превратилось в вопрос: а не будет ли интересно заставить SNES выполнять рейтрейсинг?. Так родилась идея чипа SuperRT.

Я хотел попробовать создать нечто, напоминающее чип Super FX, используемый в таких играх, как Star Fox. SNES в них выполняет игровую логику и передаёт описание сцены чипу в картридже, который занимается генерированием графики. Я намеренно ограничил себя использованием в конструкции единого самодельного чипа, а не ядра ARM на плате DE10 или любых других внешних вычислительных ресурсов.

Окончательные результаты выглядят примерно так:


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


С показанной здесь Super Nintendo (строго говоря, это Super Famicom) снята крышка, чтобы было место для подключения проводов, но во всём остальном она совершенно не модифицирована. К ней подключена печатная плата с копией ужасной игры Pachinko, которую я приобрёл за 100 йен в местном секонд-хенде. ROM игры извлечён и заменён на кабельную муфту. Далее она проходит через набор схем сдвига уровня для преобразования 5 вольт SNES в 3,3 В, а затем подключается к плату разработки DE10-Nano FPGA с Cyclone V FPGA. Платы схем сдвига уровня совершенно ужасны, а их сборка превратилась в кошмар из-за обязательных интегральных схем, которые продаются только в корпусах с поверхностным монтажом. Однако со своей работой они справляются.


Чип SuperRT создаёт сцену при помощи специализированного языка команд, исполняемых одним из трёх блоков паралелльного выполнения кода чипа (по сути, это специализированные процессоры CISC) для расчётов тестов пересечения лучей. Описание сцены позволяет создавать объекты при помощи подмножества операций CSG: в качестве базовых строительных блоков используются сферы и плоскости, а при помощи операций OR, AND и вычитания они применяются для построения нужной геометрии. AABB тоже поддерживаются и в основном используются для тестов усечения (при желании их тоже можно рендерить, но они обладают более низкой точностью позиционирования по сравнению с другими примитивами, поэтому не особо полезны, за исключением задач отладки).


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


Цвет луча для каждого пикселя вычисляется движком лучей, обрабатывающим весь цикл жизни луча; он использует модуль движка исполнения для выполнения управляющей программы, которая описывает сцену столько раз, сколько необходимо для вычисления результатов луча. Сама управляющая программа загружается со SNES и хранится в локальном буфере ОЗУ на 4 КБ анимация реализуется записью в этот буфер модифицированных команд. Дизассемблированный буфер команд выглядит следующим образом:

0000 Start0001 Plane 0, -1, 0, Dist=-20002 SphereSub OH 2, 1, 5, Rad=50003 SphereSub OH 4, 1, 4, Rad=40004 SphereSub OH 5, 1, 9, Rad=90005 SphereSub OH 2, 1, 2, Rad=20006 SphereSub OH -0.5, 1, 2, Rad=20007 RegisterHitNoReset 0, 248, 0, Reflectiveness=00008 Checkerboard ORH 48, 152, 48, Reflectiveness=00009 ResetHitState0010 Plane 0, -1, 0, Dist=-2.1501460011 RegisterHit 0, 0, 248, Reflectiveness=1530012 AABB 4, -2.5, 11,    8, 3.5, 130013 ResetHitStateAndJump NH 440014 Origin 6, 2, 120015 Plane -0.2929688, 0, -0.9570313, Dist=0.24975590016 PlaneAnd OH 0.2919922, 0, 0.9560547, Dist=0.250017 PlaneAnd OH 0, 1, 0, Dist=10018 PlaneAnd OH 0, -1, 0, Dist=40019 PlaneAnd OH -0.9570313, 0, 0.2919922, Dist=-10020 PlaneAnd OH 0.9560547, 0, -0.2929688, Dist=1.4997560021 RegisterHit 248, 0, 0, Reflectiveness=0

Каждый движок исполнения это процессорный модуль с 14-тактным конвейером, и обычно за такт завершается выполнение одной команды, поэтому каждый модуль исполнения может вычислять примерно по 50 миллионов пересечений сфер, плоскостей или AABB. Исключением является то, что операциям ветвления нужно очищать весь конвейер, а следовательно, они тратят 16 тактов (14 тактов на очистку конвейера + 2 тактов задержки на получение команды). Чтобы по возможности избегать этого, используется система прогнозирования ветвления к счастью, часто пространственная связность соседних лучей приводит к высокому уровню совпадения прогнозов.


Пересечения в движке исполнения вычисляются двумя конвейерами один обрабатывает AABB, другой сферы и плоскости. Система в целом работает исключительно с 32-битной целочисленной математикой в формате фиксированной запятой 18.14; если известно, что значения находятся в интервале 1, то используется 16-битный (2.14) формат, а конвейер вычисления пересечений сфер/плоскостей имеет два дополнительных специализированных математических блока, вычисляющих операции обратных значений и квадратного корня.


При рендеринге кадра модуль преобразования PPU превращает буфер кадра в формат, который при помощи DMA можно передать напрямую во VRAM консоли SNES для отображения, ужав его до 256 цветов и заменив его на битовые плоскости тайлов символов. Экран имеет разрешение 200x160, то есть полный кадр занимает ровно 32000 байт данных изображений, которые из-за ограничений пропускной способности передаются во VRAM как два фрагмента по 16000 байт в следующих друг за другом кадрах. Следовательно, полное изображение можно обновлять только раз в два кадра, что ограничивает максимальную частоту кадров 30FPS. Однако тестовая сцена работает с частотой ближе к 20FPS (в основном из-за узких мест на стороне логики SNES).

Большое спасибо участникам этого треда на SNESdev за множество полезных идей о DMA полноэкранного чипа расширения, благодаря которым я придумал описанное мной решение.


В этом чипе также реализовано множество других базовых функций есть интерфейс с шиной картриджа SNES, а также небольшой ROM для программ, содержащий 32 КБ кода для SNES (он ограничен тем, что плата интерфейса пока подключена только к линиям адресной шины A консоли SNES, а поэтому доступное адресное пространство составляет всего 64 КБ, из которых 32 КБ используются для регистров ввода-вывода с отображением в память, применяемых для связи с чипом SuperRT). Также присутствует блок ускорения операций умножения, позволяющий SNES быстро выполнять операции умножения 16x16 бит.


Для отладки я использовал интерфейс HDMI платы DE10, выводя данные на второй монитор, а также геймпад Megadrive, подключённый к контактам GPIO для управления системой отладки. Однако из-за ограниченных ресурсов при включении всех трёх ядер движка лучей отладку приходится отключать.

Вот краткий обзор системы, в будущем я планирую опубликовать новые статьи с более подробным описанием работы отдельных компонентов. Однако если у вас есть вопросы или мысли, то свяжитесь со мной, и я постараюсь ответить!

Огромное спасибо Мэтту, Джеймин, Рику и всем тем, кто помогал советами, вдохновением и поддержкой!





На правах рекламы


Надёжный сервер в аренду и правильный выбор тарифного плана позволят меньше отвлекаться на неприятные уведомления мониторинга всё будет работать без сбоев и с очень высоким uptime!



Подробнее..

Зажигаем светодиодную ленту на базе WS2811 при помощи ПЛИС

29.11.2020 12:04:16 | Автор: admin


Всем привет. Уже почти два года назад я приобрел на aliexpress китайский набор, состоящий из отладочной платы EasyFPGA A2.2, с Cyclone IV EP4CE6E22C8N на борту, ИК пульта SE-020401, программатора, пары USB проводов и шлейфов. Долгое время все это добро лежало у меня без дела, т.к. я никак не мог придумать для себя какой-то интересной и не слишком затратной по времени задачи.


Еще в прошлом году на том-же aliexpress я заказал RGB светодиодную ленту на базе всем известных WS2811 микросхем. Перед покупкой, посмотрев обзор в YouTube на специфический протокол этих микросхем, я решил, что будет интересно написать свой драйвер для них под ПЛИС. А т.к. вышеупомянутая плата на борту имеет фотоприемник, то еще и добавить возможность пощелкать режимы пультиком из комплекта. Такой себе предновогодний проект выходного дня.


Работа с WS2811


По сути, из даташита на WS2811 видно что протокол довольно прост: на вывод DIN микросхемы нужно передать 24 бита данных цвета в формате RGB888 MSB-first. Следующие 24 бита принятых данных микросхема продублирует на выводе DOUT, что позволяет объединять WS2811 в последовательные цепочки:


Схема последовательного подключения WS2811 микросхем:
Схема последовательного подключения WS2811 микросхем


Каждый бит данных кодируется выставлением на выводе DIN логической единицы и логического нуля на определенный промежуток времени. Для значения один это высокий уровень в течении 1.2 s и низкий в течении 1.3 s, а для нуля 0.5 s и 2.0 s соответственно. Из чего видно что общее время передачи одного бита информации в обоих случаях составляет 2.5 s. Для завершения передачи достаточно удерживать низкий уровень более чем 50 s, что послужит сигналом для микросхемы изменить скважность ШИМ на выводах OUTR ,OUTG и OUTB, в соответствии с новым значением цвета.


Тайминги для WS2811:


Тайминги для WS2811


Реализация WS2811 протокола в модуле WS2811Transmitter


WS2811Transmitter.sv
module WS2811Transmitter# (    CLOCK_SPEED = 50_000_000)(    input clkIN,    input nResetIN,    input startIN,    input [23:0] dataIN,    output busyOUT,    output txOUT);localparam DIVIDER_100_NS = 10_000_000; // 1 / 0.0000001 = 10000000reg [4:0]  cnt100ns;reg [24:0] dataShift;reg busy;reg tx;wire [24:0] dataShifted = (dataShift << 1);wire clock100ns;initial begin    busy = 0;    tx  = 0;    cnt100ns = 5'd0;endassign busyOUT = busy;assign txOUT = tx;ClockDivider #(.VALUE(CLOCK_SPEED / DIVIDER_100_NS)) clock100nsDivider (    .clkIN(clkIN),    .nResetIN(busy),    .clkOUT(clock100ns));always @(negedge clkIN or negedge nResetIN) begin    if (!nResetIN) begin        busy <= 0;        tx  <= 0;        cnt100ns <= 5'd0;    end    else begin        if (startIN && !busy) begin            busy <= 1;            dataShift <= {dataIN, 1'b1};            tx <= 1;        end        if (clock100ns && busy) begin            cnt100ns <= cnt100ns + 5'd1;            if (cnt100ns == 5'd4 && !dataShift[24]) begin                tx <= 0;            end            if (cnt100ns == 5'd11 && dataShift[24]) begin                tx <= 0;            end            if (cnt100ns == 5'd24) begin                cnt100ns <= 5'd0;                dataShift <= dataShifted;                if (dataShifted == 25'h1000000) begin                    busy <= 0;                end                else begin                    tx <= 1;                end                     end        end    endendendmodule

Основное здесь это то, что я завел счетчик clock100nsDivider с частотой в 100 ns, и каждый импульс сигнала clock100ns увеличивает значение счетчика cnt100ns на единицу. Переведя сигнал startIN в 1, мы инициируем передачу данных, после чего получаем 1 на сигнале busyOUT. В начале передачи каждого бита на сигнале txOUT выставляется логическая единица, а после 12 отсчетов счетчика cnt100ns для значения один или 5 отсчетов для значения ноль сигнал txOUT переводится в логический ноль. После 25 отсчетов процедура повторяется для следующего бита, пока все 24 бита не будут переданы, что переведет сигнал busyOUT в 0.


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


Временная диаграмма передачи 24 бит данных цвета FF0055h для WS2811Transmitter:


Временная диаграмма передачи 24 бит данных цвета FF0055h для WS2811Transmitter


Работа с ИК пультом


Для передачи данных в нем используется стандартный NEC Infrared Transmission Protocol. Ноль здесь кодируется наличием сигнала в течении 562.5s и паузой в течении 562.5s. Единица 562.5s и 1.6875ms соответственно. Начало передачи данных 9ms сигнал и 4.5ms пауза. А последний импульс в течении 562.5s обозначает окончание передачи.


Весь пакет данных состоит из: стартовой последовательности (9ms сигнал и 4.5ms пауза), 8 бит данных адреса принимающего устройства, 8 бит побитовая инверсия этого адреса, 8 бит команда, 8 бит ее побитовая инверсия и 562.5s импульс окончания передачи. Все данные передаются в формате LSB-first.


Пример передачи пакета данных посредством NEC Infrared Transmission протокола:


Пример передачи пакета данных посредством NEC Infrared Transmission протокола


Реализация NEC протокола в модуле NecIrReceiver


NecIrReceiver.sv
module NecIrReceiver# (    CLOCK_SPEED = 50_000)(    input clkIN,    input nResetIN,    input rxIN,    output dataReceivedOUT,    output [31:0] dataOUT);localparam DIVIDER_281250_NS = 3556; // 562.5s / 2 = 281.25s; 1 / 0.00028125  3556reg [23:0] pulseSamplerShift;reg [33:0] dataShift;reg [31:0] dataBuffer;reg [1:0] rxState;reg rxPositiveEdgeDetect;reg clock281250nsParity;reg clock281250nsNReset;wire clock281250ns;wire startFrameReceived;wire dataPacketReceived;initial begin    rxState = 2'd0;    rxPositiveEdgeDetect = 0;    clock281250nsParity = 0;    clock281250nsNReset = 0;    pulseSamplerShift = 24'd0;    dataShift = 34'd0;    dataBuffer = 32'd0;endassign dataReceivedOUT = rxState[0];assign dataOUT = dataBuffer;assign dataPacketReceived = dataShift[32];assign startFrameReceived = dataShift[33];ClockDivider #(.VALUE(CLOCK_SPEED / DIVIDER_281250_NS)) clock281250nsDivider (    .clkIN(clkIN),    .nResetIN(clock281250nsNReset),    .clkOUT(clock281250ns));always @(posedge clkIN or negedge nResetIN) begin    if (!nResetIN) begin        rxState <= 2'd0;        rxPositiveEdgeDetect <= 0;        clock281250nsParity <= 0;        clock281250nsNReset <= 0;        pulseSamplerShift <= 24'd0;        dataShift <= 34'd0;        dataBuffer <= 32'd0;    end    else begin        case ({dataPacketReceived, rxState[1:0]})            3'b100 : begin                dataBuffer[31:0] <= dataShift[31:0];                rxState <= 2'b11;            end            3'b111, 3'b110 : rxState <= 2'b10;            default : rxState <= 2'd0;        endcase        case ({rxIN, rxPositiveEdgeDetect})            2'b10 : begin                rxPositiveEdgeDetect <= 1;                clock281250nsParity <= 0;                clock281250nsNReset <= 0;                pulseSamplerShift <= 24'd0;                case ({startFrameReceived, dataPacketReceived, pulseSamplerShift})                    26'h0ffff00 : dataShift <= 34'h200000001;                    26'h2000002 : dataShift <= {1'd1, dataShift[31:0], 1'd0};                    26'h2000008 : dataShift <= {1'd1, dataShift[31:0], 1'd1};                    default : dataShift <= 34'd0;                endcase            end            2'b01 : rxPositiveEdgeDetect <= 0;        endcase        if (clock281250nsNReset == 0) begin            clock281250nsNReset <= 1;        end        if (clock281250ns) begin            clock281250nsParity <= ~clock281250nsParity;            if (!clock281250nsParity) begin                pulseSamplerShift <= {pulseSamplerShift[22:0], rxIN};            end        end    endendendmodule

По сути все тайминги здесь делятся на 562.5s. Значит заведем сдвиговый регистр pulseSamplerShift в который будем сэмплировать состояние rxIN через каждые 562.5s. Т.к. захват сигнала нужно производить между его фронтами, то модуль делителя частоты ClockDivider настраивается на частоту в два раза меньшую 281.25s. По импульсу от clock281250ns изменяем значение четности clock281250nsParity, и на все нечетные состояния производим захват. Сигналом rxPositiveEdgeDetect находим передний фронт сигнала, и проверяем состояние pulseSamplerShift на наличие стартового импульса, ноля или единицы.


Временная диаграмма приема пакета данных 00FF0FF0h для NecIrReceiver:


Временная диаграмма приема пакета данных 00FF0FF0h для NecIrReceiver


Основной модуль Main


Main.sv
module Main(    input clkIN,    input nResetIN,    input rxIN,    output txOUT);localparam IR_COMMAND_EQ   = 32'h00ff906f;localparam IR_COMMAND_PLAY = 32'h00ffc23d;localparam IR_COMMAND_PREV = 32'h00ff22dd;localparam IR_COMMAND_NEXT = 32'h00ff02fd;localparam IR_COMMAND_MINS = 32'h00ffe01f;localparam IR_COMMAND_PLUS = 32'h00ffa857;localparam UNITS_NUMBER = 100;localparam PATTERN_COLORS_NUMBER = 128;localparam PATTERNS_NUMBER = 4;localparam CLOCK_SPEED = 50_000_000;localparam UPDATES_PER_SECOND = 20;reg [$clog2(PATTERNS_NUMBER) - 1:0] patternIndex;reg [$clog2(PATTERN_COLORS_NUMBER) - 1:0] colorIndex;reg [$clog2(PATTERN_COLORS_NUMBER) - 1:0] colorIndexShift;reg colorIndexShiftDirection;reg [2:0] colorSwapIndex;reg [$clog2(UNITS_NUMBER) - 1:0] unitCounter;reg txStart;reg pause;reg beginTransmissionDelay;wire ws2811Busy;wire beginTransmission;wire [23:0] colorData;wire [23:0] colorDataSwapped;wire [0:$clog2(PATTERNS_NUMBER * PATTERN_COLORS_NUMBER) - 1] colorIndexComputed;wire irCommandReceived;wire [31:0] irCommand;wire rxFiltered;initial begin    patternIndex = 0;    colorIndex = 0;    colorIndexShift = 0;    colorIndexShiftDirection = 0;    colorSwapIndex = 0;    unitCounter = 0;    txStart = 0;    pause = 0;    beginTransmissionDelay = 0;endassign colorIndexComputed = {patternIndex, (colorIndex + colorIndexShift)};ROM1 rom(    .clock(clkIN),    .address(colorIndexComputed),    .q(colorData));ColorSwap colorSwapper (    .dataIN(colorData),    .swapIN(colorSwapIndex),    .dataOUT(colorDataSwapped));RXMajority3Filter rxInFilter (    .clockIN(clkIN),    .nResetIN(nResetIN),    .rxIN(rxIN),    .rxOUT(rxFiltered));NecIrReceiver #(.CLOCK_SPEED(CLOCK_SPEED))    necIrReceiver (    .clkIN(clkIN),    .nResetIN(nResetIN),    .rxIN(~rxFiltered),    .dataReceivedOUT(irCommandReceived),    .dataOUT(irCommand));ClockDivider #(.VALUE(CLOCK_SPEED / UPDATES_PER_SECOND))    beginTransmissionTrigger (    .clkIN(clkIN),    .nResetIN(nResetIN),    .clkOUT(beginTransmission));WS2811Transmitter #(.CLOCK_SPEED(CLOCK_SPEED))     ws2811tx (    .clkIN(clkIN),    .nResetIN(nResetIN),    .startIN(txStart),    .dataIN(colorDataSwapped),    .busyOUT(ws2811Busy),    .txOUT(txOUT));always @(posedge clkIN or negedge nResetIN) begin    if (!nResetIN) begin        patternIndex <= 0;        colorIndex <= 0;        colorIndexShift <= 0;        colorIndexShiftDirection <= 0;        colorSwapIndex <= 0;        unitCounter <= 0;        txStart <= 0;        pause <= 0;        beginTransmissionDelay <= 0;    end    else begin        if (irCommandReceived) begin            case (irCommand)                IR_COMMAND_PLAY : pause <= ~pause;                IR_COMMAND_EQ   : colorIndexShiftDirection <= ~colorIndexShiftDirection;                IR_COMMAND_NEXT : patternIndex <= patternIndex + 1;                IR_COMMAND_PREV : patternIndex <= patternIndex - 1;                IR_COMMAND_PLUS : colorSwapIndex <= (colorSwapIndex == 3'd5) ? 0 : (colorSwapIndex + 1);                IR_COMMAND_MINS : colorSwapIndex <= (colorSwapIndex == 0) ? 3'd5 : (colorSwapIndex - 1);            endcase        end        if (beginTransmission) begin            unitCounter <= UNITS_NUMBER;            colorIndex <= 0;            case ({colorIndexShiftDirection, pause})                2'b10 : colorIndexShift <= colorIndexShift + 1;                2'b00 : colorIndexShift <= colorIndexShift - 1;            endcase            beginTransmissionDelay <= 1;        end        else if (beginTransmissionDelay) begin            beginTransmissionDelay <= 0;        end        else if (unitCounter != 0 && !ws2811Busy) begin            colorIndex <= colorIndex + 1;            unitCounter <= unitCounter - 1;            txStart <= 1;        end        else begin            txStart <= 0;        end    endendendmodule

Здесь настраиваются все модули и задаются параметры проекта. Вся логика программы заключается в реакции на beginTransmission сигнал, который инициирует передачу новой последовательности цветов на светодиодную ленту. По сигналу irCommandReceived происходит реакция на принятую команду от ИК пульта: остановка бегущих огней, изменение направления, переключения набора цветов и смена варианта перемешивания RGB каналов при помощи ColorSwap модуля.


Наборы цветов хранятся во внутренней памяти EP4CE6E22C8N чипа, называемой M9K Memory Blocks. Эти блоки, в моем случае, настроены как ROM, и организованы 24-х битными словами. Для этого был создан файл формата .mif с данными, и при помощи ROM Megafunction в Quartus сгенерирован ROM.v модуль. При синтезе проекта данные из .mif файла попадают в конечный .sof файл, и загружаются в чип на стадии конфигурации энергонезависимой памяти ПЛИС.


Для генерации набора цветов был написан небольшой скрипт color_patterns_generator.js под Node.js, который и создает rom.mif файл:


color_patterns_generator.js
fs = require("fs");const MODE_REPEAT = "repeat";const MODE_STRETCH = "stretch";const MODE_GRADIENT_STRETCH = "gradient-stretch";const ROM_FILE_NAME = "rom.mif";const COLORS_NUM = 128;const COLORS_PATTERNS = [{        mode: MODE_GRADIENT_STRETCH,        colors: [            0xff0000,            0xff0000,            0xff00ff,            0xff00ff,            0x0000ff,            0x0000ff,            0xff00ff,            0xff00ff,            0xffff00,            0xffff00,            0x00ffff,            0x00ffff,            0x00ff00,            0x00ff00,            0xff0000,        ]    }, {        mode: MODE_STRETCH,        colors: [            0xff0000,            0xff0000,            0xff00ff,            0xff00ff,            0x0000ff,            0x0000ff,            0xff00ff,            0xff00ff,            0xffff00,            0xffff00,            0x00ffff,            0x00ffff,            0x00ff00,            0x00ff00,        ]    }, {        mode: MODE_REPEAT,        colors: [            0xff0000,            0xff0000,            0xff0000,            0xff0000,            0xff0000,            0xff0000,            0xff0000,            0xffffff,            0xff00ff,            0xff00ff,            0xff00ff,            0xff00ff,            0xff00ff,            0xff00ff,            0xff00ff,            0xffffff,            0x0000ff,            0x0000ff,            0x0000ff,            0x0000ff,            0x0000ff,            0x0000ff,            0x0000ff,            0xffffff,            0xff00ff,            0xff00ff,            0xff00ff,            0xff00ff,            0xff00ff,            0xff00ff,            0xff00ff,            0xffffff,            0xffff00,            0xffff00,            0xffff00,            0xffff00,            0xffff00,            0xffff00,            0xffff00,            0xffffff,            0x00ffff,            0x00ffff,            0x00ffff,            0x00ffff,            0x00ffff,            0x00ffff,            0x00ffff,            0xffffff,            0x00ff00,            0x00ff00,            0x00ff00,            0x00ff00,            0x00ff00,            0x00ff00,            0x00ff00,            0xffffff,        ]    }, {        mode: MODE_REPEAT,        colors: [            0xff0000,            0xff0000,            0x00ff00,            0x00ff00,            0xffff00,            0xffff00,            0xff0000,            0xff0000,            0xff0000,            0x00ff00,            0x00ff00,            0x00ff00,            0xffff00,            0xffff00,            0xffff00,            0xff00ff,            0xff00ff,            0xff00ff,            0xff00ff,            0x00ff00,            0x00ff00,            0x00ff00,            0x00ff00,            0xffff00,            0xffff00,            0xffff00,            0xffff00,        ]    }];function getRed(color) {    return ((color >> 16) & 0xff)}function getGreen(color) {    return ((color >> 8) & 0xff)}function getBlue(color) {    return ((color) & 0xff)}function toHex(d) {    let result = Number(d).toString(16).toUpperCase();    return result.length % 2 ? "0" + result : result;}function generate() {    let result = "";    let byteAddress = 0;    result += "WIDTH = 24;                   -- The size of data in bits\n";    result += "DEPTH = " + (COLORS_NUM * COLORS_PATTERNS.length) + ";                   -- The size of memory in words\n";    result += "ADDRESS_RADIX = HEX;          -- The radix for address values\n";    result += "DATA_RADIX = HEX;             -- The radix for data values\n";    result += "CONTENT                       -- start of (address : data pairs)\n";    result += "BEGIN\n";    let red;    let green;    let blue;    for (let pattern of COLORS_PATTERNS) {        for (let i = 0; i < COLORS_NUM; i++) {            if (pattern.mode === MODE_GRADIENT_STRETCH) {                let index = i * (pattern.colors.length - 1) / COLORS_NUM;                let colorA = pattern.colors[Math.floor(index)];                let colorB = pattern.colors[Math.floor(index) + 1];                let colorBValue = index % 1;                let colorAValue = 1 - colorBValue;                red = Math.round(getRed(colorA) * colorAValue + getRed(colorB) * colorBValue);                green = Math.round(getGreen(colorA) * colorAValue + getGreen(colorB) * colorBValue);                blue = Math.round(getBlue(colorA) * colorAValue + getBlue(colorB) * colorBValue);            } else if (pattern.mode === MODE_STRETCH) {                let index = Math.floor(i * pattern.colors.length / COLORS_NUM);                let color = pattern.colors[index];                red = getRed(color);                green = getGreen(color);                blue = getBlue(color);            } else if (pattern.mode === MODE_REPEAT) {                let index = i % pattern.colors.length;                let color = pattern.colors[index];                red = getRed(color);                green = getGreen(color);                blue = getBlue(color);            }            result +=                toHex(i + byteAddress) + " : " +                toHex(red) +                toHex(green) +                toHex(blue) + ";\n";        }        byteAddress += COLORS_NUM;    }    result += "END;";    return result;}try {    fs.writeFileSync(ROM_FILE_NAME, generate());    console.log("Success");} catch (err) {    console.log("Failed\n", err);}

И вот что в итоге получилось из всего этого:



Собственно вот такие пироги.


Ссылка на проект в GitHub

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru