在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 }

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

 

  

原文地址:https://www.cnblogs.com/christsong/p/5996089.html