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

  • Так есть ли толк в ковариационной матрице?

    Задался этим вопросом применительно к своему прибору чуть более 2 недель назад. Рыл носом землю с попеременным успехом ( раз, два, три, четыре),…

  • Big Data, чтоб их ... (4)

    Наконец-то стряхнул пыль с компьютерной модели сближения, добавил в неё код, чтобы мы могли определить интересующие нас точки, и выписать…

  • Потёмкинская деревня - 2

    В ноябре 2020 года нужно было сделать скриншот несуществующей программы рабочего места под несуществующий прибор, чтобы добавить его в документацию.…

  • Великая Октябрьская резня бензопилой

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

  • Очередная несуразность в единицах измерения

    Когда-то я написал программу PhysUnitCalc - калькулятор, умеющий работать с размерностями. Мне казалось, что я наступил уже на все грабли, которые…

  • Big Data, чтоб их... (3)

    "В предыдущих сериях": мой прибор выдаёт 6 значений: 3 координаты и 3 угла, т.е все 6 степеней свободы твёрдого тела. Причём ошибки измерения этих 6…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 5 comments