То В этом примере показано, как использовать Рабочий процесс Генерации Ядра IP HDL Coder™, чтобы разработать исходный дизайн для частей Xilinx® без встроенного существующего процессора ARM®, но которые все еще используют HDL Coder™, сгенерировало интерфейс AXI, чтобы управлять DUT. А именно, этот пример будет использовать плату Xilinx Kintex-7 KC705, и мягкий процессор MicroBlaze™, запускающий LightWeightIP, (lwIP) базировался, сервер встроенного программного обеспечения TCP/IP в исходном проекте, чтобы получить доступ к HDL Coder™ сгенерировал регистры DUT отовсюду в связанной сети. Далее, этот пример также подсветит различие между доступом к данным из набора регистров, реализованных как несколько скалярных портов и набором регистров, реализованных как один векторный порт.
Комплект Проекта Xilinx Vivado, с поддерживаемой версией, перечисленной в документации HDL Coder
Макетная плата Xilinx Kintex-7 KC705
Пакет поддержки HDL Coder™ для Советов FPGA Xilinx
Соединение Ethernet
MicroBlaze является простым, универсальным ненавязчивым процессором, который может использоваться в FPGA Xilinx только платформы, такие как Kintex-7, чтобы выполнить функциональность законченный процессор, или как гибкий, программируемый IP. Когда программы малы, ELF может находиться в BRAM, и проект становится полностью сам содержавшимся в FPGA. Существует много приложений, хорошо подходящих для того, чтобы быть реализованным на MicroBlaze. Здесь мы перечисляем только некоторых:
Удаленное сетевое управление развернутого Основного алгоритма IP
Встроенный веб-сервер для управления и отображения данных
Интегрирование существующих алгоритмов программного обеспечения на платформы только для оборудования
Следующее является схемой уровня системы для этой системы MicroBlaze:
Исходный проект, "Xilinx MicroBlaze TCP/IP AXI4-облегченному Ведущему устройству", использует Vivado™ MicroBlaze IP, чтобы перевести пакеты TCP/IP в AXI4-облегченные чтения и записи. Ниже блок-схема полной системы, включая все периферийные устройства, требуемые управлять сервером TCP/IP и отладкой через последовательную консоль UART.
Для того, чтобы управлять сервером TCP/IP, MicroBlaze IP нужны несколько основных периферийных устройств:
локальная память (BRAM) для данных/инструкций
Ядро Ethernet для передачи и приема кадров
Ядро UART для отправки сообщений отладки
Ядро таймера для генерации прерываний тайм-аута
Контроллер прерываний для обработки прерываний от всех этих периферийных устройств.
Как видно в редакторе адреса ниже, все эти периферийные устройства соединяются с MicroBlaze через интерфейсы AXI4.
Сумма локальной памяти, выделенной с помощью BRAM, составляет 1 МБ. Эта сумма необходима, чтобы запустить стек lwIP. Преимущество определения BRAM для локальной памяти - то, что исполняемый ELF может быть включен в поток битов, который упрощает программирование и позволяет предназначаться для существующих плат FPGA, которые не могут иметь внешней памяти DRAM. Однако это удобство прибывает в стоимость увеличенного использования:
Если использование BRAM является беспокойством, и ресурсы DRAM доступны, можно решить заменить локальную память BRAM на внешнюю память DRAM. Дополнительную информацию см. в примечании "xapp1026" к приложению Xilinx по альтернативным настройкам и информации приложений.
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.EmbeddedCoderSupportPackage = hdlcoder.EmbeddedCoderSupportPackage.None; %None
Обратите внимание на то, что исходный проект включает исполняемый файл MicroBlaze mw_lwip_tcpip_axi4.elf
в свойстве CustomFiles
исходного проекта. Это скопирует исполняемый файл от исходного проекта до проекта Vivado так, чтобы это могло быть сопоставлено с MicroBlaze IP.
'system_top.tcl' файл, включенный в наиболее исходные проекты, используется, чтобы создать верхний уровень блок-схема Интегратора IP Vivado, содержащая большую часть IP исходного проекта. Здесь мы добавляем некоторый дополнительный код Tcl в этот файл после того, как блок-схема была создана, чтобы сопоставить автономный исполняемый файл MicroBlaze ELF с MicroBlaze IP.
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. Создайте путь к инструменту Xilinx Vivado™ при помощи следующей команды:
>> hdlsetuptoolpath('ToolName', 'Xilinx Vivado', 'ToolPath', 'C:\Xilinx\Vivado\2017.4\bin\vivado.bat');
Используйте свой собственный путь к установке Xilinx Vivado™ при выполнении команды.
3. Откройте модель Simulink, которая реализует мигание LED, а также векторные выходные порты для сравнения со скалярными портами, с помощью команды:
open_system('hdlcoder_led_vector')
4. Запустите HDL Workflow Advisor от hdlcoder_led_vector/DUT
подсистема путем щелчка правой кнопкой по DUT
подсистема и HDL Code выбора> HDL Workflow Advisor.
5. Выберите исходный проект из выпадающего на шаге 1.2
6. Присвойте порты регистра "MicroBlaze AXI4-облегченный Интерфейс". Они затем будут доступны при шестнадцатеричном смещении, показанном в таблице.
7. Запустите остающиеся шаги в рабочем процессе, чтобы сгенерировать поток битов и программировать целевое устройство.
Базовый адрес для Ядра IP HDL Coder™ задан в исходном проекте 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-облегченный, адрес, данные и команда, которая будет выполняться, должны быть закодированы в пакете, отправленном в сервер 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]
Изменить уровень отладки раньше распечатывало к консоли, отправляло отладку 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
Соответствующая отладка выход на последовательной консоли будет:
Эта демонстрация подсветила использование MicroBlaze, который только проектирует ненавязчивый процессор в FPGA. MicroBlaze хорошо подходит функционировать как законченный процессор или как гибкий IP рабочий устаревший код С как микропрограммное приложение. Эта демонстрация также показала различие между набором скалярных портов и векторным портом в отношении синхронизации данных через интерфейс AXI4.
Этот раздел покажет, как создать новый проект SDK Xilinx и включить код из этого примера, чтобы затем изменить или расширить.
1. Откройтесь проект Xilinx Vivado путем щелчка по ссылке на шаге 4.1 HDL Workflow Advisor "Создают Проект":
2. Экспортируйте существующий проект, включая сгенерированный поток битов, к локальной папке. Если это сделано, разрешение и "SDK Запуска" также.
3. Из SDK создайте новый проект приложения
4. У вас затем будет опция именования проекта и создания нового bsp
Затем можно выбрать из нескольких предварительно сконфигурированных проектов примера/шаблона начать. Этот пример создается прочь "lwIP проект" Сервера Эха, так выбор это теперь.
5. Используя сервер эха как шаблон, можно заменить следующие 3 метода на фрагмент кода ниже, чтобы изменить поведение сервера
#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.