编写fpga的串口代码时遇到的一个大坑!

编写fpga的串口代码时遇到的一个大坑!

1 warning 10240!!!

  在调试串口时遇到一个很诡异的问题。我打算用串口循环发送一个频率值,间隔为1s,然而下载到板子后只能发一次。我反复地检查我的时序设计图,然后用testbench仿真观察波形,都没有错!第一次遇到仿真正确然而下板验证时却出错的情况,值得纪念一下。不过难受的是我为这件事卡了一个星期,这一个星期里我反复地修改串口模块、发送频率模块和顶层模块,然而都无济于事,甚至还出现很多莫名其妙的情况,比如发送间隔为1s时只发了一次,改为10ms时就发送了两次,再改为1us时就什么也收不到了,完全不知道里面发生了什么……
  转机出现在今晚,我想起了以前看过的fpga开发分享贴,里面提到调试fpga时如果实在找不出bug所在,就看看warning。这一看不得了了!当我把下面这条10240警告改掉之后,程序就跑通了!!!

10240 Verilog HDL Always Construct warning at send_freq.v(38): inferring latch(es) for variable "r_next_state", which holds its previous value in one or more paths through the always construct

  这一条警告出现的原因是写了if没写else,或者写了case没写default,产生了锁存器。我在send_freq.v里面是这样写的:

always @(posedge i_clk or negedge i_rst_n) begin
	if(!i_rst_n)
		r_cur_state <= S_IDLE;
	else
		r_cur_state <= r_next_state;
end

always @(*) begin
	case(r_cur_state)
		S_IDLE: 
			if(i_freq_send_req)	
				r_next_state <= S_SEND_TENTH;
		S_SEND_TENTH:
		...
		default: r_next_state <= S_IDLE;
	endcase
end

  我没有给r_next_state赋初值,复位后r_cur_state为S_IDLE,如果在紧接着的时钟沿到来前i_freq_send_req没有出现,那么r_cur_state就被赋值为r_next_state,一个未初始化的值。然后再次进入case判断时,因为有default的存在,r_next_state第一次被赋值为S_IDLE。接着r_cur_state又被赋值为S_IDLE,这个时候程序才走上正轨,该赋值的变量都赋值了。按理说不应该出现我遇到的问题,所以这个玄学我暂时没法解释。先记录在此,以后回过头来看也许就能一目了然。如果各位看官知道其中道理的,还望不吝赐教。
  以后要是要重视编程规范呀。

2 代码

  这部分是可以运行的代码,分别是串口发送模块,频率发送控制模块和顶层模块。

2.1 uart_tx.v

module uart_tx(
input		i_clk,
input		i_rst_n,
input		i_uart_send_req,
input[7:0]	i_uart_send_byte,
output		o_uart_send_ack,
output		o_uart_tx_pin
);
localparam C_CYCLE = 50 * 1000000 / 115200;

localparam S_IDLE 	= 5'b00001;
localparam S_START 	= 5'b00010;
localparam S_DATA 	= 5'b00100;
localparam S_STOP 	= 5'b01000;
localparam S_ACK	= 5'b10000;

reg[4:0] r_cur_state;
reg[4:0] r_next_state;

reg[31:0]r_cycle_cnt;
reg[2:0] r_bit_cnt;
reg[7:0] r_uart_send_byte;
reg		 r_uart_send_ack;
reg		 r_uart_tx_pin;

assign o_uart_tx_pin = r_uart_tx_pin;
assign o_uart_send_ack = r_uart_send_ack;

always @(posedge i_clk or negedge i_rst_n) begin
	if(!i_rst_n)
		r_cur_state <= S_IDLE;
	else
		r_cur_state <= r_next_state;
end

always @(*) begin
	case(r_cur_state)
		S_IDLE: 
			if(i_uart_send_req) 
				r_next_state <= S_START;
			else
				r_next_state <= S_IDLE;
		S_START: 
			if(r_cycle_cnt == C_CYCLE - 1)
				r_next_state <= S_DATA;
			else
				r_next_state <= S_START;
		S_DATA: 
			if((r_cycle_cnt == C_CYCLE - 1) && r_bit_cnt == 3'd7)
				r_next_state <= S_STOP;
			else
				r_next_state <= S_DATA;
		S_STOP: 
			if(r_cycle_cnt == C_CYCLE - 1)
				r_next_state <= S_ACK;
			else
				r_next_state <= S_STOP;
		S_ACK: r_next_state <= S_IDLE;
		default: r_next_state <= S_IDLE;
	endcase
end

always @(posedge i_clk or negedge i_rst_n) begin
	if(!i_rst_n)
		r_uart_tx_pin <= 1'b1;
	else
		case(r_next_state)
			S_IDLE: r_uart_tx_pin <= 1'b1;
			S_START: r_uart_tx_pin <= 1'b0;
			S_DATA: r_uart_tx_pin <= r_uart_send_byte[r_bit_cnt];
			S_STOP, S_ACK: r_uart_tx_pin <= 1'b1;
			default: r_uart_tx_pin <= 1'b1;
		endcase
end

always @(posedge i_clk or negedge i_rst_n) begin
	if(!i_rst_n)
		r_cycle_cnt <= 32'd0;
	else if(r_cur_state != r_next_state || (r_next_state == S_DATA && r_cycle_cnt == C_CYCLE - 1))
		r_cycle_cnt <= 32'd0;
	else
		r_cycle_cnt <= r_cycle_cnt + 32'd1;
end

always @(posedge i_clk or negedge i_rst_n) begin
	if(!i_rst_n)
		r_bit_cnt <= 3'd0;
	else if(r_cur_state == S_DATA)
		if(r_cycle_cnt == C_CYCLE - 1)
			r_bit_cnt <= r_bit_cnt + 3'd1;
		else
			r_bit_cnt <= r_bit_cnt;
	else
		r_bit_cnt <= 3'd0;
end

always @(posedge i_clk or negedge i_rst_n) begin
	if(!i_rst_n)
		r_uart_send_byte <= 8'd0;
	else if(i_uart_send_req)
		r_uart_send_byte <= i_uart_send_byte;
	else
		r_uart_send_byte <= r_uart_send_byte;
end

always @(posedge i_clk or negedge i_rst_n) begin
	if(!i_rst_n)
		r_uart_send_ack <= 1'b0;
	else if(r_next_state == S_ACK)
		r_uart_send_ack <= 1'b1;
	else
		r_uart_send_ack <= 1'b0;
end

endmodule

2.2 send_freq.v

module send_freq(
input		i_clk,
input		i_rst_n,
input		i_freq_send_req,
input[9:0]	i_freq,
output		o_freq_send_ack,
output		o_uart_tx_pin
);

localparam S_IDLE 		= 6'b000001;
localparam S_SEND_TENTH = 6'b000010;
localparam S_SEND_DOT 	= 6'b000100;
localparam S_SEND_INT 	= 6'b001000;
localparam S_WAIT 		= 6'b010000;
localparam S_ACK		= 6'b100000;

reg[5:0] r_cur_state;
reg[5:0] r_next_state;
reg[5:0] r_last_state;

reg[9:0] r_freq;
reg[7:0] r_uart_send_byte;

reg	    r_freq_send_ack;
reg 	r_uart_send_req;
wire 	r_uart_send_ack;

assign o_freq_send_ack = r_freq_send_ack;

always @(posedge i_clk or negedge i_rst_n) begin
	if(!i_rst_n)
		r_cur_state <= S_IDLE;
	else
		r_cur_state <= r_next_state;
end

always @(*) begin
	case(r_cur_state)
		S_IDLE: 
			if(i_freq_send_req)	
				r_next_state <= S_SEND_TENTH;
			else // important!!
				r_next_state <= S_IDLE;
		S_SEND_TENTH,S_SEND_DOT,S_SEND_INT: r_next_state <= S_WAIT;
		S_WAIT:
			if(r_uart_send_ack) begin
				if(r_last_state == S_SEND_TENTH)
					r_next_state <= S_SEND_DOT;
				else if(r_last_state == S_SEND_DOT)
					r_next_state <= S_SEND_INT;
				else if(r_last_state == S_SEND_INT && (|r_freq))
					r_next_state <= S_SEND_INT;
				else
					r_next_state <= S_ACK;
			end
			else
				r_next_state <= S_WAIT;
		S_ACK: r_next_state <= S_IDLE;
		default: r_next_state <= S_IDLE;
	endcase
end

always @(posedge i_clk or negedge i_rst_n) begin
	if(!i_rst_n)
		r_freq_send_ack <= 1'b0;
	else if(r_next_state == S_ACK)
		r_freq_send_ack <= 1'b1;
	else
		r_freq_send_ack <= 1'b0;
end

always @(posedge i_clk or negedge i_rst_n) begin
	if(!i_rst_n)
		r_last_state <= S_IDLE;
	else if(r_next_state == S_SEND_TENTH || r_next_state == S_SEND_DOT || r_next_state == S_SEND_INT)
		r_last_state <= r_next_state;
	else
		r_last_state <= r_last_state;
end

always @(posedge i_clk or negedge i_rst_n) begin
	if(!i_rst_n)
		r_uart_send_req <= 1'b0;
	else if(r_next_state == S_SEND_TENTH)
		r_uart_send_req <= 1'b1;
	else if(r_next_state == S_ACK)
		r_uart_send_req <= 1'b0;
	else
		r_uart_send_req <= r_uart_send_req;
end

always @(posedge i_clk or negedge i_rst_n) begin
	if(!i_rst_n)
		r_freq <= 10'd0;
	else if(r_next_state == S_SEND_TENTH)
		r_freq <= i_freq / 10'd10;
	else if(r_next_state == S_SEND_INT && (|r_freq))
		r_freq <= r_freq / 10'd10;
	else
		r_freq <= r_freq;
end

always @(posedge i_clk or negedge i_rst_n) begin
	if(!i_rst_n)
		r_uart_send_byte <= 8'hff;
	else if(r_next_state == S_SEND_TENTH)
		r_uart_send_byte <= i_freq % 10'd10 + "0";
	else if(r_next_state == S_SEND_DOT)
		r_uart_send_byte <= ".";
	else if(r_next_state == S_SEND_INT && (|r_freq))
		r_uart_send_byte <= r_freq % 10'd10 + "0";
	else
		r_uart_send_byte <= r_uart_send_byte;
end

uart_tx uart_tx_inst(
.i_clk					(i_clk),
.i_rst_n				(i_rst_n),
.i_uart_send_req		(r_uart_send_req),
.i_uart_send_byte		(r_uart_send_byte),
.o_uart_send_ack		(r_uart_send_ack),
.o_uart_tx_pin			(o_uart_tx_pin)
);

endmodule

2.3 send_freq_top.v

module send_freq_top(
input  i_clk,
input  i_rst_n,
output o_uart_tx_pin
);

localparam S_IDLE = 4'b0001;
localparam S_SEND = 4'b0010;
localparam S_WAIT = 4'b0100;
localparam S_SEND2= 4'b1000;

reg[3:0]  r_next_state;
reg[31:0] r_clk_cnt;
reg[9:0]  r_freq;
reg		  r_freq_send_req;
wire      r_freq_send_ack;

always @(posedge i_clk or negedge i_rst_n) begin
	if(!i_rst_n) begin
		r_next_state <= S_IDLE;
		r_freq <= 10'd1021;
		r_freq_send_req <= 1'b0;
		r_clk_cnt <= 32'd0;
	end
	else
		case(r_next_state)
			S_IDLE: r_next_state <= S_SEND;
			S_SEND: begin
				r_clk_cnt <= 32'd0;
				if(r_freq_send_req && r_freq_send_ack) begin
					r_freq_send_req <= 1'b0;
					r_next_state <= S_WAIT;
				end
				else if(!r_freq_send_req)
					r_freq_send_req <= 1'b1;
			end
			S_WAIT: begin				
				r_clk_cnt <= r_clk_cnt + 32'd1;
				if(r_clk_cnt >= 32'd50000000)
					r_next_state <= S_SEND;
			end
			default: r_next_state <= S_IDLE;
		endcase
end

send_freq send_freq_inst(
.i_clk						(i_clk),
.i_rst_n					(i_rst_n),
.i_freq_send_req			(r_freq_send_req),
.i_freq						(r_freq),
.o_freq_send_ack			(r_freq_send_ack),
.o_uart_tx_pin				(o_uart_tx_pin)
);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值