蜻蜓点水之Microblaze-Custom-PLBIP及中断

[复制链接]
4970|10
 楼主| SuperX-man 发表于 2012-7-13 12:57 | 显示全部楼层 |阅读模式
本帖最后由 SuperX-man 于 2012-7-13 13:21 编辑

本实验完成了使用Microblaze-Custom-IP实现INTEL 8255A芯片的端口A模式2的功能。

输入功能:没有数据输入时LD0亮,输入数据按右下方BTNU、LD0灭,船形开关SW0-3数据读入T8255IP读寄存器。随后T8255IP向CPU发出中断请求,CPU响应中断并在3秒后读取读寄存器并解除中断,此时LD0重新点亮。
输出功能:有数据输出时,数据先到输出寄存器,此时数据时LD1亮,按右下方BTNL,寄存器数据输出LED4-7,此时LD1灭、并向CPU发出中断请求,CPU响应中断后清空中断请求(本实验数据在输入数据请求按下后即将数据输出,且仅输入输入4bit数据)。

这里首先介绍一下Microblaze的中断系统及软件操作,Microblaze内核支持外部中断响应,但仅支持一个外部中断,其中断入口地址为0x10。PC指针保存在R14寄存器内,中断控制位为MSR的IEbit(详细介绍请看附件中PDF手册)。
单中断操作比较简单,仅需用
  1. void myISR( ) __attribute__ ((interrupt_handler));
函数声明即可,然后添加用户代码即可
  1. void myISR( void )
  2. {
  3. }

未使用函数声明的编译代码

  1. void myISR( void );

  2. void myISR( void )
  3. {
  4. }

  5. Compiles to:

  6. myISR:
  7.   .frame r1,0,r15    # vars= 0, regs= 0, args= 0
  8.   .mask  0x00000000
  9. $LM2:
  10.   .stabn  68,0,55,$LM2-myISR
  11.   rtsd  r15,8  
  12.   nop    # Unfilled delay slot

  13.   .end  myISR

使用函数声明编译后的代码
  1. void myISR( void ) __attribute__ ((interrupt_handler));

  2. void myISR( void )
  3. {
  4. }

  5. myISR:
  6. _interrupt_handler:
  7. .frame r1,20,r15 # vars= 0, regs= 3, args= 0
  8. .mask 0x00060800
  9. addik r1,r1,-20
  10. swi r15,r1,0
  11. swi r11,r1,8
  12. swi r17,r1,12
  13. mfs r11,rmsr #mfs
  14. swi r18,r1,16
  15. swi r11,r1,4
  16. $LM2:
  17. .stabn 68,0,55,$LM2-myISR
  18. lwi r15,r1,0
  19. lwi r11,r1,4
  20. mts rmsr,r11 #mts
  21. lwi r11,r1,8
  22. lwi r17,r1,12
  23. lwi r18,r1,16
  24. rtid r14,0

  25. addik r1,r1,20

  26. .end _interrupt_handler
多中断则需要用到int_ctrl控制器,入下图所示,这个控制器管理所有外部中断,将多个外部中断合为一个,中断响应后通过判断此控制器内部寄存器以判断是哪个中断,下面是没有用到库函数时是例程

  1. // ISR Code****************************************************************
  2. #define   TIMER_INT  XPAR_TIMER_INTERRUPT_MASK
  3. #define   BTN_INT  XPAR_BUTTONS_IP2INTC_IRPT_MASK
  4. #define   SPI_INT  XPAR_SPI_IP2INTC_IRPT_MASK

  5. #define   INTC_IPR  (*((volatile unsigned long *)(XPAR_INT_CTRL_BASEADDR + 0x04)))
  6. #define   INTC_IER  (*((volatile unsigned long *)(XPAR_INT_CTRL_BASEADDR + 0x08)))
  7. #define   INTC_IAR  (*((volatile unsigned long *)(XPAR_INT_CTRL_BASEADDR + 0x0C)))
  8. #define   INTC_MER  (*((volatile unsigned long *)(XPAR_INT_CTRL_BASEADDR + 0x1C)))

  9. void myISR( void ) __attribute__ ((interrupt_handler));

  10. void myISR( void )
  11. {
  12.   if( INTC_IPR & TIMER_INT )    // Timer Interrupt Is Pending
  13.     timer_ISR();
  14.    
  15.   if( INTC_IPR & BTN_INT )    // Button interrupt is pending
  16.     button_ISR();
  17.    
  18.   if( INTC_IPR & SPI_INT )    // SPI interrupt is pending
  19.     spi_ISR();
  20.    
  21.   INTC_IAR = INTC_IPR;      // Acknowledge Interrupts
  22. }
  23. // *************************************************************************


  24. // Timer Specific Code *****************************************************
  25. #define  TCSR0   (*((volatile unsigned long *)(XPAR_TIMER_BASEADDR + 0x00)))
  26. #define   TLR0    (*((volatile unsigned long *)(XPAR_TIMER_BASEADDR + 0x04)))

  27. void timer_ISR( void )
  28. {
  29.   // Do Stuff Here
  30.   TCSR0 = TCSR0;   // Acknogledge Interrupt In Timer (Clear pending bit)
  31. }
  32. // *************************************************************************


  33. // GPIO (Connected to Buttons) Specific Code *******************************
  34. #define  BTNS    (*((volatile unsigned long *)(XPAR_BUTTONS_BASEADDR)))
  35. #define  BTN_OE  (*((volatile unsigned long *)(XPAR_BUTTONS_BASEADDR + 0x04)))
  36. #define  BTN_GIE  (*((volatile unsigned long *)(XPAR_BUTTONS_BASEADDR + 0x11C)))
  37. #define  BTN_IER  (*((volatile unsigned long *)(XPAR_BUTTONS_BASEADDR + 0x128)))
  38. #define  BTN_ISR  (*((volatile unsigned long *)(XPAR_BUTTONS_BASEADDR + 0x120)))

  39. void button_ISR( void )
  40. {
  41.   // Do Stuff Here
  42.   BTN_ISR = BTN_ISR;  // Clear any pending button interrupts
  43. }
  44. // *************************************************************************

  45. // SPI Specific Code *******************************************************
  46. #define  SPI_GIE  (*((volatile unsigned long *)(XPAR_SPI_BASEADDR+0x1C)))
  47. #define   SPI_ISR  (*((volatile unsigned long *)(XPAR_SPI_BASEADDR+0x20)))
  48. #define   SPI_IER  (*((volatile unsigned long *)(XPAR_SPI_BASEADDR+0x28)))

  49. void spi_ISR( void )
  50. {
  51.   // Do Stuff Here
  52.   SPI_ISR = SPI_ISR;  // Clear pending interrupts
  53. }
  54. // *************************************************************************

  55.   void main( void )
  56. {
  57.   // ...

  58. TCSR0 = 0x000007F6;    // Timer Load and Clear any Pending Ints
  59.   TCSR0 = 0x000007D6;    // Timer Clear Load Bit

  60. BTN_OE = ~0x00;    // Buttons are inputs
  61. BTN_IER = BTN_CHNL1;    // Enable Interrupts for all 3 buttons;
  62.   BTN_GIE = ~0x00;    // Enable Interrupts for Button GPIO

  63. SPI_IER = SPI_TxEMPTY;   // Enable Interrupt for empty  
  64. SPI_GIE = ~0x00000000;   // Global SPI Interrupt Enable

  65. // Enable Timer and Button Interrupt in IntC
  66. INTC_IER = TIMER_INT | BTN_INT | SPI_INT;   
  67.   INTC_MER = 0x03;    // Int Controller Master Enable
  68.   microblaze_enable_interrupts();

  69.   //...
  70. }


下面是使用库函数后的代码,要简洁的多,,此例程硬件有多个中断,使用int_ctrl控制器,但程序中仅声明一个中断

  1.   T8255_EnableInterrupt(XPAR_T8255_0_BASEADDR);
  2.    // Enable MicroBlaze Interrupts
  3.   microblaze_enable_interrupts();
  4.    
  5.   /* Register the t8255 interrupt handler in the vector table */
  6.   XIntc_RegisterHandler(XPAR_XPS_INTC_0_BASEADDR,
  7.                              XPAR_XPS_INTC_0_T8255_0_IP2INTC_IRPT_INTR,
  8.                              (XInterruptHandler) t8255_int_handler,
  9.                              (void *)XPAR_T8255_0_BASEADDR);

  10.   /* Start the interrupt controller */
  11.   XIntc_MasterEnable(XPAR_XPS_INTC_0_BASEADDR);
  12.   XIntc_EnableIntr(XPAR_XPS_INTC_0_BASEADDR, 0x1);


说完软件,来看看硬件中断是怎么连接及生成的。在生成用户IP时,勾选下面图片选项,第一个为使用中断,第二个为中断优先级编码控制,暂时用不上这么高级的玩意。中断数量为2。里面还有一个中断触发条件选项,有上升沿、下降沿、高低电平等。这里选上升沿触发。

生成PLB总线IP代码后,需要修改三个文件t8255_v2_1_0.mpduser_logic.v、t8255.vhd,分别在目录\lab_8255\atlys\pcores\t8255_v1_00_a\data、\lab_8255\atlys\pcores\t8255_v1_00_a\hdl下,由于本版比较熟悉Verilog,所以在生成用户代码时勾选了生成.V选项。
中断线连接很简单,在user_logic.v文件里,已经例化了中断线,数量为刚才选着的2,将这个信号和用户想要使用的触发中断信号连接起来即可。这里将他和外部按键项链,按键按下触发中断。代码如下
  1. output [0 : C_NUM_INTR-1] IP2Bus_IntrEvent;
  2. assign IP2Bus_IntrEvent[1] = ~wt_cnt;
  3. assign IP2Bus_IntrEvent[0] = rd_cnt;
读写寄存器代码如下,PLB总线读写信号高电平有效,用户需在PLB总线读写信号到来时,将数据输出或输入到总线上即可。代码如下

  1.   // --USER logic implementation added here
  2.     /***  read data logic  ***/
  3.     reg           rd_cnt;
  4.     reg    [3:0]  rd_reg1;
  5.    
  6.     always @(posedge Bus2IP_Clk or posedge Bus2IP_Reset) begin
  7.        if(Bus2IP_Reset)  begin
  8.            rd_cnt     <= 1'd0;
  9.            rd_reg1    <= 4'd0;
  10.        end else begin
  11.            if(key1[0] == 1'd1) begin
  12.               rd_cnt <= 1'd1;
  13.               rd_reg1    <= switch1;
  14.            end
  15.            if(Bus2IP_RdCE[0])   rd_cnt <= 1'd0;
  16.        end
  17.     end
  18.    
  19.     assign IP2Bus_IntrEvent[0]  = rd_cnt;
  20.     assign IP2Bus_Data   = {28'd0,rd_reg1};   
  21.     //assign   led1[7]    = ~rd_cnt;
  22.     /***  wite data logic  ***/
  23.     reg           wt_cnt;
  24.     reg    [3:0]  wt_reg1;
  25.     reg    [3:0]  led_reg;
  26.    
  27.     always @(posedge Bus2IP_Clk or posedge Bus2IP_Reset) begin
  28.        if(Bus2IP_Reset) begin
  29.            wt_cnt <= 1'd0;
  30.            wt_reg1    <= 4'd0;
  31.        end else begin
  32.            if(Bus2IP_WrCE[0]) begin
  33.               wt_cnt <= 1'd1;
  34.               wt_reg1    <= Bus2IP_Data[0:3];
  35.            end
  36.            if(key1[1] == 1'd1)begin
  37.               wt_cnt <= 1'd0;
  38.               led_reg    <= wt_reg1;
  39.            end
  40.        end
  41.     end
  42.    
  43.     assign IP2Bus_IntrEvent[1]  = ~wt_cnt;
  44.     assign led1 = {~rd_cnt,wt_cnt,2'd0,led_reg};

t8255_v2_1_0.mpd文件里更改需要添加进的端口,代码如下

  1. ## Ports
  2. PORT key1 = "",DIR = I,VEC = [0:1]
  3. PORT switch1 = "",DIR = I,VEC = [0:3]
  4. PORT led1 = "",DIR = O,VEC = [0:7]

t8255.vhd同样,这里就不贴出来了。修改完成后,双击左边T8255IP添加进MICROBLAZE内核,并将其接入mb_plb总线,连接网表,连接t8255中断至microblaze—interrupt,下拉key1,switch1,和led1的选项,选择make external将IP端口引出,引出网标如下。分配t8255地址,至此整个工程结束图片如下:



附件lab5为基于Spartan3e的EDK13.2工程,为多中断系统使用中断控制器例子。
Atyls_success为基于Atyls的EDK13.2工程,为单中断系统不使用中断控制器例子。
Lab_source为程序原文件。

本帖子中包含更多资源

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

×
5509 发表于 2012-7-13 12:59 | 显示全部楼层
支持超版的大作,等待完善
jakfens 发表于 2012-7-13 15:17 | 显示全部楼层
jakfens 发表于 2012-7-13 15:18 | 显示全部楼层
还是verilog的好 vhdl看不懂
GoldSunMonkey 发表于 2012-7-13 15:21 | 显示全部楼层
GoldSunMonkey 发表于 2012-7-13 15:21 | 显示全部楼层
超版厉害啊
gaochy1126 发表于 2012-7-13 16:09 | 显示全部楼层
看的有点乱啦!
 楼主| SuperX-man 发表于 2012-7-14 22:00 | 显示全部楼层
7# gaochy1126

东西太多了,抱歉啊,就挑了几个重点写了下,有什么疑问可以提出来?
GoldSunMonkey 发表于 2012-7-15 17:32 | 显示全部楼层
7# gaochy1126  

东西太多了,抱歉啊,就挑了几个重点写了下,有什么疑问可以提出来?
SuperX-man 发表于 2012-7-14 22:00
其实我觉得很不错
JD21IC 发表于 2012-10-15 14:20 | 显示全部楼层
请问,如果是定时器中断,2个GPIO输入中断,那么是不是进入void myISR( void )。先要判断是否是GPIO中断,再去读GPIO口。判断是哪个引脚的中断?
GoldSunMonkey 发表于 2012-10-15 21:36 | 显示全部楼层
等超版回复你吧。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

12

主题

925

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部