Очередное изобретение велосипедов, или UART_TX и UART_RX на языке Verilog

Ну, первым был приемник, как не странно.

Что мы имеем:
1)вход rx, с которого мы принимаем наши данные
2)вход clk, на который подаем тактовую частоту, в 8 раз большую частоты передачи
3)выход данных. В процессе приема данные на нём, в принципе, не определены =)
4)и сигнал окончания приёма. Если 1 — значит, всё устаканилось и можно снять байт с выхода данных, а также принимать новые данные.
Вот он мой быдлокод:
module uart_rx (clk, rx, data, data_ready);
input wire clk;
input wire rx;
output reg [7:0] data;
output data_ready;
reg rx_ff1, rx_ff2; // две защелки, для вычленения старта передачи
// и вообще, защелки надо, чтобы всякие помехи не ловить
always @(posedge clk)
begin
rx_ff1 <= rx;
rx_ff2 <= rx_ff1;
end
// отлавливаем старт-бит
wire spad = ~rx_ff1 & rx_ff2;
// состояние приемника
reg receive;
// для корректной симуляции. Насколько знаю, квартус это пропускает
initial receive = 0;
//Ну тут понятно - если старт бит, то включаем режим приема,
//если приняли - выключаем
always @(posedge clk)
if (spad)
receive <= 1'b1;
else
if (data_ready)
receive <= 1'b0;
//cигнал начала приема. Для инициализации счетчиков.
wire start = ~receive & spad;
//поскольку у нас clk в 8 раз быстрее rx, делаем делитель
reg [2:0] count_os;
always @(posedge clk)
if (start)
count_os <= 1'b0;
else
if(receive)
count_os <= count_os + 1'b1;
//при переполнении счетчика-делителя выдираем бит из входных данных
wire get_bit = ~|count_os;
//счетчик принятых данных. Как примем 9 бит - можно останавливаться
reg [3:0] count_byte;
always @(posedge get_bit or posedge start)
begin
if (start)
count_byte <= 0;
else
count_byte <= count_byte + 4'b1;
end
wire data_ready = (count_byte == 9);
//сдвигаем регистр данных на одну позицию вправо,
//и пишем принятый бит в старший бит
always @(negedge get_bit)
if (!data_ready) data <= {rx_ff1, data[7:1]};
endmodule
Вот такое вот безобразие получилось.
Потом берем, пишем тестбенч:
`timescale 1ns / 1ps
module test_uart_rx;
reg clk;
reg rx;
wire data_received;
wire [7:0] data_in;
uart_rx u_rx (clk, rx, data_in, data_received);
always
#10 clk = ~clk;
initial
begin
clk = 0;
rx = 1;
#205 rx = 0;
#160 rx = 1;
#160 rx = 0;
#160 rx = 1;
#160 rx = 0;
#160 rx = 1;
#160 rx = 0;
#160 rx = 1;
#160 rx = 0;
#160 rx = 1;
#578 rx = 0;
#160 rx = 1;
#160 rx = 1;
#160 rx = 1;
#160 rx = 0;
#160 rx = 1;
#160 rx = 1;
#160 rx = 1;
#160 rx = 0;
#160 rx = 1;
end
initial
#10000 $finish;
initial
begin
$dumpfile("out.vcd");
$dumpvars();
end
endmodule
Симулируем Икарусом (как? можно у марсоходовцев посмотреть) и видим, что байтики принимаются, как надо.
Второй модуль — передатчик.
Ну тут всё просто:
module uart_tx(clk, data, start, tx, ready);
input wire clk;
input wire [7:0] data;
input wire start;
output reg tx;
output wire ready;
reg [7:0] data_tx;
reg [2:0] count;
reg [3:0] state;
initial count = 0;
//ну тут тоже сделано так, что передача в 8 раз медленнее клока
always @(posedge clk)
count <= count + 1;
assign clock = ~|count;
//смена состояний конечного автомата
always @(posedge clk)
case (state)
4'b0000: tx <= 1'b1; // при простое держим единицу
4'b0001: tx <= 1'b0; // старт-бит
4'b0010: tx <= data_tx[0];
4'b0011: tx <= data_tx[1];
4'b0100: tx <= data_tx[2];
4'b0101: tx <= data_tx[3];
4'b0110: tx <= data_tx[4];
4'b0111: tx <= data_tx[5];
4'b1000: tx <= data_tx[6];
4'b1001: tx <= data_tx[7];
4'b1010: tx <= 1'b1; //стоп-бит
default: tx <= 1'b1;
endcase
assign ready = (state == 4'b0000);
initial state = 0;
always @(posedge clock)
begin
if (start & ready) // шоб во время передачи не пытались
begin
data_tx <= data;
state <= 4'b0001;
end
else
if (state > 4'b1010) // усё передали
state <= 4'b0000;
else
if (!ready & clock)
state <= state + 1'b1;
end
endmodule
Тестбенч похерил, да и там он проще некуда. Интереснее оба модуля в одном тестбенче замутить, пусть один посылает, другой принимает.
Вот так вот.
Кстати, в железе еще не опробовал. Симуляцию прошел, квартус синтезирует вообще без warning-ов (правда, передатчик еще не пробовал компилить).
А теперь, товарищи знатоки, к вам вопросы:
1) Какие тут есть очевидные косяки? С плис я не так давно знаком, так что пишу пока с не особым знанием дела
2) Как можно прикинуть, будет ли данный модуль, реализованный на EP4CE22F17C6 (установлен на моей de0-nano — вроде самый быстрый циклон из данной линейки) принимать 12 мбит uart с FTDI FT2232HL и где могут быть узкие места?
UPD.1:
Поменял вот что:
always @(posedge clk)
if (start)
count_os <= 4'b101;
else
if(receive)
count_os <= count_os + 1'b1;
Это позволило попадать на середину бита rx.
А еще, в конце сделал по переднему фронту(ну тут разницы никакой, просто задний фронт — это вроде как лишний инвертор и лишняя задержка, да и вообще срабатывание по заднему фронту было в роли костыля, который теперь и не нужен):
always @(posedge get_bit)
Алсо, вот кусок времянки:

Отметил старт и стоп биты, всё что между — это передаваемый байт. Тут я передаю 01010101, что в хексе будет 55. Как видно, он выдает на выходе именно 55. Гы, да еще и готов, не дожидаясь стопбита. Алсо, там еще выведен get_bit, и, как видно, он попадает как раз на середину бита rx. То бишь, небольшие отклонения в скорости передачи ему не страшны
UPD.2
Сегодня из коробки с хламом отрыл выпотрошенный DCA-510, он же USB-COM на PL2303. C патченными CHAOS-ом дровами он поддерживает скорость 1625000 бит/с. Запаял я разъемчик, подключил к своей de0-nano, попихал байтики в терминал, светодиодики зажигаются те, что надо.
UPD.3
Понятно, что nobody cares, но надо бы апдейт бахнуть. Заметил я баг в передатчике. Там в одном месте вместо clock написал clk и передатчик не работал. Исправил. Через тот же dca-510 принимал байтЫ с плис.
Всё никак руки не дойдут перепилить с учетом всех замечаний приемник. Вроде и так работает, хотя я его не обкатывал ещё на все 100 ;-)
- +1
- 10 октября 2011, 21:03
- PPetrovich
Гм, а не проще передатчик было на базе сдвигового регистра (каковым он и является) сделать? Да и приемник тоже.
Передатчик — вполне можно, но я тут решил конечный автомат. Можно и регистр, чо. Но велосипед же с костылями, да и мне передатчик пока не нужен.
Насчет приемника — UART того, асинхронный. Был бы какой-нить SPI или другой синхронный последовательный интерфейс — тогда и правда просто регистр. А UART может в любой момент начать передачу. Поэтому модуль проверяет RX с частотой, в 8 раз превышающей частоту передачи. И как только засекает момент передачи, происходит как бы синхронизация(сброс счетчика count_os) и происходит прием. И там внутри есть сигнал get_bit, он-то выскакивает внутри битов uart. Надо вот допилить, чтобы оно попадало на середину бита uart, так лучше будет. Меньше чувствительность к неточным по длительности посылкам.
Насчет приемника — UART того, асинхронный. Был бы какой-нить SPI или другой синхронный последовательный интерфейс — тогда и правда просто регистр. А UART может в любой момент начать передачу. Поэтому модуль проверяет RX с частотой, в 8 раз превышающей частоту передачи. И как только засекает момент передачи, происходит как бы синхронизация(сброс счетчика count_os) и происходит прием. И там внутри есть сигнал get_bit, он-то выскакивает внутри битов uart. Надо вот допилить, чтобы оно попадало на середину бита uart, так лучше будет. Меньше чувствительность к неточным по длительности посылкам.
- PPetrovich
- 10 октября 2011, 22:11
- ↑
- ↓
Допилил, теперь ваще лепота. Можно прикрутить бит чОткости и сделать параметризуемым, и всё.
- PPetrovich
- 10 октября 2011, 22:34
- ↑
- ↓
По идее нужно просто выжидать стартбит (т.е. падение уровня на RX) и на этот момент синхронизировать клок выборки. А затем уже этим клоком двигать данные в регистр. Внутри кадра все равно синхронизации нету.
А так ИМХО более сложно и подозреваю (ибо ПЛИСы я щупал тока физически, «ути какой тараканчик многоногий») более требовательно к количеству элементов.
Я бы еще понял, если бы приемник реализовывал хитрые алгоритмы (как у AVR с их множественной выборкой каждого бита), а так…
А так ИМХО более сложно и подозреваю (ибо ПЛИСы я щупал тока физически, «ути какой тараканчик многоногий») более требовательно к количеству элементов.
Я бы еще понял, если бы приемник реализовывал хитрые алгоритмы (как у AVR с их множественной выборкой каждого бита), а так…
Лень компилить, но сегодня что-то около 22 LE показало. При 22320 LE, думаю, это совсем немного ))
Да и тут, по сути, как раз то, что ты и описал. По стартбиту происходит синхронизация клока выборки, и этот клок двигает данные в регистр. Всё так и есть ))
Да и тут, по сути, как раз то, что ты и описал. По стартбиту происходит синхронизация клока выборки, и этот клок двигает данные в регистр. Всё так и есть ))
- PPetrovich
- 10 октября 2011, 23:12
- ↑
- ↓
не знаю, почему, но коммент запилился недопечатанным.
Ну так вот, коллега реализовал уже свой приемник UART. За исключением того, что он нарисовал на VHDL, он сделал 16х оверсемплинг, а не 8. И у него как раз принимаемые данные побитно распихивает конечный автомат. А у меня — сдвиговый регистр
Ну так вот, коллега реализовал уже свой приемник UART. За исключением того, что он нарисовал на VHDL, он сделал 16х оверсемплинг, а не 8. И у него как раз принимаемые данные побитно распихивает конечный автомат. А у меня — сдвиговый регистр
- PPetrovich
- 10 октября 2011, 23:24
- ↑
- ↓
Приличные UAR всегда контролируют наличие стоп-бита. Тем более это важно на высоких скоростях.
также, приёмник обычно делает три информационных выборки и пропускает это дело через мажоритарную схему. и совсем продвинутые контролируют старт-бит на равенство нулю в тесение около 90% длительности бита. как-то так.
также, приёмник обычно делает три информационных выборки и пропускает это дело через мажоритарную схему. и совсем продвинутые контролируют старт-бит на равенство нулю в тесение около 90% длительности бита. как-то так.
- prostoRoman
- 18 октября 2011, 21:31
- ↓
Воспользовался вашим кодом, вопрос возник только по тактовым частотам,
как я посчитал для скорости 19200 тактовая должна быть 8х19200=153600Гц?
ну или напишите как вы выбираете тактовую для той или иной скорости?
да и насколько передатчик и приемник чувствительны к отличию тактовой от оптимальной?
как я посчитал для скорости 19200 тактовая должна быть 8х19200=153600Гц?
ну или напишите как вы выбираете тактовую для той или иной скорости?
да и насколько передатчик и приемник чувствительны к отличию тактовой от оптимальной?
да поигрался с передатчиком, не удобно работает загрузка данных для передачи, только по байтно, и пока байт не передался то следующий загрузить невозможно, да старт загрузки привязан к клоку 8х, что не удобно для двухбайтовых величин. Добавил доп. буффер и логику работы с ним. Теперь можно и в железо…
Есть одна проблемка в стандартных UART-ах — укороченный стоп-бит. У Texas Instruments есть документ — TL16C752C/TL16C754C/TL16C2752 Short STOP Bit Errata. Не увидел в приемнике борьбы с этим.
Комментарии (15)
RSS свернуть / развернуть