Menu Close

UART波特率设计与控制寄存器

当完成所有uart功能后, 有时,需要将uart模块连接到这个系统中去。 这时,就需要使用寄存器接口来配置uart的不同工作模式。 比如波特率,奇偶校验, 停止位等等信息。这些信息可能在不同的应用场景中动态更改。在这种情况下, 就不能在使用parameter 参数,需要使用模块端口进行动态配置。在cpu设计中,往往使用总线与各个外设进行连接,比如axi 总线,RISCV 的RIB总线,以及自主设计的其他总线等等。不论使用何种总线,在总线中都有data, address,read,write,等信号。本文将已自主设计总线方式,对uart 模块进行设计。

 

1 系统设计框架

图1

如图1 所示:

1)将PLL 模块从uart module 中移出,放到顶层模块中

2)将parameter更改为模块端口

3)增加uart 寄存器模块,用来连接系统总线和寄存器接口(uart_reg_if.v)

4)增加测试cpu模块,用来验证寄存器相关的读写操作(cpu_test_if.v)

图2

图2中显示各个模块之间详细连接图。

2 uart 模块

uart 模块在之前的文章中都有介绍,在本文中只对接口做了更改。 将parameter 转变为端口输入信号。

 

3 uart_reg_if 模块

uart_reg_if 模块将接收cpu总线发过来的命令,同时解析cpu命令,确定寄存器地址,读写操作,以及读写的数据。目前在这个模块中设计了2个寄存器,用户可以根据需求,继续增加寄存器。

地址 寄存器名称 注释
32’he000_0000 UART_CTRL_REG uart 模块配置寄存器
32’he000_0004 UART_BAUD_REG uart模块波特率设计寄存器
  • UART_CTRL_REG
x check_sum x parity_type parity stop_bit
bit31-bit5 bit4 bit3 bit2 bit1 bit0
no used 0 => no check sum

1 => enable check sum

no used 0 = > even parity

1=> odd parity

0 => no parity

1 => has parity

0 => 1 stop bit

1 => 2 stop bit

  • UART_BAUD_REG
baud tick count
bit 31 – 0
default: 625

baud tick count = 72_000_000/baud_rate:

for example:

625 = 72_000_000/ 115200

3750 = 72_000_000/ 19200

7500 = 72_000_000/9600

…….

 

uart_reg_if.v 代码:

`timescale 1ns / 1ps
module uart_reg_if #
(
    parameter  UART_CTRL_REG = 32'he000_0000,
    parameter  UART_BAUD_REG = 32'he000_0004
)
(
    input           sys_clk,
    
    input    [31:0] cpu_addr,   // cpu address, used for selecting register address
    input    [3:0]  cpu_wr,     // cpu write, each bit control one byte. for example: cpu_wr = 4'b1100, cpu_din[31:16] will be used to write a register[31:16], cpu_din[15:0] is masked
    input           cpu_rd,     // cpu read, cpu_dout is send to cpu
    input    [31:0] cpu_din,    // data from cpu 
    output   [31:0] cpu_dout,   // data will be send to cpu

    output   [31:0] ctrl_reg,   // 【0】 : 0 => 1 stop bit ; 1=> 2 stop bit\ 【1】 : 0 => no parity ; 1=> parity\【2】 : 0 => even ; 1=> odd ; 
    output   [31:0] baud_reg,   // 波特率使能计数器
  
  
    input           rst
);

/*
ctrl_reg:
==================================================================
|          check_sum    |   x  | parity type | parity | stop_bit |
==================================================================
|          bit4         | bit3 | bit 2       | bit 1  |   bit0   |
==================================================================


baud_reg:
==================================================================
| baud tick count                                                |
==================================================================
| bit31 - bit0                                                   |
==================================================================
| baud tick count = 72_000_000 / baud rate                       |
==================================================================
*/

//================================配置uart参数
localparam  PARITY   = "NONE";      //"ODD"--odd parity, "EVEN"--even parity, others--no parity
localparam  STOP_BIT = 1;
localparam  BAUD_RATE = 115200;
localparam  FREQUENCY = 72_000_000;
localparam  TX_DIV_COE = FREQUENCY / BAUD_RATE;


// write register
reg [31:0] baud_reg_r = TX_DIV_COE;
always @ (posedge sys_clk or posedge rst)
if(rst) baud_reg_r <= TX_DIV_COE;
else if(cpu_addr == UART_BAUD_REG)
begin
    if(cpu_wr[0]) baud_reg_r[07:00] <= cpu_din[07:00];
    if(cpu_wr[1]) baud_reg_r[15:08] <= cpu_din[15:08];
    if(cpu_wr[2]) baud_reg_r[23:16] <= cpu_din[23:16];
    if(cpu_wr[3]) baud_reg_r[31:24] <= cpu_din[31:24];
end
assign baud_reg = baud_reg_r;


reg [31:0] ctrl_reg_r = 0;
always @ (posedge sys_clk or posedge rst)
if(rst) ctrl_reg_r <= 0;
else if(cpu_addr == UART_CTRL_REG)
begin
    if(cpu_wr[0]) ctrl_reg_r[07:00] <= cpu_din[07:00];
    if(cpu_wr[1]) ctrl_reg_r[15:08] <= cpu_din[15:08];
    if(cpu_wr[2]) ctrl_reg_r[23:16] <= cpu_din[23:16];
    if(cpu_wr[3]) ctrl_reg_r[31:24] <= cpu_din[31:24];
end
assign ctrl_reg = ctrl_reg_r;


// read register
reg [31:0] cpu_dout_r = 0;
always @ (posedge sys_clk or posedge rst)
if(rst) cpu_dout_r <= 0;
else if(cpu_rd)
begin
    case(cpu_addr)
    UART_BAUD_REG: cpu_dout_r <= baud_reg_r;
    UART_CTRL_REG: cpu_dout_r <= ctrl_reg_r;
    default: cpu_dout_r <= 0;
    endcase
end
assign cpu_dout = cpu_dout_r;


endmodule

 

端口介绍:

input  sys_clk,                          // 系统时钟
input   [31:0] cpu_addr,        // cpu 地址总线
input   [3:0] cpu_wr,              // 4bit cpu 写信号, 适用于32bit 数据总线,每一个bit对应一个数据byte
input   cpu_rd,                         // cpu 读信号
input   [31:0] cpu_din,           // cpu 写数据到外设
output [31:0] cpu_dout,        // cpu 从外设中读取数据

output [31:0] ctrl_reg,           // uart 控制寄存器
output [31:0] baud_reg,        // 波特率计数器

cpu 写 外设寄存器:

always @ (posedge sys_clk or posedge rst)
if(rst) baud_reg_r <= TX_DIV_COE;
else if(cpu_addr == UART_BAUD_REG)
begin
if(cpu_wr[0]) baud_reg_r[07:00] <= cpu_din[07:00];
if(cpu_wr[1]) baud_reg_r[15:08] <= cpu_din[15:08];
if(cpu_wr[2]) baud_reg_r[23:16] <= cpu_din[23:16];
if(cpu_wr[3]) baud_reg_r[31:24] <= cpu_din[31:24];
end

1)选中寄存器地址,

2)根据不同的写信号 cpu_wr[3:0] ,决定那些数据写入到外设的寄存器中。

 

cpu 读外设寄存器:

// read register
reg [31:0] cpu_dout_r = 0;
always @ (posedge sys_clk or posedge rst)
if(rst) cpu_dout_r <= 0;
else if(cpu_rd)
begin
case(cpu_addr)
UART_BAUD_REG: cpu_dout_r <= baud_reg_r;
UART_CTRL_REG: cpu_dout_r <= ctrl_reg_r;
default: cpu_dout_r <= 0;
endcase
end
assign cpu_dout = cpu_dout_r;

根据cpu总线地址,将外设寄存器的内容返回给cpu。

 

4 cpu_test_if 模块

模拟cpu 总线,发送地址,读写命令,发送写外设数据,或者读取外设数据。本模块模拟cpu 设置uart_ctrl, uart_baud 寄存器。同时可以读取这2个寄存器。

流程图:

图3

状态机详解:

初始化:rst对模块进行初始化操作

状态0:给出写寄存器地址,写使能,写数据,跳转到状态1

状态1:给出第二个需要写的寄存器地址,写使能,写数据,跳转到状态2

状态2:写使能,写数据清0,开始计数,当计数到4的时候给读使能;计数到6的时候给读使能及读寄存器地址,跳转到状态3

状态3:对各种变量清0,保持状态3

cpu_test_if.v 代码:

`timescale 1ns / 1ps
module cpu_test_if #
(
    parameter  UART_CTRL_REG = 32'he000_0000,
    parameter  UART_BAUD_REG = 32'he000_0004
)
(
    input  sys_clk,
    
    output [31:0] cpu_addr,   // cpu address, used for selecting register address
    output [3:0]  cpu_wr,     // cpu write, each bit control one byte. for example: cpu_wr = 4'b1100, cpu_din[31:16] will be used to write a register[31:16], cpu_din[15:0] is masked
    output        cpu_rd,     // cpu read, cpu_dout is send to cpu
    output [31:0] cpu_din,    // data from cpu 
    input  [31:0] cpu_dout,   // data will be send to cpu

        
    input         rst
);

reg [3:0]  cnt;
reg [31:0] cpu_addr_r;
reg [3:0]  cpu_wr_r;
reg        cpu_rd_r;
reg [31:0] cpu_din_r;

reg [3:0] wr_st;

always@(posedge sys_clk or posedge rst)
if(rst)
begin
   cpu_addr_r <= 0;
   cpu_wr_r   <= 0;
   cpu_din_r  <= 0;
   cnt <= 0;
   cpu_rd_r <= 0;
   wr_st <= 0;
end
else case(wr_st)
0:
begin
   cnt <= 0;
   cpu_addr_r <= UART_BAUD_REG;
   cpu_wr_r <= 4'b1111;
   cpu_din_r <= 625;
   wr_st <= 1;
end
1:
begin
   cpu_addr_r <= UART_CTRL_REG;
   cpu_wr_r <= 4'b0001;
   cpu_din_r <= 32'h0000_0000;
   wr_st <= 2;
end
2:
begin
   cpu_rd_r <= 0;
   cpu_din_r <= 0;
   cpu_wr_r <= 0;
   cnt <= cnt + 1;
    
   if(cnt == 4)
   begin
      cpu_rd_r <= 1;
   end
    
   if(cnt == 6)
   begin
      cpu_rd_r <= 1;
      cpu_addr_r <= UART_BAUD_REG;
      wr_st <= 3;
   end
end
3:
begin
   wr_st <= 3;
   cpu_rd_r <= 0;
   cpu_addr_r <= 0;
    
end
default:;
endcase


assign cpu_addr = cpu_addr_r;
assign cpu_wr   = cpu_wr_r;
assign cpu_rd   = cpu_rd_r;
assign cpu_din  = cpu_din_r;



endmodule

 

5 仿真及下载调试

图4

测试波形,模拟cpu端发送, 波特率115200, 8bit, no parity, 1 stop bit。 test1: 04 01 02 f8(截断测试)   test2: 06 00 01 aa bb 92  test3: 05 00 01 aa bb 93 (截断测试)

图5

下板测试 04 01 02 f8 (截断测试) 整个数据包完整,读取波段开关只需要3 个字节。 即: 03 01 02 即可。 使用4 个字节,FPGA 可以截断有效字节。

图6

测试06 00 01 aa bb 92 ,完整数据包, 在pra006 上显示正确。

 

quartus 工程代码:

Posted in FPGA, FPGA 教材教案, Verilog, 教材与教案, 文章

发表回复

相关链接