Разработка программ для центрального процессора Redd на примере доступа к ПЛИС

В прошлой статье я сказал, что нам пора переходить к потоковым протоколам. Но начав подготовку рассказа о них, я понял, что сам плаваю в одной очень важной теме. Как уже отмечалось, у меня с Линуксом достаточно своеобразные отношения. В общем, я понял, что сам не могу создать с нуля приложение на С++, которое удовлетворяло бы всем принципам программирования для Redd. Можно было бы попросить кого-то сделать это, а затем просто воспользоваться готовым шаблоном, но цикл статей призван научить всех вести разработку под Redd с нуля. Поэтому я попросил своего начальника (большого специалиста по Линуксу), и он разъяснил мне, что за чем нажимать. Затем я чуть переосмыслил его слова и теперь считаю нужным зафиксировать все знания письменно. Это избавит таких, как я, от мучительных раздумий: «Так… Что он сделал — понятно, но как мне это повторить?». В общем, кто работает под Линуксом, можете пробежать следующие два раздела по диагонали. Вряд ли вы найдёте там что-то новое (дальше — найдёте). А остальным я предлагаю на выбор две методики разработки, которые соответствуют декларируемым принципам работы под Redd: низкие трудозатраты на разработку и удалённая отладка.

kbsk5cbh98eftndkgneifjnzgao.jpeg
Все статьи цикла:

  1. Разработка простейшей «прошивки» для ПЛИС, установленной в Redd, и отладка на примере теста памяти
  2. Разработка простейшей «прошивки» для ПЛИС, установленной в Redd. Часть 2. Программный код
  3. Разработка собственного ядра для встраивания в процессорную систему на базе ПЛИС


Работа средствами Visual Studio


Оказывается, можно вести разработку под удалённый Linux, вообще не имея его на своей локальной машине, и не устанавливая при этом никаких программных средств не от Microsoft. Здесь показано, как это ставится на Visual Studio (я поставил в версию 2019, но судя по всему, появилось оно в 2015-й) docs.microsoft.com/ru-ru/cpp/linux/download-install-and-setup-the-linux-development-workload? view=vs-2019

А вот — теория работы.

Ну, и там можно по вкладкам походить, теории много, и вся на русском.

Прекрасно! Попробуем использовать полученные знания для того, чтобы обратиться к микросхеме FT2232H, через которую центральный процессор Redd подключён к ПЛИС. Именно этот канал в будущем ляжет в основу нашей потоковой работы. Открываем Visual Studio, выбираем «Создание проекта». В фильтрах выбираем «Язык — C++», «Платформа — Linux», «Тип проекта — консоль». Я покажу, где это расположено уже для типа проекта. Из того, что нам отфильровали, выбираем «Консольное приложение»:

ao5xh4h5fwoldpjdn_-rpx8ay2a.png

Назовём его, скажем, SimpleConsole. Нам создали такой спартанский исходный код:

#include 

int main()
{
    printf("hello from SimpleConsole!\n");
    return 0;
}


Попробуем его собрать. И нам задают очень интересный вопрос про установку соединения. Это особенность разработки в Visual Studio. Среда не содержит кросс-компилятора и каких-либо библиотек. Она просто создаёт каталог с исходными текстами на удалённой машине, после чего для каждой компиляции копирует обновлённые файлы на неё и запускает компиляцию там. Именно поэтому связь с удалённой машиной должна быть установлена не для запуска, а уже для обычной сборки проекта.

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

jdsxhkvvklw29zjoecvrr1svvda.png

Если что — параметры можно поменять вот в этом секретном месте (не пытайтесь изменять в свойствах проекта, ни к чему хорошему это не приведёт):

dnxeydmzx11e50rtxzjayuleto0.png

owk-leapmriognn1l-kk4tavvyc.png

Собственно, можно поставить точку останова на единственную строку и проверить, что проект запускается и работает. Но это тривиально. Поэтому переходим к более интересной задаче — работе с FT2232. Возникает вопрос: где взять необходимые библиотеки? Для Linux всё входит в комплект драйверов. Там есть и сам драйвер, и библиотеки, и примеры приложений для работы с ними, и даже краткая инструкция. В общем, в поисковик вбиваем:

FTDI D2XX drivers 


Он покажет, откуда их можно скачать. Правда, конкретно у меня всё было плохо. Мой провайдер блокирует сайт FTDI (равно как и reprap, 7zip и даже osronline), ссылаясь на РосКомНадзор. РКН же в ответ на заявления шлёт отписки, мол, мы ничего не блокируем, а с провайдером разбирайтесь сами. Куда только мне в этих отписках не предлагали обратиться, даже в полицию. На отписки они горазды. Попытки жалоб на бездействие РКН заканчивается тем, что их пересылают в РКН, откуда приходят очередные отписки. В общем, может оказаться, что ваш провайдер также заблокирует доступ. Не пугайтесь, просто ищите иные пути для скачивания, затратив на это время. А потом удивляются, что сроки разработки ракет откладываются на десятилетия… Переписку с РКН я начал в прошлом ноябре, сейчас уже июнь, кроме отписок — никаких действий… Но я отвлёкся.

Как пользоваться драйверами, можно посмотреть в файле readme, в самом пакете. Также можно найти документ AN_220 FTDI Drivers Installation Guide for Linux. И, наконец, можно найти на YouTube видео Linux d2xx Driver Installation Guide, автор — FTDI Chips. Ссылка на него также есть на странице скачивания драйверов. В общем, FTDI не поскупились на варианты информирования пользователей. Собственно, если вы получили готовый комплекс Redd, то сами драйверы на нём уже установлены и настроены. А нас будут интересовать заголовочные файлы и примеры.

Давайте вставим кусочек примера \release\examples\EEPROM\read. Самое начало функции main, где открывается устройство и берётся его тип. Этого будет достаточно, чтобы убедиться, что всё работает. Терпеть не могу метки, но так как мы просто быстро перетаскиваем код, которому жить 10 минут, я для экономии времени перетащу и метку, которая была в оригинальном примере. Получается так:

#include 
#include 
#include 
#include 
#include "ftd2xx.h"

int main(int argc, char* argv[])
{
	printf("hello from ConsoleApplication1!\n");
	FT_STATUS	ftStatus;
	FT_HANDLE	ftHandle0;
	int iport;
	static FT_PROGRAM_DATA Data;
	static FT_DEVICE ftDevice;
	DWORD libraryVersion = 0;
	int retCode = 0;

	ftStatus = FT_GetLibraryVersion(&libraryVersion);
	if (ftStatus == FT_OK)
	{
		printf("Library version = 0x%x\n", (unsigned int)libraryVersion);
	}
	else
	{
		printf("Error reading library version.\n");
		return 1;
	}

	if (argc > 1) {
		sscanf(argv[1], "%d", &iport);
	}
	else {
		iport = 0;
	}
	printf("Opening port %d\n", iport);

	ftStatus = FT_Open(iport, &ftHandle0);
	if (ftStatus != FT_OK) {
		/*
			This can fail if the ftdi_sio driver is loaded
			use lsmod to check this and rmmod ftdi_sio to remove
			also rmmod usbserial
		 */
		printf("FT_Open(%d) failed\n", iport);
		return 1;
	}

	printf("FT_Open succeeded.  Handle is %p\n", ftHandle0);

	ftStatus = FT_GetDeviceInfo(ftHandle0,
		&ftDevice,
		NULL,
		NULL,
		NULL,
		NULL);
	if (ftStatus != FT_OK)
	{
		printf("FT_GetDeviceType FAILED!\n");
		retCode = 1;
		goto exit;
	}

	printf("FT_GetDeviceInfo succeeded.  Device is type %d.\n",
		(int)ftDevice);
exit:
	return 0;
}


Пытаемся собрать — не собирается. Не хватает заголовочных файлов.

1>main.cpp
1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(5,20): error : ftd2xx.h: Нет такого файла или каталога
1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(5,20): error :  #include "ftd2xx.h"
1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(5,20): error :                     ^
1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(5,20): error : compilation terminated.


Но я же положил их рядом! Всё просто. Локально они лежат рядом, но сборка идёт удалённо. Чтобы Студия перенесла их на удалённую машину, надо добавить их в проект. Причём из файла main.cpp я добавляю только ftd2xx.h, но он транзитом тянет за собой ещё WinTypes.h. Добавить придётся оба.

gy-2-jt8ej_mbavcpupdsphb5g8.png

kpyel11pdcxgnd5ojt39rhkkslk.png

Теперь ругается компоновщик.

1>Связывание объектов
1>D:\Work\SimpleConsole\SimpleConsole\obj\x64\Debug\main.o : error : In function `main':
1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(18): error : undefined reference to `FT_GetLibraryVersion'
1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(37): error : undefined reference to `FT_Open'
1>D:\Work\SimpleConsole\SimpleConsole\main.cpp(55): error : undefined reference to `FT_GetDeviceInfo'
1>collect2 : error : ld returned 1 exit status


Понятно, что не хватает библиотеки. Осматривая make файлы примера, я понял, что надо добавить в настройки компоновщика пару параметров:

xyzm2igciaxlmevvppuug4vk2go.png

xdp2f1nxqd3bue69dqx8m-dgabc.png

Теперь проект собирается успешно. Ставим точку останова на последнюю строку, пробуем запустить. Видим следующий текст:

hello from ConsoleApplication1!
Library version = 0x10408
Opening port 0
FT_Open succeeded.  Handle is 0x555555768540
FT_GetDeviceInfo succeeded.  Device is type 10.


В целом — неплохо. Что-то открылось, даже какое-то устройство найдено. Что за тип 10? В заголовочном файле от FTDI находим:

enum {
	FT_DEVICE_BM,
	FT_DEVICE_AM,
	FT_DEVICE_100AX,
	FT_DEVICE_UNKNOWN,
	FT_DEVICE_2232C,
	FT_DEVICE_232R,
	FT_DEVICE_2232H,
	FT_DEVICE_4232H,
	FT_DEVICE_232H,
	FT_DEVICE_X_SERIES,
	FT_DEVICE_4222H_0,
	FT_DEVICE_4222H_1_2,
	FT_DEVICE_4222H_3,
    FT_DEVICE_4222_PROG,
};


Считаем пальцем сверху вниз — перед нами FT4222H. Да, в Redd много устройств от FTDI. Теперь я просто мельком скажу, что надо перебирать номер устройства, передаваемого в функцию FT_Open (), этот номер передаётся в функцию main () в виде аргумента. Его можно задать в свойствах отладки проекта.

Здесь полезно разместить табличку с типичными проблемами. Вообще, там везде упоминается «настройте Redd» без деталей. Дело в том, что комплексы будут распространяться настроенными, поэтому всем читателям правила настройки не нужны. В случае проблем, настройкой обычно будут заниматься администраторы. Вот и получается, что описать правила настройки, конечно, можно, но это займёт много места. Имеет смысл тратить силы на это, только если оно реально кому-то нужно. Так что здесь я ограничусь только указанием на проблемы, а как их устранять — опишу, если будут заявки в обратной связи.


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

Работа средствами Linux, работающей в виртуальной машине


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

В первую очередь, ставим на локальную машину программу VirtualBox от Oracle. Если возникают какие-то лицензионные проблемы (я-то ею бесплатно пользуюсь, как физлицо, а что там с юрлицами — точно не знаю), выделяем отдельную физическую машину и ставим туда Linux. Какой? Мне проще, я примерно одинаково разбираюсь во всех в них. В смысле, почти не понимаю ни в одном. Поэтому мне начальник сказал, что надо использовать Debian, я поставил именно Debian. Можете пойти тем же путём (воспользоваться принципом «А почему бы и нет?»). По крайней мере, дальше я буду опираться на работу именно с ним.

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

  1. Если в ответ на команду нам сообщают, что не хватает прав, стоит повторить её, добавив в начало магическое заклинание sudo.
  2. Если в ответ на команду нам говорят, что нет такой, стоит попытаться установить её, подав магическое заклинание apt-get install <недостающаяВещь>.


Итак. Мы только что поставили ОС. Сразу добавим туда поддержку C++, установив компилятор g++, а также отладчик gdb. Как? Так воспользовавшись правилом 2:

apt-get install g++
apt-get install gdb

Не дали? Прекрасно! Повторяем с применением правила 1:

sudo apt-get install g++
sudo apt-get install gdb

Теперь идём в интернет, в поисковике набираем:

Eclipse IDE

Нам находят ссылку на eclipse.org, там первые варианты относятся к Java, находим и скачиваем вариант для C/C++:

96qwqkb0litad1r1tujou2a9bae.png

Скачиваем и распаковываем, скажем, в home.

Собственно, никакая установка не требуется. Просто идём в каталог, в который только что всё распаковалось и запускаем файл eclipse:

ir705r0y6iwxnnixuxatl8ajg5m.png

Мы в среде разработки. Ну, если вы уже работали с микроконтроллерами и даже процессорными ядрами под ПЛИС, то наверняка уже знаете, что такое Eclipse. Так что незнакомые вещи почти завершены. Начинаем осматриваться в более-менее знакомых вещах. Создаём C++ проект. Сразу скажу, там есть два пути. Один приведёт к успеху, второй — к тупику. Поэтому следите внимательно за путём, которым иду я:

pnp2amrhevvek40jz7nqsocol5g.png

a_gesxu9d6u_hhbocj0mqah5xik.png

14zgb0day-st8r8cyejyvxahtsk.png

Нам создали проект, который прекрасно собирается. Чтобы настроить его отладку, идём в свойства GDB:

zdzftzvpbfwkbs1mphkqdyrfv1a.png

Создаём конфигурацию типа C/C++ Remote Application, в группе Connection нажимаем New:

u1dywbnrislxi82tujf7k3crbks.png

Выбираем соединение типа SSH:

j8nttddgczes40njsbis01oamzi.png

Заполняем свойства соединения, переключив радиокнопку типа авторизации на авторизацию по паролю:

i_wel1jpfgrjiale5pgsh7un8mg.png

Собственно, система готова к отладке. Убедившись, что приветственный текст действительно отображается, пробуем перенести код из предыдущего примера (который был в Visual Studio) сюда. Чтобы прописать дополнительные библиотеки, выбираем свойства проекта:

9ib07rvhf55tqf7hvkewqsqbqau.png

Причём так как здесь сборка идёт локально, библиотеки должны лежать и в локальном каталоге usr/local/lib. Напоминаю, библиотеки скачиваются вместе с драйвером, а как их установить — есть readme, а также AN220 и видео на YouTube, подробности были в разделе про Visual Studio.

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

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

Измерение скорости записи в ПЛИС через FT2232H


Ну что. Давайте закрепим полученные навыки на более-менее реальном проекте. Разумеется, начинать что-то очень серьёзное уже нельзя, все уже устали. Но получим какой-нибудь более-менее практический результат. Например, измерим, с какой максимальной скоростью мы сможем передавать данные в ПЛИС через микросхему FT2232H. Протокол там не самый простой, поэтому в обе стороны мы передавать не будем, а ограничимся передачей от нас в канал, на другом конце которого установлена ПЛИС. В этом нам поможет документ AN_130 FT2232H Used in an FT245 Style Synchronous FIFO Mode, так как в комплексе контроллер включён именно в этом режиме (синхронный FIFO). В этом документе есть и описание выводов, в том виде, в каком они используются в этом режиме, и временные диаграммы, и даже примеры кода, из которых мы будем черпать вдохновение.

Итак. Мы хотим произвести запись в FIFO средствами контроллера. Что у нас выйдет? Я пробовал, я знаю. Уйдёт 1 килобайт данных, после чего контроллер «повиснет». Он откажется принимать дополнительные данные. Всё дело в том, что килобайт — это объём его внутреннего буфера. Пока возможно, данные будут забираться из USB и складироваться в этом пространстве. Но чтобы они ушли в синхронный канал, тот должен сообщить о готовности принимать их. Смотрим на соответствующие времянки.

qq9xljp4ma4jlpcprp5emofdraa.png

Итак. Когда у контроллера появляются какие-либо данные в FIFO, он роняет сигнал RXF. В ответ на это мы должны сначала уронить сигнал OE и продержать его в нуле минимум один такт (это следует скорее из описания к диаграмме, чем из самой диаграммы). Нам выдадут данные на шину, мы должны подтвердить их приём низким уровнем сигнала RD. И так — для всего кадра. Когда контроллер поднимет линию RXF, мы должны снять OE и RD. Пользоваться данными мы сегодня не будем. Для измерения скорости достаточно просто имитировать приём данных на стороне ПЛИС из FT2232H. Ну что ж. Для такой простенькой работы не нужна никакая процессорная система. Достаточно сделать вырожденный автомат, разработка которого займёт намного меньше времени, чем просто возня с подготовкой процессора и программы для него. Поэтому создаём проект, содержащий всего один SystemVerilog файл со следующим содержанием:

module JustRead(
input	 logic	clk,
input  logic	rxf_n,
output logic	oe_n,
output logic   rd_n
);

enum {IDLE,TRANSFER} state = IDLE;

always @ (posedge clk)
begin
	oe_n <= 1;
	rd_n <= 1;
	case (state)
		IDLE: begin
			if (rxf_n == 0)
			begin
				oe_n <= 0;
				state <= TRANSFER;
			end
		end
		TRANSFER: begin
		   if (rxf_n == 0)
			begin
				oe_n <= 0;
				rd_n <= 0;
			end else
			begin
				state <= IDLE;
			end
		end
	endcase
end

endmodule


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

module JustReadTB(
output logic	clk,
output logic	rxf_n,
input  logic	oe_n,
input  logic   rd_n
);

JustRead dut (
.clk,
.rxf_n,
.oe_n,
.rd_n
);

always 
begin
	clk = 1;
	#16;
	clk = 0;
	#16;
end


initial 
begin
	rxf_n = 1;
	#120;
	rxf_n = 0;
	#120;
	rxf_n = 1;
end
endmodule


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

pskwjww-komvozbyogvdndsukq8.png

В первом приближении, это соответствует тому, что требует документ. А фактические данные мы пока всё равно не будем обрабатывать.

Назначение ножек ПЛИС в этом проекте также не вызывает затруднений даже для правки через таблицу (переносить назначения из файла *.QSF будет дольше, а я постоянно акцентирую ваше внимание, что при разработке систем-однодневок под Redd экономия времени — приоритетная вещь).

kp8swq3ni8n0x02znmnexvjpfsk.png

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

В программе я сделал две функции. Первая ищет и открывает устройство. Кое-что я взял из прошлого теста, кое-что — позаимствовал из AN130:

FT_HANDLE OpenFT2232H()
{
	FT_HANDLE	ftHandle0;
	static FT_DEVICE ftDevice;

	// Перебираем все устройства с нулевого
	int nDevice = 0;
	while (true)
	{
		// Если устройство не открылось
		if (FT_Open(nDevice, &ftHandle0) != FT_OK)
		{
			// То считаем, что перед нами конец списка
			return 0;
		}

		// А что за устройство открылось?
		if (FT_GetDeviceInfo(ftHandle0,
			&ftDevice,
			NULL,
			NULL,
			NULL,
			NULL) == FT_OK)
		{
			// То, что мы ищем
			if (ftDevice == FT_DEVICE_2232H)
			{
				// Действия, которые я позаимствовал в AN130

				FT_SetBitMode(ftHandle0, 0xff, 0x00);
				
				usleep(1000000);

				//Sync FIFO mode 
				FT_SetBitMode(ftHandle0, 0xff, 0x40);

				FT_SetLatencyTimer(ftHandle0, 2);

				FT_SetUSBParameters(ftHandle0, maxBlockSize, maxBlockSize);

				return ftHandle0;

			}
		}
		// Закрыли это устройство
		FT_Close(ftHandle0);
		// Переходим к следующему
		nDevice += 1;
	}
}


Функцию измерения скорости мне, как любителю Windows, пришлось писать, постоянно сверяясь с Интернетом, так как обычно я пользуюсь классическими таймерами высокого разрешения из WIN32 API. Возможно, можно написать эффективнее, но это же программа-однодневка.

const int maxBlockSize = 0x100000;
uint8_t buf[maxBlockSize];
…
// Не передавать BlockSize более, чем 1М
double TestSpeed(FT_HANDLE	ftHandle0,int totalSize, int blockSize)
{
	if (blockSize > maxBlockSize)
	{
		return -1;
	}

	DWORD dwWrittenTotal = 0;
	timespec before;
	clock_gettime(CLOCK_REALTIME, &before);
	for (int i = 0; i < totalSize; i += blockSize)
	{
		DWORD dwWritten;
		FT_Write(ftHandle0, buf, blockSize, &dwWritten);
		// Учитываем фактически ушедший объём
		dwWrittenTotal += dwWritten;
	}
	timespec after;
	clock_gettime(CLOCK_REALTIME, &after);
	if (dwWrittenTotal < (DWORD)totalSize)
	{ 
		return -2;
	}
	// Это время в наносекундах
	uint64_t nsBefore = before.tv_nsec;
	uint64_t nsAfter = after.tv_nsec;

	// Там ещё секунды могли натикать
	nsAfter += (after.tv_sec - before.tv_sec) * 1000000000;
	
	// Учтём разницу
	nsAfter -= nsBefore;

	// получим вещественное время в секундах
	double res = ((double)nsAfter)/((double)1000000000);

	// А это - байт в секунду
	return ((double)dwWrittenTotal) / res;
}


Ну, и код, который выполняет основную функциональность, получился таким:

int main(int argc, char* argv[])
{
	FT_HANDLE	ftHandle0 = OpenFT2232H();

	if (ftHandle0 == 0)
	{
		printf("Cannot open device\n");
		return -1;
	}

	const int totalSize = 0x100000;
	static const int blockSizes[] = { 0x10,0x20,0x40,0x80,0x100,0x200,0x400,0x800,0x1000,0x2000,
	0x4000,0x8000,0x10000,0x20000,0x40000,0x80000,0 };
	for (int i = 0; blockSizes[i] != 0; i++)
	{
		double speed = TestSpeed(ftHandle0, totalSize, blockSizes[i]);
		printf("%d,%d\n", blockSizes[i], (int)(speed/1000.));
		int stop = 0;
	}


	// Всё, устройство можно закрыть
	FT_Close(ftHandle0);
	return 0;
}


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

16,59
32,110
64,237
128,490
256,932
512,1974
1024,3760
2048,5594
4096,10729
8192,16109
16384,20170
32768,24248
65536,26664
131072,28583
262144,29370
524288,29832


Сохраняем их в файле *.csv, загружаем в Excel, строим график зависимости скорости от размера блока.

_oepxe_wz65g7otcfeo3qete8xa.png

Предел равен 30 мегабайт в секунду. До теоретического максимума в 52 МБ/с далеко. Возможно, можно как-то ускорить, но оставим это читателям в виде практической работы. Главное, мы освоили все шаги работы с каналом и готовы связать ПЛИС с центральным процессором в единую систему.

Пока версталась статья, был найден документ AN_165, в котором сказано, что предельная скорость в режиме синхронного FIFO составляет 35 МБ/с. То есть, пространство для роста — именно до данной величины. Но оно всё равно есть.

Заключение


Мы познакомились с двумя стратегиями разработки и отладки программного кода, исполняемого на центральном процессоре комплекса Redd (средствами Microsoft Visual Studio и на виртуальной машине с ОС Linux). Также мы получили практические навыки работы с каналом, через который центральный процессор комплекса связывается с ПЛИС.

Судя по всему, теперь нам ничего не мешает передавать осмысленные данные от ЦП к ПЛИС и обратно (правда, в прошлой статье я уже писал эти слова).

Пример, содержащий простейшую «прошивку» для ПЛИС и программу, измеряющую скорость записи в USB, можно скачать тут.

© Habrahabr.ru