Блокирующие и неблокирующие присваивания в Verilog. Часть Вторая, внезапная.

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

Итак имеем состояние, в котором считаем входящие байты по фронту стоп-бита UART, и в зависимости от номера - делаем с ним что-то. Повторяем операцию каждые четыре байта. Всё просто, пишем:
после чего пишем обработчик каждого из четырёх входящих битов, case (oneWrd) но получаем смещение на один байт в самом начале передачи. Обратимся к осциллограмме:

И правда, видим, что до четырёх мы досчитали уже на стоп-бите третьего байта. Меняем:

Теперь, предполагаем, что присваивание выполнится "параллельно", и смотрим в осциллограф:

Ай, да мы, ай, да молодцы.

Итак, почему-же  это происходит? Всё дело в тактовом сигнале. На момент наступления условия, прописанного в скобках строки always@(...) begin будь то фронт или срез тактового сигнала, ПЛИС помнит все значения, содержащиеся внутри этого блока. И в один момент присваивает всем левым частям все правые части. Так происходит с неблокирующими (<=) присваиваниями. Блокирующие-же выполняются в том порядке, в каком стоят в коде. То есть, в моём случае, описанном выше, ПЛИС сначала присвоила переменной oneWrd значение содержащееся в тернарной операции, а потом, пошла пересчитывать значения в обработчике. с уже новым oneWrd. Это может и создаёт путаницу у начинающих заниматься цифровой схемотехникой и описанием цифровых схем, поэтому от блокирующих присваиваний на первых порах лучше отказаться, как бы "похожи на обычное программирование" они не были.

Блокирующие и неблокирующие присваивания в Verilog

Вкратце: модуль принимает сигнал, отсчитывает задержку, и выдаёт последовательным кодом заранее заготовленные значения из массива с добавлением стартового и стопового битов. Опишем входы/выходы:
module UartTX(
iClk80, iReq,
oTX, oDirTX, oDirRX);
input iClk80, iReq;
output reg oDirTX, oDirRX, oTX;
Опишем вспомогательные переменные:
reg tmpReq, tx;
reg [5:0] delay = 0;
reg [3:0] cntBit = 0;
reg [3:0] cntArr = 0;
reg [4:0] cntClk = 0;
reg [7:0] reqArr [13:0]; //На самом деле массив наполняется из входящих шин, не важно
reg [7:0] elem = 0;
Ну и дадим знать ПЛИС, что должно происходить.
always@(posedge iClk80)
begin
Для начала поделим тактовую частоту, чтобы из входящей получить частоту передачи:
cntClk = cntClk + 1'b1;
if (cntClk == 16) begin 
cntClk = 0;
Найдём фронт сигнала о передаче
if (tmpReq == 1 && iReq == 0)tx = 1;
tmpReq = iReq;
и, собственно, начнём: где-то там, внизу, создадим счётчик, который будет у нас управлять задержкой и временем передачи, а основную работу будем выполнять в виде конечного автомата:
if (tx == 1) begin
case (delay)
5: oDirRX <= 1; //отключили приём
10: oDirTX <= 1; //включили передачу
25: begin
delay = 24; //отменили увеличение задержки
case (cntBit)
0: oTX <= 0;
1,2,3,4,5,6,7,8: begin
elem = reqArr[cntArr];
oTX <= elem[cntBit-1];
end
9: oTX <= 1;
endcase
cntBit = cntBit + 1'b1;
if (cntBit==10) begin //перебрали 10 бит передаваемых данных
cntBit = 0;
cntArr = cntArr + 1'b1;
if (cntArr == 14) begin //перебрали 14 элементов массива
cntArr = 0;
delay = delay + 1'b1; //продолжаем считать задержку
end
end
end
40: oDirTX <= 0; //отключили передачу
45: oDirRX <= 0; //включили приём
endcase;
delay = delay + 1'b1;
if (delay == 50) begin
delay = 0; //закончили считать
tx <= 0; //до следующего сигнала забыли об этом блоке
end;
end
end
end
endmodule
А теперь, самое интересное. Выдача последовательным кодом и добавление стартового-стопового битов происходит непосредственно в этом маленьком кейсе:
case (cntBit)
0: oTX <= 0;
1,2,3,4,5,6,7,8: begin
elem = reqArr[cntArr];
oTX <= elem[cntBit-1];
end
9: oTX <= 1;
endcase
При присвоении регистру elem значения при помощи блокирующего присваивания, на выходе получаем ровную картинку значений 1,2,3,4,5,6,7,8,9,10,11,12,13,14. Все танцуют. Но если ту-же строку записать
elem <= reqArr[cntArr];
Картинка на выходе получается весьма прискорбной: 1,3,2,5,4,7,6,9,8,11,10,13,12,14. Как видно, после первой итерации, переменной присваивается следующее нечётное значение, затем предшествующее ему, затем следующее нечётное, затем ситуация повторяется. 

Поймать-то я это поймал, а объяснить пока-что не могу...



Как заменить одно оборудование другим


У любого начинающего программиста/инженера есть одна проблема. Точнее, проблем-то много, но лишь об одной из них я хочу попробовать рассказать. Отсутствие подходящего оборудования для работы/исследований. И если с компьютером всё понятно, его отсутствие сложно чем-то заменить, то вот с другими предметами можно ещё что-то придумать. Волею судеб и моего руководителя я стал обладателем настольного осциллографа. Вещь классная, но в ряде случаев совершенно, на первый взгляд бесполезная.
В один прекрасный вечер у меня появилась необходимость припаять крохотный микроконтроллер к макетной плате. И всё бы ничего, на первый взгляд я с задачей справился, если бы не одно "но": нет возможности проверить, не устроил-ли я где-то между дорожками коротенькое такое замыкание. А тестера нет. А пускать через контроллер напряжение - рано, ни земли ни питание не разведено... А осциллограф меряет уровни напряжения.
Как хорошо, что осциллограф генерирует тактовый сигнал 1КГц. Срочно спаял проводок с подобием щупа и подключил его к сигналу, ещё один такой-же проводок подключен клеммой к "земле" осциллографа, и всё, дело в шляпе. Подаём сигнал щупом самодельного проводка на начало цепи и меряем щупом осциллографа на конце, если на экране видим сигнал - всё припаялось хорошо, если нет - плохо. Если видим сигнал также и на соседних дорожках - припаялось что-то лишнее.

Желаю всем фантазии и вдохновения в решении ваших задач!