Да, наверно действительно надо бы уменьшить концентрацию лытдыбра в журнале, а вместо него писать чего-нибудь более интересное. И, пожалуй, есть у меня кое-что. Оно уже "сдано куда надо", но вдруг оно понадобится кому-то ещё. Так что, можно об этом и упомянуть.
Понадобилось мне организовать на ПЛИС uart приёмник. Хоть я в конце и получил ачивку по кодингу ПЛИСин, я не перестану от них плеваться и говорить, что микроконтроллеры проще. Дабы не кодить всё с нуля, я спросил у яндекса... И выдал он мне псто
Очередное изобретение велосипедов, или UART_TX и UART_RX на языке Verilog. Ну и его клоны ещё... Так как на тот момент в кодинге ПЛИС я был полный нуб, для начала было решено брать код прямо так. Взлетело. Но пришлось допиливать напильником. Одной копипастой в инете больше, одной меньше... Но тут хоть есть "личный вклад автора". Итоговый код получился таким:
module uart_rx (clk, rx, data, data_ready, bad_data);
input wire clk;
input wire rx;
output reg [7:0] data;//+1 stop bit
output data_ready;
output bad_data;
reg rx_ff;
reg rx_ff2; // две защелки, для вычленения старта передачи
// и вообще, защелки надо, чтобы всякие помехи не ловить
always @(posedge clk)
begin
rx_ff <= rx;
rx_ff2 <= rx_ff;
end
// отлавливаем старт-бит
wire spad = ~rx_ff & rx_ff2;
// состояние приемника
reg receive;
// для корректной симуляции. Насколько знаю, квартус это пропускает
initial receive = 0;
//Ну тут понятно - если старт бит, то включаем режим приема,
//если приняли - выключаем
always @(posedge clk)
if (spad)
receive <= 1'b1;
else
// if (count_byte == 10)//(data_ready)
if(count_byte>7 && (data_ready || bad_data))
receive <= 1'b0;
//cигнал начала приема. Для инициализации счетчиков.
wire start = ~receive & spad;
//поскольку у нас clk в 8 раз быстрее rx, делаем делитель
reg [2:0] count_os;
always @(posedge clk)
if (start)
count_os <= 3'd5;//чтобы попасть в середину бита
else
if(receive)
count_os <= count_os + 1'b1;
//при переполнении счетчика-делителя выдираем бит из входных данных
wire get_bit = ~|count_os;
//счетчик принятых данных. Как примем 9 бит - можно останавливаться
reg [4:0] count_byte;
always @(posedge get_bit or posedge start)
begin
if (start)
count_byte <= 0;
else
count_byte <= count_byte + 4'b1;
end
reg data_ready;
reg bad_data;
//wire data_ready = (count_byte == 9) && rx_ff==1;//приняли все биты и крайний (стоповый) бит равен 1
//сдвигаем регистр данных на одну позицию вправо,
//и пишем принятый бит в старший бит
always @(negedge get_bit)
begin
// if(count_byte==1 && ~rx_ff)//no start bit
if(count_byte>1 && count_byte<10) data <= {rx_ff, data[7:1]};
data_ready = (count_byte == 5'd10) && rx_ff==1;//приняли все биты и крайний (стоповый) бит равен 1
bad_data = (count_byte == 5'd10) && rx_ff==0;//приняли все биты и крайний (стоповый) бит НЕ равен 1
end
endmodule
Собственно, что хотелось бы отметить.
БОЛЬШЕ СИНХРОННОСТИ БОГУ СИНХРОННОСТИ!!111
Мозг программиста подсказывает экономить триггеры там, где можно обойтись и без них. Но, во первых, экономия из этого вряд ли получится, а во вторых, выйдет боком. Отсутствие триггера по выходу резко повышает вероятность того, что его будет колбасить.
Автора оригинального поста ругали за отсутствие контроля стопового бита. Не хорошо - подумал я и допилил.
Ещё одна правка связана с попаданием в середину бита. Но это уже я слепой - не заметил, что автор и сам это исправил. По хорошему бы тут ещё стартовый бит контролировать, да по три выборки на каждый бит делать, но я чёт ниасилил, а "работает и так" не сильно мотивирует копать в эту сторону.
Вот, в общем-то, и всё на этот раз. Вопросы в комментариях приветствуются. В следующий раз расскажу как полученные байты обрабатываются дальше и вообще для чего всё это понадобилось.