IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> USART - 逻辑设计部分 -> 正文阅读

[嵌入式]USART - 逻辑设计部分


逻辑设计部分可以参照uart进行设计,但是还是那句话——设计好再写代码!设计好再写代码!着急写代码是吧,着急写就白想活着!

1. baud_clk_gen

这个就单纯的根据波特率分频出一个sck时钟,不解释!

1.1. 代码

module baud_clk_gen#(
	parameter	BAUD_RATE	=	12500000,
	parameter	PCLK_FREQ	=	50000000
	)(
		input	prstn_sck,
		input	pclk,
		output	tx_sck
	);

localparam	BAUD_CNT_END = PCLK_FREQ / BAUD_RATE;
localparam	BAUD_CNT_WIDTH = $clog2(BAUD_CNT_END);

reg		[BAUD_CNT_WIDTH-1:0]	baud_cnt;
reg								tx_sck_r;

always@(posedge pclk) begin
	if(!prstn_sck)
		baud_cnt <= 'b0;
	else if(baud_cnt < BAUD_CNT_END-1)
		baud_cnt <= baud_cnt + 'b1;
	else
		baud_cnt <= 'b0;
end

always@(posedge pclk) begin
	if(!prstn_sck)
		tx_sck_r<= 'b0;
	else
		tx_sck_r <= !tx_sck_r;
end

assign tx_sck = tx_sck_r;

endmodule

2. usart_tx

这部分内容与uart_tx基本相同,只需把波特率计数器baud_cnt去掉就ok了

仍然是基于状态机进行设计,状态转换图如下

在这里插入图片描述

2.1. 代码

module usart_tx#(				
	parameter PDATA_WIDTH 		= 16,
	parameter ASYNC_FIFO_DEPTH 	= 4096
	)(
		input							prstn,
		input							pclk,			
		input 							tx_sck,
		input	[PDATA_WIDTH-1:0]		fifo_wdata,
		input							fifo_wdata_val,
		output							fifo_full,
		output							txd
	);


localparam IDLE 	= 1'b0;
localparam TRANS 	= 1'b1;
localparam FIFO_ADDR_WIDTH = $clog2(ASYNC_FIFO_DEPTH/PDATA_WIDTH + 1);
localparam FRAME_WIDTH = PDATA_WIDTH + 3;
localparam BIT_CNT_WIDTH = $clog2(FRAME_WIDTH+1);

reg 							cur_state;
reg 							nxt_state;
reg 	[FRAME_WIDTH-1:0]		data_shift;
reg 	[BIT_CNT_WIDTH-1:0]		bit_cnt;
wire							parity;
reg								fifo_rd_en;
wire 	[PDATA_WIDTH-1:0]		fifo_rdata;
wire							fifo_rdata_val;
wire							fifo_empty;


always@(posedge tx_sck) begin
	if(!prstn)
		cur_state <= IDLE;
	else 
		cur_state <= nxt_state;
end

always@(*) begin
	case(cur_state)
		IDLE:
			if(fifo_rdata_val)
				nxt_state = TRANS;
			else
				nxt_state = IDLE;
		TRANS:
			if(bit_cnt == FRAME_WIDTH)
				nxt_state = IDLE;
			else
				nxt_state = TRANS;
		default:	nxt_state = IDLE;
	endcase
end

always@(posedge tx_sck) begin
	if(!prstn)
		fifo_rd_en <= 1'b1;
	else if(cur_state == IDLE) begin
			if(fifo_rd_en && !fifo_empty)
				fifo_rd_en <= 1'b0;
		end
	else if(cur_state == TRANS) begin
			if(bit_cnt == FRAME_WIDTH)
				fifo_rd_en <= 1'b1;
		end
end


always@(posedge tx_sck) begin
	if(!prstn)
		data_shift <= {FRAME_WIDTH{1'b1}};
	else if(cur_state == IDLE) begin
			if(fifo_rdata_val)
				data_shift <= {1'b0,fifo_rdata,parity,1'b1};
		end
	else if(cur_state == TRANS)
			data_shift <= {data_shift[FRAME_WIDTH-2:0],1'b1};
end

assign parity = (fifo_rdata_val)?(^fifo_rdata+1'b1):1'b0;

assign txd = data_shift[FRAME_WIDTH-1];


always@(posedge tx_sck) begin
	if(!prstn)
		bit_cnt <= 'd0;
	else if(cur_state == IDLE) begin
			if(fifo_rdata_val)
				bit_cnt <= 'd1;
		end
	else if(cur_state == TRANS) 
			bit_cnt <= bit_cnt + 'd1;
end


async_fifo#(
	.ASYNC_FIFO_DEPTH 		(ASYNC_FIFO_DEPTH	),
	.WDATA_WIDTH 			(PDATA_WIDTH      	),
	.RDATA_WIDTH 			(PDATA_WIDTH       	),
	.PROG_DEPTH 			(ASYNC_FIFO_DEPTH   ),
	.ADDR_WIDTH				(FIFO_ADDR_WIDTH    )
	)u_async_fifo(
		.rstn		(prstn					),
		.wclk		(pclk 					),
		.wdata		( fifo_wdata   			),
		.wr_en		( fifo_wdata_val   		),
		.full		( fifo_full 	   		),
		.rclk		( tx_sck    			),
		.rd_en		( 	fifo_rd_en   		),
		.rdata		( 	fifo_rdata   		),
		.valid		( 	fifo_rdata_val  	),
		.empty		( 	fifo_empty   		),
		.pfull		(						)
	);


endmodule

3. usart_rx

整个状态机的流程也与uart_rx类似,不过要注意由于是同步串口,usart_tx对txd是上升沿驱动,所以在对rxd采样时要使用下降沿,但将并数据写入FIFO则还是基于上升沿,所以这里有一个转化的过程。

在这里插入图片描述

3.1. IDLE

IDLE时等待rxd由高变低,表示开始传输。

状态机是rx_sck上升沿驱动,rxd也是rx_sck上升沿驱动,那么必然导致在rx_sck上升沿处对rxd采样以判断低电平到来,违背了在rx_sck下降沿采样rxd的初衷。

注意,由于usart_rx和usart_tx是同步的,所以rxd不同打拍

那怎么办?我们可以在rx_sck下降沿将rxd采样至某个信号,然后在rx_sck上升沿处对该信号采样以判断是否发生状态转换,而这个信号就可以是bit_cnt,如下图

在这里插入图片描述

3.3. REC

在此阶段不断在rx_sck下降沿将rxd串转并,由于最终要写入FIFO,故直接将数据存入fifo_wdata即可,存完fifo_wdata还需要存储校验位

在这里插入图片描述

如上图所示,由于是rx_sck下降沿采样,所以bit_cnt计数、fifo_wdata串转并、校验位parity都一定是在rx_sck下降沿驱动。

3.4. FIFO_WRITE

rx_sck下降沿采样rxd对上升沿采样fifo_wdata如FIFO没有影响。

在这里插入图片描述

3.5. 代码

module usart_rx#(
	parameter PDATA_WIDTH 		= 16,
	parameter ASYNC_FIFO_DEPTH 	= 4096
	)(
		input							prstn,
		input							pclk,			
		input 							rx_sck,
		input							rxd,
		input							fifo_rd_en,
		output	[PDATA_WIDTH-1:0]		fifo_rdata,
		output							fifo_rdata_val,
		output							fifo_empty
	);


localparam IDLE 		= 2'b00;
localparam REC 			= 2'b01;
localparam FIFO_WRITE 	= 2'b11;

localparam FIFO_ADDR_WIDTH = $clog2(ASYNC_FIFO_DEPTH/PDATA_WIDTH + 1);
localparam FRAME_WIDTH = PDATA_WIDTH + 3;
localparam BIT_CNT_WIDTH = $clog2(FRAME_WIDTH+1);
	
reg 	[1:0]					cur_state;
reg 	[1:0]					nxt_state;
reg								parity;
reg 	[PDATA_WIDTH-1:0]		fifo_wdata;
reg								fifo_wr_en;
reg 	[BIT_CNT_WIDTH-1:0]		bit_cnt;
wire							fifo_full;

	

always@(posedge rx_sck) begin
	if(!prstn)
		cur_state <= IDLE;
	else 
		cur_state <= nxt_state;
end

always@(*) begin
	case(cur_state)
		IDLE:
			if(bit_cnt == 'd1)
				nxt_state = REC;
			else
				nxt_state = IDLE;
		REC:
			if(bit_cnt == FRAME_WIDTH) begin
				if(parity == ^fifo_wdata + 1'b1)							//parity check
					nxt_state = FIFO_WRITE;
				else
					nxt_state = IDLE;
			end
			else
				nxt_state = REC;
		FIFO_WRITE:
			if(fifo_wr_en && !fifo_full)
				nxt_state = IDLE;
			else
				nxt_state = FIFO_WRITE;
		default:	nxt_state = IDLE;
	endcase
end

always@(negedge rx_sck) begin
	if(!prstn)
		bit_cnt <= 'd0;
	else if(cur_state == IDLE) begin
			if(!rxd)
				bit_cnt <= 'd1;
		end
	else if(cur_state == REC)
		bit_cnt <= bit_cnt + 'd1;
end

always@(negedge rx_sck) begin
	if(!prstn)
		fifo_wdata <= 'd0;
	else if(cur_state == REC) begin
			if(bit_cnt >= 'd1 && bit_cnt <= PDATA_WIDTH)
				fifo_wdata <= {fifo_wdata[PDATA_WIDTH-2:0],rxd};
		end
end


always@(negedge rx_sck) begin
	if(!prstn)
		parity <= 1'b0;
	else if(cur_state == REC)	begin
			if(bit_cnt == FRAME_WIDTH-2)
				parity <= rxd;
		end
end


always@(posedge rx_sck) begin
	if(!prstn)
		fifo_wr_en <= 1'b0;
	else if(cur_state == REC) begin
			if(bit_cnt == FRAME_WIDTH && (parity == ^fifo_wdata + 1'b1))
				fifo_wr_en <= 1'b1;
		end
	else if(cur_state == FIFO_WRITE) begin
			if(fifo_wr_en && !fifo_full)
				fifo_wr_en <= 1'b0;
		end
end




async_fifo#(
	.ASYNC_FIFO_DEPTH 		(ASYNC_FIFO_DEPTH	),
	.WDATA_WIDTH 			(PDATA_WIDTH      	),
	.RDATA_WIDTH 			(PDATA_WIDTH       	),
	.PROG_DEPTH 			(ASYNC_FIFO_DEPTH   ),
	.ADDR_WIDTH				(FIFO_ADDR_WIDTH    )
	)u_async_fifo(
		.rstn		(prstn					),
		.wclk		(rx_sck 				),
		.wdata		( fifo_wdata   			),
		.wr_en		( fifo_wr_en   			),
		.full		( fifo_full 	   		),
		.rclk		(  pclk   				),
		.rd_en		( 	fifo_rd_en   		),
		.rdata		( 	fifo_rdata   		),
		.valid		( 	fifo_rdata_val  	),
		.empty		( 	fifo_empty   		),
		.pfull		(						)
	);


endmodule

4. usart

顶层模块除了连连看,还要明确usart_tx和usart_rx各信号与usart的APB接口是什么关系,注意pready。

4.1. 代码

module usart#(
	parameter BAUD_RATE 		= 10000000,					
	parameter PCLK_FREQ 		= 50000000,					
	parameter PADDR_WIDTH 		= 1,
	parameter PDATA_WIDTH 		= 16,						
	parameter ASYNC_FIFO_DEPTH 	= 4096
	)(
		input							prstn_sck,
		input 							pclk,
		input							prstn,
		input	[PADDR_WIDTH-1:0]		paddr,					//1'b1 visits usart_tx.fifo and 1'b0 visits usart_rx.fifo
		input							pwrite,
		input							psel,
		input							penable,
		input	[PDATA_WIDTH-1:0]		pwdata,
		output	[PDATA_WIDTH-1:0]		prdata,
		output							pready,
		
		input 							rx_sck,
		input							rxd,
		output							tx_sck,
		output							txd
	);



reg		[PDATA_WIDTH-1:0]		tx_fifo_wdata;	
reg 							tx_fifo_wdata_val; 
wire 							tx_fifo_full;	
reg 							rx_fifo_rd_en;		
wire		[PDATA_WIDTH-1:0]	rx_fifo_rdata;		
wire 							rx_fifo_rdata_val;	
wire 							rx_fifo_empty;
reg		[PDATA_WIDTH-1:0]		prdata_r;	
reg 							pready_r;	



baud_clk_gen#(
	.BAUD_RATE					(BAUD_RATE		),
	.PCLK_FREQ					(PCLK_FREQ		)
	)u_baud_clk_gen(
		.prstn_sck				(prstn_sck		),
		.pclk					(pclk			),
		.tx_sck					(tx_sck			)
	);



usart_tx#(				
	.PDATA_WIDTH 				(PDATA_WIDTH		),
	.ASYNC_FIFO_DEPTH 			(ASYNC_FIFO_DEPTH	)
	)u_usart_tx(
		.prstn 					(prstn				),
		.pclk 					(pclk				),			
		.tx_sck 				(tx_sck				),
		.fifo_wdata 			(tx_fifo_wdata		),
		.fifo_wdata_val 		(tx_fifo_wdata_val	),
		.fifo_full 				(tx_fifo_full		),
		.txd 					(txd				)
	);
	
	
	
usart_rx#(
	.PDATA_WIDTH 				(PDATA_WIDTH		),
	.ASYNC_FIFO_DEPTH 			(ASYNC_FIFO_DEPTH	)
	)u_usart_rx(
		.prstn 					(prstn				),
		.pclk 					(pclk				),			
		.rx_sck 				(rx_sck				),
		.rxd 					(rxd				),
		.fifo_rd_en 			(rx_fifo_rd_en		),
		.fifo_rdata 			(rx_fifo_rdata		),
		.fifo_rdata_val 		(rx_fifo_rdata_val	),
		.fifo_empty 			(rx_fifo_empty		)
	);
	
always@(*) begin
	if(psel && penable && pwrite && paddr == 'b1) begin
		tx_fifo_wdata = pwdata;
		tx_fifo_wdata_val = 1'b1;
	end
	else begin
		tx_fifo_wdata = 'd0;
		tx_fifo_wdata_val = 1'b0;
	end
end

always@(*) begin
	if(psel && penable && !pwrite && paddr == 'b0) 
		prdata_r = rx_fifo_rdata;
	else
		prdata_r = 'd0;
end

assign prdata = prdata_r;

always@(posedge pclk) begin
	if(!prstn)
		rx_fifo_rd_en <= 1'b0;
	else if(psel && !pwrite && paddr == 'b0) begin
		if(!penable)
			rx_fifo_rd_en <= 1'b1;
		else if(rx_fifo_empty)
			rx_fifo_rd_en <= 1'b1;
		else
			rx_fifo_rd_en <= 1'b0;
	end
	else
		rx_fifo_rd_en <= 1'b0;
end

always@(*) begin
	if(psel && penable && pwrite && paddr == 'b1) 
		pready_r = !tx_fifo_full;
	else if(psel && penable && !pwrite && paddr == 'b0) 
		pready_r = rx_fifo_rdata_val;
	else
		pready_r = 1'b0;
end

assign pready = pready_r;
	
endmodule

5. usart_tb

此处依然使用回环测试,将usart_tx的txd和tx_sck与usart_rx的rxd和rx_sck直连,检测发送的数据和读出的数据是否一致。

`timescale 1ns/1ps

module usart_tb();

parameter BAUD_RATE 		= 10000000;					
parameter PCLK_FREQ 		= 50000000;					
parameter PADDR_WIDTH 		= 1;
parameter PDATA_WIDTH 		= 16;					
parameter ASYNC_FIFO_DEPTH 	= 4096;


logic							prstn_sck;
logic							pclk;
logic							prstn;
logic	[PADDR_WIDTH-1:0]		paddr;				
logic							pwrite;
logic							psel;
logic							penable;
logic	[PDATA_WIDTH-1:0]		pwdata;
logic	[PDATA_WIDTH-1:0]		prdata;
logic							pready;
logic							txd2rxd;
logic							sck;


initial begin
	pclk = 0;
	#10;											//rx_clk and tx_clk are async
	forever #10 pclk = !pclk;					//50MHZ
end


initial begin
	prstn_sck = 1;
	prstn = 1;
	#50 prstn_sck = 0;
	#50 prstn_sck = 1;
	#50 prstn 	= 0;
	#50 prstn 	= 1;
end

initial begin
	paddr = 'd0;	
	pwrite = 1'b0;
	psel = 1'b0;
	penable = 1'b0;
	pwdata = 'd0;
	#200;
	usart_loop_test();
end


task usart_loop_test();
	usart_tx_sequence(13);
	#50;
	usart_tx_sequence(45);
	#50;
	usart_tx_sequence(23);
	#50;
	usart_tx_sequence(18);
	#50;
	usart_rx_sequence();
	#50;
	usart_rx_sequence();
	#50;
	usart_rx_sequence();
	#50;
	usart_rx_sequence();
endtask

task usart_tx_sequence(input int a);
	@(posedge pclk);
	#1;
	paddr = 1'b1;
	pwrite = 1'b1;
	psel = 1'b1;
	penable = 1'b0;
	pwdata = a;
	@(posedge pclk);
	#1;
	penable = 1'b1;
	forever begin
		@(posedge pclk);
		if(psel && penable && pready && paddr == 1'b1) begin
			$display("Writing data %h at usart_tx's FIFO success",a);
			#1;
			psel = 1'b0;
			break;
		end
	end
endtask

task usart_rx_sequence();
	@(posedge pclk);
	#1;
	paddr = 'b0;
	pwrite = 1'b0;
	psel = 1'b1;
	penable = 1'b0;
	@(posedge pclk);
	#1;
	penable = 1'b1;
	forever begin
		@(posedge pclk);
		if(psel && penable && pready && paddr == 1'b0) begin
			$display("usart_rx reads data %h from FIFO", prdata);
			#1;
			psel = 1'b0;
			break;
		end
	end
endtask


usart#(
	.BAUD_RATE				(BAUD_RATE			), 						
	.PCLK_FREQ 				(PCLK_FREQ			),						
	.PADDR_WIDTH 			(PADDR_WIDTH		),		
	.PDATA_WIDTH 			(PDATA_WIDTH		),							
	.ASYNC_FIFO_DEPTH 		(ASYNC_FIFO_DEPTH	)	
	)u_usart(
		.prstn_sck			(prstn_sck		),
		.pclk				(pclk			),
		.prstn				(prstn			),
		.paddr				(paddr			),					
		.pwrite				(pwrite			),
		.psel				(psel			),
		.penable			(penable		),
		.pwdata				(pwdata			),
		.prdata				(prdata			),
		.pready				(pready			),
	
		.rx_sck				(sck			),
		.rxd				(txd2rxd		),
		.tx_sck				(sck			),
		.txd				(txd2rxd		)
	);

endmodule
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-06-01 15:23:19  更:2022-06-01 15:24:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 1:59:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码