nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

"16-битный" передатчик UART

До сих пор у меня передатчик UART подсоединялся к 16-битной шине данных процессора, но брал из неё только младшие 8 бит, остальные игнорировал. Для передачи текстовой информации самое то, особенно с появлением байтового режима доступа к памяти. Но вот передавать 16-битные данные становится очень большой проблемой. Для дампа памяти ещё нашлось решение, как раз-таки через байтовый режим прочитать сначала все старшие байты, потом все младшие, а уже на компьютере их "склеить", но тоже костыль, что ни говори.

Всё-таки я уже решил: текстовую информацию буду выводить на ЖК-экранчик, чтобы наиболее наглядно продемонстрировать, что он уже "сам" всё обнаруживает и вычисляет. (когда оно подключено к компьютеру, могут возникнуть сомнения - может компьютер тоже вносит посильную лепту?) А уж если мы к этому делу приплели компьютер, надо уже переходить на передачу информации в "машинном" виде, тем более что рано или поздно предстоит заменить UART и RS485 на МКО, он же МКИО, он же Mil-Std 1553, он же ГОСТ Р 52070-2003, а там данные идут 16-битными словами.

То есть, я хочу, чтобы одна команда OUT, когда источником выбран UART, передавала бы два байта подряд. Какой сначала - старший или младший (endianness пресловутый) - это на наше усмотрение, главное потом на принимающей стороне не ошибиться.

Вроде бы халявная задача, а чего-то сегодня полдня соображал "как лучше" это сделать...


Первой идеей был "адаптер", стоящий между процессором и передатчиком UART, который принимает команду OUT, защёлкивает в свой регистр все 16 бит данных, а потом по 8 бит отправляет передатчику. Уже вроде бы и конечный автомат продумал, какие состояния должны быть и по каким условиям переключаться. Затем мысль пошла - а в обычном, 8-битовом режиме пущай тоже "пользу приносит", выступает в качестве небольшого буфера, чтобы процессор мог сразу несколько символов положить - и продолжить заниматься своими делами!

Тут подумалось, что это выходит ещё одна разновидность FIFO (первый вошёл - первый вышел), но позволяющий "бесплатно" навесить дополнительную функциональность. То есть, "в базовой комплектации" оно выглядит так:



В злополучном FIFO на ЛЭ входные данные могли быть защёлкнуты в ЛЮБОЙ регистр, для чего в каждом регистре должно быть два входа (один на сдвиг, второй на загрузку), ещё один для выбора и ещё один для разрешения работы - АККУРАТ ХВАТАЕТ 4 входов у базового ЛЭ этой ПЛИС.

А здесь данные ВСЕГДА поступают слева, но реализована логика, чтобы пустые ячейки поскорее "стравить". Верхние "крупные" по схеме регистры - 8-битные, содержат данные, которые мы туда помещаем. Нижние - 1-битные, говорят, занята ли данная ячейка. И далее логика управления сдвигом. Защёлкнуть данные в регистр можно в двух случаях:
- он пустой
- его содержимое на этом же такте будет защёлкнуто ещё правее.

Дальше я попробовал "на бумажке" изобразить работу. Сначала всё было пусто, и UART ничем не занят, и тут мы подали команду OUT 'A'. Разрешена работа всех регистров, поэтому на первую колонку будет защёлкнута эта буква 'A' и "единичка", означающая наличие команды.
К следующему такту они появятся на выходе первой колонки, и принято решение защёлкнуть их во вторую. Затем они появятся на выходе второй, тем самым придёт сигнал st=1 для передатчика UART - и он начнёт шевелиться. Если больше команд не поступало, то вслед за единичкой в нижнем регистре опять пойдут нули, а когда на передатчик поступает st=0, то и busy=0. Так что работа ВСЕХ регистров по-прежнему будет разрешена. И допустим, мы поместим новую команду OUT 'B'. Первый раз всё сработает точно так же, и к следующему такту она будет на выходе первого регистра. Ещё такт спустя - на выходе второго регистра, а внизу st=1. Поскольку UART ещё не передал байт (это довольно медленный процесс), будет busy=1, он запретит защёлкивать новые значения во вторую колонку регистров, но пока будет разрешено защёлкивать данные в первую колонку, т.к она была пустой.

Всё это напоминает автомобильную пробку - данные поступают слева, и двигаются направо, по одному регистру за такт, пока не "упрутся" в очередь из предыдущих, ещё не обработанных данных.

Прелесть этой конструкции в том, насколько "свободны" регистры данных - каждый использует всего 2 входа из 4! А значит, мы можем в регистр второй колонки добавить ещё один вход - 8 СТАРШИХ бит из шины данных - и доработать логику, чтобы оба байта защёлкивались одновременно, при условии что оба свободны (либо левый свободен, а правый ровно сейчас освободится).

Недостатки - такой буфер чуточку медленнее работает, чем "классический FIFO": когда он почти пустой, уходят лишние такты, чтобы протащить новое значение на выход. Думаю, есть много приложений, где это несущественно, и этот UART - одно из таких. На передачу одного БИТА на 921600 бод уходит 37 тактов, не думаю, что несколько тактов движения по этому буферу чего-то изменят.

Второй недостаток можно понять из аналогии с автомобильной пробкой. Если проследить, окажется, что разрешение сдвига самого первого элемента является функцией от состояния ИХ ВСЕХ. То есть, здесь как с переносом в сумматорах, чем длиннее цепочка - тем больше время распространения сигнала. Так что особенно длинные буферы делать не стоит. Хотя, из той же пробочной аналогии мы знаем выход из ситуации: если начались проблемы с таймингами, просто задержи эти сигналы разрешения на такт, и тогда если впереди наконец-то "дали зелёный", не все "машины" поедут синхронно, а только первые, а задние будут очухиваться!

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

Почему бы не сказать, что у нас есть 16-битный UART, имеющий форму:

0 b0 b1 b2 b3 b4 b5 b6 b7 1 0 b8 b9 bA bB bC bD bE bF 1


То есть, ничто иное, как два сообщения, "слепленных вместе".

В нашем обычном передатчике UART непосредственно за передачу отвечал 9-битный сдвиговый регистр:

	reg [8:0] ShiftReg = 9'b1111_1111_1;
	assign txd = ShiftReg[0];

always @(posedge clk) if (DoStart | r_ce)
	ShiftReg <= DoStart? {Data, 1'b0} : {1'b1, ShiftReg[8:1]};


Как видно, он во время старта заполняется нашими данными и стартовым битом - нулём в самом начале. А по мере работы происходит сдвиг вправо, а на освободившееся место поступают единицы, "автоматически" обеспечив и стоповый бит.

А теперь сделаем вот так:
	reg [18:0] ShiftReg = 19'b1111_1111_11_1111_1111_1;
	assign txd = ShiftReg[0];

always @(posedge clk) if (DoStart | r_ce)
ShiftReg <= DoStart? {Data[15:8],2'b01, Data[7:0], 1'b0} : {1'b1, ShiftReg[18:1]};


Останется только переделать счётчик, чтобы он отсчитал чуть больше бит, причём если выбирать, до скольких он считает - элементарно получим переключение между байтовым режимом (мало ли Hello, World какой) и 16-битным.


Завтра попробуем запустить. Сегодня ждал приезда заказчиков из РКК Энергия, приехал по такому случаю с утра, а оказалось, они так и не приедут, узнал об этом где-то час назад. Чего-то утром гораздо хуже мне соображается. Куда-то совсем не в ту степь полез, и только к 16:55 понял, как надо :) А тут уж и уходить пора.
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

  • Так ли страшно 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 

  • 5 comments