目录
目录
发送串口(tx发送串口 因为使用了状态机 所以计数器和控制集成到1个always块)
3.接收标志(在接收使能高且捕捉到上升沿rx_negedge时拉高)
输入输出
input [31:0] addr_i, //地址(用于输入输出类型的判断)
input clk, //时钟
input rst, //复位
input rx_pin, //接收串口
input we_i, //写使能,再addr_i对应的寄存器中写入data_i
input [31:0] data_i, //数据输入(控制信息)
output reg [31:0] data_o, //数据输出(接收串口的数据再在接受结束over拉高后写入uart_rx寄存等待addr_i是读取数据是输出)
output wire tx_pin //发送串口
内部的信号
localparam baud = 32'h1B2;//波特率115200 对应分屏系数434
//串口发送状态机的四个状态
localparam S_IDLE = 4'b0001;
localparam S_START = 4'b0010;
localparam S_SEND_BYTE = 4'b0100;
localparam S_STOP = 4'b1000;
assign tx_pin = tx_reg;
//addr_i[7:0] 对应的控制类型选择
localparam UART_CTRL = 8'h0;
localparam UART_STATUS = 8'h4;
localparam UART_BAUD = 8'h8;
localparam UART_TXDATA = 8'hc;
localparam UART_RXDATA = 8'h10;
// addr: 0x00
// rw. bit[0]: tx enable, 1 = enable, 0 = disable
// rw. bit[1]: rx enable, 1 = enable, 0 = disable
reg[31:0] uart_ctrl;//控制寄存器
// addr: 0x04
// ro. bit[0]: tx busy, 1 = busy, 0 = idle
// rw. bit[1]: rx over, 1 = over, 0 = receiving
// must check this bit before tx data
reg[31:0] uart_status;
// addr: 0x08
// rw. clk div
reg[31:0] uart_baud;//波特率配置寄存器
// addr: 0x10
// ro. rx data
reg[31:0] uart_rx;
reg tx_data_valid;//发送许可由addr_i[7:0]识别为UART_TXDATA后得到
reg tx_data_ready;//发送完成标志 发送结束后在s_stop时拉高 用于拉低uart_status[0]
reg [3:0] state; //发送状态的的状态
reg [15:0] cycle_cnt; //发送计数器在uart_baud时清零
reg [3:0] bit_cnt; //bit计数器 计数发送的第几个bit 在计数器计数到uart_baud时加一
reg [7:0] tx_data; //发送输出
reg tx_reg; //发送寄存器
reg rx_q0; //接收串口打一拍 同步到本时钟域下
reg rx_q1; //接收串口打两拍 提取下降沿
wire rx_negedge;
reg rx_start; // RX使能
reg [3:0] rx_clk_edge_cnt; // clk时钟沿的个数
reg rx_clk_edge_level; // clk沿电标志位
reg rx_done; //无用
reg [15:0] rx_clk_cnt; //接收计数器 在等于rx_div_cnt清零
reg [15:0] rx_div_cnt; //第一次等于uart_baud一半其余uart_baud
reg [7:0] rx_data; //接收数据移位存储在rx_over时赋值给uart_rx
reg rx_over; //接收结束标志位
写寄存器
//写寄存器
always @(posedge clk ) begin
if (rst==1'b0) begin
uart_rx<=32'b0; //接收数据
uart_ctrl<=32'b0; //控制寄存器
uart_baud<=baud; //分频系数
tx_data_valid<=1'b0; //发送指令许可寄存器(在接收到指令是发送,且发送使能1,发送状态0,时置1)
uart_status<=32'b0; //发送状态寄存器
end else begin
if(we_i==1'b1) begin //在写使能拉高时根据控制字段写入对应的寄存器
case (addr_i[7:0])
UART_CTRL:begin
uart_ctrl<=data_i;
end
UART_STATUS:begin
uart_status[1]<=data_i[1];
end
UART_BAUD:begin
uart_baud<=data_i;
end
UART_TXDATA:begin
if(uart_ctrl[0]==1'b1&&uart_status[0] == 1'b0)begin //发送使能打开且发送状态空
tx_data<=data_i[7:0]; //装填输入
uart_status[0] <= 1'b1; //发送状态标志位置1(忙)
tx_data_valid <= 1'b1; //允许发送
end
end
endcase
end else begin
tx_data_valid<=1'b0;
if(tx_data_ready==1'b1)begin //如果发送完毕
uart_status[0] <= 1'b1; //发送状态标志位置0(空闲)
end
if(uart_ctrl[1]==1'b1)begin //如果接受使能
if (rx_over == 1'b1) begin //接受结束
uart_status[1] <= 1'b1; //接收状态标志位置0(空闲)
uart_rx <= {24'h0, rx_data}; //接收数据存入寄存器以备读取
end
end
end
end
end
读寄存器(组合逻辑)
//读寄存器
always @(*) begin
if(rst==1'b0)begin
data_o<=32'b0;
end else begin
case (addr_i[7:0])
UART_CTRL:begin
data_o = uart_ctrl;
end
UART_STATUS:begin
data_o = uart_status;
end
UART_BAUD:begin
data_o = uart_baud;
end
UART_RXDATA:begin
data_o =uart_rx;
end
default:begin
data_o =32'b0;
end
endcase
end
end
发送串口(tx发送串口 因为使用了状态机 所以计数器和控制集成到1个always块)
always @(posedge clk ) begin
if(rst==1'b0)begin
state<=S_IDLE; //发送状态
cycle_cnt<=16'b0; //循环计数到波特率分频
tx_reg<=1'b0; //发寄存器
bit_cnt<=4'b0; //bit计数
tx_data_ready<=1'b0; //发送完成标志 发送结束后在s_stop时拉高 用于拉低uart_status[0]
end else begin
if (state==S_IDLE) begin
tx_reg<=1'b1; //空闲状态
tx_data_ready<=1'b0;
if(tx_data_valid==1'b1)begin //收到发送指令并可以发送时(在他拉高时uart_status[0]拉高置忙)
state<=S_START; //状态转移为发送
tx_reg<=1'b0; //串口下拉(串口起始位)
bit_cnt<=4'b0;
cycle_cnt<=16'b0;
end
end else begin //在不是IDLE状态时
cycle_cnt<=cycle_cnt+1'b1;
if(cycle_cnt==uart_baud)begin
cycle_cnt<=1'b0;
case (state) //开始状态(发送完起始位)
S_START:begin
tx_reg<=tx_data[bit_cnt]; //发送数据
bit_cnt<=bit_cnt+1'b1;
state<=S_SEND_BYTE; //状态转移
end
S_SEND_BYTE: begin
bit_cnt<=bit_cnt+1'b1;
if(bit_cnt==4'd8)begin //发送结束bit=8
state<=S_STOP; //状态转移到停止
tx_reg<=1'b1; //串口拉高(空闲)
end else begin
tx_reg<=tx_data[bit_cnt];
end
end
S_STOP:begin
state<=S_IDLE;
tx_data_ready<=1'b1; //发送完成(用于uart_status[0]拉低置闲)
tx_reg<=1'b1; //串口拉高(空闲)
end
default: begin
end
endcase
end
end
end
end
接收串口(没有状态机计数器和控制分开)
1.接收串口打两拍
always @(posedge clk ) begin
if(rst==1'b0)begin
rx_q0<=1'b0;
rx_q1<=1'b0;
end else begin
rx_q0<=rx_pin;//第一次打拍为了同步到本时钟域
rx_q1<=rx_q0;//第二次为了提取下降沿
end
end
2.提取下降沿
assign rx_negedge=rx_q1&&~rx_q0;//提取下降沿
3.接收标志(在接收使能高且捕捉到上升沿rx_negedge时拉高)
always @(posedge clk ) begin
if(rst==1'b0)begin
end else begin
if(uart_ctrl[1]==1'b1)begin
if(rx_negedge==1'b1)begin
rx_start<=1'b1;
end else if (rx_clk_edge_cnt==4'd9)begin
rx_start<=1'b0;
end
end else begin
rx_start<=1'b0;
end
end
end
4.分频系数
//rx_div_cnt接受分频系数
always @(posedge clk ) begin
if(rst==1'b0)begin
rx_div_cnt<=16'b0;
end else begin
if(rx_start==1'b1&&rx_clk_edge_cnt==4'b0)begin
rx_div_cnt<={1'b0,uart_baud[15:1]}; //第一次分频系数只有波特率分频系数的一半(将采样点放在中间�?
end else begin
rx_div_cnt<=uart_baud;
end
end
end
5.接收计数器
always @(posedge clk ) begin
if (rst==1'b0) begin
rx_clk_cnt<=16'b0;
end else begin
if(rx_start==1'b1)begin
if(rx_clk_cnt==rx_div_cnt)begin
rx_clk_cnt<=16'b0;
end else begin
rx_clk_cnt<=rx_clk_cnt+16'b1;
end
end else begin
rx_clk_cnt<=16'b0;
end
end
end
6.接收bit计数,接收bit标志
//rx_clk_edge_cnt 接受bit计数
//rx_clk_edge_level 接受1-8的rx_div_cnt 标志
always @(posedge clk ) begin
if(rst==1'b0)begin
rx_clk_edge_cnt<=4'b0;
rx_clk_edge_level<=1'b0;
end else if(rx_start==1'b1)begin
if(rx_clk_cnt==rx_div_cnt)begin
if(rx_clk_edge_cnt==4'd9)begin
rx_clk_edge_cnt<=4'b0;
rx_clk_edge_level<=1'b0;
end else begin
rx_clk_edge_cnt<=rx_clk_edge_cnt+1'b1;
rx_clk_edge_level<=1'b1;
end
end else begin
rx_clk_edge_cnt<=rx_clk_edge_cnt;
rx_clk_edge_level<=1'b1;
end
end else begin
rx_clk_edge_cnt<=4'b0;
rx_clk_edge_level<=1'b0;
end
end
7.接收bit
//接受bit
always @(posedge clk ) begin
if(rst==1'b0)begin
rx_data <= 8'h0;
rx_over <= 1'b0;
end else begin
if(rx_start==1'b1)begin
if(rx_clk_edge_level==1'b1)begin
case (rx_clk_edge_cnt)
1: begin
end
2,3,4,5,6,7,8,9:begin
rx_data<=rx_data|(rx_pin<<(rx_clk_edge_cnt-2));
if(rx_clk_edge_cnt==4'd9)begin
rx_over<=1'b1; //uart_rx会在rx_over=1是更新为rx_data
end
end
endcase
end
end else begin
rx_data <= 8'h0; //不会影响uart_rx
rx_over <= 1'b0;
end
end
end
endmodule
发送仿真
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2022/07/19 14:50:13
// Design Name:
// Module Name: uart_sim
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_sim(
);
reg [31:0] addr_i; //地址(用于输入输出类型的判断)
reg clk; //时钟
reg rst; //复位
reg rx_pin; //接收串口
reg we_i; //写使能,再addr_i对应的寄存器中写入data_i
reg [31:0] data_i; //数据输入(控制信息)
wire [31:0] data_o; //数据输出(接收串口的数据再在接受结束over拉高后写入uart_rx寄存等待addr_i是读取数据是输出)
wire tx_pin;
uart my_uart(
.addr_i(addr_i), //地址(用于输入输出类型的判断)
.clk(clk), //时钟
.rst(rst), //复位
.rx_pin(rx_pin), //接收串口
.we_i(we_i), //写使能,再addr_i对应的寄存器中写入data_i
.data_i(data_i), //数据输入(控制信息)
.data_o(data_o), //数据输出(接收串口的数据再在接受结束over拉高后写入uart_rx寄存等待addr_i是读取数据是输出)
.tx_pin (tx_pin) //发送串口
);
localparam UART_CTRL = 8'h0;
localparam UART_STATUS = 8'h4;
localparam UART_BAUD = 8'h8;
localparam UART_TXDATA = 8'hc;
localparam UART_RXDATA = 8'h10;
initial begin
clk=1;
rst=0;
we_i=0;
addr_i=0;
rx_pin=1;
data_i=0;
#20
rst=1;
#20
we_i=1;
addr_i={24'd0,UART_CTRL};
data_i={24'd0,8'b00000011};//收发使能打开
#20
addr_i={24'd0,UART_STATUS};
data_i={24'd0,8'b00000010};//接收完成
#20//发送数据
addr_i={24'd0,UART_TXDATA};
data_i={24'd0,8'b1001_0010};//数据
#20
we_i=0;
end
always begin
#10
clk=~clk;
end
endmodule
发送仿真波形
接收仿真
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2022/07/19 14:50:13
// Design Name:
// Module Name: uart_sim
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_sim_r(
);
reg [31:0] addr_i; //地址(用于输入输出类型的判断)
reg clk; //时钟
reg rst; //复位
reg rx_pin; //接收串口
reg we_i; //写使能,再addr_i对应的寄存器中写入data_i
reg [31:0] data_i; //数据输入(控制信息)
wire [31:0] data_o; //数据输出(接收串口的数据再在接受结束over拉高后写入uart_rx寄存等待addr_i是读取数据是输出)
wire tx_pin;
uart my_uart(
.addr_i(addr_i), //地址(用于输入输出类型的判断)
.clk(clk), //时钟
.rst(rst), //复位
.rx_pin(rx_pin), //接收串口
.we_i(we_i), //写使能,再addr_i对应的寄存器中写入data_i
.data_i(data_i), //数据输入(控制信息)
.data_o(data_o), //数据输出(接收串口的数据再在接受结束over拉高后写入uart_rx寄存等待addr_i是读取数据是输出)
.tx_pin (tx_pin) //发送串口
);
localparam UART_CTRL = 8'h0;
localparam UART_STATUS = 8'h4;
localparam UART_BAUD = 8'h8;
localparam UART_TXDATA = 8'hc;
localparam UART_RXDATA = 8'h10;
initial begin
clk=1;
rst=0;
we_i=0;
rx_pin=1;
addr_i=0;
rx_pin=1;
data_i=0;
#20
rst=1;
#20
we_i=1;
addr_i={24'd0,UART_CTRL};
data_i={24'd0,8'b00000011};//收发使能打开
#20
addr_i={24'd0,UART_STATUS};
data_i={24'd0,8'b00000010};//接收完成
#20
we_i=0;
#20
rx_pin=0;
#8680//434*20
rx_pin=1;
#8680
rx_pin=0;
#8680
rx_pin=0;
#8680
rx_pin=1;
#8680
rx_pin=1;
#8680
rx_pin=0;
#8680
rx_pin=1;
#8680
rx_pin=0;
#8680
rx_pin=1;//拉高空闲
#20
addr_i={24'd0,UART_RXDATA};//读数据
end
always begin
#10
clk=~clk;
end
endmodule