FPGA UART Receiver — Приемник UART, реализованный на ПЛИС

Данная статья посвящена приемнику UART.  До этого две статьи были посвящены передатчику UART, и полной схеме, состоящей из приемника и передатчика[ F(clk) = 20 MHz, V(uart) = 115200 кБит/с ].

В статье рассмотрим модуль, который легко параметризуется и меняется в зависимости от тактовой частоты и необходимой скорости UART — необходимо лишь поменять один параметр.

Здесь мы не будем рассматривать теоретическую часть работы UART, предполагается, что Вы с ней ознакомлены, если же нет, то теоретическая часть приводится здесь.


Как использовать модуль приемника

В модуле используются следующие порты:

clk — входной однобитный порт — подача тактового сигнала,
rx — входной однобитный вход — входные данные последовательного интерфейса,
data_o — выходные данные (8 бит),
data_rdy — выходной порт — сигнализирует о готовности и валидности данных,
have_prblm — выходной порт — сигнализирует что в блоке были найдены некоторые ошибки и возможны неточности


Код приемника … Verilog код

Здесь  сountOfStrobe — параметр, меняя который может регулировать информационную скорость нашего UART’а. В данном случае UART настроен на скорость 115200 кБит/с при тактовой частоте clk = 20 МГц.

сountOfStrobe = Fclk(Гц)/Vuart(бит/c) = 20*10^6 / 115200 = 173;

[php]
module uart_param_receiver #(
parameter countOfStrobe = 173, // Параметр, который высчитывается из соотношения Fclk[Гц] / V[бит/c]
// соответствует тому, сколько тактов приходится на прием одного
// бита данных при выбраной скорости UART(бит/с) и тактовой частоте CLK (Гц)
parameter firstCheckStrobe = 99, // Первый check bit
parameter secondCheckStrobe = 120, // Второй check bit
parameter thirdCheckStrobe = 150 // Третий check bit
)(
input clk, // Тактовая частота [20 МГц]
input rx, // Входные данные
output reg [7:0] data_o, // Выходные данные
output reg data_rdy = 0, // Строб, сигнализирующий что на шине data_o валидные данные и их можно "забирать"
output reg have_prblm = 0 // Строб, сигнализирующий о проблемах в определении входного бита. Если он == 0 -> все ок, иначе -> проблемы
);

reg [1:0] state = 2’b00; // Регистр статуса
reg [8:0] shift; // Сдвиговый регистр. В него побитово кладутся данные, приходящие с rx
reg [10:0] cntStrobe = 0; // Счетчик countOfStrobe
reg [2:0] checker; // Регистр проверки -> На один информационный бит приходится countOfStrobe тактов. Имеет смысл "разделить"
// данный бит и записать данные в регистр checker
reg [4:0] cntData = 0; // Счетчик данных [счетчик битов]

always @(posedge clk)
begin
case (state)
2’b00 : begin
if (rx == 0)
begin
state <= 2’b01;
data_rdy <= 0;
end
end
2’b01 : begin
if (cntStrobe < countOfStrobe)
begin
cntStrobe <= cntStrobe + 1;
if (cntStrobe == firstCheckStrobe | cntStrobe == secondCheckStrobe | cntStrobe == thirdCheckStrobe)
begin
checker[0] <= rx;
checker[2:1] <= checker[1:0];
end
end
else
begin
if (cntData < 9)
begin
// Проверяем все ли у нас хорошо
if (checker[0] != checker[1] | checker[0] != checker[2])
have_prblm <= 1;
else
have_prblm <= 0;
// Запись бита в регистр
shift[8] <= checker[1];
shift[7:0] <= shift[8:1];
cntData <= cntData + 1;
cntStrobe <= 0;
end
else
begin
have_prblm <= 0;
data_o <= shift[8:1];
cntData <= 0;
cntStrobe <= 0;
state <= 2’b10;
end
end
end
2’b10 : begin
data_rdy <= 1;
state <= 2’b00;
end

endcase
end

endmodule
[/php]


Testbench. Симуляция работы приемника

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

При этом передатчик заведем на частоте 100 МГц, а наш приемник на 20 МГц, т.к. такой вариант событий наиболее вероятен — это два независимых устройство со своей тактовой частотой.

Код тестбенча :

[php]

`timescale 1ns / 100ps;
module receiver_tb();

parameter fr = 50;
reg clk = 0;

always
begin
#(fr/2) clk = 1;
#(fr/2) clk = 0;
end

// Абсолютно другой клок, чтобы отвязаться от клока проекта
// Имеется ввиду, что наш проект частота, на которой работает UART приемник — 20 МГц,
// А частота передатчика — то есть нашего testbench’a = 100 МГц

parameter frTR = 10;
reg sclk = 0;

always
begin
#(frTR/2) sclk = 1;
#(frTR/2) sclk = 0;
end

reg [7:0] data_shift = 8’b1100_0011;
reg [7:0] data;
reg data_rdy = 0;
wire tx_rx;
wire areTransRdy;
reg [2:0] state = 2’b00;

always @(posedge sclk)
begin
case (state)
2’b00 : if (areTransRdy)
begin
state <= 2’b01;
data_rdy <= 1;
data <= data_shift;
end
2’b01 : begin
data_rdy <= 0;
state <= 2’b00;
data_shift <= data_shift + 1;
end
endcase
end

uart_param_receiver receiver(
.clk(clk),
.rx(tx_rx),
.data_o(),
.data_rdy(),
.have_prblm()
);
uart_param_trans transmitter(
.clk(sclk),
.data(data),
.data_rdy(data_rdy),
.tx(tx_rx),
.transm_rdy(areTransRdy)
);

endmodule;

[/php]

Код передатчика UART, настроенного на частоту 100 МГц и скорость 115200 КБит/с.

[php]
module uart_param_trans #(
parameter countOfStrobe = 868 // Параметр, который высчитывается из соотношения Fclk[Гц] / V[бит/c]
// соответствует тому, сколько тактов приходится на передачу одного
// бита данных при выбраной скорости UART(бит/с) и тактовой частоте CLK (Гц)
)(
input clk, // Тактовая частота [20 МГц]
input [7:0] data, // Данные, которые собираемся передать по TX
input data_rdy, // Строб, который соответствует тому, что данные валидные и их нужно передать
output reg tx = 1, // Выходной порт, передает данные по последовательному интерфейсу
output reg transm_rdy = 1 // Строб, который сигнализирует о том, что данные переданы и блок готов к передаче новых данных
);

reg [1:0] state = 2’b00; // Регистр, который будет менять значение в зависимости от состояния нашего модуля
reg [10:0] cntStrobe = 0; // Регистр — счетчик, который будет накапливаться до необходимого числа стробов (до countOfStrobe)
reg [4:0] cntBit = 0; // Регистр — счетчик, указываюший на номер передаваемого бита из data
reg [7:0] shiftData; // Сдвиговый регистр, в который мы записываем входные данные (data), а затем последовательно, побитово
// передаем по TX

always @(posedge clk)
begin
case(state)
2’b00 : begin
if (data_rdy)
begin
state <= 2’b01;
shiftData <= data;
transm_rdy <= 0;
tx <= 0;
end
end
2’b01 : begin
if (cntBit == 0)
begin
if (cntStrobe < countOfStrobe)
cntStrobe <= cntStrobe + 1;
else
begin
cntStrobe <= 0;
cntBit <= 1;
tx <= shiftData[0];
shiftData[6:0] <= shiftData[7:1]; end end if (cntBit > 0 && cntBit < 9)
begin
if (cntStrobe < countOfStrobe)
cntStrobe <= cntStrobe + 1;
else
begin
cntStrobe <= 0;
cntBit <= cntBit + 1;
tx <= shiftData[0];
shiftData[6:0] <= shiftData[7:1];
end
end
if (cntBit == 9)
begin
if (cntStrobe < countOfStrobe)
begin
cntStrobe <= cntStrobe + 1;
tx <= 1;
end
else
begin
cntStrobe <= 0;
cntBit <= 0;
transm_rdy <= 1;
state <= 2’b00;
end
end
end

endcase
end
endmodule
[/php]


Результаты моделирования в ModelSim

Ниже представлен результат моделирования (кликнув на картинку вы сможете просмотреть ее в полном масштабе — она откроется в новой вкладке).

modelsim

Как видно из результатов данные data_o (выход приемника) и data (вход передатчика) совпадают…

Это свидетельствует о правильной работе блока как передатчика, так и приемника.


Возможные ошибки

  1. При увеличении параметра countOfStrobe необходимо увеличивать разрядность cntStrobe
  2. При изменении countOfStrobe необходимо также менять firstCheckStrobe, secondCheckStrobe и thirdCheckStrobe

Вопросыыыыы??? 

cropped-img_1479-1024x683
Ну че, типа это … пиши если что 🙂

 

Добавить комментарий