exponenta event banner

Рабочий процесс поколения IP Core с процессором MicroBlaze: Xilinx Kintex-7 KC705

Этот пример показывает, как использовать IP HDL Coder™ Основной Технологический процесс Поколения, чтобы разработать справочный дизайн для частей Xilinx® без встроенного существующего процессора ARM®, но которые все еще используют произведенный интерфейс AXI HDL Coder™, чтобы управлять DUT. А именно, этот пример будет использовать правление Xilinx Kintex-7 KC705, и мягкий процессор MicroBlaze™, управляющий LightWeightIP, (lwIP) базировался, микропрограммный сервер TCP/IP в справочном дизайне, чтобы получить доступ к HDL Coder™ произвел регистры DUT отовсюду в связанной сети. Кроме того, этот пример также выделит различие между доступом к данным из набора регистров, реализованных в виде множества скалярных портов, и набором регистров, реализованных в виде одного векторного порта.

Требования

  • Xilinx Vivado Design Suite с поддерживаемой версией, указанной в документации HDL Coder

  • Плата разработки Xilinx Kintex-7 KC705

  • Пакет поддержки HDL Coder™ для плат Xilinx FPGA

  • Подключение Ethernet

Плата разработки Xilinx Kintex-7 KC705

Примеры ссылочных конструкций

MicroBlaze - это простой, универсальный мягкоядерный процессор, который может использоваться только в платформах Xilinx FPGA, таких как Kintex-7, для выполнения функций полноценного процессора или как гибкий, программируемый IP. Когда программы малы, ELF может сидеть в BRAM и дизайн становится полностью автономным в FPGA. Существует много приложений, хорошо подходящих для реализации на MicroBlaze. Здесь мы перечислим всего несколько:

  1. Удаленное сетевое управление развернутым алгоритмом IP Core

  2. Встроенный веб-сервер для управления и отображения данных

  3. Интеграция существующих программных алгоритмов с аппаратными платформами

Ниже приведена схема уровня системы для этой системы MicroBlaze:

Эталонная конструкция «Xilinx MicroBlaze TCP/IP to AXI4-Lite Master» использует Vivado™ MicroBlaze IP для преобразования пакетов TCP/IP в AXI4-Lite чтения и записи. Ниже приведена блок-схема всей системы, включая все периферийные устройства, необходимые для работы сервера TCP/IP и отладки через последовательную консоль UART.

Настройка MicroBlaze

Для работы сервера TCP/IP IP MicroBlaze требуется несколько базовых периферийных устройств:

  • локальная память (BRAM) для данных/инструкций

  • Ядро Ethernet для передачи и приема кадров

  • Ядро UART для отправки сообщений отладки

  • Ядро таймера для генерации прерываний тайм-аута

  • Контроллер прерываний для обработки прерываний со всех этих периферийных устройств.

Как видно из редактора адресов ниже, все эти периферийные устройства подключаются к MicroBlaze через AXI4 интерфейсы.

Объем локальной памяти, выделенной с помощью BRAM, равен 1MB. Эта сумма необходима для запуска стека lwIP. Преимущество указания BRAM для локальной памяти состоит в том, что исполняемый ELF может быть включен в битовый поток, что упрощает программирование и позволяет нацеливаться на существующие платы FPGA, которые могут не иметь внешней памяти DRAM. Однако это удобство достигается за счет увеличения коэффициента использования:

Если использование BRAM является проблемой и ресурсы DRAM доступны, можно заменить локальную память BRAM внешней памятью DRAM. Дополнительные сведения об альтернативных конфигурациях и информации о приложениях см. в заметке приложения Xilinx «xapp1026».

Пример ссылочной конструкции plugin_rd.m

Ниже приведены plugin_rd.m для этой эталонной конструкции:

function hRD = plugin_rd()
% Reference design definition
%   Copyright 2014-2018 The MathWorks, Inc.
% Construct reference design object
hRD = hdlcoder.ReferenceDesign('SynthesisTool', 'Xilinx Vivado');
hRD.ReferenceDesignName = 'Xilinx MicroBlaze TCP/IP to AXI4-Lite Master';
hRD.BoardName = 'Xilinx Kintex-7 KC705 development board';
% Tool information
hRD.SupportedToolVersion = {'2017.2','2017.4'};
%% Add custom design files
% add custom Vivado design
hRD.addCustomVivadoDesign( ...
  'CustomBlockDesignTcl', 'system_top.tcl',...
  'VivadoBoardPart',      'xilinx.com:kc705:part0:1.1');
% add custom files, use relative path
hRD.CustomFiles         = {'mw_lwip_tcpip_axi4.elf'};
%% Add interfaces
% add clock interface
hRD.addClockInterface( ...
  'ClockConnection',      'clk_wiz_0/clk_out1', ...
  'ResetConnection',      'proc_sys_reset_0/peripheral_aresetn',...
  'DefaultFrequencyMHz',  100,...
  'MinFrequencyMHz',      100,...
  'MaxFrequencyMHz',      100,...
  'ClockNumber',          1,...
  'ClockModuleInstance',  'clk_wiz_0');
% add AXI4 and AXI4-Lite slave interfaces
hRD.addAXI4SlaveInterface( ...
  'InterfaceConnection', 'microblaze_0_axi_periph/M04_AXI', ...
  'BaseAddress',         '0x44A00000',...
  'MasterAddressSpace',  'microblaze_0/Data',...
  'InterfaceType',       'AXI4-Lite',...
  'InterfaceID',         'MicroBlaze AXI4-Lite Interface');
hRD.HasProcessingSystem = false; % No hard processing system

Обратите внимание, что эталонный дизайн включает исполняемый файл MicroBlaze mw_lwip_tcpip_axi4.elf в свойстве ссылочной конструкции CustomFiles. При этом будет скопирован исполняемый файл из эталонного дизайна в проект Vivado, чтобы его можно было связать с IP-адресом MicroBlaze.

Дополнительный код в «system _ top.tcl» для присоединения ELF к uBlaze

Файл «system _ top.tcl», включенный в большинство эталонных проектов, используется для создания блок-схемы IP-интегратора Vivado верхнего уровня, содержащей большую часть эталонного проекта IP. Здесь мы добавляем дополнительный код Tcl в этот файл после создания блок-схемы, чтобы связать автономный исполняемый файл MicroBlaze ELF с IP-адресом MicroBlaze.

   import_files -norecurse mw_lwip_tcpip_axi4.elf
   generate_target all [get_files system_top.bd]
   set_property SCOPED_TO_REF system_top [get_files -all -of_objects [get_fileset sources_1] {mw_lwip_tcpip_axi4.elf}]
   set_property SCOPED_TO_CELLS { microblaze_0 } [get_files -all -of_objects [get_fileset sources_1] {mw_lwip_tcpip_axi4.elf}]

Это позволяет упаковывать ELF с битовым потоком и программировать его в память MicroBlaze BRAM одновременно с FPGA.

Выполнение рабочего процесса IP Core

Используя приведенную выше справочную конструкцию, вы создадите IP-ядро HDL, мигающее светодиодными индикаторами на плате KC705. Затем вы будете использовать tcpclient для отправки/получения форматированных пакетов в MicroBlaze для выполнения операций чтения/записи через интерфейс AXI4-Lite в сгенерированное ядро IP HDL. Файлы, используемые в следующей демонстрации, расположены по адресу:

  • matlab/toolbox/hdlcoder/hdlcoderdemos/customboards/KC705

1. Добавьте файлы проектирования ссылок MicroBlaze к пути MATLAB с помощью команды:

>> addpath(fullfile(matlabroot,'toolbox','hdlcoder','hdlcoderdemos','customboards','KC705'));

2. Настройте траекторию инструмента Xilinx Vivado™ с помощью следующей команды:

>> hdlsetuptoolpath('ToolName', 'Xilinx Vivado', 'ToolPath', 'C:\Xilinx\Vivado\2017.4\bin\vivado.bat');

При выполнении команды используйте собственный путь установки Xilinx Vivado™.

3. Откройте модель Simulink, реализующую мигание светодиода, а также векторные выходные порты для сравнения со скалярными портами, используя команду:

open_system('hdlcoder_led_vector')

4. Запуск помощника по рабочим процессам HDL из hdlcoder_led_vector/DUT путем щелчка правой кнопкой мыши по DUT и выберите «Код HDL» > «Помощник по рабочим процессам HDL».

5. Выберите эталонную конструкцию из раскрывающегося списка на шаге 1.2

6. Назначьте порты регистров интерфейсу MicroBlaze AXI4-Lite. Затем они будут доступны в шестнадцатеричном смещении, показанном в таблице.

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

Определение адресов из базового отчета IP

Базовый адрес для ядра HDL Coder™ IP определяется в ссылочном plugin_rd.m проектирования с помощью следующей команды:

% add AXI4 and AXI4-Lite slave interfaces
hRD.addAXI4SlaveInterface( ...
  'InterfaceConnection', 'microblaze_0_axi_periph/M04_AXI', ...
  'BaseAddress',         '0x44A00000',...
  'MasterAddressSpace',  'microblaze_0/Data',...
  'InterfaceType',       'AXI4-Lite',...
  'InterfaceID',         'MicroBlaze AXI4-Lite Interface');

Для этой конструкции базовым адресом является 0x44A0_0000. Смещения можно найти в таблице сопоставления адресов регистров основных отчетов IP:

Считывание/запись векторных данных с синхронизацией строба

Векторные данные поддерживаются на интерфейсах AXI4/AXI4-Lite с R2017a года. В отличие от набора скалярных портов, все элементы векторных данных рассматриваются как синхронные логике алгоритма IP Core. Дополнительные стробные регистры, добавленные для каждого входного и выходного векторного порта, поддерживают эту синхронизацию на протяжении нескольких последовательных AXI4 операций чтения/записи.

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

Подключение к серверу TCP/IP

Чтобы начать взаимодействие с сервером TCP/IP, работающим на MicroBlaze, сначала подключите последовательную консоль UART для просмотра сообщений отладки, что поможет обеспечить работу вещей в соответствии с ожиданиями. Сначала найдите последовательный порт, подключенный к UART на плате:

Затем используйте этот порт для подключения с помощью такой программы, как PuTTY™:

После подключения к последовательной консоли UART запустите hdlworkflow_ProgramTargetDevice.m сценарий для перепрограммирования платы.

   >>hdlworkflow_ProgramTargetDevice
   ### Workflow begin.
   ### Loading settings from model.
   ### ++++++++++++++ Task Program Target Device ++++++++++++++
   ### Generated logfile: hdl_prj\hdlsrc\hdlcoder_led_vector\workflow_task_ProgramTargetDevice.log
   ### Task "Program Target Device" successful.
   ### Workflow complete.

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

Примечание.Если плата подключена к сети с включенным сервером DHCP, IP-информация будет отличаться от приведенной выше. В этом случае необходимо изменить строку 43 read_write_test.m скрипт для подключения к правильному IP-адресу платы:

t = tcpclient('192.168.1.10',7);

Отправка AX4-Lite транзакций в MicroBlaze из MATLAB с использованием tcpipclient

Для выполнения операций чтения и записи в ядро IP через TCP/IP и AXI4-Lite адрес, данные и команда, которые должны быть выполнены, должны быть закодированы в пакете, отправляемом на сервер TCP/IP. В этом примере используется следующий формат пакета:

               [--Address--]  32-bits
               [----Cmd----]  lower byte of 32-bit word (READ = 0, WRITE = 1, DEBUG = 2)
               [---Length---]  lower byte of 32-bit work (N<255)
               [----Data---]  32-bits, used only for WRITE cmd

Например, пакет, выдающий считывание 3 последовательных значений, начиная с адреса 0x44a0010c:

               [44 a0 01 0c]
               [00 00 00 00]
               [00 00 00 03]

Это возвращает данные при смещениях 0x10c, 0x110,0x114.

Например, пакет, выдающий запись 0x0 для смещения 0x04, будет:

               [44 a0 00 04]
               [00 00 00 01]
               [00 00 00 01]
               [00 00 00 00]

Чтобы изменить уровень отладки, используемый для печати на консоль, отправьте пакет debug cmd:

               [xx xx xx xx]
               [00 00 00 03]
               [00 00 00 01] %0 = no msg, 1 = READ|WRITE, 2 = full pkt

Запуск сценария read_write_test.m

Этот пример включает сценарий, который устанавливает соединение с сервером TCP/IP, работающим на MicroBlaze, создает команды для включения/отключения DUT, считывает 6 скалярных портов и те же данные, что и векторный порт, и сравнивает результаты.

1. Чтобы запустить этот сценарий, сначала скопируйте его в локальный каталог

>> copyfile(fullfile(matlabroot,'toolbox','hdlcoder','hdlcoderdemos','ublaze_lwip_read_write_vector_test.m'),'ublaze_test.m');

и откройте сценарий в редакторе:

>> edit('ublaze_test.m');

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

2. Выполнить раздел 1. В качестве массивов типов uint32 будут созданы следующие команды:

read6_cmd      = uint32([hex2dec('44a0010c') 0 6]);   %read 6 32-bit regs
read_vec_cmd   = uint32([hex2dec('44a00140') 0 6]);   %read 6 elements of vec
strobe_vec_cmd = uint32([hex2dec('44a00160') 1 1 1]); %write strobe for vec
enable_cmd     = uint32([hex2dec('44a00004') 1 1 1]); %enable ip core
disable_cmd    = uint32([hex2dec('44a00004') 1 1 0]); %disable ip core
debug0_cmd     = uint32([hex2dec('00000000') 3 0]);   %disable all debug printfs
debug1_cmd     = uint32([hex2dec('00000000') 3 1]);   %enable READ|WRITE printfs
debug2_cmd     = uint32([hex2dec('00000000') 3 2]);   %enable pkt printfs

ПРИМЕЧАНИЕ: эти массивы хранят данные в формате endian, используемом локальной машиной, что для многих систем x86 является небольшим форматом endian. Однако сервер TCP/IP ожидает значения в формате big endian (сетевой порядок байтов). В результате, если система, в которой вы находитесь, мало endian, байты в каждом элементе должны быть заменены с помощью swapbytes.

3. Выполните раздел 2 для деактивации логики DUT и считывания одного значения счетчика, подключенного к 6 скалярным портам, а также ко всем 6 элементам векторного порта. Обратите внимание, что все значения счетчика совпадают. Это происходит потому, что одни и те же данные передаются на все порты, а DUT отключен, поэтому асинхронный доступ через интерфейс AXI4 не виден.

   Scalar port (top) vs vector port (bottom) access with DUT disabled:
      7e8aec14   7e8aec14   7e8aec14   7e8aec14   7e8aec14   7e8aec14
      7e8aec14   7e8aec14   7e8aec14   7e8aec14   7e8aec14   7e8aec14

4 . Выполните раздел 3 для повторного включения логики DUT и считывания тех же значений счетчика. Обратите внимание, что все 6 скалярных портов имеют разные значения, в то время как 6 элементов векторного порта являются одинаковыми. Это связано с последовательным доступом, который должен происходить через интерфейс AXI4, и отсутствием регистра синхронизации в случае скалярного порта и наличием явного регистра синхронизации в случае векторного порта.

   Scalar port (top) vs vector port (bottom) access with DUT enabled:
      7f7796dc   7fc70e4b   8016860a   8065fdce   80b5758d   8104ed4d
      815964dd   815964dd   815964dd   815964dd   815964dd   815964dd

Соответствующие выходные данные отладки на последовательной консоли будут следующими:

Резюме

Эта демонстрация продемонстрировала использование мягкого процессора MicroBlaze только в конструкциях FPGA. MicroBlaze хорошо подходит для работы в качестве полноценного процессора или гибкого IP-интерфейса, выполняющего устаревший код C в качестве встроенного ПО. Эта демонстрация также показала разницу между набором скалярных портов и векторным портом в отношении синхронизации данных через интерфейс AXI4.

Приложение A: Создание и редактирование приложения Xilinx SDK

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

1. Откройте проект Xilinx Vivado, щелкнув ссылку на шаге 4.1 «Создать проект» помощника по рабочим процессам HDL:

2. Экспорт существующего проекта, включая созданный поток битов, в локальную папку. Как только это будет сделано, продолжайте и «Запуск SDK».

3. В SDK создайте новый проект приложения

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

Далее можно выбрать из нескольких предварительно настроенных проектов примеров/шаблонов для начала работы. Этот пример создан на основе проекта «lwIP Echo Server», поэтому выберите этот вариант сейчас.

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

Приложение B: Копирование содержимого файла C в проект

/* Copyright 2016-2020 The MathWorks, Inc. */
#define IPCOREBASE 0x44a00000
#define WRITE  0x01
#define READ   0x00
#define DEBUG  0x03

int transfer_data() {
    return 0;
}

void print_app_header()
{
    xil_printf("\n\r\n\r-----MathWorks HDL Coder AXI4-Lite IP Core Read/Write Server ------\n\r");
    xil_printf("  TCP packets sent to port 7 will be issued as AXI4-Lite Read/Writes\n\r");
    xil_printf("\n\r");
    xil_printf("  [ 32-bit address ] (Base Address = 0x44a0_0000)\n\r");
    xil_printf("  [ 32-bit   cmd   ] (read = 0x00, write =0x01, debug = 0x03)\n\r");
    xil_printf("  [ 32-bit   len   ] ( N<255)\n\r");
    xil_printf("  [ 32-bit   data  ] (N 32-bit data values for write cmd)\n\r");
    xil_printf("------------------------------------------------------------ ------\n\r");

}

void print_packet(struct pbuf *p) {
    u16 ii;
    u8 *pktPtr;

    pktPtr = p->payload;
    xil_printf("DEBUG | packet payload:\r\n");
    for (ii=0;ii<p->len;ii+=4) {
        xil_printf("%02x %02x %02x %02x\r\n",*(pktPtr+ii),*(pktPtr+ii+1),*(pktPtr+ii+2),*(pktPtr+ii+3));
    }


}
err_t recv_callback(void *arg, struct tcp_pcb *tpcb,
                               struct pbuf *p, err_t err)
{
    u8 *pktPtr,*pktEnd;
    volatile u32 *addr;
    u32 data[255],cmd;
    u16 len;
    int ii;
    static u8 debug = 3;

    /* do not read the packet if we are not in ESTABLISHED state */
    if (!p) {
        tcp_close(tpcb);
        tcp_recv(tpcb, NULL);
        return ERR_OK;
    }

    /* indicate that the packet has been received */
    tcp_recved(tpcb, p->len);
    if (debug > 1) print_packet(p);

    //[ 32 bits address ]
    //[ 32 bits read = 0x00, write =0x01]
    //[ 32 bits length ]
    //[ 32 bits write data]
    pktPtr = p->payload;
    pktEnd = pktPtr+p->len;


    /* could be multiple commands per packet */
    while ( pktPtr < pktEnd) {
        addr = (u32*) (pktPtr[0]<<24 | pktPtr[1]<<16 | pktPtr[2]<<8 | pktPtr[3]);
        cmd  = (u32) pktPtr[7];      // cmd is 32 bits, but only 1st byte used, ignore rest
        pktPtr += 8;

        switch(cmd) {
        case WRITE :
            len  = (u32) pktPtr[3];  // len is 32 bits, but only 1st byte used, ignore rest
            pktPtr += 4;
            for (ii=0;ii<len; ii++) {
                data[0] = (u32) (pktPtr[0]<<24 | pktPtr[1]<<16 | pktPtr[2]<<8 | pktPtr[3]);
                *addr = data[0];
                if (debug > 0) xil_printf("WRITE | address: 0x%08x, data[0]: 0x%08x\r\n",addr,data[0]);
                addr++;
                pktPtr += 4;
            }
            break;
        case READ :
            len  = (u32)   pktPtr[3]; // len is 32 bits, but only 1st byte used, ignore rest
            pktPtr += 4;
            for (ii=0;ii<len; ii++) {
                data[ii] = *addr;
                if (debug > 0) xil_printf("READ  | address: 0x%08x, data[%d]: 0x%08x\r\n",addr,ii,data[ii]);
                addr++;
            }
            /* send the packet back */
            if (tcp_sndbuf(tpcb) > p->len)
                err = tcp_write(tpcb, data, 4*len, 1);
            else
                xil_printf("no space in tcp_sndbuf\n\r");
            break;
        case DEBUG:
            debug = pktPtr[3]; // only need the low byte
            pktPtr += 4;
            xil_printf("Debug level set to : 0x%02x\r\n",debug);
            break;
        default :
            xil_printf("INVALID | cmd: 0x%08x\r\n",cmd);

        }
    }
        /* free the received pbuf */
        pbuf_free(p);

        return ERR_OK;
}

6. Сохраните измененный файл echo.c, и приложение будет перестроено.

Приложение C: Программирование FPGA с ELF и битовым потоком

Теперь можно запрограммировать FPGA с помощью экспортированного битового потока и только что созданного ELF-файла.