博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在xilinxFPGA上使用microblaze及自写GPIO中断
阅读量:5825 次
发布时间:2019-06-18

本文共 29134 字,大约阅读时间需要 97 分钟。

 

 

  很久很久没有更新过博客了,今天来扒一扒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

生成的电路如图所示:

 

打包生成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 
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\n\r");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\n\r",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\n\r",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;//100000041 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!\n\r");69 70 }71 72 73 74 void DeviceDriverHandler(void *CallbackRef)75 {76 print("Entering interrup!\n\r");77 user_intr_flag = 1;78 }

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

 

 

  

posted on
2016-10-25 11:34 阅读(
...) 评论(
...)

转载于:https://www.cnblogs.com/christsong/p/5996089.html

你可能感兴趣的文章
MySQL增量订阅&消费组件Canal POC
查看>>
Sqlite多线程
查看>>
数据结构-时间复杂度
查看>>
对象与字符串相互转换
查看>>
[NOIp2017提高组]小凯的疑惑
查看>>
《C程序设计语言》练习1-5
查看>>
$\frac{dy}{dx}$ 是什么意思?
查看>>
Go开发之路(目录)
查看>>
RHEL6.5安装成功ORACLE11GR2之后,编写PROC程序出错解决方法
查看>>
(50)与magento集成
查看>>
Ubuntu设置python3为默认版本
查看>>
JsonCpp 的使用
查看>>
问题账户需求分析
查看>>
JavaSE-代码块
查看>>
爬取所有校园新闻
查看>>
32、SpringBoot-整合Dubbo
查看>>
python面向对象基础
查看>>
HDU 2044 一只小蜜蜂(递归)
查看>>
docker 下 安装rancher 笔记
查看>>
spring两大核心对象IOC和AOP(新手理解)
查看>>