nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

"МКО (Mil-Std1553) через UART", часть 1

Громко подумали, пора начать ковыряние.

Вообще, я очень надеюсь, что написанный сейчас модуль практически без изменений подойдёт для "нормального" МКО. Там, разве что, ещё логику резервирования надо будет продумать, когда два приёмника, два передатчика, и ответ надо посылать по той шине, по которой мы получили запрос.

Сейчас идея, что этот контроллер работает независимо от процессора, напрямую обращаясь к памяти. Это "Оконечное Устройство" (ОУ, оно же Remote Terminal), т.е оно само не может инициировать информационный обмен, только отвечать контроллеру шины (КШ, Bus Controller).

И сделаем "задел" для реализации по ГОСТ 52070-2003 задания адреса оконечного устройства с помощью перемычек, хотя на первое время они будут "виртуальными" внутри ПЛИС :)

Не буду пытаться сделать "универсальный модуль на все времена", потому как неизбежно получится что-то вроде 1895ВА2Т, на которую под 300 страниц документации, где ещё попробуй разберись! Реализую лишь те функции, что нужны мне конкретно в этом приборе.

Начинаем традиционно с заголовка, тут он очень упитанный:
module MilStdRemoteTerminalController (input clk,
		//интерфейс с приёмником МКО
		input [15:0] D, input RXisData, input DataValid,
		//интерфейс с передатчиком МКО
		output [15:0] Q, output TXisData, output start, input TxBusy,
		//интерфейс с оперативной памятью (малого объёма, около 1000 слов)
		output [MemWidth-1:0] Addr, input [15:0] MemIn, input MemReady, output MemWrReq, output MemRdReq,
		//интерфейс с часами реал времени
		output sync, input [15:0] TimeStamp,
		//интерфейс с адресной заглушкой
		input [4:0] OurAddr, input AddrParity);
		
parameter MemWidth = 8;





С тактовой частотой всё понятно. Что интересно, конкретно этому модулю даже не особенно важно, какая именно это частота.

Приёмник, когда получает очередное слово, посылает DataValid=1, в этот самый такт можно взять 16 бит данных из D, а также узнать полярность синхроимпульса по RXisData. Когда RXisData=1, это слово данных, в противном случае командное слово либо ответное слово (их друг от друга не отличить по синхроимпульсу).

Когда мы собираемся отправить следующее слово на передачу, мы ставим start=1, и выдаём в Q 16 бит данных, а также ставим TXisData, чтобы задать полярность синхроимпульса. Если передатчик ещё не закончил с предыдущим словом, он выдаёт busy=1. Выдавать ли busy=1, если слово передаётся, но мы не торопимся передавать следующее (start=0) - не знаю, наверное можно его со start не увязывать, мы же сами этот start формируем, сами как-нибудь разберёмся.

В нашем UART вместо нормального МКО нет такой роскоши, как полярность синхроимпульса, что-нибудь придумаем. Наверное, для этого макета "договоримся", что за каждой посылкой (командное слово, слова данных) обязательно последует пауза, так что она будет "сбрасывать" RXisData в ноль, а первое за ней слово будет устанавливать RXisData опять в единицу. "Формат 3" (где контроллер шины просит одно устройство передать данные другому) и в лётном изделии применяться не будет, так что можно не переживать особо.

Далее идёт интерфейс с оперативной памятью. Я предполагаю, что будет сделан "рудиментарный" DMA (Direct Memory Access), для которого процессор всегда имеет приоритет, а "обслуживать" контроллер МКО будет по остаточному принципу. Это не так страшно, как звучит, т.к у нас сейчас тактовая частота 25 МГц (один такт 40 нс), за один раз мы берём из памяти все 16 бит, на передачу которых уходит 20 мкс (16 мкс на сами слова + 3 мкс синхроимпульс + 1 мкс бит чётности), т.е задержаться на несколько тактов в получении слова - ну вообще не проблема!

Мало того, шину адреса берём одну, как на чтение, так и на запись, хотя в ПЛИС память двухпортовая - можно задать два разных адреса и осуществлять запись с чтением одновременно (лишь бы не по одному адресу, тогда нужно внимательно курить мануалы, какое именно значение прочитается). Но пока, из жадности, ставлю одну шину, как и в процессоре, где мне не захотелось ставить два "формирователя эффективного адреса".

Когда надо прочитать слово из памяти, мы ставим нужный адрес и посылаем MemRdReq=1 ("запрос на чтение"). На том такте, где нам наконец-то выдадут искомое по входу MemIn, придёт также MemReady=1. Такие запросы предположительно будут идти по одному каждые 20 мкс, так что особенно "не запутаемся". (если бы мы заказывали целую прорву адресов, а потом, через неизвестное число тактов, они начали бы приходить, то путаница очень реальна. Но здесь заказал, получил - и тишина на сотни тактов).

И наконец, для записи мы посылаем MemWrReq=1, адрес выставляется нужный, а данные берутся непосредственно с выхода приёмника, только эти данные мы и будем записывать. Подтверждения записи мы даже не дожидаемся, что называется Fire and Forget. В смысле, что мы вполне уверены, что за 20 мкс наш запрос будет-таки удовлетворён, в общем-то в первые 200 нс :)

Далее, отдельным устройством у нас приходят "часы реального времени". Их вход D напрямую подключён к выходу приёмника, а вот sync мы формируем только в момент прихода слова данных из команды управления (КУ) "Синхронизация (с СД)". А наш вход TimeStamp подключён к выходу Q этих "часов", и используется в нужный момент для передачи этой метки времени в сообщении с целевой информацией. Это не просто "текущее время" (его контроллер шины и так знает, зачем его повторно генерить??), а "середина экспозиции" кадра, по которому мы и вычислили параметры сближения. Если бортовой вычислитель будет уметь использовать эту информацию - это очень серьёзное подспорье. А также это показатель, что мы не "зависли" наглухо, хотя вот тут тоже осторожность нужна. Надо, чтобы "середину экспозиции" защёлкивал именно процессор, чтобы знать: он сам не завис! Не смог это сделать - будем выдавать результаты с одной и той же, давно устаревшей меткой времени - и это будет "звоночком", что надо нас выключить и снова включить!

И, наконец, входы "адресной заглушки". Это мы исполняем пункт 4.4.1.2 "Поле ОУ" из ГОСТ Р 52070-2003:


Поле "Адрес ОУ" (разряды 4-8) должно содержать код адреса ОУ,
которому предназначено КС.
Каждому ОУ должен быть присвоен собственный адрес из кодов от 00000
до 11110. Адрес ОУ должен устанавливаться через внешний соединитель,
который является частью монтажа системы. Изменение собственного адреса
ОУ не должно требовать физической модификации или воздействия на любую
часть оборудования ОУ.
Способ контроля собственного адреса ОУ определяется разработчиком
интерфейсного модуля.
ОУ должно, как минимум, распознавать и контролировать собственный
адрес на достоверность при включении электропитания. Одиночный отказ
любого элемента не должен приводить к тому, чтобы ОУ определяло
собственный ложный адрес как достоверный. ОУ не должно отвечать на
любое сообщение, если оно определило свой собственный адрес как
недостоверный.
Адрес с кодом 11111 не должен быть применен в качестве собственного
адреса ОУ. Код 11111 может быть присвоен любому из ОУ в качестве общего
адреса в дополнение к собственному адресу. КС с кодом адреса ОУ 11111
называется групповой командой, а сообщение, содержащее групповую
команду, - групповым.


Поначалу я думал, что два прибора (основной и резервный) сидят на одной шине и имеют разные адреса. Тогда наличие такой заглушки - вещь неимоверно полезная! Это позволяет отгрузить заказчикам два прибора ШТАТНО, ещё один в ЗИП (запчасти, инструменты и принадлежности), и если в самый ответственный момент, перед стартом, один из приборов оказывается неисправным - его тут же заменяют. Это оказывается возможным, поскольку адрес прибора зашит не в нём самом, а в этой самой заглушке, которую в него вставляют. В противном случае пришлось бы поставлять два запасных, один на замену "левого" второй на замену "правого".

Впрочем, оказалось, что они сидят на разных шинах, а адрес один и тот же! Ну ладно, зато если что-то у них в сети поменяется, они смогут просто сделать новую заглушку, а приборы останутся теми же самыми.

Но ещё позже оказалось, что заглушку всё-таки должны сделать МЫ. А тогда, если они всё-таки решат адрес поменять - надо будет к нам обращаться на изготовление новой, и тогда я не шибко понимаю смысл всей этой движухи. С тем же успехом можно было бы сам приборчик отдать, а перемычки иметь запаяными прямо на плате. Ну да ладно, "любой каприз за ваши деньги". Хотите лишних 24 грамма (разъём и ответная часть) - пожалуйста. В общем-то, сейчас это далеко не главный источник "лишней массы", так что пущай живёт.

Мы используем 5-битный вход адреса OurAddr и вход чётности, который надо устанавливать так, чтоб сумма всех бит оказывалась НЕЧЁТНОЙ. Это даст нам совместимость с той самой 1895ВА2Т. Выбор НЕЧЁТНОГО значения оправдан: тем самым все "нулевые" входы, равно как и все, сидящие на лог. "1" становятся "вне закона", а именно такое поведение характерно, если мы забыли вставить адресную заглушку! Другое дело, у меня на заглушку выведены 5 бит адреса, 1 бит чётности и ОБЩИЙ ПРОВОД, а подтяжка на +3,3 должна делаться непосредственно в ПЛИС (у Воронежских есть такая опция), тогда неустановленная заглушка даёт адрес 11111, а это по-любому ШИРОКОВЕЩАТЕЛЬНЫЙ АДРЕС, который задавать нельзя! Так что и другая чётность сгодилась бы, но почему бы и нет :) Такой вот я Буриданов осёл - на каждом шагу муки выбора.

Наконец, с входами и выходами разобрались! Пока ещё не сделан интерфейс с внешней статической памятью, чтобы получить изображение. Возможно, он и не нужен, взамен процессор будет нам подготавливать кусочки и отправлять во "внутреннюю" память. Будем считать, что так и есть :)

И для очистки совести напишем хоть немного кода:
reg OurAddrOK = 1'b0;
always @(posedge clk)
	OurAddrOK <= (OurAddr != 5'b11111) & (^OurAddr^AddrParity);


Как раз по проверке своего адреса. Если OurAddrOK = 0, мы можем вообще ничего не делать, т.к не знаем, на какой адрес отвечать. Можно было его регистром не делать, но так немного "спокойнее", гораздо меньше шансов, что какой-то "мусор" придёт извне.

Даже получив адрес, мы не должны ничего делать, пока не придёт командное слово с нашим родным адресом. Давайте "разобьём" 16 бит на отдельные поля, для удобства:
wire [4:0] curWordAddr = D[15:11];		//адрес ОУ
wire curWordDoTransmit = D[10];			//признак "приём-передача" (1 значит мы должны передавать данные)
wire [4:0] curWordSubAddr = D[9:5];		//подадрес
wire [4:0] curWordWordsCount = D[4:0];	//число слов данных (0 означает 32), при подадресах 00000 или 11111 это "команды управления"


Буду воспринимать первые переданные биты как "старшие", последние как "младшие". Пусть в ГОСТе другая нумерация, слева направо, но там и вовсе первые 3 разряда это "синхроимпульс", так что ну их нафиг.

Очевидно, у нас должен быть регистр, выражающий состояние, и начальное состояние - это sIdle ("ждём"). Сколько ещё состояний - сейчас сообразим. Переход в какое угодно другое состояние должен произойти, когда пришло командное слово (т.е TXisData = 0) с нашим адресом.

Далее, мы должны определить, корректное ли пришло сообщение? Во-первых, это должна быть верная комбинация "приём/передача - подадрес - число слов". Далее, если данные передают в нашу сторону, нужно проверить заголовок (мы решили применить там коды Рида-Мюллера) на соответствие подадресу, и в самом конце - CRC.

Пожалуй, нужен регистр messageError, определяющий верность переданного нам сообщения. В состоянии sIdle он устанавливается в 0, а затем по любой ошибке устанавливается в 1.

Когда передача сообщения нам окончена, мы отправляем ответное слово, в которое включается этот самый регистр на месте флажка "ошибка в сообщении". А уже после него, если нужно, и если ошибки не было - отправляем данные, сколько запросили.


Нда, снова громко подумали, написав аж 15 строчек кода... Продолжение следует! Там уже возьмёмся всерьёз.
Tags: ПЛИС, работа, странные девайсы
Subscribe

Recent Posts from This Journal

  • Так ли страшно 0,99969 вместо 1?

    В размышлениях о DMA, о возможных последствиях "смешивания" старых и новых значений при выдаче целевой информации, опять выполз вопрос: насколько…

  • Как продлить агонию велотрансмиссии на 1500+ км

    Последний раз о велосипедных делах отчитывался в середине мая, когда прошёл год "велопробегом по коронавирусу". Уже тогда я "жаловался", что…

  • DMA для QuatCore

    Вот фрагмент схемы нашего "процессорного ядра" QuatCore: Справа сверху "притаилась" оперативная память. На той ПЛИС, что у меня есть сейчас…

  • "МКО через UART" в железе - 2

    Продолжим проверять этот модулёк. "Для закрепления пройденного", снова запрашиваем телеметрию, сначала 1 слово данных (командное слово 0x3701, и…

  • "МКО через UART" в железе

    Долго мучали эту штуковину на симуляции, пора уже в макет это прошить и посмотреть, как оно себя поведёт. Оставлю "основную схему" (процессор, SPI,…

  • "Ошибка на единицу" при передаче CRC

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

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments