Verilog实现8/16/24/32bitUART发送机

目录

 设计思路

波特率计数器:

bit计数器:

帧计数器:

Verilog代码

uart_tx:

testbench: 

wave(部分截图): 


概述:

通过修改代码中的参数和输入数据位宽,可以实现在不同波特率下发送8的倍数(如8、16、24、32等)位数据。实现方法是将数据拆分成8bit一组,采用状态机结合并转串的方式实现分组发送。

uart通信协议的相关知识我就不介绍了,在网上都能找到非常详细的讲解。首先确定串口通信的数据格式及波特率,文中所采用的帧格式如下图所示。起始位为1位,数据位为8位,停止位为1位。无奇偶校验位。系统时钟50MHz,波特率为9600bps。

 设计思路

波特率计数器:

以文中的50MHz系统时钟,9600波特率为例,发送1bit时间为1/9600(s),时钟周期为20ns,那么发送1bit需要的系统周期为(1/9600)/ 20ns ≈ 5208(个)。在发送过程中使用一个计数器计数,计数区间为(0~5208-1),这样的区间一共10个(一个字节需要发送10个bit)。

bit计数器:

需要一个计数器对发送的bit数计数(每当上一个计数器计数到5207则表示发送完了一个bit),计数区间(0~10)。以发送32bit为例,在上电初期bit计数器从0(IDLE状态)开始累加计数,当第一帧数据发送完毕后,bit计数器直接跳转到1(START状态)开始计数,直到4帧数据都发送完毕后,清零。

帧计数器:

初始上电时,在START状态由0变1,指示第一帧数据开始发送,每当状态跳转到STOP状态时,加一指示即将发送下一帧数据。当所有数据都发送完毕时,清零。

Verilog代码

uart_tx:

module uart_tx(
	input wire 			sys_clk		,  //时钟
	input wire 			sys_rst_n	,  //复位,低电平有效

	input wire 			data_vld	,  //输入数据有效标志
	input wire [31:0]	uart_tx_i	,  //输入并行数据
	
	output reg 			uart_tx_o	  //输出的串行一位数据
);

reg			work_en;    //指示发送机状态
reg [12:0]	baud_cnt;   //0~baud_cnt_max-1
reg [3:0]	bit_cnt;    //0~9 0:开始位 1~8:数据位 9:停止位
reg [2:0]	byte_cnt;   //分成4段发送 0~4:有效的是1~4
reg			bit_flag;   //在baud_cnt记到3时发送数据
reg [7:0]	data_temp;  //分段发送32bit数据
reg [7:0]	data;       //移位实现并串转换

reg			start;
reg			send;

reg [3:0]	state;
reg [3:0]	nstate;

wire 		end_work;

localparam 	IDLE	= 4'b0001,
			START	= 4'b0010,
			SEND	= 4'b0100,
			STOP	= 4'b1000;

parameter	BAUD_CNT_MAX = 13'd5208;  //50MHz,9600bps
parameter	BYTE_MAX = 3'd4;
parameter	BIT_CNT_MAX = 4'd11;

//指示发送机状态
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		work_en <= 1'b0;
	end
	else if(data_vld) begin
		work_en <= 1'b1;
	end
	else if(end_work) begin
		work_en <= 1'b0;
	end
end

//发送机结束状态标志
assign end_work = ((byte_cnt == BYTE_MAX) && bit_flag && (bit_cnt == 4'd10)) ? 1'b1 : 1'b0;

//当baud_cnt从0记到最大值时,表明可以发送1bit数据
//计数范围0~BAUD_CNT_MAX-1
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		baud_cnt <= 13'b0;
	end
	else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0)) begin
		baud_cnt <= 13'b0;
	end
	else if(work_en == 1'b1) begin
		baud_cnt <= baud_cnt + 1'b1;
	end
end

//在baud_cnt记到3时发送数据
//发送数据标志
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		bit_flag <= 1'b0;
	end
	else if(baud_cnt == 13'd3) begin
		bit_flag <= 1'b1;
	end
	else begin
		bit_flag <= 1'b0;
	end
end

//记录bit数 计数范围1~10,1是开始位,2~9是数据位,10是停止位
//注意优先级
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		bit_cnt <= 4'b0;
	end
	else if(bit_flag && bit_cnt == BIT_CNT_MAX - 1'b1) begin
		case(byte_cnt)
			BYTE_MAX: bit_cnt <= 4'b0;
			default: bit_cnt <= 4'd1;
		endcase
	end
	else if(work_en && bit_flag) begin
		bit_cnt <= bit_cnt + 1'b1;
	end
end

//字节计数,0~4
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		byte_cnt <= 3'd0;
	end
	else if(end_work) begin
		byte_cnt <= 3'd0;
	end
	else if(state == IDLE && bit_flag) begin
		byte_cnt <= 3'b1;
	end
	else if(state == STOP && bit_flag) begin
		byte_cnt <= byte_cnt + 1'b1;
	end
end

//状态机第一段
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		state <= IDLE;
	end
	else begin
		state <= nstate;
	end
end

//状态机第二段
always @(*) begin
	case(state)
		IDLE: begin
			if(work_en && bit_flag) begin
				nstate = START;
			end
			else begin
				nstate = IDLE;
			end
		end
		START: begin
			if(bit_flag && bit_cnt == 4'd1) begin
				nstate = SEND;
			end
			else begin
				nstate = START;
			end
		end
		SEND: begin
			if(bit_flag && bit_cnt == 4'd9) begin
				nstate = STOP;
			end
			else begin
				nstate = SEND;
			end
		end
		STOP: begin
			if(bit_flag && bit_cnt == 4'd10) begin
				if(byte_cnt < BYTE_MAX) begin
					nstate = START;
				end
				else begin
					nstate = IDLE;
				end
			end
			else begin
				nstate = STOP;
			end
		end
		default: nstate = IDLE;
	endcase
end

//状态机第三段
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		uart_tx_o <= 1'b1;
	end
	else begin
		case(nstate)
			IDLE: begin
				uart_tx_o <= 1'b1;
			end
			START: begin
				uart_tx_o <= 1'b0;
			end
			SEND: begin
				uart_tx_o <= data[0];
			end
			STOP: begin
				uart_tx_o <= 1'b1;
			end
		endcase	
	end
end

//分段赋值
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		data_temp <= 8'b0;
	end
	else begin
		case(byte_cnt)
			3'd1: data_temp <= uart_tx_i[7:0];
			3'd2: data_temp <= uart_tx_i[15:8];
			3'd3: data_temp <= uart_tx_i[23:16];
			3'd4: data_temp <= uart_tx_i[31:24];
		endcase
	end
end

//状态机开始状态
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		start <= 1'b0;
	end
	else if(nstate == START) begin
		start <= 1'b1;
	end
	else begin
		start <= 1'b0;
	end
end

//状态机发送数据状态
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		send <= 1'b0;
	end
	else if(nstate == SEND) begin
		send <= 1'b1;
	end
	else begin
		send <= 1'b0;
	end
end

//数据在开始状态被赋值,在发送状态进行循环右移位
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		data <= 8'b0;
	end
	else if(start) begin
		data <= data_temp;
	end
	else if(send && bit_flag) begin
		data <= {data[0],data[7:1]};
	end
end

endmodule

testbench: 

`timescale 1ns/1ns

module tb_uart_tx();
	reg 			sys_clk		; 
	reg 			sys_rst_n	; 

	reg 			data_vld	; 
	reg [31:0]		uart_tx_i	; 

	wire 			uart_tx_o	; 

always #10 sys_clk = ~sys_clk;

 initial begin
	sys_clk = 1'b1;
	sys_rst_n <= 1'b0;
 #20;
	sys_rst_n <= 1'b1;
 end
 
 //模拟发送7次数据,分别为0~7
 initial begin
 uart_tx_i <= 32'b0;
 data_vld <= 1'b0;
 #200
 //发送数据0
 uart_tx_i <= 32'h11223344;
 data_vld <= 1'b1;
 #20
 data_vld <= 1'b0;
 //每发送1bit数据需要5208个时钟周期,一帧8位数据为10bit
 //需要发送32位数据,对应发送4帧
 //所以需要数据延时(5208*20*60)后再产生下一个数据
 
 #(5208*20*60);
 //发送数据1
 uart_tx_i <= 32'hddccbbaa;
 data_vld <= 1'b1;
 #20
 data_vld <= 1'b0;
 /* #(5208*20*10);
 //发送数据2
 uart_tx_i <= 8'd2;
 data_vld <= 1'b1;
 #20
 data_vld <= 1'b0;
 #(5208*20*10);
 //发送数据3
 uart_tx_i <= 8'd3;
 data_vld <= 1'b1;
 #20
 data_vld <= 1'b0;
 #(5208*20*10);
 //发送数据4
 uart_tx_i <= 8'd4;
 data_vld <= 1'b1;
 #20
 data_vld <= 1'b0;
 #(5208*20*10);
 //发送数据5
 uart_tx_i <= 8'd5;
 data_vld <= 1'b1;
 #20
 data_vld <= 1'b0;
 #(5208*20*10);
 //发送数据6
 uart_tx_i <= 8'd6;
 data_vld <= 1'b1;
 #20
 data_vld <= 1'b0;
 #(5208*20*10);
 //发送数据7
 uart_tx_i <= 8'd7;
 data_vld <= 1'b1;
 #20
 data_vld <= 1'b0; */
 
 end


 
 //\* Instantiation \//
 

 //------------------------uart_rx_inst------------------------
 uart_tx uart_tx_inst(
 .sys_clk (sys_clk ), //input sys_clk
 .sys_rst_n (sys_rst_n ), //input sys_rst_n
 .uart_tx_i (uart_tx_i ), //output [7:0] uart_tx_i
 .data_vld (data_vld ), //output data_vld

 .uart_tx_o (uart_tx_o ) //input tx
 );

 endmodule

wave(部分截图): 

 代码我已经全部仿真上板试验过了,都是没有问题的,文章中如果有遗漏或者错误的地方,还请小伙伴们多多指出。

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值