zoukankan      html  css  js  c++  java
  • 在xilinxFPGA上使用microblaze及自写GPIO中断

      很久很久没有更新过博客了,今天来扒一扒FPGA上CPU软核的使用。

      主要完成的功能:使用的开发板是nexys 4 DDR,板上有16个switch以及16个LED,需要完成microblaze对led的控制以及将switch作为外部中断源。

      一、自定义GPIO IP核

      还是在Tools里面选择Create and Package IP,新建AXI4外设,本次需要新建两个GPIO外设,一个作为GPIO_IN,一个作为GPIO_OUT。

      GPIO_OUT就是简单的将CPU下发的数据输出到PIN脚,输出需要有高阻态并且三态可通过CPU配置;GPIO_IN需要有中断功能,并且其触发方式(上升沿/下降沿触发,高/低电平触发)需要可配置。

      1.GPIO_OUT:

      首先将CPU下发的数据输出,即

    output [31:0] reg_out,
    .
    .
    .
    assign reg_out = slv_reg0;
    .
    .
    .

      其次我们要实现输出三态,最开始的想法是在IP核内部使用原语OBUFT,其原语如下:

    OBUFT U_OBUFT1(
     .I(I),//输入
     .O(O),//输出
     .T(T) //三态控制      
    );

    后来经某师兄提醒,使用原语可能不方便后期移植,通用性比较差,所以改成了如下形式:

    generate
            genvar i;
            for(i=0;i<C_S_AXI_DATA_WIDTH;i=i+1)
                assign reg_out[i] = slv_reg1[i] ? 1'bz : slv_reg0[i];
        endgenerate

    生成的电路如图所示:

    打包生成IP核,例化的用户接口如图所示:

       2.GPIO_IN:

      在使用IP生成向导的时候注意勾选使能中断自动生成的模块结构如图所示:

    gpio_in_..._AXI没什么好说的,直接将PIN脚信号传递给CPU就可以了。gpio_in_..._AXI_INTR主要实现了中断逻辑,部分代码如下:

    module axi_user_logic_gpio_in_v1_0_S_AXI_INTR #
        (
            // Users to add parameters here
    
            // User parameters ends
            // Do not modify the parameters beyond this line
    
            // Width of S_AXI data bus
            parameter integer C_S_AXI_DATA_WIDTH    = 32,
            // Width of S_AXI address bus
            parameter integer C_S_AXI_ADDR_WIDTH    = 5,
            // Number of Interrupts
            parameter integer C_NUM_OF_INTR = 1,
            // Each bit corresponds to Sensitivity of interrupt :  0 - EDGE, 1 - LEVEL
            parameter  C_INTR_SENSITIVITY   = 32'hFFFFFFFF,
            // Each bit corresponds to Sub-type of INTR: [0 - FALLING_EDGE, 1 - RISING_EDGE : if C_INTR_SENSITIVITY is EDGE(0)] and [ 0 - LEVEL_LOW, 1 - LEVEL_LOW : if C_INTR_SENSITIVITY is LEVEL(1) ]
            parameter  C_INTR_ACTIVE_STATE  = 32'hFFFFFFFF,
            // Sensitivity of IRQ: 0 - EDGE, 1 - LEVEL
            parameter integer C_IRQ_SENSITIVITY = 1,
            // Sub-type of IRQ: [0 - FALLING_EDGE, 1 - RISING_EDGE : if C_IRQ_SENSITIVITY is EDGE(0)] and [ 0 - LEVEL_LOW, 1 - LEVEL_LOW : if C_IRQ_SENSITIVITY is LEVEL(1) ]
            parameter integer C_IRQ_ACTIVE_STATE    = 1
        )
        (
            // Users to add ports here
            input [31:0] sig_in,
            // User ports ends
    .
    .
    .
    //-- Number of Slave Registers 5
        reg [0 : 0] reg_global_intr_en;           //全局中断使能
        reg [C_NUM_OF_INTR-1 :0] reg_intr_en;     //具体到某一个中断的使能      
        reg [C_NUM_OF_INTR-1 :0] reg_intr_sts;    //中断状态查询      
        reg [C_NUM_OF_INTR-1 :0] reg_intr_ack;    //清除中断      
        reg [C_NUM_OF_INTR-1 :0] reg_intr_pending;//中断等待      
        reg [C_NUM_OF_INTR-1 :0] intr;            //中断源      
        reg [C_NUM_OF_INTR-1 :0] det_intr;        //检测中断信号      
        wire intr_reg_rden;                             
        wire intr_reg_wren;                             
        reg [C_S_AXI_DATA_WIDTH-1:0]     reg_data_out;   
        // reg [3:0]   intr_counter;                        
        genvar i;                                       
        integer j;                                      
        reg [C_NUM_OF_INTR-1 :0] intr_all_tmp; //Xilinx自动生成的IP核intr_all存在多驱动的问题,所以添加了该信号
        wire intr_all = |intr_all_tmp;                                  
        reg [C_NUM_OF_INTR-1 :0] intr_ack_all_tmp; //同样是因为intr_ack_all多驱动
        wire intr_ack_all = |intr_ack_all_tmp;                              
        wire s_irq;                                     
        reg intr_all_ff;                                
        reg intr_ack_all_ff;                 
    .
    .
    .
    reg [31:0] sig_in_ff1,sig_in_ff2;    //对输入信号打拍以消除亚稳态
        always @(posedge S_AXI_ACLK) begin
            sig_in_ff1 <= sig_in;
            sig_in_ff2 <= sig_in_ff1;
        end     
    
        generate                                                                 
          for(i=0; i<= C_NUM_OF_INTR-1; i=i+1)//对输入信号消抖,采样16个时钟周期,全一则认为触发了中断
            begin : debounce     
                reg [3:0]   intr_counter;           
                always @ ( posedge S_AXI_ACLK )                                                                                                             
                  if ( S_AXI_ARESETN == 1'b0 )                                                                                                          
                      intr_counter[3:0] <= 4'hF;                                                                                                         
                  else if (sig_in_ff2[i] == 1'b1) begin  
                    if(intr_counter [3:0] != 4'h0)
                        intr_counter[3:0] <= intr_counter[3:0] - 1;
                    end
                  else 
                      intr_counter[3:0] <= 4'hF;                                                                
    
                always @ ( posedge S_AXI_ACLK )                                                                                                              
                    if ( S_AXI_ARESETN == 1'b0)                                                                                                    
                        intr[i] <= 1'b0;                                                                                                 
                    else                                                                 
                      begin                                                              
                        if (intr_counter[3:0] == 0)                                                                                             
                            intr[i] <= 1'b1;                                                                                          
                        else                                                           
                            intr[i] <= 1'b0;                                                              
                      end                                                                                                                                     
            end                                                                                                                                      
        endgenerate
    
        generate                                                                 
        for(i=0; i<= C_NUM_OF_INTR-1; i=i+1)                                     
          begin : gen_intrall                                                    
                                                                                 
              // detects interrupt in any intr input                             
             always @ ( posedge S_AXI_ACLK)                                      
               begin                                                             
                if ( S_AXI_ARESETN == 1'b0 || intr_ack_all_ff == 1'b1)           
                    begin                                                        
                      intr_all_tmp[i] <= 1'b0;                                          
                    end                                                          
                  else                                                           
                    begin                                                        
                    //  for(j=0; j<= C_NUM_OF_INTR-1; j=j+1)                     
                    if (reg_intr_pending[i])                                     
                      begin                                                      
                      intr_all_tmp[i] <= 1'b1;                                          
                    end                                                          
                end                                                              
            end                                                                  
                                                                                 
            // detects intr ack in any reg_intr_ack reg bits                     
            always @ ( posedge S_AXI_ACLK)                                       
              begin                                                              
                if ( S_AXI_ARESETN == 1'b0 || intr_ack_all_ff==1'b1)             
                    begin                                                        
                      intr_ack_all_tmp[i] <= 1'b0;                                      
                    end                                                          
                  else                                                           
                    begin                                                        
                     // for(j=0; j<= C_NUM_OF_INTR-1; j=j+1)                     
                     if (reg_intr_ack[i])                                        
                       begin                                                     
                       intr_ack_all_tmp[i] <= 1'b1;                                     
                     end                                                         
                end                                                              
            end                                                                  
                                                                                  
         end                                                                     
        endgenerate                       
    .
    .
    .
      // detect interrupts for user selected number of interrupts              
                                                                                 
        generate                                                                 
          for(i=0; i<= C_NUM_OF_INTR-1; i=i+1)                                   
            begin : gen_intr_detection                                           
                                                                                 
              if (C_INTR_SENSITIVITY[i] == 1'b1)                                 
                begin: gen_intr_level_detect                                     
                                                                                 
                    if (C_INTR_ACTIVE_STATE[i] == 1'b1)                          
                      begin: gen_intr_active_high_detect                         
                                                                                 
                        always @ ( posedge S_AXI_ACLK )                          
                        begin                                                    
                          if ( S_AXI_ARESETN == 1'b0 | reg_intr_ack[i] == 1'b1)  
                            begin                                                
                              det_intr[i] <= 1'b0;                               
                            end                                                  
                          else                                                   
                            begin                                                
                              if (intr[i] == 1'b1)                               
                                begin                                            
                                  det_intr[i] <= 1'b1;                           
                                end                                              
                            end                                                  
                        end                                                      
                                                                                 
                      end                                                        
                    else                                                         
                      begin: gen_intr_active_low_detect                          
                                                                                 
                        always @ ( posedge S_AXI_ACLK )                          
                        begin                                                    
                          if ( S_AXI_ARESETN == 1'b0 | reg_intr_ack[i] == 1'b1)  
                            begin                                                
                              det_intr[i] <= 1'b0;                               
                            end                                                  
                          else                                                   
                            begin                                                
                              if (intr[i] == 1'b0)                               
                                begin                                            
                                  det_intr[i] <= 1'b1;                           
                                end                                              
                            end                                                  
                        end                                                      
                                                                                 
                      end                                                        
                                                                                 
                                                                                 
                end                                                              
              else                                                               
                begin:gen_intr_edge_detect                                       
                                                                                 
                  wire [C_NUM_OF_INTR-1 :0] intr_edge;                           
                  reg [C_NUM_OF_INTR-1 :0] intr_ff;                              
                  reg [C_NUM_OF_INTR-1 :0] intr_ff2;                             
                                                                                 
                    if (C_INTR_ACTIVE_STATE[i] == 1)                             
                      begin: gen_intr_rising_edge_detect                         
                                                                                 
                                                                                 
                        always @ ( posedge S_AXI_ACLK )                          
                        begin                                                    
                          if ( S_AXI_ARESETN == 1'b0 || reg_intr_ack[i] == 1'b1) 
                            begin                                                
                              intr_ff[i] <= 1'b0;                                
                              intr_ff2[i] <= 1'b0;                               
                            end                                                  
                          else                                                   
                            begin                                                
                              intr_ff[i] <= intr[i];                             
                              intr_ff2[i] <= intr_ff[i];                         
                            end                                                  
                        end                                                      
                                                                                 
                        assign intr_edge[i] = intr_ff[i] && (!intr_ff2);         
                                                                                 
                        always @ ( posedge S_AXI_ACLK )                          
                        begin                                                    
                          if ( S_AXI_ARESETN == 1'b0 | reg_intr_ack[i] == 1'b1)  
                            begin                                                
                              det_intr[i] <= 1'b0;                               
                            end                                                  
                          else if (intr_edge[i] == 1'b1)                         
                            begin                                                
                              det_intr[i] <= 1'b1;                               
                            end                                                  
                        end                                                      
                                                                                 
                      end                                                        
                    else                                                         
                      begin: gen_intr_falling_edge_detect                        
                                                                                 
                        always @ ( posedge S_AXI_ACLK )                          
                        begin                                                    
                          if ( S_AXI_ARESETN == 1'b0 | reg_intr_ack[i] == 1'b1)  
                            begin                                                
                              intr_ff[i] <= 1'b1;                                
                              intr_ff2[i] <= 1'b1;                               
                            end                                                  
                          else                                                   
                            begin                                                
                              intr_ff[i] <= intr[i];                             
                              intr_ff2[i] <= intr_ff[i];                         
                            end                                                  
                        end                                                      
                                                                                 
                        assign intr_edge[i] = intr_ff2[i] && (!intr_ff);         
                                                                                 
                        always @ ( posedge S_AXI_ACLK )                          
                        begin                                                    
                          if ( S_AXI_ARESETN == 1'b0 | reg_intr_ack[i] == 1'b1)  
                            begin                                                
                              det_intr[i] <= 1'b0;                               
                            end                                                  
                          else if (intr_edge[i] == 1'b1)                         
                            begin                                                
                              det_intr[i] <= 1'b1;                               
                            end                                                  
                        end                                                      
                                                                                 
                                                                                 
                      end                                                        
                                                                                 
            end                                                                  
                                                                                 
            // IRQ generation logic                                               
                                                                                  
            reg s_irq_lvl;                                                        
                                                                                  
            if (C_IRQ_SENSITIVITY == 1)                                           
            begin: gen_irq_level                                                  
                                                                                  
                if (C_IRQ_ACTIVE_STATE == 1)                                      
                begin: irq_level_high                                             
                                                                                  
                    always @ ( posedge S_AXI_ACLK )                               
                    begin                                                         
                        if ( S_AXI_ARESETN == 1'b0 || intr_ack_all == 1'b1)       
                        begin                                                     
                            s_irq_lvl <= 1'b0;                                    
                        end                                                       
                        else if (intr_all == 1'b1 && reg_global_intr_en[0] ==1'b1)
                        begin                                                     
                            s_irq_lvl <= 1'b1;                                    
                        end                                                       
                    end                                                           
                    assign s_irq = s_irq_lvl;                                     
                end                                                               
                else                                                              
                begin:irq_level_low                                               
                                                                                  
                    always @ ( posedge S_AXI_ACLK )                               
                    begin                                                         
                        if ( S_AXI_ARESETN == 1'b0 || intr_ack_all == 1'b1)       
                        begin                                                     
                            s_irq_lvl <= 1'b1;                                    
                        end                                                       
                        else if (intr_all == 1'b1 && reg_global_intr_en[0] ==1'b1)
                        begin                                                     
                            s_irq_lvl <= 1'b0;                                    
                        end                                                       
                    end                                                           
                    assign s_irq = s_irq_lvl;                                     
                end                                                               
                                                                                  
            end                                                                   
                                                                                  
            else                                                                  
                                                                                  
            begin: gen_irq_edge                                                   
                                                                                  
                reg s_irq_lvl_ff;                                                 
                                                                                  
                if (C_IRQ_ACTIVE_STATE == 1)                                      
                    begin: irq_rising_edge                                        
                                                                                  
                    always @ ( posedge S_AXI_ACLK )                               
                        begin                                                     
                        if ( S_AXI_ARESETN == 1'b0 || intr_ack_all == 1'b1)       
                            begin                                                 
                            s_irq_lvl <= 1'b0;                                    
                            s_irq_lvl_ff <= 1'b0;                                 
                            end                                                   
                        else if (intr_all == 1'b1 && reg_global_intr_en[0] ==1'b1)
                            begin                                                 
                            s_irq_lvl <= 1'b1;                                    
                            s_irq_lvl_ff <= s_irq_lvl;                            
                            end                                                   
                        end                                                       
                                                                                  
                    assign s_irq =  s_irq_lvl && (!s_irq_lvl_ff);                 
                                                                                  
                    end                                                           
                else                                                              
                    begin:irq_falling_edge                                        
                                                                                  
                    always @ ( posedge S_AXI_ACLK )                               
                        begin                                                     
                        if ( S_AXI_ARESETN == 1'b0 || intr_ack_all == 1'b1 )      
                            begin                                                 
                            s_irq_lvl <= 1'b1;                                    
                            s_irq_lvl_ff <= 1'b1;                                 
                            end                                                   
                        else if (intr_all == 1'b1 && reg_global_intr_en[0] ==1'b1)
                            begin                                                 
                            s_irq_lvl <= 1'b0;                                    
                            s_irq_lvl_ff <= s_irq_lvl;                            
                            end                                                   
                    end                                                           
                                                                                  
                    assign s_irq =  !(s_irq_lvl_ff && (!s_irq_lvl));              
                                                                                  
                end                                                               
            end                                                                   
                                                                                  
            assign irq = s_irq;                                                   
                                                                                 
        end                                                                      
        endgenerate         
    .
    .
    .

    打包生成IP核,其用户接口如下:

      至此,IP核的生成就做完了。

      二、创建BD块

      首先将生成的IP核添加到IP Catalog里:

      添加IP模块

    对模块进行例化配置:

     本次开启了16个GPIO中断,中断的检测方式是高电平,触发方式也是高电平

     开启了32个GPIO输出,均初始化为高阻态

     先run block automation,勾选CPU中断控制器

     对时钟模块进行配置,注意这里默认的是差分时钟,根据需要,我选择了单端时钟

     在run connection automation的时候,注意复位信号。两个复位信号一个默认是高有效,一个低有效,如果你把这两个连到一个外部复位,需要使其复位电平保持一致。

     

     最后,CPU的测试少不了串口,当然,如果你只做仿真的话,就不用添加串口了,如果要上板,最好是把串口也放进来,下面是总图:

     

     分配一下地址,由于我在SDK里面建立了hello world工程,对CPU存储的要求略高,所以将两个mem都改成了256k。如果你建立的是空的工程并且不开启串口的话,估计使用默认的8KB存储空间也可以。

     

     保存BD块,validate一下,没有错误的话就generate output product并且创建wrapper,然后可以直接导出到SDK,并且打开SDK进行CPU开发,然后将生成的ELF文件关联到vivado里,到此,就可以使用CPU核FPGA联合仿真了。

    在BD块上右键关联elf文件,成功后会在design source里看到ELF文件。仿真如下:

    如果你要上板的话,需要在vivado中生成bit流,并将其导入到SDK里,使用SDK进行程序的烧录。

      SDK的主要代码如下:

     1 #include <stdio.h>
     2 #include "platform.h"
     3 #include "xil_printf.h"
     4 
     5 #include "xintc.h"
     6 //#include "intc_header.h"
     7 #include "AXI_USER_LOGIC_GPIO_OUT.H"
     8 #include "AXI_USER_LOGIC_GPIO_IN.H"
     9 
    10 #include "xintc_test.h"
    11 
    12 int user_intr_flag = 0;
    13 
    14 int main()
    15 {
    16     init_platform();
    17     IntcInit(INTC_DEVICE_ID);//中断初始化
    18     print("Hello World
    
    ");
    19 
    20 
    21     AXI_USER_LOGIC_GPIO_OUT_mWriteReg(XPAR_AXI_USER_LOGIC_GPIO_OUT_0_S00_AXI_BASEADDR, 4, 0xffff0000);//配置gpio_out低16位为输出
    22 
    23        int i;
    24        int intr_cnt=0;
    25        unsigned int intr_status;
    26 
    27        for (i=0; i>-1;i++)
    28        {
    29            AXI_USER_LOGIC_GPIO_OUT_mWriteReg(XPAR_AXI_USER_LOGIC_GPIO_OUT_0_S00_AXI_BASEADDR, AXI_USER_LOGIC_GPIO_OUT_S00_AXI_SLV_REG0_OFFSET, i);//循环向GPIO_OUT输出数据
    30            printf("reg_out:%x
    
    ",AXI_USER_LOGIC_GPIO_OUT_mReadReg(XPAR_AXI_USER_LOGIC_GPIO_OUT_0_S00_AXI_BASEADDR, AXI_USER_LOGIC_GPIO_OUT_S00_AXI_SLV_REG0_OFFSET));//反向读出GPIO PIN的状态
    31            intr_status = AXI_USER_LOGIC_GPIO_IN_mReadReg(INTR_BaseAddr,REG_INTR_STS);//查询GPIO IN中断的状态
    32            if(intr_status){
    33                printf("intr:%x,cnt:%d,intr_flag:%d
    
    ",intr_status,++intr_cnt,user_intr_flag);
    34                if(user_intr_flag){
    35                    AXI_USER_LOGIC_GPIO_IN_mWriteReg(INTR_BaseAddr,REG_INTR_ACK,intr_status);//清除中断
    36                    user_intr_flag = 0;
    37                }
    38 
    39            }
    40            int delay_cnt = 10000000;//1000000
    41            while(delay_cnt--);
    42        }
    43 
    44     cleanup_platform();
    45     return 0;
    46 }
    47 
    48 void IntcInit(u16 DeviceId)
    49 {
    50 
    51     AXI_USER_LOGIC_GPIO_IN_mWriteReg(INTR_BaseAddr, REG_Global_INTR_EN, 1);
    52     AXI_USER_LOGIC_GPIO_IN_mWriteReg(INTR_BaseAddr, REG_INTR_EN, 0xffffffff);
    53 
    54     XIntc_Initialize(&InterruptController, DeviceId);
    55 
    56     XIntc_Connect(&InterruptController, INTC_DEVICE_ID,
    57                        (XInterruptHandler)DeviceDriverHandler,
    58                        (void *)0);
    59     XIntc_Enable(&InterruptController, INTC_DEVICE_ID);
    60 
    61     microblaze_register_handler(XIntc_DeviceInterruptHandler, INTC_DEVICE_ID);
    62     microblaze_enable_interrupts();
    63     XIntc_Start(&InterruptController, XIN_REAL_MODE);
    64 
    65 //    XGpio_InterruptEnable(&InterruptController, 1);
    66 //    XGpio_InterruptGlobalEnable(&InterruptController);
    67 
    68     print("intr config done!
    
    ");
    69 
    70 }
    71 
    72 
    73 
    74 void DeviceDriverHandler(void *CallbackRef)
    75 {
    76     print("Entering interrup!
    
    ");
    77     user_intr_flag = 1;
    78 }

    上板后串口接收到的数据:

     

      

  • 相关阅读:
    ActiveSync合作关系对话框的配置
    WINCE对象存储区(object store)
    Wince 隐藏TASKBAR的方法
    Wince输入法换肤换语言机制
    poj 3080 Blue Jeans 解题报告
    codeforces A. Vasily the Bear and Triangle 解题报告
    hdu 1050 Moving Tables 解题报告
    hdu 1113 Word Amalgamation 解题报告
    codeforces A. IQ Test 解题报告
    poj 1007 DNA Sorting 解题报告
  • 原文地址:https://www.cnblogs.com/christsong/p/5996089.html
Copyright © 2011-2022 走看看