Chisel(Constructing Hardware In a Scala Embedded Language)是UC Berkeley开发的一种开源硬件构造语言。
站长xddcore有话说:在我大二的时候,因为项目需要,接触了Chisel。在体验过后,我被它深深的吸引了。我幻想着它十年后的样子,充满希望。于是创建了这个博客,让更多人的了解Chisel,学习Chisel。

FPGA学习之路(六)之通用异步收发传输器(UART)搭建

内容纲要
Reading Time: 2 minutes

写在前面

之前在数电课上也有用NE555+CD4017构成的纯数字电路搭过一个baud=4800的RS232协议的串口(原理图见下图),感觉还是蛮好玩的。于是今天今天用FPGA搭一下试试。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

RS232协议说明

一些客套介绍话

在下面的RS232字符发生器的设计开始之前,首先介绍RS232协议的规范。因为这对整个RS232字符发生器的设计非常重要。
RS-232是美国电子工业联盟(EIA)开发的串行数据通信接口标准。原始全名为EIA-RS-232(简称232、RS232)。它广泛应用于计算机串行接口外围设备连接.RS-232C标准,其中EIA(电子工业协会)代表美国电子工业协会,RS(推荐标准)代表推荐标准,232为识别号,C代表RS232(1969)第三次修订版。有RS232B,RS232A。

同样,这些介绍对写程序没啥用。作为程序猿,我更希望知道它的时序图/协议格式。
在这里插入图片描述
如上为RS232协议中一帧数据的格式
以下解释下图上的几个关键信息:
0.每帧数据有1位起始位
1.每帧数据有5/6/7/8位数据位
2.LSB最低有效位先出
3.1bit/2bit停止位
4.NONE/奇偶校验停止位

关于波特率(Baud)

波特率指得是一秒钟发送bit的个数。
板子上的是50M的晶振,如果要产生9600的波特率。最简单的计算和处理方法是50M/9600=5208得到 在9600波特率下每发一个bit需要计数5208次。
一般在采样信号时,我们会采用16倍采样/64倍采样来保证信号可靠。经过这样设计后,采样率为5208倍,可以说是非常可靠了QAQ。还有,在采样的时候,我们一般在信号中点进行采样。这样操作的原因,也是为了保证信号的可靠。

Verilog

RX

`timescale 1ns / 1ps

module uart_rx_path(
    input clk_i,
    input uart_rx_i,

    output [7:0] uart_rx_data_o,
    output uart_rx_done,
    output baud_bps_tb          //for simulation
    );

parameter [12:0] BAUD_DIV     = 13'd5208;   //波特率时钟,9600bps,50Mhz/9600=5208 (1bit信号在50M时钟下计数(采样)5208次)
parameter [12:0] BAUD_DIV_CAP = 13'd2604;   //波特率时钟中间采样点,50Mhz/9600/2=2604 

reg [12:0] baud_div=0;                        //波特率设置计数器
reg baud_bps=0;                               //数据采样点信号
reg bps_start=0;                              //波特率启动标志
always@(posedge clk_i)
begin
    if(baud_div==BAUD_DIV_CAP)               //当波特率计数器计数到采样点时,产生采样信号baud_bps
        begin
            baud_bps<=1'b1;
            baud_div<=baud_div+1'b1;
        end
    else if(baud_div<BAUD_DIV && bps_start)  //当波特率计数器启动时,计数器累加
        begin
            baud_div<=baud_div+1'b1;
            baud_bps<=0;
        end
    else
        begin
            baud_bps<=0;
            baud_div<=0;
        end
end

reg [4:0] uart_rx_i_r=5'b11111;                //数据接收缓存器
always@(posedge clk_i)
begin
    uart_rx_i_r<={uart_rx_i_r[3:0],uart_rx_i};
end
//数据接收缓存器,当连续接收到五个低电平时,即uart_rx_int=0时,作为接收到起始信号
wire uart_rx_int=uart_rx_i_r[4] | uart_rx_i_r[3] | uart_rx_i_r[2] | uart_rx_i_r[1] | uart_rx_i_r[0];

reg [3:0] bit_num=0;          //接收数据个数计数器
reg uart_rx_done_r=0;         //数据接收完成寄存器
reg state=1'b0;

reg [7:0] uart_rx_data_o_r0=0;//数据接收过程中,数据缓存器
reg [7:0] uart_rx_data_o_r1=0;//数据接收完成,数据寄存器

always@(posedge clk_i)
begin
    uart_rx_done_r<=1'b0;
    case(state)
        1'b0 : 
            if(!uart_rx_int)//当连续接收到五个低电平时,即uart_rx_int=0时,作为接收到起始信号,启动波特率时钟
                begin
                    bps_start<=1'b1;
                    state<=1'b1;
                end
        1'b1 :         
            if(baud_bps)    //每次等待波特率采样中心时,接收数据,放入数据缓存器中
                begin
                    bit_num<=bit_num+1'b1;
                    if(bit_num<4'd9)    //接收1bit起始信号,8bit有效信号,1bit结束信号
                        uart_rx_data_o_r0[bit_num-1]<=uart_rx_i;
                end
            else if(bit_num==4'd10) //接收完成时候,接收数据个数计数器清零,产生接收完成标志位,并将数据写入数据寄存器,关闭波特率时候
                begin
                    bit_num<=0;
                    uart_rx_done_r<=1'b1;
                    uart_rx_data_o_r1<=uart_rx_data_o_r0;
                    state<=1'b0;//进入状态0,再次循环检测
                    bps_start<=0;
                end
        default:;
    endcase
end

assign baud_bps_tb=baud_bps;//for simulation
assign uart_rx_data_o=uart_rx_data_o_r1;        
assign uart_rx_done=uart_rx_done_r;
endmodule

TX

`timescale 1ns / 1ps

module uart_tx_path(
    input clk_i,

    input [7:0] uart_tx_data_i,          //待发送数据
    input uart_tx_en_i,                  //发送发送使能信号

    output uart_tx_o
);

parameter BAUD_DIV     = 13'd5208;      //波特率时钟,9600bps,50Mhz/9600=5208,(1bit信号在50M时钟下计数(采样)5208次)
parameter BAUD_DIV_CAP = 13'd2604;      //波特率时钟中间采样点,50Mhz/9600/2=2604

reg [12:0] baud_div=0;                   //波特率设置计数器
reg baud_bps=0;                          //数据发送点信号,高有效
(* MARKDEBUG = "TRUE" *)reg [9:0] send_data=10'b1111111111;     //待发送数据寄存器,1bit起始信号+8bit有效信号+1bit结束信号
(* MARKDEBUG = "TRUE" *)reg [3:0] bit_num=0;                  //发送数据个数计数器
reg uart_send_flag=0;                   //数据发送标志位
reg uart_tx_o_r=1;                      //发送数据寄存器,初始状态位高

always@(posedge clk_i)
begin
    if(baud_div==BAUD_DIV_CAP)          //当波特率计数器计数到数据发送中点时,产生采样信号baud_bps,用来发送数据
        begin
            baud_bps<=1'b1;
            baud_div<=baud_div+1'b1;
        end
    else if(baud_div<BAUD_DIV && uart_send_flag)//数据发送标志位有效期间,波特率计数器累加,以产生波特率时钟
        begin
            baud_div<=baud_div+1'b1;
            baud_bps<=0; 
        end
    else
        begin
            baud_bps<=0;
            baud_div<=0;
        end
end

always@(posedge clk_i)
begin
    if(uart_tx_en_i)    //接收数据发送使能信号时,产生数据发送标志信号
        begin
            uart_send_flag<=1'b1;
            send_data<={1'b1,uart_tx_data_i,1'b0};//待发送数据寄存器装填,1bit起始信号0+8bit有效信号+1bit结束信号
        end
    else if(bit_num==4'd10)    //发送结束时候,清楚发送标志信号,并清楚待发送数据寄存器内部信号
        begin
            uart_send_flag<=1'b0;
            send_data<=10'b1111_1111_11;
        end
end

always@(posedge clk_i)
begin
    if(uart_send_flag)  //发送有效时候
        begin
            if(baud_bps)//检测发送点信号
                begin
                    if(bit_num<=4'd9)
                        begin
                            uart_tx_o_r<=send_data[bit_num]; //发送待发送寄存器内数据,从低位到高位
                            bit_num<=bit_num+1'b1;
                        end
                end
            else if(bit_num==4'd10)
                bit_num<=4'd0;
        end
    else
        begin
            uart_tx_o_r<=1'b1;  //空闲状态时,保持发送端位高电平,以备发送时候产生低电平信号
            bit_num<=0;
        end
end

assign uart_tx_o=uart_tx_o_r;

endmodule

UART Module

`timescale 1ns / 1ps

module Test05_project_CP2102_UART (
    input sys_clk,
    input sys_rst_n,
    input uart_rx,
    output uart_tx
    );

wire [7:0] uart_rx_data_o;
wire uart_rx_done;

uart_rx_path uart_rx_path_u (
    .clk_i(sys_clk), 
    .uart_rx_i(uart_rx), 
    .uart_rx_data_o(uart_rx_data_o), 
    .uart_rx_done(uart_rx_done)
    );

uart_tx_path uart_tx_path_u (
    .clk_i(sys_clk), 
    .uart_tx_data_i(uart_rx_data_o), 
    .uart_tx_en_i(uart_rx_done), 
    .uart_tx_o(uart_tx)
    );

endmodule
Share

xddcore

xddcore www.github.com/xddcore

You may also like...

发表回复

您的电子邮箱地址不会被公开。