版主: 51FPGA

分享到:
共1条 1/1 1   

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

    [您是本帖的第1284位阅读者]
xilinx论坛
我是MM
高级会员

最后登陆时间:2015-03-19 10:58:07

直达楼层
1# 发表于 2015-03-30 22:00:00

本实验完成了使用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.  
  3. void myISR( void ) 
  4. { 
  5. } 
  6.  
  7. Compiles to: 
  8.  
  9. myISR: 
  10.   .frame r1,0,r15    # vars= 0, regs= 0, args= 0 
  11.   .mask  0x00000000 
  12. $LM2: 
  13.   .stabn  68,0,55,$LM2-myISR 
  14.   rtsd  r15,8  
  15.   nop    # Unfilled delay slot 
  16.  
  17.   .end  myISR

复制代码


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

  1. void myISR( void ) __attribute__ ((interrupt_handler)); 
  2.  
  3. void myISR( void ) 
  4. { 
  5. } 
  6.  
  7. myISR: 
  8. _interrupt_handler: 
  9. .frame r1,20,r15 # vars= 0, regs= 3, args= 0 
  10. .mask 0x00060800 
  11. addik r1,r1,-20 
  12. swi r15,r1,0 
  13. swi r11,r1,8 
  14. swi r17,r1,12 
  15. mfs r11,rmsr #mfs 
  16. swi r18,r1,16 
  17. swi r11,r1,4 
  18. $LM2: 
  19. .stabn 68,0,55,$LM2-myISR 
  20. lwi r15,r1,0 
  21. lwi r11,r1,4 
  22. mts rmsr,r11 #mts 
  23. lwi r11,r1,8 
  24. lwi r17,r1,12 
  25. lwi r18,r1,16 
  26. rtid r14,0 
  27.  
  28. addik r1,r1,20 
  29.  
  30. .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.  
  6. #define   INTC_IPR  (*((volatile unsigned long *)(XPAR_INT_CTRL_BASEADDR + 0x04))) 
  7. #define   INTC_IER  (*((volatile unsigned long *)(XPAR_INT_CTRL_BASEADDR + 0x08))) 
  8. #define   INTC_IAR  (*((volatile unsigned long *)(XPAR_INT_CTRL_BASEADDR + 0x0C))) 
  9. #define   INTC_MER  (*((volatile unsigned long *)(XPAR_INT_CTRL_BASEADDR + 0x1C))) 
  10.  
  11. void myISR( void ) __attribute__ ((interrupt_handler)); 
  12.  
  13. void myISR( void ) 
  14. { 
  15.   if( INTC_IPR & TIMER_INT )    // Timer Interrupt Is Pending 
  16.     timer_ISR(); 
  17.    
  18.   if( INTC_IPR & BTN_INT )    // Button interrupt is pending 
  19.     button_ISR(); 
  20.    
  21.   if( INTC_IPR & SPI_INT )    // SPI interrupt is pending 
  22.     spi_ISR(); 
  23.    
  24.   INTC_IAR = INTC_IPR;      // Acknowledge Interrupts 
  25. } 
  26. // ************************************************************************* 
  27.  
  28.  
  29. // Timer Specific Code ***************************************************** 
  30. #define  TCSR0   (*((volatile unsigned long *)(XPAR_TIMER_BASEADDR + 0x00))) 
  31. #define   TLR0    (*((volatile unsigned long *)(XPAR_TIMER_BASEADDR + 0x04))) 
  32.  
  33. void timer_ISR( void ) 
  34. { 
  35.   // Do Stuff Here 
  36.   TCSR0 = TCSR0;   // Acknogledge Interrupt In Timer (Clear pending bit) 
  37. } 
  38. // ************************************************************************* 
  39.  
  40.  
  41. // GPIO (Connected to Buttons) Specific Code ******************************* 
  42. #define  BTNS    (*((volatile unsigned long *)(XPAR_BUTTONS_BASEADDR))) 
  43. #define  BTN_OE  (*((volatile unsigned long *)(XPAR_BUTTONS_BASEADDR + 0x04))) 
  44. #define  BTN_GIE  (*((volatile unsigned long *)(XPAR_BUTTONS_BASEADDR + 0x11C))) 
  45. #define  BTN_IER  (*((volatile unsigned long *)(XPAR_BUTTONS_BASEADDR + 0x128))) 
  46. #define  BTN_ISR  (*((volatile unsigned long *)(XPAR_BUTTONS_BASEADDR + 0x120))) 
  47.  
  48. void button_ISR( void ) 
  49. { 
  50.   // Do Stuff Here 
  51.   BTN_ISR = BTN_ISR;  // Clear any pending button interrupts 
  52. } 
  53. // ************************************************************************* 
  54.  
  55. // SPI Specific Code ******************************************************* 
  56. #define  SPI_GIE  (*((volatile unsigned long *)(XPAR_SPI_BASEADDR+0x1C))) 
  57. #define   SPI_ISR  (*((volatile unsigned long *)(XPAR_SPI_BASEADDR+0x20))) 
  58. #define   SPI_IER  (*((volatile unsigned long *)(XPAR_SPI_BASEADDR+0x28))) 
  59.  
  60. void spi_ISR( void ) 
  61. { 
  62.   // Do Stuff Here 
  63.   SPI_ISR = SPI_ISR;  // Clear pending interrupts 
  64. } 
  65. // ************************************************************************* 
  66.  
  67.   void main( void ) 
  68. { 
  69.   // ... 
  70.  
  71. TCSR0 = 0x000007F6;    // Timer Load and Clear any Pending Ints 
  72.   TCSR0 = 0x000007D6;    // Timer Clear Load Bit 
  73.  
  74. BTN_OE = ~0x00;    // Buttons are inputs 
  75. BTN_IER = BTN_CHNL1;    // Enable Interrupts for all 3 buttons; 
  76.   BTN_GIE = ~0x00;    // Enable Interrupts for Button GPIO 
  77.  
  78. SPI_IER = SPI_TxEMPTY;   // Enable Interrupt for empty  
  79. SPI_GIE = ~0x00000000;   // Global SPI Interrupt Enable 
  80.  
  81. // Enable Timer and Button Interrupt in IntC 
  82. INTC_IER = TIMER_INT | BTN_INT | SPI_INT;   
  83.   INTC_MER = 0x03;    // Int Controller Master Enable 
  84.   microblaze_enable_interrupts(); 
  85.  
  86.   //... 
  87. }

复制代码



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

  1.  
  2.   T8255_EnableInterrupt(XPAR_T8255_0_BASEADDR);
  3.    // Enable MicroBlaze Interrupts
  4.   microblaze_enable_interrupts();
  5.    
  6.   /* Register the t8255 interrupt handler in the vector table */
  7.   XIntc_RegisterHandler(XPAR_XPS_INTC_0_BASEADDR,
  8.                              XPAR_XPS_INTC_0_T8255_0_IP2INTC_IRPT_INTR,
  9.                              (XInterruptHandler) t8255_int_handler,
  10.                              (void *)XPAR_T8255_0_BASEADDR);
  11.  
  12.   /* Start the interrupt controller */
  13.   XIntc_MasterEnable(XPAR_XPS_INTC_0_BASEADDR);
  14.   XIntc_EnableIntr(XPAR_XPS_INTC_0_BASEADDR, 0x1);

复制代码


说完软件,来看看硬件中断是怎么连接及生成的。在生成用户IP时,勾选下面图片选项,第一个为使用中断,第二个为中断优先级编码控制,暂时用不上这么高级的玩意。中断数量为2。里面还有一个中断触发条件选项,有上升沿、下降沿、高低电平等。这里选上升沿触发。
<ignore_js_op> 
生成PLB总线IP代码后,需要修改三个文件t8255_v2_1_0.mpd、user_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.  
  2.   // --USER logic implementation added here
  3.     /***  read data logic  ***/
  4.     reg           rd_cnt;
  5.     reg    [3:0]  rd_reg1;
  6.     
  7.     always @(posedge Bus2IP_Clk or posedge Bus2IP_Reset) begin
  8.        if(Bus2IP_Reset)  begin
  9.            rd_cnt     <= 1'd0;
  10.            rd_reg1    <= 4'd0;
  11.        end else begin
  12.            if(key1[0] == 1'd1) begin
  13.               rd_cnt <= 1'd1;
  14.               rd_reg1    <= switch1;
  15.            end 
  16.            if(Bus2IP_RdCE[0])   rd_cnt <= 1'd0;
  17.        end
  18.     end
  19.     
  20.     assign IP2Bus_IntrEvent[0]  = rd_cnt;
  21.     assign IP2Bus_Data   = {28'd0,rd_reg1};   
  22.     //assign   led1[7]    = ~rd_cnt;
  23.     /***  wite data logic  ***/
  24.     reg           wt_cnt;
  25.     reg    [3:0]  wt_reg1;
  26.     reg    [3:0]  led_reg;
  27.     
  28.     always @(posedge Bus2IP_Clk or posedge Bus2IP_Reset) begin
  29.        if(Bus2IP_Reset) begin
  30.            wt_cnt <= 1'd0;
  31.            wt_reg1    <= 4'd0;
  32.        end else begin
  33.            if(Bus2IP_WrCE[0]) begin
  34.               wt_cnt <= 1'd1;
  35.               wt_reg1    <= Bus2IP_Data[0:3];
  36.            end
  37.            if(key1[1] == 1'd1)begin
  38.               wt_cnt <= 1'd0;
  39.               led_reg    <= wt_reg1;
  40.            end
  41.        end
  42.     end
  43.     
  44.     assign IP2Bus_IntrEvent[1]  = ~wt_cnt;
  45.     assign led1 = {~rd_cnt,wt_cnt,2'd0,led_reg};

复制代码

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

  1.  
  2. ## Ports
  3. PORT key1 = "",DIR = I,VEC = [0:1]
  4. PORT switch1 = "",DIR = I,VEC = [0:3]
  5. 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
为程序原文件。

 

 

共1条 1/1 1   
快速回复主题
  • 匿名不能发帖!请先 [ 登陆 注册 ]