Этот пример показывает, как использовать 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

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», включенный в большинство эталонных проектов, используется для создания блок-схемы 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-ядро 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. Выполните оставшиеся шаги в рабочем процессе, чтобы создать битовый поток и запрограммировать целевое устройство.
Базовый адрес для ядра 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, работающим на 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
ПРИМЕЧАНИЕ: эти массивы хранят данные в формате 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 7e8aec147e8aec14 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 8104ed4d815964dd 815964dd 815964dd 815964dd 815964dd 815964dd
Соответствующие выходные данные отладки на последовательной консоли будут следующими:

Эта демонстрация продемонстрировала использование мягкого процессора MicroBlaze только в конструкциях FPGA. MicroBlaze хорошо подходит для работы в качестве полноценного процессора или гибкого IP-интерфейса, выполняющего устаревший код C в качестве встроенного ПО. Эта демонстрация также показала разницу между набором скалярных портов и векторным портом в отношении синхронизации данных через интерфейс AXI4.
В этом разделе показано, как создать новый проект Xilinx SDK и включить код из этого примера для последующего изменения или расширения.
1. Откройте проект Xilinx Vivado, щелкнув ссылку на шаге 4.1 «Создать проект» помощника по рабочим процессам HDL:

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

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

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

Далее можно выбрать из нескольких предварительно настроенных проектов примеров/шаблонов для начала работы. Этот пример создан на основе проекта «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-файла.
