В этом примере показано, как использовать рабочий процесс HDL Coder™ IP Core Generation для разработки исходных проектов для деталей Xilinx ® без встроенного процессора ARM ®, но которые все еще используют HDL Coder™ сгенерированный интерфейс AXI для управления DUT. В частности, этот пример использует плату Kintex-7 KC705 Xilinx и MicroBlaze™ мягкий процессор, работающий на сервере микропрограммного обеспечения TCP/IP (lwIP) на основе LightWeightIP (lwIP) в исходный проект, для доступа к HDL Coder™ сгенерированным регистрам DUT из любой точки подключенной сети. Кроме того, этот пример также высвечивает различие между доступом к данным из набора регистров, реализованных как несколько скалярных портов, и набором регистров, реализованным как один векторный порт.
Xilinx Vivado Design Suite с поддерживаемой версией, перечисленной в документации HDL Coder
Плата разработки Kintex-7 KC705 Xilinx
Пакет поддержки HDL Coder™ для плат Xilinx FPGA
Подключение Ethernet
MicroBlaze - простой, универсальный мягкий процессор, который может использоваться только в платформах Xilinx FPGA, таких как Kintex-7, для выполнения функциональности полноценного процессора, или как гибкий, программируемый IP. Когда программы малы, ELF может сидеть в BRAM, и проект становится полностью автономным в FPGA. Существует много приложений, хорошо подходящих для реализации на MicroBlaze. Здесь мы перечисляем всего несколько:
Удаленное сетевое управление развернутым алгоритмом IP Core
Встраиваемый веб-сервер для управления и отображения данных
Интегрирование существующих алгоритмов программного обеспечения с аппаратными платформами
Ниже приведен уровень системы схема для этой системы MicroBlaze:
Исходный проект, «Xilinx MicroBlaze TCP/IP to AXI4-Lite Master», использует Vivado™ MicroBlaze IP, чтобы преобразовать пакеты TCP/IP в AXI4-Lite чтения и записи. Ниже приведен блок схема всей системы, включая все периферийные устройства, необходимые для работы сервера TCP/IP и отладки через последовательную консоль UART.
Для порядок работы 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 для этого исходного проекта:
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', включенный в большинство исходных проектов, используется для создания блока Vivado IP Integrator верхнего уровня, содержащей большую часть исходного проекта 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-ядро 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. Настройте путь инструмента Vivado™ Xilinx с помощью следующей команды:
>> hdlsetuptoolpath('ToolName', 'Xilinx Vivado', 'ToolPath', 'C:\Xilinx\Vivado\2017.4\bin\vivado.bat');
Используйте собственный путь установки Vivado™ Xilinx при выполнении команды.
3. Откройте модель Simulink, которая реализует LED-мигание, а также векторные выходные порты для сравнения со скалярными портами, используя команду:
open_system('hdlcoder_led_vector')
4. Запустите HDL Workflow Advisor из hdlcoder_led_vector/DUT
подсистема щелчком правой кнопкой мыши по DUT
и выберите HDL-код > HDL Workflow Advisor.
5. Выберите исходный проект из выпадающего списка на шаге 1.2
6. Назначьте порты регистров «MicroBlaze AXI4-Lite Interface». Затем они будут доступны при шестнадцатеричном смещении, показанном в таблице.
7. Запустите оставшиеся шаги в рабочем процессе, чтобы сгенерировать битовый поток и запрограммировать целевое устройство.
Базовый адрес для 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 Core Report Register Address Mapping:
Векторные данные поддерживаются на AXI4/AXI4-Lite интерфейсах на R2017a год. В отличие от набора скалярных портов, все элементы вектора данных рассматриваются как синхронные логике IP- Основного алгоритма. Дополнительные регистры строба, добавленные для каждого вектора входа и выхода порта, поддерживают эту синхронизацию через несколько последовательных AXI4 чтения/записи.
Для входных портов регистр строба управляет разрешениями на наборе теневых регистров, позволяя логике ядра 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);
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
Этот пример включает скрипт, который будет настройкой подключение к серверу 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
ПРИМЕЧАНИЕ: эти массивы хранят данные в эндовом формате, используемом локальной машиной, которая для многих систем x86 является маленьким эндовым форматом. Однако сервер TCP/IP ожидает значения в большом эндовом формате (порядок сетевых байтов). В результате, если система, на которой вы работаете, является маленькой эндовой, байты в каждом элементе должны быть заменены с помощью 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
Соответствующий выход отладки на последовательной консоли:
В этой демонстрации подчеркивалось использование soft-core процессора MicroBlaze только в проектах FPGA. MicroBlaze хорошо подходит для работы как полноценный процессор или как гибкий IP, работающий под управлением устаревших Кодов С как приложение для прошивки. Эта демонстрация также показала различие между набором скалярных портов и вектора портом в отношении синхронизации данных через интерфейс AXI4.
В этом разделе будет показано, как создать новый проект Xilinx SDK и включить код из этого примера, чтобы затем изменить или расширить.
1. Откройте проект Xilinx Vivado, нажав на ссылку в HDL Workflow Advisor шаг 4.1 «Создать проект»:
2. Экспорт существующего проекта, включая сгенерированный битовый поток, в локальную папку. Как только это сделано, вперед и «Запуск SDK» также.
3. Из SDK создайте новый проект приложения
4. Затем у вас будет опция назвать проект и создать новый bsp
Далее можно выбрать из нескольких предварительно настроенных проектов example/template для начала работы. Этот пример построен из проекта «lwIP Echo Server», поэтому выберите его сейчас.
5. Используя эхо-сервер в качестве шаблона, можно заменить следующие 3 метода фрагментом кода ниже, чтобы изменить поведение сервера
/* 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, и приложение будет перестроено.
Теперь можно запрограммировать FPGA с помощью экспортированного битового потока и только что созданного файла ELF.