FPGA学习之UART串口通信初尝试之二 (更加健壮的接收模块)

UART串口接收V2

背景

在前几天写完串口接收、发送代码后,自己也去查看了小梅哥的FPGA教程课,在他的教程中,接收模块还做了多次采集以确保模块在面对不稳定链路时的健壮性;个人感觉还是值得重写一遍作为新人练手的,其中代码思路延续小梅哥的,但是代码复现为本人从头重写,故会与小梅哥所提供代码例程存在差异。

代码难度

代码总体不难,但是量对比之前版本多上不少。我花了一天的时间去复现+调试,总是存在连续接收数据时会错过前三位数据,当时大致猜到了是错过了接收开始标志位导致。调试一天主要原因是屏幕太小了,看时序图真的很难受,自己也有点摆烂了,后面回家接上27寸大屏一分析,不到一小时便定位到错误并进行了代码更正。

UART接收代码V2

//
//
//				UART 接收模块
//			   此代码中将额外考虑通讯链路稳定问题
//				接收数据将进行7次采样,出现大于4次的信号数据将作为最终采样结果
//
/
module uart_rx_v2(
	input clk,
	input rst_n,
	input rx_data,
	output reg rx_done,
	output reg [7:0]save_rx_data
);
//
//
//				 检测开始接收标志位
//			  
/
	
	wire RX_START;
	reg RX_data_0;
	reg RX_data_1;
	always @(posedge clk, negedge rst_n)begin
		if(!rst_n) begin
			RX_data_0 <= 0;
			RX_data_1 <= 0;
		end else begin
			RX_data_0 <= rx_data;
			RX_data_1 <= RX_data_0;
		end
	end
	assign RX_START = !RX_data_0 && RX_data_1;
	
//
//
//								bps设置以及计时器
//			  
/	
	parameter CLK_HZ		=		50_000_000,
				 BPS			=		9600,
				 BPS_CNT		=		CLK_HZ / BPS,          //每BPS
				 BPS_CNT_7 =		BPS_CNT / 7;		  //1BPS的7次采样
	

//计数7分之1个bps的中间位置--更加精确采集数据	
	reg [31:0]bps_cnter;
	reg [7:0]receive_num;   
	reg START_CNT;
	reg half_bps_flag;
	always @(posedge clk, negedge rst_n)begin
		if(!rst_n) begin
			bps_cnter <= 32'b0;
			receive_num <= 8'b0;
			half_bps_flag <= 1'b0;
		end else begin
			if(START_CNT) begin
				if(bps_cnter > BPS_CNT_7 - 1) begin
					receive_num <= receive_num + 1'b1;
					half_bps_flag <= 1'b1;
					bps_cnter <= 1'b0;
				end else begin
					bps_cnter <= bps_cnter + 1'b1;
					half_bps_flag <= 1'b0;
					receive_num <= receive_num;
				end
			end else begin
				bps_cnter <= 32'b0;
				half_bps_flag <= 1'b0;
				receive_num <= 8'b0;
			end
		end
	end
	
//产生开始计数标志位
	
	always @(posedge clk, negedge rst_n) begin
		if(!rst_n) begin
			START_CNT <= 1'b0;
		end else begin
			if(RX_START) begin
				START_CNT <= 1'b1;
			end else begin
				if(rx_done) begin
					START_CNT <= 1'b0;
				end
			end
		end
	end

//
//
//								数据接收
//			  
/		

	parameter IDLE = 3'd1,
			    STRAT_JUDGE = 3'd2,
				 RECEIVE_BEGIN = 3'd3,
				 FINISH = 3'd4;
	reg [2:0]current_state;	
	reg [2:0]each_data_7[9:0];
	always @(posedge clk, negedge rst_n) begin
		if(!rst_n) begin
			current_state <= IDLE;
		end else begin
			case(current_state)
				IDLE: if(RX_START) current_state <= STRAT_JUDGE;
				STRAT_JUDGE: begin 
						if(each_data_7[0][2] == 0 && receive_num > 7 - 1) current_state <= RECEIVE_BEGIN;
						else begin
							if(receive_num > 7 - 1) begin
								current_state <= IDLE;
							end
						end 
				end
				RECEIVE_BEGIN: if(receive_num > 69 - 1) current_state <= FINISH;
				FINISH: current_state <= IDLE;
				default: current_state <= IDLE;
			endcase
		end
	end
	
	always @(posedge clk, negedge rst_n) begin
		if(!rst_n) begin
			save_rx_data <= 8'b0;
			each_data_7[0] <= 'd0;
			each_data_7[1] <= 'd0;
			each_data_7[2] <= 'd0;
			each_data_7[3] <= 'd0;
			each_data_7[4] <= 'd0;
			each_data_7[5] <= 'd0;
			each_data_7[6] <= 'd0;
			each_data_7[7] <= 'd0;
			each_data_7[8] <= 'd0;
			each_data_7[9] <= 'd0;
		end else begin
			case(current_state)
				IDLE: begin
					rx_done <= 1'b0;
					each_data_7[0] <= 'd0;
					each_data_7[1] <= 'd0;
					each_data_7[2] <= 'd0;
					each_data_7[3] <= 'd0;
					each_data_7[4] <= 'd0;
					each_data_7[5] <= 'd0;
					each_data_7[6] <= 'd0;
					each_data_7[7] <= 'd0;
					each_data_7[8] <= 'd0;
					each_data_7[9] <= 'd0;
				end
				STRAT_JUDGE: begin
					if(half_bps_flag) begin
						case(receive_num) 
							'd0,'d1,'d2,'d3,'d4,'d5, 'd6: each_data_7[0] <= each_data_7[0] + rx_data;
						endcase					
					end
				end
				RECEIVE_BEGIN: begin
					if(half_bps_flag) begin
						case(receive_num) 
							'd7,'d8,'d9,'d10,'d11,'d12, 'd13: each_data_7[1] <= each_data_7[1] + rx_data;
							'd14,'d15,'d16,'d17,'d18,'d19, 'd20: each_data_7[2] <= each_data_7[2] + rx_data;
							'd21,'d22,'d23,'d24,'d25,'d26, 'd27: each_data_7[3] <= each_data_7[3] + rx_data;
							'd28,'d29,'d30,'d31,'d32,'d33, 'd34: each_data_7[4] <= each_data_7[4] + rx_data;
							'd35,'d36,'d37,'d38,'d39,'d40, 'd41: each_data_7[5] <= each_data_7[5] + rx_data;
							'd42,'d43,'d44,'d45,'d46,'d47, 'd48: each_data_7[6] <= each_data_7[6] + rx_data;
							'd49,'d50,'d51,'d52,'d53,'d54, 'd55: each_data_7[7] <= each_data_7[7] + rx_data;
							'd56,'d57,'d58,'d59,'d60,'d61, 'd62: each_data_7[8] <= each_data_7[8] + rx_data;
							'd63,'d64,'d65,'d66,'d67,'d68, 'd69: each_data_7[9] <= each_data_7[9] + rx_data;  //停止位
						endcase
					end
				end
				FINISH: begin 
					rx_done <= 1'b1;
					save_rx_data <= {each_data_7[8][2], each_data_7[7][2],each_data_7[6][2],each_data_7[5][2],each_data_7[4][2],
					each_data_7[3][2],each_data_7[2][2],each_data_7[1][2]};
				end
			endcase
		end
	end	
endmodule

自我告诫

1、在调试过程中,自己还修改了部分发送模块代码,自以为不会存在问题便没有进行功能仿真,但是就是因为一时的自大,反而耽误了一上午的时间。在此警示自己,凡是改动的代码一定要提前备份+再次验证,不然将花费更多的精力去分析查错。
2、代码设计完成,不管多简单,一定要进行功能仿真,有时候你真的不知道自己会犯怎么离谱的错(误把复位输入给成时钟信号的我表示泪目)

题外话

之前和一个已经工作了三年的FPGAer交流时,发现他写的一个延时功能模块让我的一整个世界观都崩塌了。以下是他的代码:

`timescale 1ns / 1ps
module delay_time#(
    parameter DELAY_COUNT = 2'd1
    )(
        input       sys_clk,
        input       signal,
        output      signal_delay  
    );
    
    reg signal_delay_reg = 1'b0;
    assign signal_delay = signal_delay_reg;
    
    always@(*) begin
        if(DELAY_COUNT == 2'd1) begin
            signal_delay_reg <= #10000000000 signal;
        end
        else if(DELAY_COUNT == 2'd2) begin
            signal_delay_reg <= #20000000000 signal;
        end
        else begin
            signal_delay_reg <= #10000000000 signal;
        end
    end
endmodule

疑惑点

1、从代码来看,这就是个很简单的延时输入功能代码。但是!!!据我目前所学、以及查询到的相关资料来看,在设计中使用 # 来进行延时是不可综合的,在上述代码中应该是直接等价于赋值操作。
2、我当时也反复跟那个工程师表达了疑惑,他确定的跟我说这个操作是可以的;而且当时我把该代码生成的电路图给他看,他也还是确定该操作时可行的。所以我很好奇是在什么平台上该操作是可行的呢?
3、像initial这种虽然不可综合,但是利用它来进行寄存器初始化操作也是会实际生效的,但是#这种我并不认为会起作用。

实际验证

代码综合出的电路图:
在这里插入图片描述
相信大家看到这,都可以发现这个根本就是个端到端的电路,根本不会生成任何延时的功能电路。
但是!!!!!我对这个代码进行了功能仿真测试,在功能测试里面这个代码确实表现出了延时功能!
后续我把这段代码烧录进cyclone IV系列的FPGA中进行验证,实际测试也说明了这段代码是不可行的。在我按下板上按键时,对应LED灯立马作出了反应,表明延时根本不存在。

结论

这段代码是不可综合的,无法实现其功能,但在功能测试中会表现正常。

可能这种写法在其他平台可以使用?还希望老司机能告知。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值