Xilinx-Spartan6-学习笔记(21):UART时序分析

Xilinx-Spartan6-学习笔记(21):UART时序分析

由于黑金教程只给出了代码和最终结果,没有中间分析的过程。经过犹豫后,还是决定花功夫进行一下时序分析,为了后续复习方便,也同时监督自己认真对待每一行代码。

话不多说,黑金教程里讲的咱们全部都跳过,直接上干货。

1、clkdiv.v文件

这个文件主要目的是通过50Mhz的系统时钟,分出一个较低频率的时钟用于UART串口驱动。取16倍波特率的目的是对每比特数据有16个时钟进行采样,从而确保不会漏采或者错采。

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0) begin
		clkout <= 1'b0;
		cnt <= 16'd0;
	end
	else
		if(cnt == 16'd162) begin
				clkout <= 1'b1;
				cnt <= cnt + 16'd1;
		end
		else if(cnt == 16'd325) begin
				clkout <= 1'b0;
				cnt <= 16'd0;
		end
		else begin
				cnt <= cnt + 16'd1;
		end	
assign w_clkout = clkout;

分频系数的计算公式为:500000000/(16*9600)=325.52~=326。50Mhz代表每秒50M个时钟周期,而我们需要每秒16*9600个时钟周期,相当于每326个50Mhz的时钟周期构成了一个新的时钟周期,即162个周期翻转一次。因此我们可以定义一个wire型时钟变量w_clkout,通过计数器的方法实现0 ~162个周期w_clkout为高,163 ~325个周期w_clkout为低,最终得到的w_clkout信号为最终所需的时钟周期。

2、uartrx.v文件
always @(posedge sclk)
begin
	rxbuf <= rx;
	rxfall <= rxbuf & (~rx);	//检测线路的下降沿
end

在这里插入图片描述
这一小段用来检测rx的下降沿,通过定义一个寄存器变量打一拍的方式即可检测rx是否发生下降沿,如果发生的话rxfall拉高一个周期。

always @(posedge sclk)	
	if(rxfall && (~idle))	//检测到线路的下降沿并且原先线路为空闲,启动接收数据进程
		receive <= 1'b1;
	else if(cnt == 8'd168)		//接收数据完成(8bit数据,每个bit需要16个时钟)
		receive <= 1'b0;

这里表示当数据线为空闲状态时(idle为1),且检测到了rx有下降沿出现,将receive拉高。此时计数器cnt保持计数,当到168时,receive拉低,表示接收完毕。计数器记到168是由于8bit数据,每bit数据需要16个时钟周期采样,即8*16=168。

always @(posedge sclk)
	if(receive == 1'b1) begin
		case(cnt)	
		8'd0:begin
			idle <= 1'b1;
			cnt <= cnt + 8'd1;
			rdsig <= 1'b0;
		end
……
	else begin
		cnt <= 8'd0;
		idle <= 1'b0;
		rdsig <= 1'b0;
	end

在这里插入图片描述

这一小段表示将rx数据线上的数据接收到dataout中的过程。当receive信号被拉高后,接收端状态机开始执行。状态机的切换以cnt作为标志,将rx上的值放到dataout中(正好取得是tx的中间位置,避免了错误取值),当8bit数据发送完毕后,发送停止位,rdsig信号拉高(接收的时候rdsig为低),标志结束。

最终dataout输出连接到了uartctrlrxdata(后续再分析)。

3、uarttx.v文件
always @(posedge sclk)
begin
	wrsigbuf <= wrsig;	//检查wrsig的上升沿
	wrsigrise <= (~wrsigbuf) & wrsig;
end

这一小段同样是用来检测wrsig的上升沿的,当wrsig上升沿到来时,wrsigrise拉高。

always @(posedge sclk)	
	if(wrsigrise && (~idle))	//当发送命令有效且线路为空闲时,启动新的数据发送进程
		send <= 1'b1;
	else if(cnt == 8'd168)		//一帧数据发送结束
		send <= 1'b0;

这里表示当数据线为空闲状态时(idle为1),且检测到了wrsig有下降沿出现,则send拉高。此时计数器cnt保持计数,当到168时,send拉低,表示发送完毕。计数器记到168与接收原理相同。

always @(posedge sclk)
	if(send == 1'b1) begin
		case(cnt)		//产生起始位
		8'd0:begin
			tx <= 1'b0;
			idle <= 1'b1;
			cnt <= cnt + 8'd1;
		end
……
	else begin
		tx <= 1'b1;
		cnt <= 8'd0;
		idle <= 1'b0;
	end

在这里插入图片描述
这一小段表示将预发送的数据datain放到tx数据线上的过程。当send信号被拉高后,发送端状态机开始执行。状态机的切换以cnt作为标志,将datain中的数据放到tx线上,当8bit数据发送完毕后,idle信号拉高(发送时候idle为低),标志结束。

其中datain输入连接到了uartctrltxdata(后续再分析)。

4、uartctrl.v文件

这个文件的作用主要是协调配合TX和RX两个模块的,当RX接收到数据时,发送数据。如果没有接收到新的数据,那么循环发送预先设置好的数据,此部分数据在本文件中进行配置。对于这个选择开关,掌握在信号data_sel手里。

assign dataout = data_sel ? dataout_reg:rxdata;
assign wrsig = data_sel ? wrsig_reg:rdsig;

data_sel为1时,dataout被赋值为dataout_regwrsig被赋值为wrsig_reg
data_sel为0时,dataout被赋值为rxdatawrsig被赋值为rdsig

这意味着data_sel为1时,输出预先存储好的数据,并且循环发送;data_set为0时,输出RX端接收到的数据。

always @(posedge sclk)
	if(rdsig == 1'b1) begin
		uart_cnt <= 0;
		uart_stat <= 3'b000;
		data_sel <= 1'b0;
		k <= 0;
	end
	else begin
		case(uart_stat)
		3'b000: begin
			if(rx_data_valid == 1'b1) begin
				uart_stat <= 3'b001;
				data_sel <= 1'b1;
			end
		end
		3'b001: begin
			if(k == 18) begin
				if(uart_cnt == 0) begin
					dataout_reg <= store[18];
					uart_cnt <= uart_cnt + 1'b1;
					wrsig_reg <= 1'b1;
				end
				else if(uart_cnt == 254) begin
					uart_cnt <= 'd0;
					wrsig_reg <= 1'b0;
					uart_stat <= 3'b010;
					k <= 0;
				end
				else begin
					uart_cnt <= uart_cnt + 1'b1;
					wrsig_reg <= 1'b0;
				end
			end
			else begin
				if(uart_cnt == 0) begin
					dataout_reg <= store[k];
					uart_cnt <= uart_cnt + 1'b1;
					wrsig_reg <= 1'b1;
				end
				else if(uart_cnt == 254) begin
					uart_cnt <= 'd0;
					wrsig_reg <= 1'b0;
					k <= k + 1'b1;
				end
				else begin
					uart_cnt <= uart_cnt + 1'b1;
					wrsig_reg <= 1'b0;
				end
			end
		end
		3'b010: begin
			uart_stat <= 3'b000;
			data_sel <= 1'b0;
		end
		default: uart_stat <= 3'b000;
		endcase
	end

从此段代码可以看出,data_selrdsig为1时被置为0,也就是8bit信号发送完毕后,此时将所有状态清零。然后rdsig信号又变为0,进入到通过uart_stat控制的状态机中,如果rx_data_valid满足,表示可以发送预存好的数据。将store中的数据一个一个地赋给dataout_reg

always @(negedge sclk)
	if(rdsig == 1'b1) begin
		uart_wait <= 0;
		rx_data_valid<=1'b0;
	end
	else begin
		if(uart_wait == 18'h3ffff) begin
			uart_wait <= 0;
			rx_data_valid <= 1'b1;
		end
		else begin
			uart_wait <= uart_wait + 1'b1;
			rx_data_valid <= 1'b0;
		end
	end

此段代码中uart_wait的作用是设定发送时间间隔的(发送内部定义数据)。当计数到18’h3ffff的时候,rx_data_valid置为1,表示可以发送内部数据。

5、数据流梳理

将所有模块综合起来,梳理如下:

(1)当发送内部数据时
首先根据9600*16的时钟节拍div_clk,在uartctrl模块中将数据保存在了store中,此时rdsig信号为0时,data_sel为1,将store中的数据一位一位地赋给了dataout_reg寄存器,最终将dataout_reg寄存器中的值赋给了dataout,通过端口给了txdatatxdata进入到了uarttx模块中,通过datain端口输入。当wrsig信号上升沿出现(uartctrl模块中内部数据保存完毕后)且数据线处于idle状态下,send信号被拉高,datain输入进来的数据按照div_clk时钟节拍,一位一位地将数据放到tx数据线上发送出去,tx数据线连接到了FPGA板卡的D12端口,即通过UART输出。

(2)当发送接收到的数据时
首先在模块uartrx中,当rx数据线上出现下降沿时且数据线处于idle状态时,receive信号被拉高,将数据线rx上的数据按照状态机一位一位地接收到dataout寄存器中,通过端口给了rxdatarxdata进入到了uartctrl中。当发送完成后rdsig信号被拉高,同样输入到了uartctrl模块中。当rdsig为高时,data_sel置为0,那么wrsig被赋值为rdsigwrsig信号触发uarttx使得数据进行发送。与此同时,dataout也被赋值为了rxdata,即实现了将接收到的数据给了发送端,通过发送端发送出去(发送的方法与发送内部数据相同)。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值