tinyriscv-uart

目录

目录

输入输出

内部的信号

写寄存器

读寄存器(组合逻辑)

发送串口(tx发送串口  因为使用了状态机 所以计数器和控制集成到1个always块)

接收串口(没有状态机计数器和控制分开)

1.接收串口打两拍

2.提取下降沿

3.接收标志(在接收使能高且捕捉到上升沿rx_negedge时拉高)

4.分频系数

5.接收计数器

6.接收bit计数,接收bit标志

7.接收bit

发送仿真

发送仿真波形

 接收仿真

接收波形



输入输出

    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

接收波形

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值