zl程序教程

您现在的位置是:首页 >  后端

当前栏目

重温FPGA开发12

开发 12 FPGA 重温
2023-09-14 09:09:12 时间

verilog 设计串口通信(需要手动调试)

复习我们要设计的模块,多了一个波特率输入

在这里插入图片描述
在这里插入图片描述

uart_byte_tx.v

module uart_byte_tx(
	clk,
	reset_n,
	data,
	send_en,    // 什么时候开始发送
	uart_tx,    // 输出的串行信号
	tx_done,
	baud_set
	)	// 发完了 直呼一声,好发送下一个

	input clk;
	input reset_n;
	input [7:0]data;
	input send_en;
	input [2:0]baud_set;
	output reg uart_tx;
	output reg tx_done;
	
	// 译码器 
	// bps_dr = 0, 波特率9600
	// bps_dr = 1, 波特率19200
	// bps_dr = 2, 波特率38400
	// bps_dr = 3, 波特率57600
	// bps_dr = 4, 波特率115200
	// 1000000000/9600/20 就是bps_dr的值
	reg [17:0] bps_dr;
	always@(*)
		case(bps_dr)
			0: 	bps_dr = 1000000000/9600/20;
			1: 	bps_dr = 1000000000/19200/20;
			2: 	bps_dr = 1000000000/38400/20;
			3: 	bps_dr = 1000000000/57600/20;
			4: 	bps_dr = 1000000000/115200/20;
			default:bps_dr = 1000000000/9600/20;
		endcase
	
	// 计数器 这个需要算位宽
	reg [17:0]div_cnt;
	always@(posedge clk or negedge reset)
	if(!reset)
		div_cnt <= 0;
	else if(send_en)
		if(div_cnt == bps_dr - 1)
			div_cnt <= 0;
		else 
			div_cnt <= div_cnt + 1'b1;
	else
		div_cnt <= 0;

	 // 发送数据的时候 0 是起始位,1 是停止位。需要发送10段数据需要11个点来确定。需要一个计数器来计数11次
	reg [3:0]bps_cnt;
	always@(posedge clk or negedge reset)
	if(!reset)
		bps_cnt <= 0;
	else if(div_cnt == bps_dr - 1) begin
		if(bps_cnt == 11)
			bps_cnt <= 0;
		else
			bps_cnt <= bps_cnt + 1'b1;
	end

	// 发送数据
	always@(posedge clk or negedge reset)
	if(!reset)
		uart_tx <= 1'b1;
		tx_done <= 0;
	else begin
		case(bps_cnt)
			0: begin uart_tx <= 0; tx_done <= 1'b0; end
			1: uart_tx <= Data[0];
			2: uart_tx <= Data[1];	
			3: uart_tx <= Data[2];
			4: uart_tx <= Data[3];	
			5: uart_tx <= Data[4];
			6: uart_tx <= Data[5];	
			7: uart_tx <= Data[6];
			8: uart_tx <= Data[7];	
			9: uart_tx <= Data[8];
			10: uart_tx <= 1;
			11: begin uart_tx <= 1; tx_done <= 1'b1; end
			default: uart_tx <= 1;
		endcase
	end

endmoudle

testbench.v

`timescale 1ns/1ns

module uart_byte_tx_tb():
	
	reg clk;
	reg reset_n;
	reg [7:0]data;
	reg send_en;
	reg [2:0]baud_set;
	reg uart_tx;
	reg tx_done;


	uart_byte_tx uart_byte_tx(
	.clk(clk),
	.resnet(resnet),
	.data(data),
	.send_en(send_en),
	.baud_set(baud_set),
	.uart_tx(uart_tx),
	.tx_done(tx_done)
	)

	initial clk = 1;
	always #10 clk = ~clk;

	initial begin
		reset = 0;
		data = 0;
		send_en = 0;
		baud_set = 4;
		#201;   // 为什么是201 不是200
		#100;
		data = 8'h57;
		send_en = 1;
		#20;
		@(posedge tx_done); // 阻塞语句 一直等待直到完成
		
		#20000;
		data = 8'h57;
		send_en = 1;
		#20;
		@(posedge tx_done); // 阻塞语句 一直等待直到完成
		#20000;
		$stop;
endmodule

出了bug,开始调试
问题1:reset = 0
在这里插入图片描述
reset一直没有拉高,修改代码

		reset = 0;
		data = 0;
		send_en = 0;
		baud_set = 4;
		#201;   // 为什么是201 不是200
		#100;

改为:
		reset = 0;
		data = 0;
		send_en = 0;
		baud_set = 4;
		#201;   // 为什么是201 不是200
		reset = 1;   // 添加
		#100;

问题2:uart_tx = 0
在这里插入图片描述
当我们发送send en之后等了一位才发送数据。应该是bps cnt等于0,就发送起始位。。发现的bug,就是bps cnt 没有即使清零,修改为:
在这里插入图片描述

在修改send en 为1 的时候 bps cnt为0,要加begin end,有多行的if else,记住

在这里插入图片描述

在这里插入图片描述

还有个问题 uart tx信号,空闲的时候为高电平
在这里插入图片描述
当 bps cnt为0 的时候才为0,bsp cnt空闲的时候为0的状态,又命中了case里面的0.
修改为:
在这里插入图片描述
在这里插入图片描述
需要避开这个0,用1 起始就可以。
还有个问题没解决,当开始send en为高电平的时候,延迟了一个位
在这里插入图片描述
为什么这么久才发送一个起始位。
由于send en开始之后,bps cnt需要从0 才开始是计数。修改为
在这里插入图片描述
计满才加1,我现在改成1,就立马发送起始位了

现在还有很多细节没有验证,大致的宏观波形没有问题。
在这里插入图片描述

一位的时间是 8.68us,所以波特率一位的时间,没有问题。

体现bps clk
在这里插入图片描述
用的地方:
在这里插入图片描述
在这里插入图片描述
12个分成了11段!多出来了一段。数据位多了一位。

在这里插入图片描述
在这里插入图片描述

很多人认为写到这个地方就结束了。大多数也是结束了