[X]关闭

Mis603开发板MB SOC开发教程:第十章 协同设计超级串口

文档创建者:Milinker_XU
浏览次数:5986
最后更新:2016-01-24
本帖最后由 Milinker_XU 于 2016-1-21 10:12 编辑

    第九章中,我们用自定义IP设计了定时器,用了MB系统中的通用寄存器,通过设置通过寄存器的值,我们就能完成所需的设计。本节来进行一个更为复杂的设计,自定义串口IP。通过添加自定义的串口,完成串口的发送与接收。
◎设计之前,我们先打开HDL系统中的串口循环工程,目录:…\MiS603\CODE_SRC_X16\HDL_Code\
CH06_Uart_Loop下。分析代码可以看到,串口包括三个方面:1.数据的接收端,通过uart_rx_i引脚,接收数据至uart_rx_data_o中,接收完成标志位uart_rx_done;2.数据发送端,通过uart_tx_o发送引脚,发送数据为uart_tx_data_i,发送使能信号uart_tx_en_i;3.波特率设置通过波特率计数器分频得到,波特率控制字BAUD_DIV和采样控制字BAUD_DIV_CAP,位宽均为12bit,其中BAUD_DIV_CAP=BAUD_DIV>>1。
◎由上面分析可以得到,如果设计一个串口通信,那么对于一个32位通用寄存器,可以完成上述目标。同样,我们列出了寄存器的功能表。
  
Bit
  
读/写
说明
描述
  
Bit0 – Bit7
  
读/写
8bit数据接收寄存器
uart_rx_data_o
  
Bit8
  
读/写
数据接收状态寄存器
该位为1时,表示接收数据完成
  
Bit9 – Bit16
  
读/写
8bit数据发送寄存器
uart_tx_data_i
  
Bit17
  
读/写
数据发送状态寄存器
该位为1时,表示启动数据发送
  
Bit18-Bit31
  
读/写
波特率设置寄存器
设置波特率控制字14bit
◎下面我们来开始设计该串口,在EDK环境中,打开菜单Hardware下Createor Import Peripheral…。
◎弹出创建IP向导,点击Next。
◎PeripheralFlow中保持默认,创建新的。
◎一直到Name and Version对话框,将IP命名为soc_uart。IP版本号,保持默认即可。
◎在BusInterface中,将IP挂在AXI4-Lite总线下。
◎在IPIFservices中,选择所有功能。
◎User S/W Register下选择软件可访问的寄存器个数。由于uart我们就用到了一个通用寄存器,故保持默认值为1即可。
◎在IP Interconnet中,保持默认。这些信号是IP挂在总线内部的信号,其实不需要我们处理的,xilinx开发工具已经帮我们处理好。
◎PeripheralSimulation Support中保持默认,不产生BFM仿真。
◎下面的窗口中,将其全部选中。
◎继续NEXT,直到出现Finish。
◎进行向导完成后,打开生成的ip。文件目录:…\mis603_soc\pcores\soc_uart_v1_00_a\devl\projnav。
◎打开soc_uart.xise工程。可以看到刚刚生成的IP源代码。其中user_logic.v中,是用户文件,需要进行相应的修改。
◎首先打开顶层文件soc_uart.vhd,添加外部接口。添加的代码截图如下所示。
◎在顶层的user_logic端口部分,添加输入输出端口。
◎在顶层文件中的例化部分,完成例化。
◎顶层文件添加完成后,修改用户逻辑文件user_logic.v。红色字体为添加或修改的字体。
  
//----------------------------------------------------------------------------
  
// user_logic.v -  module
  
//----------------------------------------------------------------------------
  
//
  
//  ***************************************************************************
  
// ** Copyright (c)  1995-2012 Xilinx, Inc.  All rights reserved.            **
  
// **                                                                        **
  
// ** Xilinx,  Inc.                                                           **
  
// ** XILINX IS  PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"         **
  
// ** AS A COURTESY  TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND       **
  
// ** SOLUTIONS FOR  XILINX DEVICES.  BY PROVIDING THIS  DESIGN, CODE,        **
  
// ** OR INFORMATION  AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,        **
  
// ** APPLICATION OR  STANDARD, XILINX IS MAKING NO REPRESENTATION           **
  
// ** THAT THIS  IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,     **
  
// ** AND YOU ARE  RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE      **
  
// ** FOR YOUR  IMPLEMENTATION.  XILINX EXPRESSLY  DISCLAIMS ANY              **
  
// ** WARRANTY  WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE               **
  
// **  IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR        **
  
// **  REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF       **
  
// ** INFRINGEMENT,  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS       **
  
// ** FOR A  PARTICULAR PURPOSE.                                              **
  
// **                                                                        **
  
//  ***************************************************************************
  
//
  
//----------------------------------------------------------------------------
  
// Filename:          user_logic.v
  
// Version:           1.00.a
  
// Description:       User logic module.
  
// Date:              Wed Jan 20 16:06:06 2016 (by  Create and Import Peripheral Wizard)
  
// Verilog  Standard:  Verilog-2001
  
//----------------------------------------------------------------------------
  
// Naming  Conventions:
  
//   active low signals:                    "*_n"
  
//   clock signals:                         "clk",  "clk_div#", "clk_#x"
  
//   reset signals:                         "rst",  "rst_n"
  
//   generics:                              "C_*"
  
//   user defined types:                    "*_TYPE"
  
//   state machine next state:              "*_ns"
  
//   state machine current state:           "*_cs"
  
//   combinatorial signals:                 "*_com"
  
//   pipelined or register delay signals:   "*_d#"
  
//   counter signals:                       "*cnt*"
  
//   clock enable signals:                  "*_ce"
  
//   internal version of output port:       "*_i"
  
//   device pins:                           "*_pin"
  
//   ports:                                 "-  Names begin with Uppercase"
  
//   processes:                              "*_PROCESS"
  
//   component instantiations:               "<ENTITY_>I_<#|FUNC>"
  
//----------------------------------------------------------------------------
  
  
`uselib  lib=unisims_ver
  
`uselib  lib=proc_common_v3_00_a
  
  
module user_logic
  
(
  
  // -- ADD USER PORTS BELOW THIS LINE  ---------------
  
  // --USER ports added here
  
  // -- ADD USER PORTS ABOVE THIS LINE  ---------------
  
  uart_rx_i,
  
  uart_tx_o,
  
  // -- DO NOT EDIT BELOW THIS LINE  ------------------
  
  // -- Bus protocol ports, do not add to or  delete
  
  Bus2IP_Clk,                     // Bus to IP clock
  
  Bus2IP_Resetn,                  // Bus to IP reset
  
  Bus2IP_Data,                    // Bus to IP data bus
  
  Bus2IP_BE,                      // Bus to IP byte  enables
  
  Bus2IP_RdCE,                    // Bus to IP read chip  enable
  
  Bus2IP_WrCE,                    // Bus to IP write chip  enable
  
  IP2Bus_Data,                    // IP to Bus data bus
  
  IP2Bus_RdAck,                   // IP to Bus read transfer  acknowledgement
  
  IP2Bus_WrAck,                   // IP to Bus write  transfer acknowledgement
  
  IP2Bus_Error                    // IP to Bus error  response
  
  // -- DO NOT EDIT ABOVE THIS LINE  ------------------
  
); // user_logic
  
  
// -- ADD USER  PARAMETERS BELOW THIS LINE ------------
  
// --USER parameters  added here
  
// -- ADD USER  PARAMETERS ABOVE THIS LINE ------------
  
  
// -- DO NOT EDIT  BELOW THIS LINE --------------------
  
// -- Bus protocol  parameters, do not add to or delete
  
parameter  C_NUM_REG                      = 1;
  
parameter  C_SLV_DWIDTH                   = 32;
  
// -- DO NOT EDIT  ABOVE THIS LINE --------------------
  
  
// -- ADD USER PORTS  BELOW THIS LINE -----------------
  
// --USER ports  added here
  
// -- ADD USER PORTS  ABOVE THIS LINE -----------------
  
input                                              uart_rx_i;
  
output                                              uart_tx_o;
  
// -- DO NOT EDIT  BELOW THIS LINE --------------------
  
// -- Bus protocol  ports, do not add to or delete
  
input                                      Bus2IP_Clk;
  
input                                      Bus2IP_Resetn;
  
input      [C_SLV_DWIDTH-1 : 0]           Bus2IP_Data;
  
input      [C_SLV_DWIDTH/8-1 : 0]         Bus2IP_BE;
  
input      [C_NUM_REG-1 : 0]              Bus2IP_RdCE;
  
input      [C_NUM_REG-1 : 0]              Bus2IP_WrCE;
  
output     [C_SLV_DWIDTH-1 : 0]           IP2Bus_Data;
  
output                                     IP2Bus_RdAck;
  
output                                     IP2Bus_WrAck;
  
output                                     IP2Bus_Error;
  
// -- DO NOT EDIT  ABOVE THIS LINE --------------------
  
  
//----------------------------------------------------------------------------
  
// Implementation
  
//----------------------------------------------------------------------------
  
  
  // --USER nets declarations added here, as  needed for user logic
  
  
  // Nets for user logic slave model s/w  accessible register example
  
  reg         [C_SLV_DWIDTH-1 : 0]            slv_reg0;
  
  wire        [0 : 0]                         slv_reg_write_sel;
  
  wire        [0 : 0]                         slv_reg_read_sel;
  
  reg         [C_SLV_DWIDTH-1 : 0]            slv_ip2bus_data;
  
  wire                                       slv_read_ack;
  
  wire                                       slv_write_ack;
  
  integer                                    byte_index, bit_index;
  
  
// USER logic implementation added here
  
reg [13:0] BAUD_DIV;
  
  
reg [13:0] baud_div1=0;             //波特率设置计数器
  
reg baud_bps1=0;                    //数据采样点信号
  
reg bps_start=0;                    //波特率启动标志
  
always@(posedge  Bus2IP_Clk)
  
begin
  
    if(baud_div1==(BAUD_DIV>>1))        //当波特率计数器计数到采样点时,产生采样信号baud_bps
  
        begin
  
            baud_bps1<=1'b1;
  
            baud_div1<=baud_div1+1'b1;
  
        end
  
    else if(baud_div1<BAUD_DIV && bps_start)//当波特率计数器启动时,计数器累加
  
        begin
  
            baud_div1<=baud_div1+1'b1;
  
            baud_bps1<=0;
  
        end
  
    else
  
        begin
  
            baud_bps1<=0;
  
            baud_div1<=0;
  
        end
  
end
  
  
reg [4:0]  uart_rx_i_r=5'b11111;         //数据接收缓存器
  
always@(posedge  Bus2IP_Clk)
  
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_num1=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;//数据接收完成,数据寄存器
  
  
wire [7:0]  uart_rx_data_o;
  
wire uart_rx_done;
  
  
always@(posedge  Bus2IP_Clk)
  
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_bps1)   //每次等待波特率采样中心时,接收数据,放入数据缓存器中
  
                begin
  
                    bit_num1<=bit_num1+1'b1;
  
                    if(bit_num1<4'd9)   //接收1bit起始信号,8bit有效信号,1bit结束信号
  
                        uart_rx_data_o_r0[bit_num1-1]<=uart_rx_i;
  
                end
  
            else if(bit_num1==4'd10) //接收完成时候,接收数据个数计数器清零,产生接收完成标志位,并将数据写入数据寄存器,关闭波特率时候
  
                begin
  
                    bit_num1<=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  uart_rx_data_o=uart_rx_data_o_r1;        
  
assign  uart_rx_done=uart_rx_done_r;
  
  
reg uart_tx_en_i;
  
reg [7:0]  uart_tx_data_i;
  
  
reg [13:0] baud_div2=0;         //波特率设置计数器
  
reg baud_bps2=0;                //数据发送点信号,高有效
  
  
reg [9:0]  send_data=10'b1111111111;//待发送数据寄存器,1bit起始信号+8bit有效信号+1bit结束信号
  
reg [3:0] bit_num2=0;   //发送数据个数计数器
  
reg uart_send_flag=0;   //数据发送标志位
  
reg uart_tx_o_r=1;      //发送数据寄存器,初始状态位高
  
  
always@(posedge  Bus2IP_Clk)
  
begin
  
    if(baud_div2==(BAUD_DIV>>1))    //当波特率计数器计数到数据发送中点时,产生采样信号baud_bps,用来发送数据
  
        begin
  
            baud_bps2<=1'b1;
  
            baud_div2<=baud_div2+1'b1;
  
        end
  
    else if(baud_div2<BAUD_DIV && uart_send_flag)//数据发送标志位有效期间,波特率计数器累加,以产生波特率时钟
  
        begin
  
            baud_div2<=baud_div2+1'b1;
  
            baud_bps2<=0;   
  
        end
  
    else
  
        begin
  
            baud_bps2<=0;
  
            baud_div2<=0;
  
        end
  
end
  
  
always@(posedge  Bus2IP_Clk)
  
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_num2==4'd10)    //发送结束时候,清楚发送标志信号,并清楚待发送数据寄存器内部信号
  
        begin
  
            uart_send_flag<=1'b0;
  
            send_data<=10'b1111_1111_11;
  
        end
  
end
  
  
always@(posedge  Bus2IP_Clk)
  
begin
  
    if(uart_send_flag)  //发送有效时候
  
        begin
  
            if(baud_bps2)//检测发送点信号
  
                begin
  
                    if(bit_num2<=4'd9)
  
                        begin
  
                            uart_tx_o_r<=send_data[bit_num2];   //发送待发送寄存器内数据,从低位到高位
  
                            bit_num2<=bit_num2+1'b1;
  
                        end
  
                end
  
            else if(bit_num2==4'd10)
  
                bit_num2<=4'd0;
  
        end
  
    else
  
        begin
  
            uart_tx_o_r<=1'b1;  //空闲状态时,保持发送端位高电平,以备发送时候产生低电平信号
  
            bit_num2<=0;
  
        end
  
end
  
  
assign  uart_tx_o=uart_tx_o_r;
  
  //  ------------------------------------------------------
  
  // Example code to read/write user logic  slave model s/w accessible registers
  
  //
  
  // Note:
  
  // The example code presented here is to  show you one way of reading/writing
  
  // software accessible registers  implemented in the user logic slave model.
  
  // Each bit of the Bus2IP_WrCE/Bus2IP_RdCE  signals is configured to correspond
  
  // to one software accessible register by  the top level template. For example,
  
  // if you have four 32 bit software  accessible registers in the user logic,
  
  // you are basically operating on the  following memory mapped registers:
  
  //
  
  //     Bus2IP_WrCE/Bus2IP_RdCE   Memory  Mapped Register
  
  //                     "1000"   C_BASEADDR + 0x0
  
  //                     "0100"   C_BASEADDR + 0x4
  
  //                     "0010"   C_BASEADDR + 0x8
  
  //                     "0001"   C_BASEADDR + 0xC
  
  //
  
  // ------------------------------------------------------
  
  
  assign
  
    slv_reg_write_sel = Bus2IP_WrCE[0:0],
  
    slv_reg_read_sel  = Bus2IP_RdCE[0:0],
  
    slv_write_ack     = Bus2IP_WrCE[0],
  
    slv_read_ack      = Bus2IP_RdCE[0];
  
  
  // implement slave model register(s)
  
  always @( posedge Bus2IP_Clk )
  
    begin
  
      if ( Bus2IP_Resetn == 1'b0 )
  
        begin
  
          slv_reg0 <= 0;
  
        end
  
      else
  
            begin
  
                BAUD_DIV<=slv_reg0[31:18];
  
                uart_tx_en_i<=slv_reg0[17];
  
                uart_tx_data_i<=slv_reg0[16:9];
  
                 if(uart_rx_done)
  
                    begin
  
                        slv_reg0[8]<=uart_rx_done;
  
                        slv_reg0[7:0]<=uart_rx_data_o;
  
                    end
  
                else
  
                  case ( slv_reg_write_sel )
  
                     1'b1 :
  
                        for ( byte_index = 0;  byte_index <= (C_SLV_DWIDTH/8)-1; byte_index = byte_index+1 )
  
                          if ( Bus2IP_BE[byte_index] == 1 )
  
                             slv_reg0[(byte_index*8) +: 8] <=  Bus2IP_Data[(byte_index*8) +: 8];
  
                     default : begin
  
                        slv_reg0 <=  slv_reg0;
  
                                  end
  
                    endcase
  
            end
  
  
    end // SLAVE_REG_WRITE_PROC
  
  
  // implement slave model register read mux
  
  always @( slv_reg_read_sel or slv_reg0 )
  
    begin
  
  
      case ( slv_reg_read_sel )
  
        1'b1 : slv_ip2bus_data <=  slv_reg0;
  
        default : slv_ip2bus_data <= 0;
  
      endcase
  
  
    end // SLAVE_REG_READ_PROC
  
  
  // ------------------------------------------------------------
  
  // Example code to drive IP to Bus signals
  
  //  ------------------------------------------------------------
  
  
assign IP2Bus_Data =  (slv_read_ack == 1'b1) ? slv_ip2bus_data :   0 ;
  
  assign IP2Bus_WrAck = slv_write_ack;
  
  assign IP2Bus_RdAck = slv_read_ack;
  
  assign IP2Bus_Error = 0;
  
  
endmodule
  
◎修改完成后,编译该工程,没有问题后,关闭该ISE工程。
◎跳转到XPS平台,这时候大家别急着添加IP,需要更新下刚刚的串口IP。点击菜单Hardware->Createand Import Periperal…。如下图所示。
◎似乎又到了自定义ip阶段,但这时候跟之前有所差别。选择导入个已经存在的peripheral。
◎选择To an XPSproject,对当前工程进行更新。
◎选择需要更新的IP和版本号。
◎在source filetype下选择HDL源文件选项。
◎选择Useexisting Peripheral Analysis Order file(*.pao),然后点击Browse按钮。
◎在对应的uart_ip路径下,选择.pao文件。
◎从HDL AnalysisInformation中可以看到需要添加的文件。
◎继续Next,选择合适的Bus Interface。总线接口为AXI4Lite Slave模式。
◎其端口保持默认即可。
◎选择自定义串口ip在AXI4Lite上对应的高地址和基地址。
◎参数特性中,保持默认。
◎在端口特性中,可以看到对应的串口两个端口。这里不需要修改。
◎点击finish,完成ip的更新。
◎IP更新完成后,在XPS中,添加soc_uart。可以看到,新添加的ip挂在AXI4Lite总线下。
◎在Ports下,设置soc_uart_0的两个引脚为外部引脚。
◎此时在.mhs文件中,可以看到对应的硬件。在mis603_soc.ucf文件中,为串口端口添加对应的IO引脚约束。由于之前我们添加过了串口,因此此时将原来的约束去掉,将约束改为新的端口。
◎为工程产生网表,在Hardware下运行Generate NetList。运行完成后,返回ISE界面。
◎点击mis603_soc,使用Generate TOP HDL Source,为工程产生新的顶层文件。
◎编译工程,直到生成bit文件。在XPS界面中,进入SDK平台。新建一个Uart_soc工程,以helloworld作为模板。
◎将helloworld.c文件修改成Uart_soc.c文件。
◎根据该章节开始定义的协议,写一个串口循环读写的文件,代码如下所示。
#include <stdio.h>
  
#include "platform.h"
  
#include "xparameters.h"
  
#include "xil_io.h"
  
  
int main()
  
{
  
    init_platform();
  
    u32 uart_set=0;         //串口波特率设置
  
    u32 uart_status=0;      //读串口寄存器
  
    u8 uart_receive=0;      //串口接收数据
  
    u32 uart_send=0;        //写串口寄存器
  
    u32 send_en=0b00000000000000100000000000000000;  //发送使能信号
  
    uart_set=uart_set | 0xA2C00000;                  //串口波特率设置为9600bps
  
     Xil_Out32(XPAR_SOC_UART_0_BASEADDR,uart_set);  //串口波特率设置为9600bps
  
    while(1)
  
    {
  
     uart_status=Xil_In32(XPAR_SOC_UART_0_BASEADDR);  //读串口寄存器
  
     if(uart_status &  0x00000100)                //判断是否接收到数据
  
     {
  
         uart_receive=uart_status  & 0xFF;     //截取接收到的数据
  
    uart_send = uart_receive<<9;         //将接收数据送往待发送区
  
Xil_Out32(XPAR_SOC_UART_0_BASEADDR,uart_set  | uart_send | send_en);  //发送接收到的数据
  
            Xil_Out32(XPAR_SOC_UART_0_BASEADDR,uart_set  & (~send_en));  //关闭串口发送使能
  
            Xil_Out32(XPAR_SOC_UART_0_BASEADDR,uart_set  & 0xFFFFFEFF);  //清楚串口接收标志
  
            uart_status=uart_status&0x00000000;//清楚状态
  
  
     }
  
    }
  
    return 0;
  
}


◎在ISE中将bit文件下载到mis603中,在SDK中运行Uart_soc工程。打开串口调试助手,设置波特率为9600,无校验位,数据位8bit,停止位1bit。打开串口,在发送区发送数据,可以看到接收区能够接收到刚刚发送的数据,如下图所示。
◎惭愧,在MB系统中整了个如此傻逼的32位寄存器。其实,如果使用多个寄存器,分别设置波特率、串口接收和串口发送以及各个状态信号,效果会更好。上面的教程的确脑残了,希望大家别批斗,后续将重新修改更为简单的串口自定义IP。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

发表评论已发布 3

uisrc

发表于 2016-1-23 23:49:12 | 显示全部楼层

资料太详细了,这么长
越努力越幸运!加油!

uisrc

发表于 2016-1-23 23:49:34 | 显示全部楼层

越努力越幸运!加油!
回复

使用道具 举报

Milinker_XU

发表于 2016-1-24 09:49:55 | 显示全部楼层

admin 发表于 2016-1-23 23:49
资料太详细了,这么长

傻瓜教程
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则