- 这个代码是整个库文件包工程的一部分,希望能在两年的时间写出完整的包,但是时间也不多,只能晚上空闲时断断续续写的,毕竟不能耽误打游戏不是
- 该工程的地址分享在gitee,建议直接看gitee的,blog的代码不会更新。基于Tank_nano_4k小蜜蜂开发板的常用开发库: 给用gw1nsr设计的小蜜蜂开发板开发的常用软件库
- 这个uart模块支持rx、tx波特率分开,在应对io数量抓级时有可以rx、tx分别接只需要rx或tx的ic\模块
- 支持1200到最高1152000,其实更高也是可以的,设计时理论到11.52Mhz,不过只测到示波器3M,因为上位机软件都已经不支持了。所以只取1200到1.152Mhz这个上位机usb转ttl收发模块都支持的波特率区间
- 支持1-32bit数据,不过只测试过5/6/78bit,因为上位机只支持5/6/7/8bit所有只取5/6/7/8bit
- 带硬件流控以及支持校验位,软件流控用的不多,暂时不添加
- 这个模块对资源的占用很大,在综合软件上看用了129个逻辑资源,而且逻辑写的也不咋地,所以这个模块基本是很难使用了,所以可以转道我的另一个uart_simple.v代码了,通过切除不需要的功能得到一个最简uart模块
/*
* @Author:
* @Date: 2023-11-22 11:20:27
* @LastEditors:
* @LastEditTime: 2023-12-20 02:08:13
* @FilePath: \gw1nsr_4_package\package\uart\uart.v
* @Description: baud rate list : 1200*n -> 1152000
*
* Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.
*/
module uart #(
parameter baudRateTX = 9600, //1200*n -> 1152000
parameter baudRateRX = 9600, //1200*n -> 1152000
parameter dataFlowControl = 0, //0(none) 1(hard) 2(soft)
parameter verifyBit = 0, //0(none) 1(even parity) 2(odd parity) 3(space parity) 4(mark parity)
parameter dataBitWidth = 8, //<= 32
parameter stopBit = 1 //1、2 //不支持1.5停止位
) (
input rst, clk_2304000Hz,//clk = 1200Hz * n
input [dataBitWidth-1 : 0] send,
input write_en, read_en,//写1使能,读1使能
output reg [dataBitWidth-1 : 0] receive,
output reg read_operation_int,//读1中断发生,
output write_operation_allow, //写1允许
input rx,
output tx,
input cts, //clear to send
output reg rts //require to send
);
reg [21:0]clk_divTX = 22'b0;
reg [21:0]clk_divRX = 22'b0;
reg clk_swapTX = 1'b0;
reg clk_swapRX = 1'b0, RXclk_clr = 1'b0, RXclk_clr_last = 1'b0;
// 分频出给tx、rx使用的时钟
always @(posedge clk_2304000Hz) begin
if(!rst) begin
clk_divTX <= 22'b0;
clk_divRX <= 22'b0;
clk_swapTX <= 1'b0;
clk_swapRX <= 1'b0;
RXclk_clr_last <= 1'b0;
end else begin
// TX的时钟
if(clk_divTX == 1152000*2*10 / baudRateTX - 1) begin
clk_swapTX <= 1'b1;
clk_divTX <= 22'b0;
end else if(clk_divTX < 1152000*2*10 / baudRateTX - 1) begin
clk_swapTX <= 1'b0;
clk_divTX <= clk_divTX + 1'b1;
end
if(RXclk_clr == 1'b1 && RXclk_clr_last == 1'b0) begin
clk_divRX <= 22'b0;
RXclk_clr_last <= RXclk_clr;
end else begin
RXclk_clr_last <= RXclk_clr;
// RX的时钟
if(clk_divRX == 1152000*2*10 / baudRateRX - 1) begin
clk_swapRX <= 1'b1;
clk_divRX <= 22'b0;
end else if(clk_divRX <= 1152000*10 / baudRateRX - 1) begin
clk_swapRX <= 1'b1;
clk_divRX <= clk_divRX + 1'b1;
end else if(clk_divRX > 1152000*10 / baudRateRX - 1) begin
clk_swapRX <= 1'b0;
clk_divRX <= clk_divRX + 1'b1;
end
end
end
end
reg [5:0]count = 6'b0;
reg [dataBitWidth-1:0]tx_buff_reg = 32'b0;
reg txbuff_full = 32'b0;//发送缓冲寄存器
reg [dataBitWidth-1:0]tx_displacement_reg = 32'b0;
reg tx_busy = 32'b0;//移位寄存器
reg verify = 1'b0;
reg [2:0]stop_count = 3'b0;
reg tx_pin = 1'b1;
reg [5:0]count_rx = 6'b0;
reg [dataBitWidth-1:0]rx_buff_reg = 32'b0;
reg rxbuff_full = 1'b0;//接收缓冲寄存器
reg [dataBitWidth-1:0]rx_displacement_reg = 32'b0;
reg rx_busy = 1'b0;//接收移位寄存器
reg verify_rx = 1'b0, verify_ok = 1'b1;
reg [2:0]stop_count_rx = 3'b0;
wire rx_pin;
generate
case (dataFlowControl)
32'd0 ,32'd1 : begin : none_or_hard
always @(negedge clk_2304000Hz) begin
if(rx == 1'b0 && rx_busy == 1'b0) begin
RXclk_clr <= 1'b1;//重置时钟计数
end else if(rx_busy == 1'b1) begin
RXclk_clr <= 1'b0;
end
end
always @(negedge clk_swapRX) begin//rx
if (!rst) begin
count_rx <= 6'b0;
rx_buff_reg <= 32'b0;
rxbuff_full <= 1'b0;
rx_displacement_reg <= 32'b0;
rx_busy <= 1'b0;
verify_rx <= 1'b0;
verify_ok <= 1'b1;
stop_count_rx <= 3'b0;
rts <= 1'b0;
end else begin
if(rxbuff_full) begin
if(read_en) begin
receive <= rx_buff_reg;
rx_buff_reg <= 32'b0;
read_operation_int <= 1'b0;
rxbuff_full <= 1'b0;
end
end
if(rx == 1'b0 && rx_busy == 1'b0) begin
//忙碌接收移位寄存器
rx_busy <= 1'b1;
end else if(rx_busy == 1'b1) begin
//接收数据到移位寄存器,并校验数据
if(count_rx == dataBitWidth) begin
if (verifyBit == 0) begin//无校验
//查空,把移位寄存器数据上发到缓冲,并清除或设置各个中间变量
if(rxbuff_full == 1'b0) begin
rx_buff_reg <= rx_displacement_reg;
rxbuff_full <= 1'b1;
stop_count_rx <= 2'b0;
verify_rx <= 1'b0;
count_rx <= 6'b0;
rx_busy <= 1'b0;
read_operation_int <= 1'b1;
rts <= 1'b0;
end else begin
rts <= 1'b1;
end
end else begin
if(stop_count_rx == 1'b0)begin
verify_rx <= verify_rx + rx_pin;
end else if(stop_count_rx == 1'b1) begin
//检查校验位
if (verifyBit == 1) begin//偶校验
if(verify_rx == 1'b0) begin
verify_ok <= 1'b0;
end
end else if (verifyBit == 2) begin//奇校验
if(verify_rx == 1'b1) begin
verify_ok <= 1'b0;
end
end else if (verifyBit == 3) begin//恒0
if(verify_rx == 1'b0) begin
verify_ok <= 1'b0;
end
end else if (verifyBit == 4) begin//恒1
if(verify_rx == 1'b1) begin
verify_ok <= 1'b0;
end
end
end
if (stop_count_rx == stopBit) begin
//查空,把移位寄存器数据上发到缓冲,并清除或设置各个中间变量
if(rxbuff_full == 1'b0) begin
rx_buff_reg <= rx_displacement_reg;
rxbuff_full <= 1'b1;
stop_count_rx <= 2'b0;
verify_rx <= 1'b0;
count_rx <= 6'b0;
rx_busy <= 1'b0;
read_operation_int <= 1'b1;
rts <= 1'b0;
end else begin
rts <= 1'b1;
end
end else begin
stop_count_rx <= stop_count_rx + 1'b1;
end
end
end else begin
rx_displacement_reg <= rx_displacement_reg >> 1;
rx_displacement_reg[dataBitWidth-1] <= rx_pin;
verify_rx <= verify_rx + rx_pin;
count_rx <= count_rx + 1'b1;
end
end
end
end
assign rx_pin = rx;
always @(posedge clk_swapTX) begin//tx
if (!rst) begin
count <= 6'b0;
tx_buff_reg <= 32'b0;
txbuff_full <= 1'b0;
tx_displacement_reg <= 32'b0;
tx_busy <= 1'b0;
verify <= 1'b0;
stop_count <= 3'b0;
tx_pin <= 1'b1;
end else begin
//上层检查缓存满标志为空,将data发送到缓冲寄存器,外设自动置位缓存满标志
if (write_en) begin//检查到写使能后把数据压入缓冲
tx_buff_reg <= send;
txbuff_full <= 1'b1;
end
//检查流控是否允许传输
if(!cts) begin
//检查移位寄存器忙碌标志,若空闲则缓冲满标志清除,同时把缓冲发送到移位寄存器,并移位寄存器标志位忙碌
if(txbuff_full) begin
if (!tx_busy) begin
tx_displacement_reg <= tx_buff_reg;
tx_buff_reg <= 32'b0;
tx_busy <= 1'b1;
txbuff_full <= 1'b0;
end
end
end
//发送数据、发送校验位、发送停止位
if (tx_busy) begin
if(count == dataBitWidth+1) begin
//发送停止位
if (verifyBit == 1'b0) begin //无校验
if (stop_count == stopBit) begin
//移位寄存器发送后,移位寄存器空闲
count <= 5'b0;
tx_busy <= 1'b0;
stop_count <= 2'b0;
end else begin
tx_pin <= 1'b1;
stop_count <= stop_count + 1'b1;
end
end else begin
if (stop_count == stopBit+1'b1) begin
//移位寄存器发送后,移位寄存器空闲
count <= 5'b0;
tx_busy <= 1'b0;
stop_count <= 2'b0;
end else begin
if (stop_count == 1'b0) begin
//发送校验位
if (verifyBit == 1) begin//偶校验
tx_pin <= verify;
end else if (verifyBit == 2) begin//奇校验
tx_pin <= !verify;
end else if (verifyBit == 3) begin//恒0
tx_pin <= 1'b0;
end else if (verifyBit == 4) begin//恒1
tx_pin <= 1'b1;
end
end else begin
tx_pin <= 1'b1;
end
stop_count <= stop_count + 1'b1;
end
end
end else begin
if (count == 5'b0) begin
tx_pin <= 1'b0;//起始位
count <= count + 1'b1;
end else begin
verify <= verify + tx_displacement_reg[0];
tx_pin <= tx_displacement_reg[0];
tx_displacement_reg <= tx_displacement_reg >> 1;
count <= count + 1'b1;
end
end
end
end
end
assign write_operation_allow = !txbuff_full;
assign tx = tx_pin;
end
32'd2 : begin : soft
end
default : begin : NOP end
endcase
endgenerate
endmodule
/*
* @Author:
* @Date: 2023-12-05 09:17:34
* @LastEditors:
* @LastEditTime: 2023-12-21 00:31:41
* @FilePath: \gw1nsr_4_package\project\uart_test\uart_top.v
* @Description:
*
* Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.
*/
//`define __UART_TOP_V__
`ifdef __UART_TOP_V__
`include "../../package/uart/uart.v"
`endif
module uart_top (
input rst, rx, clk,
output tx, tx_copy, rx_copy
);
wire clk_23_04Mhz;
reg [7:0] send;
wire [7:0] receive;
reg write_en = 1'b0, read_en = 1'b0;
wire read_operation_int, write_operation_allow;
reg wait_allow = 1'b0;
reg read_flag = 1'b0;
reg operation_r2 = 3'b0;
assign tx_copy = tx;
assign rx_copy = rx;
always @(posedge clk) begin
if (!rst) begin
read_en <= 1'b0;
send <= 8'b0;
operation_r2 <= 3'b0;
wait_allow <= 1'b0;
end else begin
if (read_operation_int) begin//rx
read_en <= 1'b1;
read_flag <= 1'b1;
write_en <= 1'b0;
end else begin
if (read_flag == 1'b1) begin
read_flag <= 1'b0;
read_en <= 1'b0;
operation_r2 <= 1'b1;
end
end
//operation_r2[2:1] <= {operation_r2[1], operation_r2[0]};
if (operation_r2 == 1'b1) begin
if (write_operation_allow == 1'b1) begin
send <= receive;
write_en <= 1'b1;
wait_allow <= 1'b1;
end
if (wait_allow == 1'b1 && write_operation_allow == 1'b0) begin
wait_allow <= 1'b0;
write_en <= 1'b0;
operation_r2 <= 3'b0;
end
end else begin
write_en <= 1'b0;
end
end
end
Gowin_PLLVR clk27_to_23Mhz(
.clkout(clk_23_04Mhz),
.clkin(clk)
);
uart #(
.baudRateTX (1152000 ),
.baudRateRX (1152000 ),
.dataFlowControl (0 ), //0(none)
.verifyBit (0 ), //0(none)
.dataBitWidth (5 ),
.stopBit (1 )
) uart_advance (
.rst (rst),
.clk_2304000Hz (clk_23_04Mhz),
.send (send),
.write_en (write_en),
.read_en (read_en),
.receive (receive),
.read_operation_int (read_operation_int),
.write_operation_allow (write_operation_allow),
.rx (rx),
.tx (tx),
.cts (1'b0),
.rts ()
);
endmodule
/*
* @Author:
* @Date: 2023-12-05 16:17:51
* @LastEditors:
* @LastEditTime: 2023-12-19 00:30:44
* @FilePath: \gw1nsr_4_package\project\uart_test\tb.v
* @Description: 这是对uart模块的测试
*
* Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.
*/
`timescale 1ns/1ns
`default_nettype none
module tb_uart;
reg clk;
reg rst_n;
reg tx = 1'b1;
wire rx;
uart_top gt(
.rst (rst_n),
.clk (clk),
.rx (tx),
.tx (rx)
);
localparam CLK_PERIOD = 36;
always begin //27Mhz
#(CLK_PERIOD/2) clk=1'b0;
#(CLK_PERIOD/2+1) clk=1'b1;
end
initial begin
$dumpfile("tb_.vcd");
$dumpvars(0, tb_uart);
end
initial begin
#1 rst_n<=1'bx;clk<=1'bx;
#(CLK_PERIOD*3) rst_n<=1;
#(CLK_PERIOD*3) rst_n<=0;clk<=0;
repeat(5) @(posedge clk);
rst_n<=1;
@(posedge clk);
repeat(2) @(posedge clk);
//$finish(2);
end
reg clk_9600;
reg [11:0]count_div;
always @(posedge clk) begin
if(!rst_n) begin
count_div <= 12'b0;
end else begin
if(count_div == 12'd2812) begin
count_div <= 12'b0;
clk_9600 <= 1'b1;
end else begin
count_div = count_div + 1'b1;
clk_9600 <= 1'b0;
end
end
end
reg [7:0]count = 8'b0;
always @(posedge clk_9600)begin
if(~rst_n) begin
count <= 8'b0;
end else begin
if(count == 8'd9) begin
count = 8'd0;
tx <= 1'b1;
end else if(count >= 8'd8) begin
tx <= 1'b1;
count = count + 1'b1;
end else begin
tx <= 1'b0;
count = count + 1'b1;
end
end
end
endmodule
`default_nettype wire
因为rx模块上传与tx模块下传都要消耗一单位特率时间,导致模块内部rx连tx来转发上位机发来的数据到上位机时,总会因为这个2单位时间消耗掉所有的缓冲。所以无流控设计时注意好tx、rx的数据量。