基于riscv指令设计一个简易SOC——uart

uart部分

SOC中一般必不可少uart串口,是异步收发(不用同一个时钟信号)传输接口,且只能一个bit一个bit进行传输。
标准传输一个字符需要1bit(起始位)+7bit(数据位)+1bit(校验位)+1bit(结束位),也就是说传输一个标准的字符需要传输十位,将在后面详细介绍。
波特率:谈到波特率之前先说一下比特率,譬如比特率为9600意味着每秒传输9600个bit,两相调制时波特率和比特率相同,导致很多人以为这是一个东西,当四相调制或八相调制乃至16时时,比特率就是波特率的2倍和三、四倍。所谓波特率,就是传输码元的速率,当四相调制时,就需要两个比特来表示一个码元了,以此类推。过于复杂的电磁环境不宜使用过高的波特率,波特率高了意味着每个bit时间短了,在这样的环境容易出错。
uart 协议传输图
在这里插入图片描述

传输数据位包括以下几个部分:
1,空闲位:

UART协议规定,当总线处于空闲状态时信号线的状态为‘1’即高电平,表示当前线路上没有数据传输。

2,起始位:

每开始一次通信时发送方先发出一个逻辑”0”的信号(低电平),表示传输字符的开始。因为总线空闲时为高电平所以开始一次通信时先发送一个明显区别于空闲状态的信号即低电平。
3,数据位:

起始位之后就是我们所要传输的数据,数据位可以是5、6、7、8,9位等,构成一个字符(一般都是8位)。如ASCII码(7位),扩展BCD码(8位)。先发送最低位,最后发送最高位,使用低电平表示‘0’高电平表示‘1’完成数据位的传输。

4,奇偶校验位:

数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。校验位其实是调整个数,串口校验分几种方式:

1)无校验(no parity)。
2)奇校验(odd parity):如果数据位中“1”的数目是偶数,则校验位为“1”,如果“1”的数目是奇数,校验位为“0”。
3)偶校验(even parity):如果数据为中“1”的数目是偶数,则校验位为“0”,如果为奇数,校验位为“1”。
4)mark parity:校验位始终为1(不常用)。
5)parity:校验位始终为0(不常用)。

那么利用uart进行Verilog编程时我们需要定义哪些输入输出端口呢。
发送部分;clk(时钟),rstn(复位),en(使能),uart_tx(输出信号端),还有一个8位片上输入数据端wdata_in_uart,最后传输完成还有一个完成标志,代码入下:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/05/26 16:10:58
// Design Name: 
// Module Name: uart_send
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module uart_send #(
    parameter freq=50000000,  //trime frequncy
    parameter bps=9600 //baud fre
    )(
    input clk,
    input rstn,
    
    input [7:0] wdata_i_uart,
    input tx_en,
    output reg uart_tx,  //output send data (a time a bit)
    output send_done
    );
    localparam bps_cnt=freq/bps;
    reg tx_flag;//send flag
    reg [15:0] bps_cnt_reg;
    reg [3:0] tx_cnt;
    reg [7:0] tx_data;
    //
    always@(posedge clk or negedge rstn) begin
        if(!rstn) begin
            bps_cnt_reg<=16'b0;
            tx_cnt<=4'b0;
        end
        else if(bps_cnt_reg<bps_cnt-1'b1) begin
            bps_cnt_reg<= bps_cnt_reg+1'b1;
            tx_cnt<=tx_cnt+1'b1;
        end
        else begin
            bps_cnt_reg<= 16'b0;
            tx_cnt<=tx_cnt;
        end 
    end
    //
    always@(posedge clk or negedge rstn) begin
        if(!rstn) begin
            tx_flag<=1'b0;
            tx_data<=8'b0;
        end
        else if(tx_en)begin
            tx_flag<=1'b1;
            tx_data<=wdata_i_uart;            
        end
        else if((tx_cnt==4'h9)&&(bps_cnt==bps_cnt_reg/2)) begin
            tx_flag<=1'b0;
            tx_data<=8'b0;            
        end
        else begin
            tx_flag<=tx_flag;
            tx_data<=tx_data;
        end
    end
    
    always@(posedge clk or negedge rstn) begin
        if(!rstn) begin
            uart_tx<=1'b1;
        end
        else begin
            if(tx_flag)begin
                case(tx_cnt)
                    4'd0:uart_tx<=1'b0;  //begin data
                    4'd1:uart_tx<=tx_data[0];
                    4'd2:uart_tx<=tx_data[1];  
                    4'd3:uart_tx<=tx_data[2];
                    4'd4:uart_tx<=tx_data[3];
                    4'd5:uart_tx<=tx_data[4];  
                    4'd6:uart_tx<=tx_data[5];
                    4'd7:uart_tx<=tx_data[6];
                    4'd8:uart_tx<=tx_data[7];
                    4'd9:uart_tx<=1'b1;//stop data 
                    4,b10:send_done<=1'b1;  
                    default:begin uart_tx<=1'b0; send_done<=1'b1; end                                   
                endcase
            end
            else begin
                uart_tx<=1'b1;
            end
        end
      end
endmodule

接收part

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/05/26 20:52:54
// Design Name: 
// Module Name: uart_recv
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//
uart rx module

module uart_recv #(
parameter bps=9600,
parameter freq=50000000 //clock freq
 )(
input clk,
input rstn,
input uart_rx,
output reg [7:0] tx_data, 
output reg rx_done,
input write_uart
    );
    
localparam bps_cnt=freq/bps;

wire start_flag;
reg uart_rxd1;
reg uart_rxd2;

reg rx_flag;
reg [3:0] rx_cnt;
reg [15:0] bps_cnt_reg;
reg [7:0] reg_data;

always@(posedge clk or negedge rstn) begin
    if(!rstn) begin
      uart_rxd1<=0;
      uart_rxd2<=0;
    end
    else begin
      uart_rxd1<=uart_rx;
      uart_rxd2<=uart_rxd1;
    end
end
//
assign start_flag=uart_rxd2&(~uart_rxd1);
//
always@(posedge clk or negedge rstn) begin
    if(!rstn) begin
        rx_flag<=1'b0;
    end
    else if(start_flag)begin
        rx_flag<=1'b1;
     end
    else if((bps_cnt_reg==bps_cnt/2)&(rx_cnt==4'd9)) begin
        rx_flag<=1'b0;
    end
    else 
        rx_flag<=rx_flag;
 end
 ///
always@(posedge clk or negedge rstn) begin
    if(!rstn) begin
        bps_cnt_reg<=16'b0;
        rx_cnt<=4'b0;
    end
    else if(rx_flag)begin
            if(bps_cnt_reg<bps_cnt-1'b1)begin//一个数据传输周期长度为50000000/9600个周期
                bps_cnt_reg<=bps_cnt_reg+1'b1;
                rx_cnt<=rx_cnt;
             end
             else begin
                bps_cnt_reg<=16'b0;
                rx_cnt<=rx_cnt+1'b1;
             end
          end
     else begin
          bps_cnt_reg<=16'b0;
          rx_cnt<=4'b0;
     end
 end      
 
always@(posedge clk or negedge rstn) begin
    if(!rstn)begin
        reg_data<=8'b0;
        rx_done<=1'b0;
    end
    else if(rx_flag)
            case(rx_cnt)
                4'd0:begin reg_data[1]<=uart_rx; rx_done<=1'b0; end
                4'd1:reg_data[2]<=uart_rx;
                4'd2:reg_data[3]<=uart_rx;
                4'd3:reg_data[4]<=uart_rx;
                4'd4:reg_data[5]<=uart_rx;
                4'd5:reg_data[6]<=uart_rx;
                4'd6: begin reg_data[6]<=uart_rx;  rx_done<=1'b0; end
                4'd7:begin reg_data[7]<=uart_rx; rx_done<=1'b1; end
                4'd8:begin rx_done<=1'b0; end
                default:  rx_done<=1'b0;
            endcase
    else
            reg_data<=reg_data;
   end
   
   always@(posedge clk or negedge rstn) begin
    if(!rstn)begin
      tx_data<=8'b0;
    end
    else if(!write_uart)begin
      tx_data<=reg_data;
    end
//
   end
   
endmodule

uart—top模块,由于这是挂靠在SOC总线下,所以有地址,多位数据总线,这里的总线需要将数据分为四块,依次发出和写入,本次分为四块。代码详情如下

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/05/26 22:15:05
// Design Name: 
// Module Name: uart_top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//
//address number is 32'b0000_0000-32'b0fffffff

module uart_top #(
parameter bps=9600,
parameter freq=50000000
  )(
input clk,
input rstn,
input write,

output wire uart_tx,  //slave
input wire[31:0]tx_data_indata,
output reg [31:0] rx_data_outdata,
input wire uart_rx_in_slave,
output wire  sign_done,
input [31:0] addr_i_uart//master

    );
     
 reg write_reg;
 reg [31:0] tx_data_in_reg;
 wire [31:0] rx_data_out_wire;
 
 reg [2:0]cnt_tx;
reg [2:0]cnt_rx;
wire [7:0] tx_data;
reg [7:0] rx_data;
wire uart_tx_salve;

 //assign rx_data_out_wire=rx_data_out;
reg [31:0]tx_data_in;

reg sign_in_out;

always@(posedge clk) begin
  if(!rstn) begin
    tx_data_in<=32'b0;
    rx_data_outdata<=32'b0;
  end
  else if(sign_in_out==1)begin
    tx_data_in<=tx_data_indata;
    rx_data_outdata<=tx_data_in_reg;
  end
  end
  ///
always@(posedge clk) begin
    if(!rstn) begin
        cnt_tx<=0;
        tx_data_in_reg<=32'b0;
        rx_data<=0;
    end
    else if(sign_done) begin
        cnt_tx<=cnt_tx+1'b1;
        tx_data_in_reg<={tx_data_in_reg[31:8],tx_data};
        tx_data_in<=tx_data_in>>8;
        rx_data<=tx_data_in[7:0];
        if(cnt_tx==3'b100) begin
            sign_in_out=1;
        end
        else begin
            sign_in_out=0;
        end
    end
    else if(cnt_tx>3'b100) begin
        cnt_tx<=0;
        tx_data_in_reg<=32'b0;
        rx_data<=0;
    end
  end
  
 localparam reg_csr=8'h00;
 localparam reg_br=8'h04;  //r=response
 localparam reg_datar=8'h08; 
 wire reg_csr_w;
 wire reg_br_w;
 wire reg_data_w;
 assign reg_csr_w=(reg_csr==addr_i_uart[7:0]);
 assign reg_br_w=(reg_br==addr_i_uart[7:0]);
 assign reg_data_w=(reg_datar==addr_i_uart[7:0]);


 ///recv module
uart_recv #(
.bps(bps),
.freq(freq) //clock freq
 )uart_recv_inst(
.clk(clk),
.rstn(rstn),

.uart_rx(uart_rx_in_slave),  //input
.tx_data(tx_data), //output
.rx_done(sign_done),
.write_uart(write)
    );
 ///
uart_send #(
    .freq(freq),  //trime frequncy
    .bps(bps) //baud fre
    )uart_send_inst(
    .clk(clk),
    .rstn(rstn),
    
    .wdata_i_uart(rx_data),//input
    .tx_en(write),
    .uart_tx(uart_tx),  //output send data (a time a bit)
    .send_done(sign_done)
    );
endmodule
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值