3. В зависимости от готовности последнего модуля цепочки выдавать данные загружать результат работы цепочки в блок выходных данных.
Если нужно оттестировать такой модуль, что выдает выходное значение практически сразу после получения входных данных, то выгрузка данных из памяти и загрузка выходных значений должна происходить параллельно. Для модулей, имеющих внутренние буферы памяти, загрузка выходных значений в память происходит по сигналу готовности выходных данных.
Например, если нужно проверить какую-нибудь комбинационную схему, допустим, n-разрядный инвертор, то выгрузка входных данных и загрузка выходных начинаются параллельно по сигналу завершения считывания служебной информации.
Если же нужно проверить модуль, имеющий внутренний буфер памяти, например, пороговое устройство, загрузка выходных данных начнется по готовности их выдачи со стороны модуля, в случае этого конкретного устройства – после последнего значения на входе.
Далее приведены описания программ ARM-части и FPGA-части, а также описание их соединения друг с другом.
Для соединения компонентов модуля сопряжения между собой используется средство Platform Designer программы Quartus Prime Lite. В нем же объявляются блоки памяти на чипе.
Размерности блоков памяти на чипе следующие:
- блок служебной информации: 5 однобайтных слов.
- блоки входных данных: по 256 двухбайтных слов в каждом.
- блок выходных данных: 512 четырехбайтных слов.
Увеличение разрядности выходных данных в 2 раза обусловлено допустимостью операции умножения на выходах некоторых модулей цепочки.
Доступ ARM-компонента и FPGA-компонента к одним и тем же блокам памяти достигается путем объявления двухпортовой памяти, тактируемой одним тактовым сигналом, и выделения ARM- и FPGA-компонентам отдельных портов. Соединение компонентов показано на рисунке 2.2.
Рисунок
2.2 – Соединение компонентов в Platform
Designer
Здесь hps_0 – ARM-компонент, ocm_service, ocm_data_input_re, ocm_data_input_im, ocm_data_output_re – блоки памяти. Один порт блоков памяти соединяется с hps_0, другой экспортируется для использования в FPGA. Блокам памяти назначаются базовые адреса, впоследствии используемые в ARM-сервере.
Полная схема соединения компонентов в Platform Designer приведена в приложении A.
Сервер написан с помощью языка C с использованием Linux-библиотеки socket, а также с использованием API для HPS компании Altera.
Форматы датаграмм, принимаемых от клиенты, представлены на рисунках 2.3 (служебная датаграмма) и 2.4 (датаграмма данных).
Рисунок 2.3 – Формат служебной датаграммы
Здесь number_of_channels – количество каналов радара, first_block_id – номер первого блока цепочки, last_block_id – номер последнего блока цепочки, data_length_h – старший байт количества данных, data_length_l – младший байт количества данных. Необходимость хранить количество данных в двух байтах обусловлена размерностью входных данных – максимум 512 отсчетов на канал, число 512 в один байт не умещается.
Рисунок 2.4 – Формат датаграммы данных
Здесь data_re – данные для первого блока памяти, data_im – данные для второго блока памяти. Всего датаграмма содержит до 256 четырехбайтных слов, каждое из которых затем делится на два двухбайтных слова и записывается в соответствующий блок памяти.
Также сервер формирует и отправляет датаграмму выходных данных, которая содержит до 512 четырехбайтных слов.
Код программы сервера приведен в приложении Б.
Для взаимодействия с памятью на чипе со стороны FPGA в соответствии со структурным подходом к проектированию разработаны три отдельных подмодуля: подмодуль для выгрузки служебной информации (eth_service), подмодуль для выгрузки и передачи входных данных в модули цепочки первичной обработки (eth_read), подмодуль для загрузки выходных данных цепочки первичной обработки в память на чипе (eth_write). Разработка модулей велась с помощью ЯПА Verilog. Выбор именно этого языка для описания модулей обусловлен прошлым знакомством с ним в рамках учебного плана, а также более простым, чем у альтернатив, написанием testbench. Оформление производилось в соответствии с правилами документации проектов для ПЛИС [20] и требованиями по единой системе программной документации. [21, 22]
Схематические обозначения подмодулей представлены на рисунке 2.5.
Рисунок
2.5 – Подмодули взаимодействия с памятью
на чипе
Каждый модуль представляет из себя конечный автомат, выполняющий операции с блоком памяти на чипе. Рассмотрим каждый подмодуль подробнее.
Листинг 2.1 содержит заголовок модуля eth_service.
Листинг 2.1 – Объявление и порты подмодуля eth_service
Данный подмодуль производит выгрузку служебной информации из служебного блока памяти на чипе и запись ее в регистры в FPGA-части для дальнейшего использования, до начала работы устанавливает выходной сигнал finished, начинает работу по нижнему уровню входного сигнала reset, в конце работы сбрасывает сигнал finished. Взаимодействие с блоком памяти на чипе производится через двунаправленные порты service_address (шина адреса блока памяти), service_readdata (шина считывания данных с блока памяти), service_write (флаг записи данных в блок памяти, требует отдельного выставления после каждой смены адреса). Подмодуль тактируется входным сигналом clock и имеет выходами считанные значения служебных признаков (number_of_channels, first_block_id, last_block_id, data_length).
Заголовок модуля eth_read представлен в листинге 2.2.
Листинг 2.2 – Объявление и порты подмодуля eth_read
Данный
подмодуль производит выгрузку данных
из одного из блоков входных данных. Как
и подмодуль eth_service,
eth_read
имеет
три двунаправленных порта для
взаимодействия с блоком памяти –
input_address(шина
адреса),
input_readdata(шина
считывания данных),
input_write(флаг
записи данных). Подмодуль начинает
работу по нижнему уровню входного
сигнала reset
и
до начала работы устанавливает выходные
сигналы input_finished
и
done.
Первый
сигнал модуль сбрасывает по окончании
считывания блока данных, начальный
адрес begin_addr
и
размер addr_span
которого
задаются соответствующими входами,
второй сигнал модуль импульсно сбрасывает
после каждого считанного значения,
считываемое
значение подается на выходную шину
read_data_output,
ширина
которой задается параметром
read_data_output_length.
Подмодуль тактируется сигналом clock.
В листинге 2.3 представлен заголовок модуля eth_write.
Листинг 2.3 – Объявление и порты модуля eth_write
Данный
подмодуль производит загрузку значений
на выходе тестируемого модуля (тестируемой
цепочки?) в блок памяти на чипе для
выходных данных. Как и предыдущие два
подмодуля, eth_write
взаимодействует
с блоком памяти на чипе через три
двунаправленных порта: шину адреса
output_address,
шину
записи данных в блок output_writedata
и флаг записи данных output_write,
который
выставляется для определенного адреса
после произведенной операции записи.
Область записи данных в блок памяти
задается входами begin_addr(начальный
адрес) и
addr_span(количество
данных для записи). Подмодуль до начала
работы выставляет выходной сигнал done,
начинает работу по нижнему уровню
сигнала reset,
по
окончании работы сбрасывает сигнал
done.
Данные
для записи в память подаются на шину
write_data_input,
ширина
которой определяется параметром
write_data_input_length.
Подмодуль
тактируется сигналом clock.
Для проверки правильности работы аппаратурных модулей была проведена симуляция в среде ModelSim. В качестве блоков памяти выступают пары вида «адрес - массив регистров», где адрес выступает индексом массива. Рассмотрим два примера, которые были описаны выше:
1. Комбинационный элемент, в данном случае возьмем 8-разрядный инвертор. Это простой пример, на котором можно промоделировать базовую функциональность и в дальнейшем экспериментально проверить связь между внешним устройством и отладочной платой.
2. Модуль, имеющий внутреннюю память, в данном случае – пороговое устройство. Этот пример уже имеет непосредственное отношение к проекту радара и позволит промоделировать и проверить модуль в реальных условиях.
Схематическое изображение соединения инвертора и подмодулей модуля сопряжение представлено на рисунке 2.6.
Рисунок 2.6 – Схема соединения модуля сопряжения и инвертора
В качестве данных для проверки была взята последовательность из 10 8-разрядных шестнадцатеричных чисел:
[0xFE, 0x54, 0x12, 0x32, 0x8A, 0xBA, 0x8C, 0xDE, 0x92, 0x11];
На выходе модуля eth_write в итоге ожидается последовательность:
[0x01, 0xAB, 0xED, 0xCD, 0x75, 0x45, 0x73, 0x21, 0x6D, 0xEE]
Результат моделирования представлен на рисунке 2.7.
Рисунок 2.7 – Результат моделирования для примера 1
Продолжение рисунка 2.7
Сигнал output_enable подается на вход reset модуля eth_write и формируется с появлением первых данных на входе инвертора (шина input_data), выход инвертора – шина output_data. Шина input_data является выходом read_data_output модуля eth_read, а шина output_data – входом write_data_input модуля eth_write. В результате моделирования видно, что данные проходят через модули верно, на выходе output_writedata формируются верные значения.
Схематическое изображение порогового устройства представлено на рисунке 2.8.
Рисунок 2.8 – Пороговое устройство
На вход порогового устройства подается массив N-разрядных данных (шина input_data), постоянный коэффициент из ПЗУ (ROM_coefficient), а также данные для сравнения (input_to_compare). Один из параметров модуля, rank, является рангом для выбора значения из input_data. Остальные параметры модуля для данной задачи рассматривать излишне.
Схематическое изображение соединения модуля порогового устройства и подмодулей модуля сопряжения представлено на рисунке 2.9.
Рисунок 2.9 – Схема взаимного соединения модулей
Пороговое устройство тактируется сигналом rtc_divided_clock с частотой, равной половине главной частоты CLOCK_50.