nabbla (nabbla1) wrote,
nabbla
nabbla1

Category:

Мучаем 5576ХС4Т, часть 'hC - полудуплексный UART

Часть 0 - покупаем, паяем, ставим драйвера и софт
Часть 1 - что это вообще за зверь?
Часть 2 - наша первая схема!
Часть 3 - кнопочки и лампочки
Часть 4 - делитель частоты
Часть 5 - подавление дребезга кнопки
Часть 6 - заканчиваем кнопочки и лампочки
Часть 7 - счетчики и жаба
Часть 8 - передатчик UART
Часть 9 - Hello, wolf!
Часть 'hA - приёмник UART
Часть 'hB - UART и жаба
Часть 'hC - полудуплексный UART.
Часть 'hD - МКО (МКИО, Mil-Std 1553) для бедных, введение.
Часть 'hE - приёмопередатчик МКО "из подручных материалов" (в процессе)
Часть 'hF - модуль передатчика МКО
Часть 'h10 - передатчик сообщений МКО
Часть 'h20 - работа с АЦП ADC124s051
Часть 'h21 - преобразование двоичного кода в двоично-десятичный (BCD)
Часть 'h22 - Bin2Bcd с последовательной выдачей данных
Часть 'h23 - перемножитель беззнаковых чисел с округлением
Часть 'h24 - перемножитель беззнаковых чисел, реализация
Часть 'h25 - передаём показания АЦП на компьютер
Часть 'h26 - работа над ошибками (быстрый UART)
Часть 'h27 - PNG и коды коррекции ошибок CRC32
Часть 'h28 - передатчик изображения PNG
Часть 'h29 - принимаем с ПЛИС изображение PNG
Часть 'h2A - ZLIB и коды коррекции ошибок Adler32
Часть 'h2B - ускоряем Adler32
Часть 'h2C - формирователь потока Zlib
Часть 'h2D - передаём сгенерированное PNG-изображение
Часть 'h2E - делим отрезок на равные части
Часть 'h2F - знаковые умножители, тысячи их!
Часть 'h30 - вычислитель множества Мандельброта
Часть 'h31 - ускоренные сумматоры
Часть 'h32 - ускоренные счётчики (делаем часы)
Часть 'h33 - ускоряем ВСЁ
Часть 'h34 - ускоренные перемножители
Часть 'h35 - умножители совсем просто
Часть 'h36 - уравновешенный четверичный умножитель




В прошлый раз мы по сути реализовали дуплексный канал обмена - приёмник и передатчик выступали как два независимых устройства, способных работать одновременно. Такая одновременная работа возможна, когда мы имеем дело с преобразователями USB в UART, наподобие cp2102, или при использовании RS232 / реального COM-порта - во всех этих случаях у нас есть отдельный провод на передачу и отдельный на приём.

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

И протоколы обмена информацией зачастую, ради простоты и универсальности, подразумевают именно полудуплексный режим, "запрос-ответ", даже если конкретная аппаратная реализация позволяет полный дуплекс.

В этой части рассмотрим модуль приёмопередатчика конкретно для полудуплексного режима работы, который за счёт объединения общих узлов получится компактнее, чем два отдельных модуля, а также будет совместим с приёмопередатчиком (драйвером) RS485.

Ох, и доставил же он мне проблем, до 6 утра его отлаживал, зато "многое понял".

Но первым делом небольшой должок с передатчиком UART...


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

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

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

`include "math.v"

module SimpleUARTtransmitter (input clk, input st, input [7:0] Data, output txd, output ready);

 parameter CLKfreq = 80_000_000;
 parameter BAUDrate = 512_000;

 localparam DividerBits = `CLOG2(CLKfreq / BAUDrate);
 localparam Limit = CLKfreq / BAUDrate - 1;
						
 reg [DividerBits - 1 : 0] FreqDivider;							
							
 reg [7:0] ShiftReg = 8'b1111_1111;

  `define txB1		4'b0000
  `define txB2		4'b0001
  `define txB3		4'b0010
  `define txB4		4'b0011
  `define txB5		4'b0100
  `define txB6		4'b0101
  `define txB7		4'b0110
  `define txB8		4'b0111
  `define txStop	4'b1000
  `define txIdle 	4'b1001
  `define txStart 	4'b1111

  reg [3:0] State = `txIdle;

  wire isIdle = State[3]&(~State[1])&State[0];

  wire ce = ((FreqDivider&Limit) == Limit);

  assign txd = 	~State[3]? ShiftReg[0] : ~State[2];

  assign ready = State[3] & (~State[2]) & ce;

  always @(posedge clk) begin
//    FreqDivider <= isIdle | ce ? 1'b0: FreqDivider + 1'b1;
    FreqDivider <= isIdle | ce | st? 1'b0: FreqDivider + 1'b1;
		
    State <= st? `txStart : ce? State + 1'b1 : State;
	
    ShiftReg <= st?                Data :
		(~State[3] & ce)?  (ShiftReg >> 1'b1) :
   				   ShiftReg;
											
  end
endmodule


Смотрим, что происходило, если импульс st приходил раньше времени, когда передатчик заканчивает передачу стопового бита.

Уже к следующему такту мы переходим в режим передачи стартового бита - всё верно.

В сдвиговый регистр заносится новый байт - тоже верно.

Лишь одна необходимая вещь не происходит - не сбрасывается делитель частоты. Он должен сбрасываться при переходе в режим ожидания, но мы не успеваем до него добраться. Поэтому стартовый импульс оказывается чрезвычайно коротким, за ним начинается сама посылка, и дальше - как повезёт, некоторые символы приёмник забракует совсем, в некоторых собьётся на одну позицию, что мы и наблюдали.

Предложенное решение - сбрасывать счётчик ещё и по стартовому импульсу.

В такой реализации мы строго выдерживаем интервалы времени между стартовым битом, битами данных и началом стопового бита, но вот сам стоповый бит может оказаться короче, чем мы хотели изначально. Как мне кажется, это не страшно. Даже если мы создадим зверскую цепочку UART-приёмников и передатчиков, передающих друг другу эти символы "на лету", без буферов, все они в итоге подстроятся под самый первый передатчик, то есть накопления дополнительных задержек и искажений по линии не происходит. И если первый передатчик (в нашем случае, он сидит в микросхеме cp2102) работает хоть сколько-нибудь стабильно - вся цепочка будет работать нормально. Если же нет - выкиньте этот передатчик на помойку и поставьте нормальный!

Полудуплекс

Нельзя не заметить, что приёмник и передатчик устроены очень похоже: в каждом стоит делитель частоты (причём в одинаковое число раз), в каждом - сдвиговый регистр, который перемещает данные вправо, только приёмник заносит в него значения в старший бит, а передатчик - забирает из младшего. И в каждом используется регистр состояния, который может принимать 11 различных значений - приём/передача восьми бит данных, приём/передача стартового бита, стопового бита и режим ожидания.

Покажем, как можно сделать объединённый модуль приёма/передачи в полудуплексном режиме.

Сперва, заголовок:
module UARThalfDuplex (	input clk, input startTx, input [7:0] DataIn, input rxd, 
			output DataOutReady, output reg [7:0] DataOut, output txd, output reg we = 0,
			output FrameError, output LineBreak, output TxReady);

Количество "проводов" немножко пугает, но все они нужны:

clk - тактовая частота, как обычно,
startTx - сюда будем подавать единичный импульс для запуска передатчика,
DataIn - в момент подачи единичного импульса на startTx, сюда мы заносим байт, который надо передать,
rxd - вход приёмника

DataOutReady - здесь появляется единичный импульс, когда мы приняли очередной байт данных,
DataOut - те самые принятые данные,
txd - выход передатчика,
we - write enable, переводит драйвер (приёмопередатчик) RS485 в режим передачи. По умолчанию, разумеется, мы в режиме приёма.

FrameError - выдаёт единичный импульс, если байт был принят ошибочно (стоповый бит не был равен единице), что может свидетельствовать о неправильных настройках канала или каких-то жестоких помехах,
LineBreak - выдаёт единичный импульс вместе с FrameError, если логический ноль держался на линии более 10 периодов подряд, как будто бы в ней обрыв.
TxReady - выдаётся единичный импульс, когда передача байта окончена.

Поначалу мне хотелось скопировать код из приёмника UART - представлять все состояния с помощью 4 бит. Но оказалось: комбинаторная часть становится настолько сложной, что при синтезе оно начинает непотребно расползаться.

Поэтому в этот раз мы практически выйдем на магистральный путь, разделив текущее состояние модуля на 4 основных режима (ожидание, старт, данные, стоп) и на счетчик бит данных (от 0 до 7), и отдельно бит "приём/передача" (we, write enable).

4 основных режима мы выразим в унитарном коде, он же one-hot:

	parameter CLKfreq = 80_000_000;
	parameter BAUDrate = 512_000;

	localparam sIdle = 4'b0001;
	localparam sStart = 4'b0010;
        localparam sData = 4'b0100;
        localparam sStop = 4'b1000;
	reg [3:0] State = sIdle;
	
	wire isIdle = State[0];
	wire isStart = State[1];
	wire isData = State[2];
	wire isStop = State[3];

	wire ce;


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

wire isIdle и остальные строки с точки зрения синтезатора вообще ничего не делают, ему что State[0], что isIdle - одно и то же. Просто мы пытаемся сделать код чуточку более читаемым.

Сделать "прибавление единички" в унитарном коде даже проще, чем в бинарном коде - надо всего лишь устроить циклический сдвиг влево.

ce - это, как обычно, clock enable, будет управлять переключением между состояниями.

И поскорее сделаем ещё одну вещь, "защёлкнем" вход rxd. Объявим регистр r_rxd, а в always-блоке добавим следующую строку:
  r_rxd <= rxd


Именно r_rxd мы должны использовать во всём нашем коде, иначе долгие бессонные ночи вам обеспечены. Приёмник работал очень неустойчиво, на симуляторе всё было хорошо, на ПЛИС - глючило, непонятно где. Когда я вывел на светодиоды биты состояния isIdle, isStart, isData, isStop, то с удивлением обнаружил - через некоторое время они все потухают, хотя такой переход попросту невозможен! Самое логичное объяснение - сигнал rxd меняется посередине такта, что приводит к некорректному присвоению нового состояния. Стоило только вместо rxd повсюду поставить r_rxd, как проблема исчезла, всё заработало как по маслу.

Когда мы пишем код конечного автомата в виде простыни case / if / else, мы разбиваем (анализируем) исходную задачу по времени: "сначала надо сделать так, а потом - вот так, пока здесь не появится единичка, а потом - эдак".

Но можно разбить задачу не по времени, а по блокам - берём каждую составную часть и начинаем думать, на какие воздействия она вообще откликается и каким образом. Поначалу может быть странно, но есть в этом своё извращённое удовольствие.

Начнём с счётчика-делителя частоты UARTfreqDivider, объявим его в нашем модуле:
  UARTFreqDivider divider (.clk (clk),
			   .sclr (isIdle | startTx),
			   .DoHalf (isIdle & (~r_rxd) & (~startTx)), //if we're paranoic
//			   .DoHalf (isIdle & (~r_rxd)), //if we're very greedy, saves 1 LE
			   .cout (ce));
						
  defparam
    divider.CLKfreq = CLKfreq,
    divider.BAUDrate = BAUDrate;


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

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

Здесь мы ещё и заложились на редчайший случай, когда ровно в момент подачи запускающего импульса на передачу, на линии появляется "нолик", из-за чего наш стартовый бит укорачивается вдвое. Появись он на такт раньше (состояние изменится на sStart) или на такт позже (уже выставился we=1) - уже ничего не случится, нужно попасть именно в те 12,5 наносекунд, что длится один такт! К примеру, если мы используем RS232 или наш любимый конвертер USB-UART, и решили с помощью этого модуля передать данные, хотя и со стороны компьютера бежит непрерывный поток данных на скорости 256 кбит/с, то каждая 312-я из наших посылок могли бы повреждаться, не добавь мы выражение & (~startTx) . Во многих приложениях можно эту штуку выкинуть. К примеру, если мы собрались передавать данные через RS485, и в этот самый момент данные полетели к нам, значит уже происходит коллизия, она в любом случае запорет нам данные! Или мы работаем всё через тот же USB, но точно знаем, что на линии молчок, когда мы соберёмся передавать данные.

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

  reg [2:0] BitCount = 1'b0;
  wire AllBitsOut = (BitCount == 3'b111);


Затем, внутри always-блока, говорим, что с этим делать:
  BitCount <= ~isData? 	1'b0 :
	      ce?	BitCount + 1'b1 :
			BitCount;


Пока мы в любом из режимов, кроме режима sData (приём/передача бит данных), счётчик сидит обнулённый. В режиме sData ему наконец-то дают считать, и посчитав до восьми (то есть от нуля до семи), он выдаёт единичку в AllBitsOut ("все биты приняты/переданы").

Теперь опишем переходы между состояниями, это одна из частей always-блока:
  State <= (ce & isStart & r_rxd & (~we))?  sIdle :
	   ((isIdle & (~r_rxd)) | startTx)? sStart :
	   ce & (~isData | AllBitsOut)?   {State[2:0], State[3]} :
                                          State;


Львиную долю тактов (более 99% из них) состояние просто остаётся прежним (самая нижняя строка).

Основной способ изменения состояния - циклический сдвиг влево, это предпоследняя строка. Условия его выполнения - обязательно по сигналу ce, clock enable. Если мы в любом из режимов, кроме sData, этого достаточно. Если же мы в режиме приёма/передачи бит данных, то должны замереть в нём, пока не передадим все 8.

Во второй строчке описано, как мы вообще должны запускаться (переходить в режим sStart): либо мы реагируем на импульс запуска передачи startTx, либо, будучи в режиме ожидания, углядели на входе приёмника нолик.

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

Следующий на очереди - сдвиговый регистр. Описан он был аж в заголовке, это не что иное, как DataOut. А так выглядит код для него в always-блоке:
DataOut <= startTx?         DataIn :
           (ce & isData) ? {r_rxd, DataOut[7:1]} :
                           DataOut;


По сигналу запуска передачи, мы защёлкиваем в этот регистр наш входной байт. Находясь в режиме приёма/передачи бит данных, мы производим сдвиг вправо, занося в освободившееся место бит из приёмника. Даже когда идёт передача данных, это нас не утянет.

В некоторых случаях мы можем сделать самотестирование. К примеру, мы используем RS485, и специально сделали, чтобы приёмник работал всегда, в том числе во время передачи. Тогда, по окончании работы нашего модуля на передачу, на его выходе DataOut должен очутиться ровно тот же байт, что мы туда и подавали. Это будет означать, что хотя бы участок схемы от ПЛИС до приёмопередатчика (драйвера) RS485 и он сам скорее всего работоспособны, а подсоединённый к ним кабель хотя бы не закорочен.

В случае RS232 можно по той же логике проводить Loopback test - соединять между собой провода на приём и передачу и смотреть: удалось ли принять ровно то же самое, что мы передавали. К сожалению, на моей отладочной плате приёмник RS485 отключается при работе передатчика, поэтому такое самотестирование провести не получится, да и loopback воткнуть некуда.

Очень важный регистр - we, write enable. Опишем, как им управлять:
we <= startTx? 1'b1 : isIdle? 1'b0 : we;


Поначалу модуль настроен на приём. На передачу он переключится, если мы его об этом попросим, и будет продолжать передачу, пока не вернётся в режим ожидания.

Вот мы и описали, как ведут себя все регистры. Осталось добавить комбинаторную логику, определяющую выходы этого модуля:

  assign DataOutReady = (~we) & isStop & ce & r_rxd ;
  assign FrameError = (~we) & isStop & ce & (~r_rxd);
  assign LineBreak = FrameError & (DataOut == 0);
  assign txd = we? (isData? DataOut[0] : ~isStart) : 1'b1;
  assign TxReady = we & isStop & ce;


Всё-таки очень умные люди придумали ПЛИС - смотришь на эти выражения и понимаешь, что 4 входа для генератора функций - наверняка оптимум.

Собираем всё воедино, практически в любой последовательности:
module UARThalfDuplex (input clk, input startTx, input [7:0] DataIn, input rxd, 
		       output DataOutReady, output reg [7:0] DataOut, output txd, output reg we = 0,
		       output FrameError, output LineBreak, output TxReady);

  parameter CLKfreq = 80_000_000;
  parameter BAUDrate = 512_000;

  localparam sIdle = 4'b0001;
  localparam sStart = 4'b0010;
  reg [3:0] State = sIdle;
	
  reg r_rxd = 1;
	
  wire isIdle = State[0];
  wire isStart = State[1];
  wire isData = State[2];
  wire isStop = State[3];

  wire ce;
	
  UARTFreqDivider divider (.clk (clk),
			   .sclr (isIdle | startTx),
			   .DoHalf (isIdle & (~r_rxd) & (~startTx) ), //if we're paranoc
//			   .DoHalf (isIdle & (~r_rxd)),               //if we're greedy, saves 1 LE
			   .cout (ce));
						
  defparam
    divider.CLKfreq = CLKfreq,
    divider.BAUDrate = BAUDrate;
						
						
  reg [2:0] BitCount = 1'b0;
  wire AllBitsOut = (BitCount == 3'b111);
						
  always @(posedge clk) begin
    r_rxd <= rxd;
	
    State <= (ce & isStart & r_rxd & (~we))? 	sIdle :
	     ((isIdle & (~r_rxd)) | startTx)? 	sStart :
	     ce & (~isData | AllBitsOut)? 	{State[2:0], State[3]} :
						State;
		
    we <= startTx? 1'b1 : isIdle? 1'b0 : we;
		
    BitCount <= ~isData? 	1'b0 :
		ce?		BitCount + 1'b1 :
				BitCount;
	
    DataOut <= startTx?	        DataIn :
	       (ce & isData) ?  {r_rxd, DataOut[7:1]} :
				DataOut;
  end

  assign txd = we? (isData? DataOut[0] : ~isStart) : 1'b1;
  assign TxReady = we & isStop & ce;
  assign DataOutReady = (~we) & isStop & ce & r_rxd ;
  assign FrameError = (~we) & isStop & ce & (~r_rxd);
  assign LineBreak = FrameError & (DataOut == 0);
endmodule


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

Синтезируется эта штуковина в 46 логических ячеек, хотя тут как повезёт. С удивлением я обнаружил, что если менять местами выходы в объявлении модуля, 46 может внезапно превратиться в 51.

Два отдельных модуля, выполненные "в лоб", занимают суммарно 120 логических ячеек, всё-таки есть, за что побороться, особенно если мы этот UART будем использовать во всей дальнейшей работе, раз за разом! По сравнению с нашими "оптимизированными" модулями, выигрыш на удивление скромен: 46 LE вместо 60. Для малых скоростей (например, 9600 бит/с) выигрыш чуть больше, за счет того, что делитель частоты используется всего один.

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

Пример для ПЛИС

Чтобы продемонстрировать работу в режиме "запрос-ответ", будем принимать строку, дожидаться байта 0A (LF, он же Line Feed, он же перевод строки), и только после этого посылать, сколько же символов нам передали. Счёт ведётся в двоично-десятичной форме, от 0 до 99.

Всю логику подсчёта и управления работой UART запихнём в один модуль:

module CountChars (input clk, input HasDataOut, input TxReady, input [7:0] D,
                   output reg StartTx, output reg [7:0] Q);

reg [3:0] msb = 1'b0;
reg [3:0] lsb = 1'b0;
reg remain = 1'b0;

wire isLF = (D == 8'h0A); //Line Feed, time to transmit!
wire Carry = (lsb == 9);

wire combStartTx = (isLF & HasDataOut) | (TxReady & remain);
wire [7:0] combQ = {4'h3, msb}; //'convert' BCD to ASCII

	always @(posedge clk) begin
		if (HasDataOut) begin
			if (isLF)
				msb <= lsb;
			else begin
				lsb <= Carry? 1'b0 : lsb + 1'b1;
				msb <= msb + Carry;
			end
			remain <= 1'b1;
		end
		else if (TxReady) begin
			msb <= 1'b0;
			lsb <= 1'b0;
			remain <= 1'b0;
		end
		
		Q <= combQ;
		StartTx <= combStartTx;
	end
endmodule

Счётчики msb (most significant bits) и lsb (least significant bits) инициализируются нулём, на выходе Q уже появляется символ '0', но без единички на выходе startTx он ничего не делает. Всё молчит.

Далее, мы начинаем посылать сообщение. По приёму каждого байта будет приходить импульс HasDataOut и сам байт данных в D. Мы тут же проверяем, не является ли этот байт переводом строки.

Если нет, мы ведём двоично-десятичный счёт: единичка прибавляется к младшему разряду, но если он уже равнялся девяти, то сбросится в ноль, а к следующему разряду прибавим единицу.

Стоит только появиться символу перевода строки, как уже тем же тактом (комбинаторно) у нас появится единичка на проводе combStartTx и байт данных на combQ. Они защёлкиваются в регистры startTx и Q, и уже они заставляют наш модуль UART "защёлкнуть" старшую цифру нашего счетчика, преобразованную в ASCII. И тогда же, по фронту тактового импульса мы сдвинем регистр счетчика влево на 4 позиции, тем самым выкинув старшую цифру и заменив её на младшую.

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

Поначалу я не использовал регистров для startTx и Q, но опять нарвался на нестабильную работу - всё-таки и выходы, и входы приёмопередатчика участвуют в довольно сложной комбинаторной логике, и в определённых условиях оно просто не хочет работать на 80 МГц.

Вот так выглядит принципиальная схема со следами отладки (на светодиоды тоже выведено двоично-десятичное число - сколько символов пришло, а ещё проверялись ошибки на линии и регистры состояния):


а результат её работы - в начале поста.

PS. Данный конкретный модуль необходимо чуть доработать, если захотим отправить его в космос - мы обязаны предусмотреть способ вывода его из некорректного состояния, когда все биты State нулевые, сам он сейчас не может из этого выкарабкаться. Мы пока что научились его туда не опрокидывать, но тяжелая заряженная частица может вызвать т.н Single Upset Event - и привет.


Пожалуй, с UART мы наконец-то наигрались, ужались до 46 логических ячеек на весь приёмопередатчик, пора приниматься за МКО (Mil-std 1553).
Tags: ПЛИС, работа, странные девайсы
Subscribe

Recent Posts from This Journal

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

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

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

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

  • DMA для QuatCore

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

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 7 comments