发布时间:2024-05-22 11:01
虚拟机:VMware -14.0.0.24051
环 境:ubuntu 18.04.1
脚 本:makefile(点击直达)
应用工具:vcs 和 verdi
UART(Universal Asynchronous Receiver/Transmitter),通用异步收发器,是一种异步全双工串行通信协议,可实现单工通信、半双工通信和全双工通信。
单工通信方式:主机只能发送数据,从机只能接收数据,数据流向始终由发送端流向接收端;
半双工通信方式:同一时刻,只能由主机向从机或从机向主机发送数据;
全双工通信方式:同一时刻,主机和从机都可以互发数据。
UART由Tx和Rx两根数据线组成,因为没有参考时钟信号,所以通信的双方必须约定串口波特率、数据位宽、奇偶校验位、停止位等配置参数,从而按照相同的速率进行通信。
UART协议相关概念:
- 波特率(baud rate)
单位bps(bit per second),每秒传输的二进制位数- 起始位(start bit)
开始发起传输的标志- 停止位(stop bit)
结束传输的标志- 校验位(check bit)
为了保证传输可靠性增加的校验信息。奇校验: 数据+校验位中总的1的个数为奇数;偶校验反之。
模块工作类型 | 信号名 | 方向 | 位宽 | 描述 |
---|---|---|---|---|
master | tx | output | 1 | 发送信号 |
master | rx | input | 1 | 接收信号 |
slave | tx | input | 1 | 接收数据 |
slave | rx | output | 1 | 发送数据 |
Signal Name | Width | Direction | Description |
---|---|---|---|
clk | 1 | input | System clk signal, 50Mhz |
rst_n | 1 | input | System reset signal,negedge |
cmd_i | 16 | input | [15]:读写指示;1:写,0:读 [14:8]:地址位 [7:0]:数据位 |
cmd_rdy | 1 | output | 握手信号ready |
cmd_vld | 1 | input | 握手信号valid |
tx | 1 | output | uart发送数据端 |
rx | 1 | input | uart接收数据端 |
read_vld | 1 | output | 读数据valid |
read_data | 8 | output | 读到的数据 |
/*-- modified by xlinxdu, 2022/04/30
-- cmd_i 16bit,rw_flag:1bit,data addr:7bit,data:8bit
-- clock freq:50MHz
-- band rate :115200bps
-- check bit :odd
-- stop bit :1bit
-- clock num of uart :freq/band_rate = 50M/115200 = 434clk
*/
module uart#(
parameter CMD_ADDR_WIDTH = 7,
parameter CMD_DATA_WIDTH = 8,
parameter RW_FLAG = 1,
parameter CMD_WIDTH = CMD_ADDR_WIDTH +CMD_DATA_WIDTH+RW_FLAG,
parameter DELAY_CLK = 1
)(
input clk_i ,
input rst_n_i ,
//-- tx
input [CMD_WIDTH-1:0] cmd_data_i ,
input cmd_vld_i ,
output reg cmd_rdy_o ,
output reg tx_o ,
//-- rx
input rx_i ,
output reg rd_vld_o ,
output reg [CMD_DATA_WIDTH-1:0] rd_data_o
);
reg uart_en ;
reg [ 8:0] tx_bit_cnt ;
reg [ 3:0] tx_bit_num ;
reg [CMD_WIDTH-1:0] cmd_data_buf ;
wire [ 7:0] cmd_data_high;
wire [ 7:0] cmd_data_low ;
wire rw_flag ;
reg wr_data_flag ;
reg tx_uart_done ;
reg dly_cnt_en ;
reg [ 6:0] dly_cnt ;
reg rd_uart_en ;
reg [ 8:0] rx_bit_cnt ;
reg [ 3:0] rx_bit_num ;
reg [CMD_DATA_WIDTH-1:0] rx_data_buf ;
reg [ 2:0] rx_dly ;
wire negedge_rx ;
wire rx_sync ;
wire rx_done ;
reg rx_uart_done ;
wire rd_vld ;//assign --wire-->reg_o
wire [CMD_DATA_WIDTH-1:0] rd_data;
//-- cmd_rdy_o
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
cmd_rdy_o <= 1'b1;
end
else if (tx_uart_done) begin
cmd_rdy_o <= 1'b1;
end
else if(cmd_vld_i) begin
cmd_rdy_o <= 'b0;
end
end
//-- uart_en
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
uart_en <= 1'b0;
end
else if(rw_flag && tx_bit_num == 4'd10 && tx_bit_cnt == 9'd433 || dly_cnt_en) begin
uart_en <= 1'b0;
end
else if(tx_uart_done) begin
uart_en <= 1'b0;
end
else if(cmd_vld_i)begin
uart_en <= 1'b1;
end
end
//-- tx_bit_cnt
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
tx_bit_cnt <= 9'd0;
end
else if(uart_en) begin
if(tx_bit_cnt == 9'd433) begin
tx_bit_cnt <= 9'd0;
end
else begin
tx_bit_cnt <= tx_bit_cnt + 1'd1;
end
end
else if(dly_cnt_en) begin
tx_bit_cnt <= 9'd0;
end
end
//-- tx_bit_num
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
tx_bit_num <= 4'd0;
end
else if(uart_en) begin
//--uart_en keep high,tx_bit_num=11~12-->delay 2bit
if(tx_bit_num == 4'd10 && tx_bit_cnt == 9'd433) begin
tx_bit_num <= 4'd0;
end
else if(tx_bit_cnt == 9'd433)begin
tx_bit_num <= tx_bit_num +1'd1;
end
end
else if(dly_cnt_en)begin
tx_bit_num <= 4'd0;
end
end
//-- cmd_data_buf
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
cmd_data_buf <= {CMD_WIDTH{1'B0}};
end
else if(cmd_vld_i && cmd_rdy_o)begin
cmd_data_buf <= cmd_data_i;
end
end
//--
assign {cmd_data_high,cmd_data_low} = cmd_data_buf;
assign rw_flag = cmd_data_buf[CMD_WIDTH-1];
//-- wr_data_flag
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
wr_data_flag <= 1'b0;
end
else if (tx_uart_done)begin
wr_data_flag <= 1'b0;
end
else if(rw_flag && tx_bit_num == 4'd10 && tx_bit_cnt == 9'd433) begin
wr_data_flag <= 1'b1;
end
end
//-- TX
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
tx_o <= 1'd1;
end
else if(uart_en) begin
//-- send start bit
if(tx_bit_num == 4'd0)begin
tx_o <= 1'b0;
end
//-- send data bit
else if(tx_bit_num >= 4'd1 && tx_bit_num <= 4'd8)begin
//--1:send cmd_data_high and cm_data_low
if(rw_flag)begin
if(wr_data_flag)begin//1:low
tx_o <= cmd_data_low[tx_bit_num-1];
end
else begin
tx_o <= cmd_data_high[tx_bit_num-1];
end
end
//--0:only send cmd_data_high
else begin
tx_o <= cmd_data_high[tx_bit_num-1];
end
end
//-- send check bit
else if(tx_bit_num == 4'd9) begin
if(rw_flag)begin//-- 1:write
tx_o <= wr_data_flag ? ~(^cmd_data_low) : ~(^cmd_data_high);
end
else begin
tx_o <= ~(^cmd_data_high);
end
end
//-- send stop bit
else if(tx_bit_num == 4'd10) begin
tx_o <= 1'b1;
end
end
else begin
if(dly_cnt_en) begin
tx_o <= 1'b1;
end
end
end
//-- tx_uart_done
assign tx_uart_done = (~rw_flag || wr_data_flag) && tx_bit_num == 4'd10 && tx_bit_cnt == 9'd433;
//assign tx_uart_done = (~rw_flag && tx_bit_num == 4'd10&& tx_bit_cnt == 9'd433)||
// ( rw_flag && tx_bit_num == 4'd10&& tx_bit_cnt == 9'd433 && wr_data_flag);
//-- dly_cnt_en
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
dly_cnt_en <= 1'b0;
end
else if(rw_flag && tx_bit_num == 4'd10 && tx_bit_cnt == 9'd433)begin
dly_cnt_en <= 1'b1;
end
else if(dly_cnt == 7'd99)begin
dly_cnt_en <= 1'b0;
end
end
//-- dly_cnt
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
dly_cnt <= 7'd0;
end
else if(dly_cnt_en)begin
dly_cnt <= dly_cnt +1'b1;
end
else begin
dly_cnt <= 7'd0;
end
end
//-- RX
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
rx_dly <= 3'b000;
end
else begin
rx_dly <= {rx_dly[1:0],rx_i};
end
end
assign negedge_rx = {rx_dly[2:1] == 2'b10};
assign rx_sync = rx_dly[2];
//-- rx_uart_done
assign rx_done = ~rw_flag && rx_bit_num == 4'd10&& rx_bit_cnt == 9'd433;
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
rx_uart_done <= 1'b0;
end
else begin
rx_uart_done <= rx_done;
end
end
//-- rd_uart_en
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
rd_uart_en <= 1'b0;
end
else if(negedge_rx)begin
rd_uart_en <= 1'b1;
end
else if(rx_done)begin
rd_uart_en <= 1'b0;
end
end
//-- rx_bit_cnt
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
rx_bit_cnt <= 9'd0;
end
else if(rd_uart_en) begin
if(rx_bit_cnt == 9'd433) begin
rx_bit_cnt <= 1'd0;
end
else begin
rx_bit_cnt <= rx_bit_cnt + 1'd1;
end
end
end
//-- rx_bit_num
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
rx_bit_num <= 4'd0;
end
else if(rd_uart_en) begin
//--rd_uart_en keep high,tx_bit_num=11~12-->delay 2bit
if(rx_bit_num == 4'd10 && rx_bit_cnt == 9'd433) begin
rx_bit_num <= 4'd0;
end
else if(rx_bit_cnt == 9'd433)begin
rx_bit_num <= rx_bit_num +1'd1;
end
end
else begin
rx_bit_num <= 4'd0;
end
end
//-- rx_data_buf
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
rx_data_buf <= 8'd0;
end
else if(rx_bit_cnt == 9'd216 && rx_bit_num >= 4'd1 && rx_bit_num <= 4'd8)begin
rx_data_buf <= {rx_sync,rx_data_buf[7:1]};
end
end
//-- check bit
assign check_flag = rx_bit_num == 4'd9 &&rx_bit_cnt ==9'd216 && rx_sync == ~^rx_data_buf;
assign rd_vld = check_flag;
assign rd_data = rd_vld?rx_data_buf:rd_data;
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
rd_vld_o <= 1'b1;
end
else if(rd_vld)begin
rd_vld_o <= 1'b1;
end
else if(negedge_rx && rx_bit_num == 4'd0)begin
rd_vld_o <= 1'b0;
end
end
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
rd_data_o <= 8'd0;
end
else if(rx_done)begin
rd_data_o <= rd_data;
end
end
endmodule
module uart_tb;
reg clk_i ;
reg rst_n_i ;
reg cmd_vld_i ;
reg [15:0] cmd_data_i ;
wire cmd_rdy_o ;
wire tx_o ;
reg rx_i ;
wire rd_vld_o ;
wire [ 7:0] rd_data_o ;
initial begin
clk_i = 1;
end
always begin
#10 clk_i = ~clk_i;
end
initial begin
rst_n_i = 1;
cmd_vld_i = 0;
cmd_data_i= 16'b1000_0010_0011_0010;//16'h8232
rx_i = 1;
#5 rst_n_i = 0;
#5 rst_n_i = 1;
#10 cmd_vld_i=1;
#180000 cmd_data_i = 16'b0110_1011_0001_1000;//16'h6b14
#90000 cmd_vld_i =0;
end
initial begin
#360020
rx_i = 0;//start bit
#8660 rx_i = 0;
#8660 rx_i = 1;
#8660 rx_i = 0;
#8660 rx_i = 0;
#8660 rx_i = 1;
#8660 rx_i = 1;
#8660 rx_i = 0;
#8660 rx_i = 0;
#8660 rx_i = 0;//check bit
#8660 rx_i = 1;//stop bit
#8660 rx_i = 1;//delay bit
#8660;
end
uart u_uart
(
.clk_i (clk_i ),
.rst_n_i (rst_n_i ),
.cmd_vld_i (cmd_vld_i ),
.cmd_data_i(cmd_data_i),
.cmd_rdy_o (cmd_rdy_o ),
.tx_o (tx_o ),
.rx_i (rx_i ),
.rd_vld_o (rd_vld_o ),
.rd_data_o (rd_data_o )
);
initial begin
#700000 $finish;
$fsdbDumpfile("uart.fsdb");
$fsdbDumpvars ;
$fsdbDumpMDA ;
end
endmodule
作者:xlinxdu
版权:本文是作者原创,版权归作者所有。
转载:未经作者允许,禁止转载,转载必须保留此段声明,必须在文章中给出原文连接。