FPGA串口学习

串口与单口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

在这里插入图片描述
各模块连接如图所示,ram设为存储位数8位,大小为4。

结果分析

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值