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

On 17.11.2016 by nikellanjilo

Данная статья посвящена приемнику 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;

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

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

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

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

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


`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; 

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

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 

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

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

modelsim

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

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


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

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

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

cropped-img_1479-1024x683

Ну че, типа это … пиши если что 🙂

 

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