Синхронизация QuatCore с компьютером

Aug 17, 2021 19:28

Вообще, рано или поздно я хочу получить нечто, напоминающее работу синхронизации в старых добрых телевизорах: пока нет внешнего сигнала, оно уже "живёт своей жизнью", на чуть отличающейся частоте, поскольку совсем не работать нельзя. Но когда начнут идти запросы на получение целевой информации, то постараемся выйти на такой режим, чтобы новый кадр был экспонирован и обработан аккурат перед следующим запросом, чтобы свести задержку к минимуму, ведь "борт" интересует самая свежая информация, где мы находимся СЕЙЧАС, а не 100 мс назад!

Как именно это сделать - ещё предстоит подумать, там фотоприёмная матрица 1205ХВ014 "подложила свинью" - в ней не предусмотрена "внешняя синхронизация", считается "мы её включили" - и она начала фигачить кадр за кадром без перерывов. Как наиболее грамотно её всё-таки "прижать к ногтю" - надо будет и с разработчиками обсудить, и самим опробовать. Можно, к примеру, переставать на неё подавать тактовую частоту, дескать "один кадр она отдала" - теперь пущай время для неё замрёт, потом в нужный момент возобновим подачу, этот кадр выйдет "запоротым", зато следующий будет нормальным, и как раз в нужный момент :) А можно "нажать Reset", быстренько перенастроить - и пущай снова работает. Это, наверное, более мягко, и чуть проще, и надёжнее в каком-то плане.

А пока, чтобы упростить отладку, сделаем более грязно - без внешних сигналов мы просто будем останавливаться, и запускать работу только по приходу команды "Синхронизация (с СД)". Программа написана, теперь нужно переделать ввод-вывод, выкинуть из него уже ненужный UART, а вместо него "подсоединиться" к протокольному контроллеру и часам реального времени...

Вот старая "схема":




UART16bitReceiverTol (приёмник UART по 16 бит за раз, "толерантный" к разбросу частоты передатчика) можно выкинуть. Мультиплексор, выбирающий, откуда подать данные на процессор, из UART или из SPI, тоже становится не нужен, т.к мы получать ДАННЫЕ как таковые из слова синхронизации не собираемся, нам важен факт его прихода как таковой!

Также на свалку отправляется QuatCore16bitUARTtx (передатчик UART по 16 бит за раз). Взамен мы хотим подавать сигнал Mark на часы реального времени, "отметить текущий момент времени". Это гораздо проще: также шина данных не нужна, да и задержать нас эта операция никак не может! Если при отправке по UART мы сплошь и рядом оказываемся "слишком быстрыми" - уже пихаем следующее слово, когда и предыдущее ещё не отправилось, то здесь мы можем друг за дружкой эти "метки" ставить, хотя на деле будем ставить по одной на "кадр".

Так что логический элемент OR на 3 входа (справа), формирующий SrcStallReq, можно сократить до 2 входов - от SPI и от LCD.

Пока что выходит так:



И теперь надо "залезть" в модуль QuatCoreIOselector и его тоже "проредить". Так он выглядел до сих пор:

//DestAddr==0000_xxxx : 'OUT'
//DestAddr==0001_xxxx : 'SIO' (select input/output device)

//xxxx_xxxx_xxxx_xx00 - UART
//xxxx_xxxx_xxxx_xx01 - LCD
//xxxx_xxxx_xxxx_xx1x - SPI

//also selecting SPI device
//xxxx_xxxx_xxxx_0xxx - Ethernet
//xxxx_xxxx_xxxx_10xx - SD
//xxxx_xxxx_xxxx_11xx - ADC

//also selecting Bit/Byte mode for UART
//xxxx_xxxx_xxx0_xxxx - byte mode (default)
//xxxx_xxxx_xxx1_xxxx - bit mode (sending image like BW instead of 8-bit grayscale)

//IN command is
//SrcAddr == 1001_0xxx
//(ALU Src was 100x_xxxx, now it is 1000_xxxx)

module QuatCoreIOselector (input clk, input [7:0] SrcAddr, input [7:0] DestAddr, input [15:0] DataBus, input DestStall, input SrcStall, input SrcDiscard, input SPI_busy,
input SPI_RX_busy, input UART_RX_busy,
output DestStallReq,
output UARTtxEN, output LCD_EN, output SPItxEN, output UARTrxEN, output SPIrxEN,
output reg [1:0] SPIdevice = 2'b0, output SCKen, output reg isBitMode = 1'b0);

parameter enableLCD = 1'b1;
parameter enableUART = 1'b1;
parameter enableSPI = 1'b1;

localparam HasChoice = enableSPI | ((enableLCD + enableUART + enableSPI) > 1);

wire isSelection = (DestAddr[7:4] == 4'b0001) & (~DestStall) & HasChoice;
wire isIO_out = (DestAddr[7:5] == 3'b000) & ((~DestAddr[4]) | (~HasChoice)) & (~DestStall);
wire isIO_in = (SrcAddr[7:3] == 5'b1001_0)&(~SrcStall)&(~SrcDiscard);

reg [1:0] sel = enableUART? 2'b00:
enableLCD? 2'b01:
2'b10;

reg [1:0] shadowSPI = 2'b0;

always @(posedge clk) if (isSelection) begin
sel <= DataBus[1:0];
shadowSPI <= DataBus[3:2];
isBitMode <= DataBus[4];
end

always @(posedge clk) if (~SPI_busy)
SPIdevice <= shadowSPI;

assign UARTtxEN = (sel==2'b00) & isIO_out & enableUART;
assign LCD_EN = (sel==2'b01) & isIO_out & enableLCD;
assign SPItxEN = sel[1] & isIO_out & enableSPI;

wire UARTrxSelected = (sel==2'b00) & enableUART;
wire SPIrxSelected = sel[1] & enableSPI;

assign UARTrxEN = UARTrxSelected & isIO_in;
assign SPIrxEN = SPIrxSelected & isIO_in;

reg busy = 1'b1;
always @(posedge clk)
busy <= (SPI_RX_busy & SPIrxSelected | UART_RX_busy & UARTrxSelected);

assign DestStallReq = isIO_in & busy;

assign SCKen = enableSPI & sel[1]; //по-хорошему сделать регистром и

endmodule

С передатчиком UART менять практически ничего не нужно. Ну разве что переименуем выход UARTtxEN в RTCmark. Ещё там был 1-битный регистр для переключения между 8-битным и 16-битным режимом передачи, он сейчас совсем не нужен, можно выкинуть.

Вход UART_RX_busy переименуем в MilStdSync - именно сюда будет приходить единичный импульс, когда приходит команда "Синхронизация (с СД)".

Регистр busy был сделан из-за того, что мультиплексор, выбирающий между выходом SPI и UART, имел в своём составе "защёлку" (иначе комбинаторные пути слишком длинные, не вписываемся в 25 МГц), поэтому и "сигнал занятости" надо было задержать на один такт, чтобы процессор "успокоился" и принял данные, когда они реально появятся на его входе.

Но сейчас-то мы этот мультиплексор выкинули, значит и регистр busy надо выкинуть!

И осталось сообразить, как организовать синхронизацию по команде "Синхронизация (с СД)", нужен ли для этого какой-то регистр? Мы предполагаем, что процессор будет справляться со своей задачей СУЩЕСТВЕННО БЫСТРЕЕ, чем ему отводится времени между соседними словами синхронизации. Поэтому к каждой следующей синхронизации процессор будет подходить "первым", и должен застрять на команде IN, и стоять на ней, пока на вход MilStdSync не поступит единичка - тут же нас "отпустит". Если бы процессор мог "запаздывать" и обязан был проскочить команду IN за 1 такт, если единичка уже поступила ранее - тогда нам бы понадобился регистр, запоминающий, что "импульс уже был". А так - не нужен, лишнее усложнение. В итоге сигнал DestStallReq формируется следующим образом:

wire busy = (SPI_RX_busy & SPIrxSelected | ~MilStdSync & UARTrxSelected);

assign DestStallReq = isIO_in & busy;

В каждый момент времени может быть выбрано один из 3 "интерфейсов": SPI (на макете к нему подсоединены Ethernet-контроллер, SD-карточка и медленный АЦП, в лётном изделии будет подсоединена фотоприёмная матрица), LCD (только на макете, для ЖК-экранчика в 20x4 символа) и UART, который нынче мы "переиначили" в RTC. Но LCD мы "волевым решением" сделали только на передачу, этого вполне хватает для работы, поэтому на приём оставалось только 2 "интерфейса". Вот мы и описываем занятость одного и другого. По SPI всё осталось как было, а по UART, ныне RTC, занятость показана всегда, кроме одного такта, когда прибывает импульс синхронизации.

Вот и выходит: по команде IN сразу же "зажигается" DestStallReq, который приостанавливает работу процессора, пока не поступит долгожданный импульс. Тогда мы страгиваемся с места со злополучной команды. Дёшево и сердито!

Получившийся код модуля:

module QuatCoreIOselectorV2(input clk, input [7:0] SrcAddr, input [7:0] DestAddr, input [15:0] DataBus, input DestStall, input SrcStall, input SrcDiscard, input SPI_busy,
input SPI_RX_busy, input MilStdSync,
output DestStallReq,
output RTCmark, output LCD_EN, output SPItxEN, output SPIrxEN,
output reg [1:0] SPIdevice = 2'b0, output SCKen);

parameter enableLCD = 1'b1;
parameter enableUART = 1'b1;
parameter enableSPI = 1'b1;

localparam HasChoice = enableSPI | ((enableLCD + enableUART + enableSPI) > 1);

wire isSelection = (DestAddr[7:4] == 4'b0001) & (~DestStall) & HasChoice;
wire isIO_out = (DestAddr[7:5] == 3'b000) & ((~DestAddr[4]) | (~HasChoice)) & (~DestStall);
wire isIO_in = (SrcAddr[7:3] == 5'b1001_0)&(~SrcStall)&(~SrcDiscard);

reg [1:0] sel = enableUART? 2'b00:
enableLCD? 2'b01:
2'b10;

reg [1:0] shadowSPI = 2'b0;

always @(posedge clk) if (isSelection) begin
sel <= DataBus[1:0];
shadowSPI <= DataBus[3:2];
end

always @(posedge clk) if (~SPI_busy)
SPIdevice <= shadowSPI;

assign RTCmark = (sel==2'b00) & isIO_out & enableUART;
assign LCD_EN = (sel==2'b01) & isIO_out & enableLCD;
assign SPItxEN = sel[1] & isIO_out & enableSPI;

wire UARTrxSelected = (sel==2'b00) & enableUART;
wire SPIrxSelected = sel[1] & enableSPI;

assign SPIrxEN = SPIrxSelected & isIO_in;

wire busy = (SPI_RX_busy & SPIrxSelected | ~MilStdSync & UARTrxSelected);

assign DestStallReq = isIO_in & busy;

assign SCKen = enableSPI & sel[1]; //по-хорошему сделать регистром и

endmodule

Синтезируется успешно в 17 ЛЭ, причём регистров только 6, остальное комбинаторика. Ну и пускай...

Создаём под этот модуль "схемотехнический символ" и размещаем на "схему" (Block Diagram File) QuatCore_GPU_v2.bdf:



Ничего особо интересного: лишние входы и выходы убрались, чуть названия поменялись. Синтезируется это безобразие (ядро + видеообработчик + периферия в лице SPI и LCD) в 1411 ЛЭ, а фиттер грозно ругается:



Переборщил с "отладочными выводами"! Ну да, Гарвардская архитектура она такая, тут отдельно адрес для ПЗУ, отдельно для ОЗУ, а тут ещё внешняя статическая память с 20-битной адресацией и кучей управляющих сигналов! Ну да ладно, не важно, это у нас промежуточная схема, надо перейти уже на "верхний уровень":



Этот обновлённый проект синтезируется в 1730 ЛЭ (1702 ЛЭ перед Place&Route, а в ПЛИС, куда я бы хотел влезть, 2800 ЛЭ), тогда как перед ковыряниями синтезировался в 1733. Похоже на правду: лишний UART мы выкинули, но RTC в кои-то веки подключили, раньше-то при "заземлённом" входе Mark квартус удалял почти всё содержимое модуля, т.к результат его работы никуда не шёл. Вот примерно и вышло столько же.

Предельная частота: 26,95 МГц, тогда как мне достаточно 25 МГц, устраивает.

Что ж, всё готово для проверки работы процессора в связке с информационным обменом! Можно начать делать ставки, как скоро оно заработает. Думаю, как только заработает - напишу заявление на отпуск. Будем считать, что для отладки этого безобразия ещё и в комплекте с обработкой изображения нужно ещё 2 недели. Успею до отпуска запустить - будет шикарно. Не успею - виной тому недостаток отдыха, чего-то производительность труда снизилась, я последний раз был в отпуске в августе 2019 года. А значит, всё равно надо отдохнуть!

странные девайсы, ПЛИС, программки, работа

Previous post Next post
Up