欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

FPGA串口学习

程序员文章站 2022-06-23 17:28:51
...

串口与单口ram操作学习

实现功能如下,电脑给开发板发送串口数据,每接收4个数据后串口将4个数据发送给电脑。

代码部分

代码有串口发送程序,串口接收程序,时钟生成程序和ram控制程序。前三个程序直接用黑金教程里的程序修改的。

时钟程序.

	`timescale 1ns/1ps
	//波特率115200
	module clkdiv(clk50 , rst_n ,clkout ) ;
	
	input clk50 ;
	input rst_n ;
	output clkout ;
	reg clkout ;
	reg [15:0]cnt ;
	
	always @(posedge clk50 or negedge rst_n)
		begin
			if(!rst_n)
				begin
					clkout <= 1'b0 ;
					cnt <= 0 ;
				end
			else if(cnt == 16'd13) 
				begin
					clkout <= 1'b1 ;
					cnt <= cnt + 16'd1 ;
				end
			else if(cnt == 16'd27)
				begin
					clkout <= 1'b0 ;
					cnt <= 16'd0 ;
				end
			else
				begin
					cnt <= cnt + 16'd1 ;
				end
		end
		
		endmodule			

串口发送程序

	`timescale 1ns/1ps 
	//module name : uart_send
	//sen 1 bit every 16 clock , 1 begin position ,8 data position , 1 check position , 1 stop position
	module uart_send(clk , rst_n , datain , wrsig , idle , tx , send_cnt , sendout) ;
	
	input clk ; //clock
	input rst_n ; //reset
	input [7:0]datain ;//data to send
	input wrsig ; //command of send
	output idle ; //status of line,1 is busy
	output tx ; //send data signal
	output sendout ; // negetive when wr allow
	output send_cnt ;

	reg idle , tx ;
	reg send , sendout ;
	reg wrsigbuf , wrsigrise ;
	reg presult ;
	reg [7:0]cnt ;
	parameter paritmode = 1'b0 ;
	reg [1:0]send_cnt ;
	
	//check rise edge of wrsig
	always @(posedge clk)
	begin
	wrsigbuf  <= wrsig ;
	wrsigrise <= (~wrsigbuf) & wrsig ;
	end
	
	//start the send function
	always @(posedge clk )
	begin
		if(wrsigrise && (~idle) )
		begin 
			send = 1'b1 ;
		end
		else if(send_cnt==3 && cnt==168)
		begin
			send = 1'b0 ;
		end
	end
	
	//function of sending
	always @(posedge clk or negedge rst_n)
	begin
		if(~rst_n)
		begin
			tx       <= 1'b0 ;
			idle     <= 1'b0 ;
			cnt      <= 8'd0 ;
			presult  <= 1'b0 ;
			send_cnt <= 0 ;
			sendout  <= 0 ;
		end
		else if(send == 1'b1)
			begin
				case(cnt)
				8'd0 : begin
					tx       <= 1'b0 ;
					idle     <= 1'b1 ;
					cnt      <= cnt + 8'd1 ;
					sendout  <= 0 ;
					end
				8'd16 : begin
					tx       <= datain[0] ;
					presult  <= datain[0] ^ paritmode ;   //奇偶校验步骤
					idle     <= 1'b1 ;
					cnt      <= cnt + 8'd1 ;
					sendout  <= 0 ;
					end
				8'd32 : begin
					tx       <= datain[1] ;
					presult  <= datain[1] ^ presult ;
					idle     <= 1'b1 ;
					cnt      <= cnt + 8'd1 ;
					sendout  <= 0 ;
					end
				8'd48 : begin
					tx       <= datain[2] ;
					presult  <= datain[2] ^ presult ;
					idle     <= 1'b1 ;
					cnt      <= cnt + 8'd1 ;
					sendout  <= 0 ;
					end
				8'd64 : begin
					tx       <= datain[3] ;
					presult  <= datain[3] ^ presult ;
					idle     <= 1'b1 ;
					cnt      <= cnt + 8'd1 ;
					sendout  <= 0 ;
					end
				8'd80 : begin
					tx       <= datain[4] ;
					presult  <= datain[4] ^ presult ;
					idle     <= 1'b1 ;
					cnt      <= cnt + 8'd1 ;
					sendout  <= 0 ;
					end
				8'd96 : begin
					tx       <= datain[5] ;
					presult  <= datain[5] ^ presult ;
					idle     <= 1'b1 ;
					cnt      <= cnt + 8'd1 ;
					end
				8'd112 : begin
					tx       <= datain[6] ;
					presult  <= datain[6] ^ presult ;
					idle     <= 1'b1 ;
					cnt      <= cnt + 8'd1 ;
					sendout  <= 0 ;
					end
				8'd128 : begin
					tx       <= datain[7] ;
					presult  <= datain[7] ^ presult ;
					idle     <= 1'b1 ;
					cnt      <= cnt + 8'd1 ;
					sendout  <= 0 ;
					end	
				8'd144 : begin
					tx       <= presult ; 
					presult  <= datain[0] ^ paritmode ;
					idle     <= 1'b1 ;
					cnt      <= cnt + 8'd1 ;
					sendout  <= 0 ;
					end
				8'd160 : begin
					tx       <= 1'b1 ;           //一帧数据发送结束
					idle     <= 1'b1 ;
					cnt      <= cnt + 8'd1 ;
					sendout  <= 0 ;
					end
				8'd168 : begin            //变量恢复
					tx       <= 1'b1 ;     
					idle     <= 1'b0 ;
					cnt      <= 0 ;
					sendout  <= 1 ;
					send_cnt <= send_cnt + 1 ;
					end
				default : begin
					cnt      <= cnt + 8'd1 ;
					sendout  <= 0 ;
					end
				endcase
			end
			else 
				begin
				tx      <= 1'b1 ;
				cnt     <= 8'd0 ;
				idle    <= 1'b0 ;
				sendout <= 0 ;
				end
	end

	endmodule
			

串口接收程序

	`timescale 1ns/1ps
	//16个时钟接收一个数据,检测下降沿,还有帧检验和奇偶检验
	module uart_rxd(clk , rst_n , dataout , rx , rsding , dataerror , fameerror) ;
	input clk ; //时钟
	input rst_n ; //复位信号
	input rx ; //接收的数据
	output dataout ;
	output rsding ; //
	output dataerror ;
	output fameerror ;
	
	reg [7:0]dataout ;
	reg rsding , dataerror ;
	reg fameerror ;
	reg [7:0]cnt ;
	reg rxbuf , rxfall , receive ;
	parameter paritymode = 1'b0 ;
	reg presult , idle ;
	reg [1:0]rec_cnt ;
	
	always @(posedge clk)
	begin 
	rxbuf   <= rx ;
	rxfall  <= rxbuf & (~rx) ;
	end
	
	always @(posedge clk)
	begin
	if(rxfall && (~idle))
	begin
	receive <= 1'b1 ;
	end
	else begin
	if(cnt == 8'd152 )
	begin
	receive <= 1'b0 ;
	end
	end
	end
	
	always @(posedge clk or negedge rst_n)
	begin
	if(~rst_n) begin
	cnt       <= 8'd0 ;
	dataerror <= 1'b0 ;
	fameerror <= 1'b0 ;
	idle      <= 1'b0 ;
	presult   <= 1'b0 ;
	rsding    <= 1'b0 ;
	rec_cnt   <= 0 ;
	end
	else if(receive == 1'b1) begin
	case(cnt)
		8'd0 : begin
			idle       <= 1'b1 ;
			rsding     <= 1'b0 ;
			cnt        <= cnt + 8'd1 ;
			end
		8'd24 : begin       //起始位16个时钟,第一位数据在8个时钟时接收,16+8 
			idle       <= 1'b1 ;
			dataout[0] <= rx ;
			cnt        <= cnt + 8'd1 ;
			presult    <= dataout[0] ^ paritymode ;
			rsding     <=  1'b0 ;
			end
		8'd40 : begin 
			idle       <= 1'b1 ;
			dataout[1] <= rx ;
			cnt        <= cnt + 8'd1 ;
			presult    <= dataout[1] ^ presult ;
			rsding     <= 1'b0 ;
			end
		8'd56 : begin 
			idle       <= 1'b1 ;
			dataout[2] <= rx ;
			cnt        <= cnt + 8'd1 ;
			presult    <= dataout[2] ^ presult ;
			rsding     <=  1'b0 ;
			end
		8'd72 : begin 
			idle       <= 1'b1 ;
			dataout[3] <= rx ;
			cnt        <= cnt + 8'd1 ;
			presult    <= dataout[3] ^ presult ;
			rsding     <=  1'b0 ;
			end
		8'd88 : begin 
			idle       <= 1'b1 ;
			dataout[4] <= rx ;
			cnt        <= cnt + 8'd1 ;
			presult    <= dataout[4] ^ presult ;
			rsding     <=  1'b0 ;
			end
		8'd104 : begin 
			idle       <= 1'b1 ;
			dataout[5] <= rx ;
			cnt        <= cnt + 8'd1 ;
			presult    <= dataout[5] ^ presult ;
			rsding     <=  1'b0 ;
			end
		8'd120 : begin 
			idle       <= 1'b1 ;
			dataout[6] <= rx ;
			cnt        <= cnt + 8'd1 ;
			presult    <= dataout[6] ^ presult ;
			rsding     <=  1'b0 ;
			end
		8'd136 : begin 
			idle       <= 1'b1 ;
			dataout[7] <= rx ;
			cnt        <= cnt + 8'd1 ;
			presult    <= dataout[7] ^ presult ;
			rsding     <=  0 ;
			rec_cnt    <= rec_cnt + 1 ;
			end
		8'd152 : begin
			idle       <= 1'b1 ;
			if(1'b1 == rx)
			fameerror  <= 1'b0 ;
			else
			fameerror  <= 1'b1 ;
			rsding     <= 1 ;
			cnt        <= 0 ;
			end
		default : begin
			cnt        <= cnt + 8'd1 ;
			rsding     <= 0 ;
			end
		endcase
		end
		else begin
		cnt <= 8'd0 ;
		rsding <= 1'b0 ;
		idle <= 1'b0 ;
		end
		end
		
		endmodule
	

RAM控制程序

//==================================================================================================
//  Filename      : uart_control.v
//  Created On    : 2019-04-27 15:00:48
//  Last Modified : 2019-04-28 20:58:31
//  Revision      : 1.0
//
//  Description   : control the rden and wren port of the ram , to realize the function 
//					that each time receive four data in and then send four data out
//					together .
//==================================================================================================
	module uart_control(
		input			clk ,
		input 			rst ,
		input 			rdsig ,
		input			send ,
		output		reg	rden ,
		output		reg	wren ,
		output		reg [1:0]	addr , 
		output		reg	sendstart 
		) ;

		reg		[1:0]	state ;
		reg 	[1:0]	cnt ;

		parameter INITIAL = 2'd0 ;
		parameter RECEIVE = 2'd1 ;
		parameter SEND    = 2'd2 ;


		always @(posedge clk or negedge rst) begin
			if (~rst) begin
				// reset
				rden      <= 0 ;
				wren      <= 0 ;
				state     <= 2'd0 ;
				addr      <= 0 ;
				cnt       <= 0 ;
				sendstart <= 0 ;
			end
			else  begin
				case(state)
					INITIAL : begin 
						rden <= 0 ;
						if(rdsig) begin
							state <= 2'd1 ; //goto receive state when data receive finish
							cnt   <= 0 ;
						end
						else begin
							state <= 2'd0 ; //waiting while no data in
						end
					end	

					RECEIVE : begin
						case(cnt) 
						0: begin
							wren <= 1 ;
							cnt  <= cnt + 1 ;
						end
						1:	begin
							cnt           <= 0 ;
							if(addr == 0) begin
								addr      <= 1 ;
								state     <= 0 ;
								wren      <= 0 ;
							end
							else if(addr == 1) begin
								addr      <= 2 ;
								state     <= 0 ;
								wren      <= 0 ;
							end
							else if(addr == 2) begin
								addr      <= 3 ;
								state     <= 0 ;
								wren      <= 0 ;
							end
							else  begin
								addr      <= 0 ;
								state     <= 2 ;
								wren      <= 0 ;
								sendstart <= 1 ;
							end
						end
						endcase 
					end	

					SEND : begin
						case(cnt) 
						0 : begin
							rden      <= 1 ;
							sendstart <= 0 ;
							cnt       <= cnt + 1 ;
						end
						1 : begin
							if (send) begin
								if (addr == 3) begin //already send four data out
									addr  <= 0 ;
									state <= 2'd0 ; //back to initial state
									cnt   <= 0 ;
								end
								else if(addr == 0) begin
									addr  <= addr + 1 ;
									wren  <=0 ;
									state <= 2'd2 ;
								end
								else begin
									addr  <= addr + 1 ;
									state <= 2'd2 ; //keep send state when still have data remaining
								end
							end
							else begin
								state <= 2'd2 ;
							end
						end
						endcase
					end
				default : begin
				end
				endcase
			end
		end
		
		endmodule

FPGA串口学习
各模块连接如图所示,ram设为存储位数8位,大小为4。

结果分析

FPGA串口学习
从结果中可以看出,每次发1个、2个和4个数据时结果正常,但是一次发其他数量数据时会有数据丢失。利用signaltap进行分析,发现是因为使用的是单口ram,因此读写不能同时进行。在发送头四个数据时,剩余的数据仍然在被接收但又没有存储下来,因此导致了丢失。(仅用单口ram能否完成此功能暂不确定,本人倾向于认为不可实现,如有知道的大神望告知)
FPGA串口学习