Verilog UART(1):TX模块

Verilog UART(1):TX模块

最近重新学verilog,拿着开发板做一些小实验。先写了一个串口收发器模块。先是Tx部分。主要学习了状态机和画波形图。觉得还是要尽量先画波形图,然后再开始写代码,不然在仿真调试上要花更多时间。

UART一帧数据的格式:

  1. 空闲状态为高电平1
  2. 起始位为0
  3. 然后是8位数据,(可能有第9位奇偶校验)
  4. 结束位是1

输入:

  1. clk:时钟
  2. tx_data_valid:开始传输&输入数据有效
  3. tx_data:需要传输的1字节数据

输出:

  1. uart_tx:串口TX信号
  2. tx_idle_flag:指示传输完成

时序:

  1. 分为4个状态,IDLE-START-TX_BYTE-STOP
  2. baud_cnt是波特率的计数器,从0数到NUM,例如,
  3. bit_cnt对发送的bit0~7计数,会在一个clk周期计数到8,,但是不影响功能,所以就不管了。
  4. tx_idle_flag:因为用了时序逻辑,比state==idle延迟1拍,不知道要不要改成组合逻辑?


顺便,时序图是用Wavedrom画的,根据文本生成波形图,比较方便,比如上面这个图的代码如下:

{signal: [
  {name: 'clk', wave: 'p..|..|..|..|..|..|.'},
  {name: 'baud_cnt',wave: '22222222222222222222', 
   data:['','', '0','...','NUM', '0','...','NUM',
        '0','...','NUM', '0','...','NUM','0','...','NUM','0','...','NUM']},
  
  {name: 'tx_data_valid', wave: '010.................'},
  {name: 'tx_data', wave: '03x.................', 
   data:['din']},
  {name: 'tx_data_latch', wave: 'x.3.................', data:['din']},
  {name: 'state', wave: '2.2..2........2..2..',
  data:['idle','start', 'tx_byte', 'stop', 'idle']},
  
  {name: 'bit_cnt', wave: '0....2..2..2..30....', 
  data:['0','...', '7', '8']},
  
  {name: 'uart_tx', wave: '1.0..2..2..2..2..1..',
  data:['bit0','...', 'bit7', 'stop']},
  
  {name: 'tx_idle_flag', wave: '1..0..............1.',
  data:['bit0','...', 'bit7', 'stop']}  
]}

4状态的版本

上板验证ok。

module uart_tx
#(
	parameter BAUD_RATE = 115200,
	parameter CLOCK_FREQ = 50 //MHz
)
(
	input clk, 
	input rst_n,
	input tx_data_valid, 
	input [7:0] tx_data,
	output reg tx_pin,
	output reg tx_idle_flag
    );

parameter BAUD_CYCLE = CLOCK_FREQ * 1000000 / BAUD_RATE - 1;
reg [7:0] tx_data_latch;

//保存tx_data
always @(posedge clk) begin 
	if(!rst_n)
		tx_data_latch <= 8'd0;
	else if(tx_data_valid && state == IDLE ) 
	//只在IDLE保存发送的1字节数据,其他时候不保存
		tx_data_latch <= tx_data;
	else
		tx_data_latch <= tx_data;
end

//波特率计数器
reg [15:0] baud_cnt;
always @(posedge clk) begin 
	if(!rst_n)
		baud_cnt <= 16'd0;
	else if(baud_cnt == BAUD_CYCLE || next_state != state)
	// 在切换状态、或是达到计数值时清零
		baud_cnt <= 16'd0;
	else
		baud_cnt <= baud_cnt + 1'b1;
end

always @(posedge clk) begin 
	if(!rst_n)
		tx_idle_flag <= 1'b1;
	else if(state == IDLE) 
	//IDLE状态可以收下一位数据
		tx_idle_flag <= 1'b1;
	else
		tx_idle_flag <= 1'b0;
end

reg [3:0] bit_cnt;
localparam BIT_NUM = 7;

reg [3:0] state;
reg [3:0] next_state; 
localparam IDLE = 4'd0;
localparam START= 4'd1;
localparam TX_BYTE= 4'd2;
localparam STOP= 4'd3;

always @(posedge clk) begin 
	if(!rst_n)
		state <= IDLE;
	else
		state <= next_state;
end

always @(*) begin 
	if(!rst_n)
		next_state <= IDLE;
	else
		case(state)
			IDLE: 
				if(tx_data_valid)
					next_state <= START;
				else
					next_state <= IDLE;
			START:
				if(baud_cnt == BAUD_CYCLE)
					next_state <= TX_BYTE;
				else	
					next_state <= START;			
			TX_BYTE:
				if(baud_cnt == BAUD_CYCLE && bit_cnt == BIT_NUM)
					next_state <= STOP;
				else	
					next_state <= TX_BYTE;	
			STOP:
				if(baud_cnt == BAUD_CYCLE)
					next_state <= IDLE;
				else	
					next_state <= STOP;
			default: next_state <= IDLE;
		endcase
end

//发送的bit计数
always @(posedge clk) begin 
	if(!rst_n)
		bit_cnt <= 4'd0;
	else if(state == TX_BYTE)
		if(baud_cnt == BAUD_CYCLE)
			bit_cnt <= bit_cnt + 1'b1;
		else 
			bit_cnt <= bit_cnt;
	else 
		bit_cnt <= 4'd0;
end

//发送的数据
//这里根据状态用组合逻辑实现
always @(*) begin 
	if(!rst_n)
		tx_pin = 1'b1;
	else
		case(state)
			IDLE: tx_pin = 1'b1;
			START: tx_pin = 1'b0; //起始位
			TX_BYTE: tx_pin = tx_data_latch[bit_cnt]; //低位在前
			STOP: tx_pin = 1'b1; //结束位
			default: tx_pin = 1'b1;
		endcase
end

endmodule

10状态的版本

最先是给每bit都写了一个状态,功能一样,就是代码比较长。。
(原来verilog可以用cpp来高亮。。虽然有些关键字不行)

module uart_tx
#(
	parameter BAUD_RATE = 115200,
	parameter CLOCK_FREQ = 50 //MHz
)
(
	input clk, 
	input rst_n,
	input tx_data_valid, 
	input [7:0] tx_data,
	output reg tx_pin,
	output reg tx_idle_flag
    );

parameter BAUD_CYCLE = CLOCK_FREQ * 1000000 / BAUD_RATE - 1;
reg [7:0] tx_data_latch;

//保存tx_data
always @(posedge clk) begin 
	if(!rst_n)
		tx_data_latch <= 8'd0;
	else if(tx_data_valid && state == IDLE ) 
	//只在IDLE保存发送的1字节数据,其他时候不保存
		tx_data_latch <= tx_data;
	else
		tx_data_latch <= tx_data;
end

//波特率计数器
reg [15:0] baud_cnt;
always @(posedge clk) begin 
	if(!rst_n)
		baud_cnt <= 16'd0;
	else if(baud_cnt == BAUD_CYCLE || next_state != state)
	// 在切换状态、或是达到计数值时清零
		baud_cnt <= 16'd0;
	else
		baud_cnt <= baud_cnt + 1'b1;
end

always @(posedge clk) begin 
	if(!rst_n)
		tx_idle_flag <= 1'b1;
	else if(state == IDLE) 
	//IDLE状态可以收下一位数据
		tx_idle_flag <= 1'b1;
	else
		tx_idle_flag <= 1'b0;
end


reg [3:0] state;
reg [3:0] next_state; 
localparam IDLE = 4'd9;
localparam START= 4'd10;
localparam BIT0= 4'd0;
localparam BIT1= 4'd1;
localparam BIT2= 4'd2;
localparam BIT3= 4'd3;
localparam BIT4= 4'd4;
localparam BIT5= 4'd5;
localparam BIT6= 4'd6;
localparam BIT7= 4'd7;
localparam STOP= 4'd8;

always @(posedge clk) begin 
	if(!rst_n)
		state <= IDLE;
	else
		state <= next_state;
end

always @(*) begin 
	if(!rst_n)
		next_state <= IDLE;
	else
		case(state)
			IDLE: 
				if(tx_data_valid)
					next_state <= START;
				else
					next_state <= IDLE;
			START:
				if(baud_cnt == BAUD_CYCLE)
					next_state <= BIT0;
				else	
					next_state <= START;			
			BIT0:
				if(baud_cnt == BAUD_CYCLE)
					next_state <= BIT1;
				else	
					next_state <= BIT0;	
			BIT1:
				if(baud_cnt == BAUD_CYCLE)
					next_state <= BIT2;
				else	
					next_state <= BIT1;	
			BIT2:
				if(baud_cnt == BAUD_CYCLE)
					next_state <= BIT3;
				else	
					next_state <= BIT2;	
			BIT3:
				if(baud_cnt == BAUD_CYCLE)
					next_state <= BIT4;
				else	
					next_state <= BIT3;	
			BIT4:
				if(baud_cnt == BAUD_CYCLE)
					next_state <= BIT5;
				else	
					next_state <= BIT4;	
			BIT5:
				if(baud_cnt == BAUD_CYCLE)
					next_state <= BIT6;
				else	
					next_state <= BIT5;	
			BIT6:
				if(baud_cnt == BAUD_CYCLE)
					next_state <= BIT7;
				else	
					next_state <= BIT6;	
			BIT7:
				if(baud_cnt == BAUD_CYCLE)
					next_state <= STOP;
				else	
					next_state <= BIT7;
			STOP:
				if(baud_cnt == BAUD_CYCLE)
					next_state <= IDLE;
				else	
					next_state <= STOP;
			default: next_state <= IDLE;
		endcase
end


//发送的数据
//这里根据状态用组合逻辑实现
always @(*) begin 
	if(!rst_n)
		tx_pin = 1'b1;
	else
		case(state)
			IDLE: tx_pin = 1'b1;
			START: tx_pin = 1'b0; //起始位
			BIT0: tx_pin = tx_data_latch[0]; //低位在前
			BIT1: tx_pin = tx_data_latch[1];
			BIT2: tx_pin = tx_data_latch[2];
			BIT3: tx_pin = tx_data_latch[3];
			BIT4: tx_pin = tx_data_latch[4];
			BIT5: tx_pin = tx_data_latch[5];
			BIT6: tx_pin = tx_data_latch[6];
			BIT7: tx_pin = tx_data_latch[7];
			STOP: tx_pin = 1'b1; //结束位
			default: tx_pin = 1'b1;
		endcase
end

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值