- void myISR( void );
- void myISR( void )
- {
- }
- Compiles to:
- myISR:
- .frame r1,0,r15 # vars= 0, regs= 0, args= 0
- .mask 0x00000000
- $LM2:
- .stabn 68,0,55,$LM2-myISR
- rtsd r15,8
- nop # Unfilled delay slot
- .end myISR
使用函数声明编译后的代码- void myISR( void ) __attribute__ ((interrupt_handler));
- void myISR( void )
- {
- }
- myISR:
- _interrupt_handler:
- .frame r1,20,r15 # vars= 0, regs= 3, args= 0
- .mask 0x00060800
- addik r1,r1,-20
- swi r15,r1,0
- swi r11,r1,8
- swi r17,r1,12
- mfs r11,rmsr #mfs
- swi r18,r1,16
- swi r11,r1,4
- $LM2:
- .stabn 68,0,55,$LM2-myISR
- lwi r15,r1,0
- lwi r11,r1,4
- mts rmsr,r11 #mts
- lwi r11,r1,8
- lwi r17,r1,12
- lwi r18,r1,16
- rtid r14,0
- addik r1,r1,20
- .end _interrupt_handler
多中断则需要用到int_ctrl控制器,入下图所示,这个控制器管理所有外部中断,将多个外部中断合为一个,中断响应后通过判断此控制器内部寄存器以判断是哪个中断,下面是没有用到库函数时是例程
- // ISR Code****************************************************************
- #define TIMER_INT XPAR_TIMER_INTERRUPT_MASK
- #define BTN_INT XPAR_BUTTONS_IP2INTC_IRPT_MASK
- #define SPI_INT XPAR_SPI_IP2INTC_IRPT_MASK
- #define INTC_IPR (*((volatile unsigned long *)(XPAR_INT_CTRL_BASEADDR + 0x04)))
- #define INTC_IER (*((volatile unsigned long *)(XPAR_INT_CTRL_BASEADDR + 0x08)))
- #define INTC_IAR (*((volatile unsigned long *)(XPAR_INT_CTRL_BASEADDR + 0x0C)))
- #define INTC_MER (*((volatile unsigned long *)(XPAR_INT_CTRL_BASEADDR + 0x1C)))
- void myISR( void ) __attribute__ ((interrupt_handler));
- void myISR( void )
- {
- if( INTC_IPR & TIMER_INT ) // Timer Interrupt Is Pending
- timer_ISR();
-
- if( INTC_IPR & BTN_INT ) // Button interrupt is pending
- button_ISR();
-
- if( INTC_IPR & SPI_INT ) // SPI interrupt is pending
- spi_ISR();
-
- INTC_IAR = INTC_IPR; // Acknowledge Interrupts
- }
- // *************************************************************************
- // Timer Specific Code *****************************************************
- #define TCSR0 (*((volatile unsigned long *)(XPAR_TIMER_BASEADDR + 0x00)))
- #define TLR0 (*((volatile unsigned long *)(XPAR_TIMER_BASEADDR + 0x04)))
- void timer_ISR( void )
- {
- // Do Stuff Here
- TCSR0 = TCSR0; // Acknogledge Interrupt In Timer (Clear pending bit)
- }
- // *************************************************************************
- // GPIO (Connected to Buttons) Specific Code *******************************
- #define BTNS (*((volatile unsigned long *)(XPAR_BUTTONS_BASEADDR)))
- #define BTN_OE (*((volatile unsigned long *)(XPAR_BUTTONS_BASEADDR + 0x04)))
- #define BTN_GIE (*((volatile unsigned long *)(XPAR_BUTTONS_BASEADDR + 0x11C)))
- #define BTN_IER (*((volatile unsigned long *)(XPAR_BUTTONS_BASEADDR + 0x128)))
- #define BTN_ISR (*((volatile unsigned long *)(XPAR_BUTTONS_BASEADDR + 0x120)))
- void button_ISR( void )
- {
- // Do Stuff Here
- BTN_ISR = BTN_ISR; // Clear any pending button interrupts
- }
- // *************************************************************************
- // SPI Specific Code *******************************************************
- #define SPI_GIE (*((volatile unsigned long *)(XPAR_SPI_BASEADDR+0x1C)))
- #define SPI_ISR (*((volatile unsigned long *)(XPAR_SPI_BASEADDR+0x20)))
- #define SPI_IER (*((volatile unsigned long *)(XPAR_SPI_BASEADDR+0x28)))
- void spi_ISR( void )
- {
- // Do Stuff Here
- SPI_ISR = SPI_ISR; // Clear pending interrupts
- }
- // *************************************************************************
- void main( void )
- {
- // ...
- TCSR0 = 0x000007F6; // Timer Load and Clear any Pending Ints
- TCSR0 = 0x000007D6; // Timer Clear Load Bit
- BTN_OE = ~0x00; // Buttons are inputs
- BTN_IER = BTN_CHNL1; // Enable Interrupts for all 3 buttons;
- BTN_GIE = ~0x00; // Enable Interrupts for Button GPIO
- SPI_IER = SPI_TxEMPTY; // Enable Interrupt for empty
- SPI_GIE = ~0x00000000; // Global SPI Interrupt Enable
- // Enable Timer and Button Interrupt in IntC
- INTC_IER = TIMER_INT | BTN_INT | SPI_INT;
- INTC_MER = 0x03; // Int Controller Master Enable
- microblaze_enable_interrupts();
- //...
- }
下面是使用库函数后的代码,要简洁的多,,此例程硬件有多个中断,使用int_ctrl控制器,但程序中仅声明一个中断
-
- T8255_EnableInterrupt(XPAR_T8255_0_BASEADDR);
- // Enable MicroBlaze Interrupts
- microblaze_enable_interrupts();
-
- /* Register the t8255 interrupt handler in the vector table */
- XIntc_RegisterHandler(XPAR_XPS_INTC_0_BASEADDR,
- XPAR_XPS_INTC_0_T8255_0_IP2INTC_IRPT_INTR,
- (XInterruptHandler) t8255_int_handler,
- (void *)XPAR_T8255_0_BASEADDR);
- /* Start the interrupt controller */
- XIntc_MasterEnable(XPAR_XPS_INTC_0_BASEADDR);
- XIntc_EnableIntr(XPAR_XPS_INTC_0_BASEADDR, 0x1);
说完软件,来看看硬件中断是怎么连接及生成的。在生成用户IP时,勾选下面图片选项,第一个为使用中断,第二个为中断优先级编码控制,暂时用不上这么高级的玩意。中断数量为2。里面还有一个中断触发条件选项,有上升沿、下降沿、高低电平等。这里选上升沿触发。
生成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,将这个信号和用户想要使用的触发中断信号连接起来即可。这里将他和外部按键项链,按键按下触发中断。代码如下- output [0 : C_NUM_INTR-1] IP2Bus_IntrEvent;
- assign IP2Bus_IntrEvent[1] = ~wt_cnt;
- assign IP2Bus_IntrEvent[0] = rd_cnt;
读写寄存器代码如下,PLB总线读写信号高电平有效,用户需在PLB总线读写信号到来时,将数据输出或输入到总线上即可。代码如下
-
- // --USER logic implementation added here
- /*** read data logic ***/
- reg rd_cnt;
- reg [3:0] rd_reg1;
-
- always @(posedge Bus2IP_Clk or posedge Bus2IP_Reset) begin
- if(Bus2IP_Reset) begin
- rd_cnt <= 1'd0;
- rd_reg1 <= 4'd0;
- end else begin
- if(key1[0] == 1'd1) begin
- rd_cnt <= 1'd1;
- rd_reg1 <= switch1;
- end
- if(Bus2IP_RdCE[0]) rd_cnt <= 1'd0;
- end
- end
-
- assign IP2Bus_IntrEvent[0] = rd_cnt;
- assign IP2Bus_Data = {28'd0,rd_reg1};
- //assign led1[7] = ~rd_cnt;
- /*** wite data logic ***/
- reg wt_cnt;
- reg [3:0] wt_reg1;
- reg [3:0] led_reg;
-
- always @(posedge Bus2IP_Clk or posedge Bus2IP_Reset) begin
- if(Bus2IP_Reset) begin
- wt_cnt <= 1'd0;
- wt_reg1 <= 4'd0;
- end else begin
- if(Bus2IP_WrCE[0]) begin
- wt_cnt <= 1'd1;
- wt_reg1 <= Bus2IP_Data[0:3];
- end
- if(key1[1] == 1'd1)begin
- wt_cnt <= 1'd0;
- led_reg <= wt_reg1;
- end
- end
- end
-
- assign IP2Bus_IntrEvent[1] = ~wt_cnt;
- assign led1 = {~rd_cnt,wt_cnt,2'd0,led_reg};
t8255_v2_1_0.mpd文件里更改需要添加进的端口,代码如下
-
- ## Ports
- PORT key1 = "",DIR = I,VEC = [0:1]
- PORT switch1 = "",DIR = I,VEC = [0:3]
- 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为程序原文件。