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 }

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

     

      

  • 相关阅读:
    单点登录
    公共的service接口
    springMvc入门--初识springMvc
    mybatis进阶--mapper输入映射和输出映射
    mybatis入门--mapper代理方式开发
    AJAX学习笔记——跨域
    AJAX学习笔记——jQuery中的AJAX
    AJAX学习笔记——JSON
    XAMPP启动Apache服务时启动失败
    Ajax的简单例子——PHP
  • 原文地址:https://www.cnblogs.com/christsong/p/5996089.html
Copyright © 2011-2022 走看看