Panel Cookies
yt_link
insta_link
fb_link
twitter_link

FPGA UART
page 1/2


Uart communication on FPGA

Help me by sharing this post



This are some small examples on the UART communication on FPGAs. The codes are very easy. First we see the RX code so how to receive data, then the TX code and finally we will make a TOP.v code and merge everything together. My FPGA has a 50MHz clock. My UART communication will work at 9600 bauds. For taht we need to make another module called baud rate generator and create some "tick" pulses 16 time faster than 9600 bauds, and with that syncronize the data read/write exactly in the middle of the bit.

uart FPGA connection code example





PART 1 - Rx module

Downlaod the files below. It will be a .zip file. Inside you will have 4 folders ¡called misc, sim, src and tb. In the misc folder we have the quartus project and the synthesis. In the sim folder we have the ModelSim project for simulations. In the src folder we have all the source codes: the UART_rx, UART_tx, baudrate_generator and the top file. In the tb folder, we usually save the test bench files, but we don't have one now.


Download all codes here:


module UART_rs232_rx (Clk,Rst_n,RxEn,RxData,RxDone,Rx,Tick,NBits);		//Define my module as UART_rs232_rx

input Clk, Rst_n, RxEn,Rx,Tick;		//Define 1 bit inputs
input [3:0]NBits;			//Define 4 bits inputs


output RxDone;				//Define 1 bit output
output [7:0]RxData;			//Define 8 bits output (this will eb the 1byte received data)


//Variabels used for state machine...
parameter  IDLE = 1'b0, READ = 1'b1; 	//We haev 2 states for the State Machine state 0 and 1 (READ adn IDLE)
reg [1:0] State, Next;			//Create some registers for the states
reg  read_enable = 1'b0;		//Variable that will enable or NOT the data in read
reg  start_bit = 1'b1;			//Variable used to notify when the start bit was detected (first falling edge of RX)
reg  RxDone = 1'b0;			//Variable used to notify when the data read process is done
reg [4:0]Bit = 5'b00000;		//Variable used for the bit by bit read loop (in this case 8 bits so 8 loops)
reg [3:0] counter = 4'b0000;		//Counter variable used to count the tick pulses up to 16
reg [7:0] Read_data= 8'b00000000;	//Register where we store the Rx input bits before assigning it to the RxData output
reg [7:0] RxData;			//We register the output as well so we store the value





///////////////////////////////STATE MACHINE////////////////////////////////
////////////////////////////////////////////////////////////////////////////
///////////////////////////////////Reset////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
always @ (posedge Clk or negedge Rst_n)			//It is good to always have a reset always
begin
if (!Rst_n)	State <= IDLE;				//If reset pin is low, we get to the initial state which is IDLE
else 		State <= Next;				//If not we go to the next state
end




////////////////////////////////////////////////////////////////////////////
////////////////////////////Next step decision//////////////////////////////
////////////////////////////////////////////////////////////////////////////
/*This is easy. Each time "State or Rx or RxEn or RxDone" will change their value
we decide which is the next step. 
	- Obviously we get to IDLE only when RxDone is high
meaning that the read process is done.
	- Also, while we are into IDEL, we get to READ state only if Rx input gets low meaning
we've detected a start bit*/
always @ (State or Rx or RxEn or RxDone)
begin
    case(State)	
	IDLE:	if(!Rx & RxEn)		Next = READ;	 //If Rx is low (Start bit detected) we start the read process
		else			Next = IDLE;
	READ:	if(RxDone)		Next = IDLE; 	 //If RxDone is high, than we get back to IDLE and wait for Rx input to go low (start bit detect)
		else			Next = READ;
	default 			Next = IDLE;
    endcase
end






////////////////////////////////////////////////////////////////////////////
///////////////////////////ENABLE READ OR NOT///////////////////////////////
////////////////////////////////////////////////////////////////////////////
always @ (State or RxDone)
begin
    case (State)
	READ: begin
		read_enable <= 1'b1;			//If we are in the Read state, we enable the read process so in the "Tick always" we start getting the bits
	      end
	
	IDLE: begin
		read_enable <= 1'b0;			//If we get back to IDLE, we desable the read process so the "Tick always" could continue without geting Rx bits
	      end
    endcase
end





////////////////////////////////////////////////////////////////////////////
///////////////////////////Read the input data//////////////////////////////
////////////////////////////////////////////////////////////////////////////
/*Finally, each time we detect a Tick pulse,we increase a couter.
- When the counter is 8 (4'b1000) we are in the middle of the start bit
- When the counter is 16 (4'b1111) we are in the middle of one of the bits
- We store the data by shifting the Rx input bit into the Read_data register 
using this line of code: Read_data <= {Rx,Read_data[7:1]};
*/
always @ (posedge Tick)

	begin
	if (read_enable)
	begin
	RxDone <= 1'b0;							//Set the RxDone register to low since the process is still going
	counter <= counter+1;						//Increase the counter by 1 with each Tick detected
	

	if ((counter == 4'b1000) & (start_bit))				//Counter is 8? Then we set the start bit to 1. 
	begin
	start_bit <= 1'b0;
	counter <= 4'b0000;
	end

	if ((counter == 4'b1111) & (!start_bit) & (Bit < NBits))	//We make a loop (8 loops in this case) and we read all 8 bits
	begin
	Bit <= Bit+1;
	Read_data <= {Rx,Read_data[7:1]};
	counter <= 4'b0000;
	end
	
	if ((counter == 4'b1111) & (Bit == NBits)  & (Rx))		//Then we count to 16 once again and detect the stop bit (Rx input must be high)
	begin
	Bit <= 4'b0000;
	RxDone <= 1'b1;
	counter <= 4'b0000;
	start_bit <= 1'b1;						//We reset all values for next data input and set RxDone to high
	end
	end
	
	

end


////////////////////////////////////////////////////////////////////////////
//////////////////////////////Output assign/////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/*Finally, we assign the Read_data register values to the RxData output and
that will be our final received value.*/
always @ (posedge Clk)
begin

if (NBits == 4'b1000)
begin
RxData[7:0] <= Read_data[7:0];	
end

if (NBits == 4'b0111)
begin
RxData[7:0] <= {1'b0,Read_data[7:1]};	
end

if (NBits == 4'b0110)
begin
RxData[7:0] <= {1'b0,1'b0,Read_data[7:2]};	
end
end




//End of the RX mdoule
endmodule








PART 2 - TX module

Now, below we have the transmitter code. Read all the comments in each code to understand more and watch my video tutorialo on thius part of FPGAs. This code is very simple. We have another state machine that will get an 8-bit data, and change the TX output depending on each bit, and by taht create the UART tx signal.


Download all codes here:


module UART_rs232_tx (Clk,Rst_n,TxEn,TxData,TxDone,Tx,Tick,NBits);	//Define my module as UART_rs232_tx

input Clk, Rst_n, TxEn,Tick;	//Define 1 bit inputs
input [3:0]NBits;		//Define 4 bits inputs
input [7:0]TxData;		//Define 8 bit inputs

output Tx;
output TxDone;




//Variabels used for state machine...
parameter  IDLE = 1'b0, WRITE = 1'b1;	//We have 2 states for the State Machine state 0 and 1 (WRITE adn IDLE)
reg  State, Next;			//Create some registers for the states
reg  TxDone = 1'b0;			//Variable used to notify when the transmission process is done
reg  Tx;				//We register the input value
reg write_enable = 1'b0;		//Variable used to activate or deactivate the transmission process			
reg start_bit = 1'b1;			//Variable used to notify if the START bit was made or not yet
reg stop_bit = 1'b0;			//Variable used to notify if the STOP bit was made or not yet
reg [4:0] Bit = 5'b00000;		//Variable used for the bit by bit write loop (in this case 8 bits so 8 loops)
reg [3:0] counter = 4'b0000;		//Counter variable used to count the tick pulses up to 16
reg [7:0] in_data=8'b00000000;		//Register where we store tha data that arrived with the TxData input and has to be sent
reg [1:0] R_edge;			//Variable used to avoid debounce of the write enable pin
wire D_edge;				//Wire used to connect the D_edge



///////////////////////////////STATE MACHINE////////////////////////////////
////////////////////////////////////////////////////////////////////////////
///////////////////////////////////Reset////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
always @ (posedge Clk or negedge Rst_n)			//It is good to always have a reset always
begin
if (!Rst_n)	State <= IDLE;				//If reset pin is low, we get to the initial state which is IDLE
else 		State <= Next;				//If not we go to the next state
end




////////////////////////////////////////////////////////////////////////////
////////////////////////////Next step decision//////////////////////////////
////////////////////////////////////////////////////////////////////////////
/* This is easy as well.  Each time "State or D_edge or TxData or TxDone" will 
change their value we decide which is the next step. 
 - If D_edge was detected, so the TxEn was enabeled, we start the write process
 - Obviously, if the TxDone is high, then we get back to IDLE and wait for next TxEn to be activated */
always @ (State or D_edge or TxData or TxDone) 
begin
    case(State)	
	IDLE:	if(D_edge)		Next = WRITE;		//If we are into IDLE and D_edge gets activated, we start the WRITE process
		else			Next = IDLE;
	WRITE:	if(TxDone)		Next = IDLE;  		//If we are into WRITE and TxDone gets high, we get back to IDLE and wait
		else			Next = WRITE;
	default 			Next = IDLE;
    endcase
end



////////////////////////////////////////////////////////////////////////////
///////////////////////////ENABLE WRITE OR NOT//////////////////////////////
////////////////////////////////////////////////////////////////////////////
always @ (State)
begin
    case (State)
	WRITE: begin
		write_enable <= 1'b1;	//If we are in the WRITE state, we enable the write process
	end
	
	IDLE: begin
		write_enable <= 1'b0;	//If we are in the IDLE state, we disable the write process
	end
    endcase
end





////////////////////////////////////////////////////////////////////////////
///////////////////////Write the data out on Tx pin/////////////////////////
////////////////////////////////////////////////////////////////////////////
/*Finally, each time we detect a Tick pulse, if the write_enable is enabeled,
we start counting ticks. First we set the Tx pin to LOW and that indicates a start bit.
Then each 16 ticks, we set the Tx output to a value acording to the "in_data" value which
is the data to eb sent. We do that by shifting the "in_data" using this lines: 
	in_data <= {1'b0,in_data[7:1]};
	Tx <= in_data[0]; */

always @ (posedge Tick)
begin

	if (!write_enable)				//if write_enable is not activated, then we reset all varaibles for enxt loop
	begin
	TxDone = 1'b0;
	start_bit <=1'b1;
	stop_bit <= 1'b0;
	end

	if (write_enable)				//if write_enable is activated, then we start counting and changing the Tx output
	begin
	counter <= counter+1;				//Increase the counter by one each positive edge of the Tick input
	
	
	if(start_bit & !stop_bit)			//We set the Tx to LOW (start bit) and pass the TxData input to the in:data register
	begin
	Tx <=1'b0;					//Create start bit  (low pulse)
	in_data <= TxData;				//Pass the data to eb sent to the in_data register so we could use it
	end		

	if ((counter == 4'b1111) & (start_bit) )	//If counter reaches 16 (4'b1111), then we create the first bit and set "start_bit" to low
	begin		
	start_bit <= 1'b0;
	in_data <= {1'b0,in_data[7:1]};
	Tx <= in_data[0];
	end


	if ((counter == 4'b1111) & (!start_bit) &  (Bit < NBits-1))	//If we reach 16 once again, we make a loop for the next 7 bits (NBits-1)
	begin		
	in_data <= {1'b0,in_data[7:1]};
	Bit <=Bit+1;
	Tx <= in_data[0];
	start_bit <= 1'b0;
	counter <= 4'b0000;
	end	



	
	if ((counter == 4'b1111) & (Bit == NBits-1) & (!stop_bit))	//We finish, so we set Tx to HIGH (Stop bit)
	begin
	Tx <= 1'b1;	
	counter <= 4'b0000;	
	stop_bit <=1'b1;
	end

	if ((counter == 4'b1111) & (Bit == NBits-1) & (stop_bit) )	//If stop bit was enabeled, than we reset the values and wait for enxt write process
	begin
	Bit <= 4'b0000;
	TxDone <= 1'b1;
	counter <= 4'b0000;
	//start_bit <=1'b1;
	end
	
	end
		

end



////////////////////////////////////////////////////////////////////////////
////////////////////////////Input enable detect/////////////////////////////
////////////////////////////////////////////////////////////////////////////
/*Here we detect if there was a reset or if the TxEn was activated.
If "TxEn" was actiavted than we start the write process and we will send
the data that is on the "TxData" input so amke sure that in the 
moment you activate "TxEn" the TxData" has the values you want to send . */
always @ (posedge Clk or negedge Rst_n)
begin

	if(!Rst_n)
	begin
	R_edge <= 2'b00;
	end
	
	else
	begin
	R_edge <={R_edge[0], TxEn};
	end
end
assign D_edge = !R_edge[1] & R_edge[0];





endmodule











Next page →

Help me by sharing this post








ADVERTISERS



PCBWAY PCB service





Curso Arduino Online nivel bajo