Menu Close

SOC系列教程 SOC编程之中断编程

通过前面的学习,我们了解了Zynq系列SOC通过AXI总线实现FPGA和ARM之间的通信,本文我们将重点介绍Zynq控制的中断体系。

1. ARM中断体系

中断主要说明几个知识点:

(1)ARM体系中,在存储地址的低位,固化了一个32字节的硬件中断向量表。

(2)异常中断发生时,程序计数器PC所指的位置不同,异常中断就不同。中断结束后,中断不同,返回地址也不同。但是,对于系统复位中断,不需要返回,因为整个应用系统就是从复位中断中开始的。

(3)SWI, IRQ, FIQ 是常用的中断

(4)每一个中断又会涉及中断优先级,中断使能,中断悬挂,中断目标(CPU0/CPU1)等,涉及的众多的寄存器使用,因此在整个学习过程中是内容最多也是最复杂的一章内容,因此我们会由浅入深,分章节逐步介绍,一些特别复杂不容易掌握的部分,我们会在后续讲解

%title插图%num

未定义的指令(Undef):ARM或协处理器认为当前指令未定义。

软件中断 (SWI) :系统定义的中断,一般由操作系统控制,当然也可以用户自定义。

数据访问终止(Data abort):数据访问的地址不存在,或者当前地址不允许访问。

外部中断请求(IRQ):外部引脚的中断请求(IRQ),多数外设使用该中断。

快速中断请求(FIQ):外部引脚的快速中断请求,比外部中断请求等级高,但是一般外设的中断请求使用外部中断请求。

指令预取终止(Instruction abort):预取指令的地址不存在,或者当前地址不允许访问。指令读取出错中断。

https://img-blog.csdn.net/20161113102115079

上图关键字:

PPI— Private Peripheral Interrupt

IOP— IO peripheral

SGI— Software Generated Interrupt

GIC— Generic Interrupt Controller

SPI — Shared Peripheral Interrupt

ICC— Interrupt Controller CPU

ICD —Interrupt Controller Distributor

APU—Accelerated Processing Unit

LSPI—locked SPI

SCU—Snoop Control Unit

ACP—Accelerator Coherency Port

WFI—Wait for Interrupt

WFE—Wait for Event

https://img-blog.csdn.net/20140303102233718?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveHp5aXZlcnNvbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast

  • PPI: Private Peripheral Interrupts 包含

(1)CPU(0,1) Timer 和AWDT

(2)PL FIQ , IRQ ;

(3)System Watchdog timer

总计5个中断源 27-31

  • SGI:SGI Distributor 将SGI分配到不同的ICC上 ,16个中断源 0-15
  • ICC:将PPI,SWT, SGI 分配到nIRQ或nFIQ上
  • SPI : Shared Peripheral Interrupt包含

IOP(44个中断源),和16个PL Fabric (16个中断源)

  • ICD : 将SPI分配到nIRQ 和nFIQ上,多路选择器将来自ICD,ICC中断选择后,分配给CPU0 与 CPU1的nIRQ或nFIQ上
  • GIC:GIC包含 SGI Distributor, CPU0 Distributor(ICC) , CPU1 Distributor (ICC)以及ICD(Interrupt ControllerDistributor)

中断过程描述:

    1. 首先在主程序中发生IRQ中断请求,程序跳到中断向量表找IRQ中断对应的解析程序地址,然后再跳到中断解析程序,进而执行中断程序。
    2. 中断程序执行完成后,按照相反的步骤返回主程序的下一条指令继续执行。
    3. 在进入异常中断处理程序时,要保存被中断程序的执行线程。从中断处理程序退出时要恢复被中断程序的执行现场。

https://img-blog.csdnimg.cn/20181030134056110.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3YxMzkxMA==,size_16,color_FFFFFF,t_70

Zynq的中断类型及终端号分配:

    1. 软件中断SGI(Software Generated Interrupt, SGI,中断号0-15)(16–26 reserved)
    2. 私有外设中断PPI(Private Peripheral Interrupt, PPI,中断号27-31).
    3. 共享外设中断SPI(Shared Peripheral Interrupt, SPI,中断号32-95).
    4. 私有外设中断(PPI):每个CPU都有一组PPI,包括全局定时器,私有看门狗定时器,私有定时器和来自PL的FIQ/IRQ.
    5. 软件中断(SGI)被路由到一个或者两个CPU上,SGI通过写ICDSGIR寄存器产生的.
    6. 共享外设中断(SPI)由PS和PL上的各种I/O控制器和存储器控制器产生.
    7. 通用中断控制器(GIC)是核心资源,用于集中管理从PS和PL产生的中断信号的资源集合控制器,可以使能、关使能、屏蔽中断源和改变中断源的优先级,并且会将中断送到对应的CPU中,CPU通过私有总线访问这些寄存器。
    8. 中断控制器(ICC,Interrupt Controller CPU)和中断控制分配器(ICD, Interrupt Controller Distributor)是GIC寄存器子集。
    9. 外部中断请求(IRQ)、快速中断请求(FIQ)
    10. 中断寄存器概述:

https://img-blog.csdn.net/20161113102128468

https://img-blog.csdn.net/20161113102140625

%title插图%num

    1. 中断分配器(ICD寄存器):

1) ICDDCR: (0xF8F01000) ICD分配控制寄存器,控制开启或者关闭中断配置。

2) ICDICFR: ICD配置寄存器。配置中断触发模式(高低电平或边沿),共6个寄存器,分别是ICDICFR 0-ICDICFR5

(0xF8F01C00-0xF8F01C14),每个寄存器32位,占4个字节,每个寄存器的位意义不一样,每2位代表一个中断,32位x6/2=96,正好包括所有中断。

3) ICDIPR:(0xF8F01400-0xF8F0145C)ICD中断优先级寄存器,共24个寄存器,ICDIPR 0- ICDIPR 23,每个寄存器32位,占4个字节,每8位代表一个中断,32位x24/8=96,正好包括所有中断。

4) ICDIPTR: (0xF8F01800-0xF8F0185C)ICD CPU目标选择寄存器,配置CPU目标选择(cpu0/cpu1),包括24个寄存器,ICDIPTR 0- ICDIPTR 23,每个寄存器32位,占4个字节,每8位代表一个中断,32位x24/8=96,正好包括所有中断。

5) ICDICER: 中断使能清除寄存器,**3个寄存器,ICDICER 0- ICDICER 2(0xF8F01180-0xF8F01888),每个寄存器32位,占4个字节,每位代表一个中断,32位x3=96,正好包括所有中断。写1表示不使能(屏蔽)。

6) ICDISER: 中断使能寄存器,3个寄存器,ICDISER 0- ICDISER 2(0xF8F01100-0xF8F01108),每个寄存器32位,占4个字节,每位代表一个中断,32位x3=96,正好包括所有中断。写1表示使能。

7) ICDICPR: 清除中断等待寄存器。3个寄存器,ICDICPR 0- ICDICPR 2(0xF8F01280-0xF8F01288),每个寄存器32位,占4个字节,每位代表一个中断,32位x3=96,正好包括所有中断。写1表示清除中断等待状态。

ICDICFR: 中断模式配置寄存器(interrupt sensitivity type),配置寄存器为电平中断还是边沿中断,一个中断需2个bit。

SGI: The bits are read-only and a bit-pair always reads as b10.

PPI: The bits are read-only

PPI[1] and [4]:b01: interrupt is active LOW level sensitive.

PPI[0], [2],and[3]:b11:interrupt is rising-edge sensitive.

SPI : The LSB bit of a bit-pair is read-only and is always b1. You can program the MSB bit of the bit-pair to alter the triggering sensitivity as follows:

B01:interrupt is active HIGH level sensitive

B11:interrupt is rising-edge sensitive.

There are 31 LSPIs, interrupts 32-62. You can configure and then lock these interrupts against further change using CFGSDISABLE. The LSPIs are present only if the SPIs are present.

寄存器 地址 中断号  中断类型
ICDICFR0 0xF8F01C00 #0-#15  SGI
ICDICFR1 0xF8F01C04 #27-#31(16-26保留)  PPI
ICDICFR2 0xF8F01C08 #32-#47(36保留) SPI
ICDICFR3 0xF8F01C0C #48-#63  SPI
ICDICFR4 0xF8F01C10 #64-#79  SPI
ICDICFR5 0xF8F01C14 #80-#95(93/94/95保留) SPI

私有中断(PPI)

    1. 包含:全局定时器,私有看门狗定时器,私有定时器以及来自PL的FIQ/IRQ。详细请参考官方手册ug585_Zynq_7000_TRM.pdf。
    2. ZYNQ每个CPU链接5个私有外设中断,所有中断的触发类型都是固定不变的。并且来自PL的快速中断信号FIQ和中断信号IRQ经反向后送到中断控制器,因此尽管在ICDICFR1寄存器内反应的他们是低电平触发,但是PS-PL接口中为高电平触发。如图所示:

https://img-blog.csdn.net/20160306105038514

私有定时器

Zynq中每个ARM core都有自己的私有定时器,私有定时器的工作频率为CPU的一半,比如ARM的工作频率为666MHZ,则私有定时器的频率为333MHz.
私有定时器的特性如下:
(1)32为计数器,达到零时产生一个中断
(2)8位预分频计数器,可以更好的控制中断周期
(3)可配置一次性或者自动重加载模式
(4)定时器时间可以通过下式计算:
定时时间 = [(预分频器的值 + 1) X(加载值 + 1)]/定时器频率

中断设计举例:

设计一个定时器中断,每1秒中断触发一次。

每次中断:

(1)依次设置读开关量,并通过串口输出。

(2)设置LED灯输出为0x55。

(3)设置LED灯输出0xAA。

(4)设置LED灯全亮。

(5)LED灯全灭。

硬件设计:由于本次中断使用只是ARM内部的定时器中断,不涉及其它硬件电路,因此只需要继续使用第四章的AXI设计。

软件设计:

头文件

#include <stdio.h>

#include “xil_types.h”

#include “Xscugic.h” //scugic,是GIC对应的库函数的头文件

#include “Xil_exception.h” //Xilinx SOC 对应的通用的中断处理函数,也就CPU直接定义根中断系统的处理,更多涉及到底层的汇编语言来处理中断 //使能,中断屏蔽等,

#include “xscutimer.h” //定时器对应的库函数,包含了对定时器的操作

(1)宏及变量的定义

#define TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID //timer ID

#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID //SCUGIC ID

#define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR

#define TIMER_LOAD_VALUE 0x13D92D3F //1s 产生一次中断Timer的值

static int time_intr_flag=0; //定义中断标志,用来与主程序交互

static XScuGic Intc; //GIC结构体

static XScuTimer Timer; //timer 结构体

(2)中断函数的声明

static void SetupInterruptSystem(XScuGic *GicInstancePtr,XScuTimer *TimerInstancePtr, u16 TimerIntrId); //建立GIC, Timer,TimerIntrID之间的联系

static void TimerIntrHandler(void *CallBackRef); //中断处理函数

(3)举例:

int main() // 函数

(1)变量定义:

unsigned char y=0;

int sec=0;

time_intr_flag=0;

XScuTimer_Config *TMRConfigPtr; //timer config

(2)中断函数初始化

TMRConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);

XScuTimer_CfgInitialize(&Timer, TMRConfigPtr, TMRConfigPtr->BaseAddr);

// 加载计数周期,私有定时器的时钟为CPU的一半,为333MHZ,如果计数1S,加载值为1sx(333x1000x1000)(1/s)-1=0x13D92D3F

XScuTimer_LoadTimer(&Timer, TIMER_LOAD_VALUE); //自动装载

XScuTimer_EnableAutoReload(&Timer); //自动装载

XScuTimer_Start(&Timer); //启动定时器

SetupInterruptSystem(&Intc, &Timer, TIMER_IRPT_INTR); //connect GIC,Timer,

//Main中的主循环:

while(1){

while(time_intr_flag)

{

time_intr_flag=0;

if( sec==4)

sec=0;

else

sec++;

switch (sec){

case 0: { Xil_Out8(XPAR_AXI4_TEST_0_SW_LED_AXI_BASEADDR+4*1,0×00);

y=(u8)Xil_In8(XPAR_AXI4_TEST_0_SW_LED_AXI_BASEADDR+4*7);

xil_printf(“y=%x\n\r”,y);

break; }

case 1: { Xil_Out8(XPAR_AXI4_TEST_0_SW_LED_AXI_BASEADDR+4*1,0xff);

Xil_Out8(XPAR_AXI4_TEST_0_SW_LED_AXI_BASEADDR, 0x55);

break; }

case 2: { Xil_Out8(XPAR_AXI4_TEST_0_SW_LED_AXI_BASEADDR+4*1,0xff);

Xil_Out8(XPAR_AXI4_TEST_0_SW_LED_AXI_BASEADDR,0xaa);

break; }

case 3: { Xil_Out8(XPAR_AXI4_TEST_0_SW_LED_AXI_BASEADDR+4*1,0xff);

Xil_Out8(XPAR_AXI4_TEST_0_SW_LED_AXI_BASEADDR, 0xff);

break;}

case 4: { Xil_Out8(XPAR_AXI4_TEST_0_SW_LED_AXI_BASEADDR+4*1,0xff);

Xil_Out8(XPAR_AXI4_TEST_0_SW_LED_AXI_BASEADDR, 0x00);

break; }

default:printf(“error\n\r”);

}

}

}

return 0;

}

static void TimerIntrHandler(void *CallBackRef)

{

static int sec=0;

XScuTimer *timerptr = (XScuTimer *)CallBackRef;

XScuTimer_ClearInterruptStatus(timerptr);

sec++;

printf(” %d Second\n\r”,sec); //每秒打印输出一次

}

void SetupInterruptSystem(XScuGic *Gicptr , XScuTimer *timerptr ,u16 TimerIntrId)

{

XScuGic_Config *IntcConfig; //GIC config

Xil_ExceptionInit();

IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);

XScuGic_CfgInitialize(Gicptr, IntcConfig, IntcConfig->CpuBaseAddress);

//connect to the hardware

Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, Gicptr);

//set up the timer interrupt

XScuGic_Connect(Gicptr, TimerIntrId, (Xil_ExceptionHandler)TimerIntrHandler,

(void *)timerptr);

//enable the interrupt for the Timer at GIC

XScuGic_Enable(Gicptr, TimerIntrId);

//enable interrupt on the timer

XScuTimer_EnableInterrupt(timerptr);

// Enable interrupts in the Processor.

Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);

}

按键的使用:

(1)按键是系统设计中重要的人机界面接口,包含按键的去抖,按键识别,以及按键功能函数调用等。

(2)FII-PE7030共设计了7个按键,如图5-3所示:功能分别定义为:MENU,UP,DOWN,LEFT,RIGHT,OK,RETURN等。

(3)按键对应的原理图对应图5-4,由于它们的结构一样,因此这里只给出了两个原理图,其它的按键的原理图参见FII-PE7030 原理图。

(4)按键的具体功能可以由用户自定义,在本实验系统的各种设计使用都是按照图中标注功能进行设计

 

%title插图%num

 

 

 

 

 

 

按键原理如图5-5, GPIO_SW_0是连接FPGA 及按键的网络,FPGA管脚为L9(图5-4)。在作为按键使用时FPGA IO定义为输入, 在按键没有按下时为低电平,当按键按下时,GPIO_SW_0的电平由VCC3V3与R506,R509分压决定,约为1.7V,满足BANK 33 IO 输入、输出电压的需求。D20,C530以及电阻组合可以吸收瞬时静电电压,起到对FPGA IO过压保护作用。

%title插图%num

7个按键的功能名称,网络名,以及FPGA对应IO管脚的对应关系见下表

功能名称 MENU UP DOWN LEFT RIGHT RETURN OK
网络名称 GPIO_SW_0 GPIO_SW_1 GPIO_SW_6 GPIO_SW_3 GPIO_SW_5 GPIO_SW_2 GPIO_SW_4
FPGA_IO管脚 L9 G4 G2 D4 F2 F4 D3
  1. 添加管脚电平定义,及管脚锁定到 arm_soc_top.xdc 文件中:

set_property PACKAGE_PIN L9 [get_ports {GPIO_SW[0]}]

set_property IOSTANDARD LVCMOS18 [get_ports {GPIO_SW[0]}]

set_property PACKAGE_PIN G4 [get_ports {GPIO_SW[1]}]

set_property IOSTANDARD LVCMOS18 [get_ports {GPIO_SW[1]}]

set_property PACKAGE_PIN F4 [get_ports {GPIO_SW[2]}]

set_property IOSTANDARD LVCMOS18 [get_ports {GPIO_SW[2]}]

#button constaints to be continue

set_property PACKAGE_PIN D4 [get_ports {GPIO_SW[3]}]

set_property IOSTANDARD LVCMOS18 [get_ports {GPIO_SW[3]}]

set_property PACKAGE_PIN D3 [get_ports {GPIO_SW[4]}]

set_property IOSTANDARD LVCMOS18 [get_ports {GPIO_SW[4]}]

set_property PACKAGE_PIN F2 [get_ports {GPIO_SW[5]}]

set_property IOSTANDARD LVCMOS18 [get_ports {GPIO_SW[5]}]

set_property PACKAGE_PIN G2 [get_ports {GPIO_SW[6]}]

set_property IOSTANDARD LVCMOS18 [get_ports {GPIO_SW[6]}]

在XDC文件中还有LED,数码管等管脚的设置与锁定,参照前面的内容,这里就不在一一列出

    1. 本部分内容注重练习AXI P2S的中断,与timer中断不同。Timer是PS端的资源,因此无论Timer的设定,还是中断都是PS内部资源因此不需要FPGA本身的编程。
    2. 由于按键是FPGA的外设,因此本部分注重练习AXI总线的操作,按键去抖,以及P2S中断,以及相对应的SDK软件操作。
    3. 复制文件夹 SOC_AXI_timer_interrupt
    4. 重命名为SOC_AXI_key_interrupt
    5. 在Vivado下启动ARM_SOC_TOP.xpr
    6. 双击arm_soc_i:arm_soc(arm_soc.bd),如图5-6红线圈住的部分,打开的界面如图5-7
    7. 在图5-7中,选中axi4_test_0.右键点击选择Edit in IP Packager
    8. 添加文件:
    9. 分频文件
    10. 按键去抖文件
    11. 菜单file
    12. 并将添加的文件放在axi_test_v1_0.v相同的目录中

%title插图%num %title插图%num %title插图%num %title插图%num

  • Verilog 程序div_us.v,将100Hz的clock分频后得到微妙脉冲输出 信号us_f
  • 文件div_1000f.v将输入in_f的频率1000分频,得到一个时钟宽度的脉冲输出信号。
  • 程序的详细过程参见FII-PE7030 FPGA实验

开发实验例程

  • 将div_us.v,div_1000f.v添加到axi4_test_v1_1_project中

module div_us

(

input rst,

input sys_clk,

output reg us_f

);

reg[6:0] us_r;

always@(posedge sys_clk)

if(rst)begin

us_r<=0;

us_f<=1’b0;

end

else begin

us_f<=1’b0;

if(us_r==99)begin

us_r<=0; us_f<=1’b1;

end

else begin

us_r<=us_r+1;

end

end

endmodule

module div_1000f

(

input rst,

input sys_clk,

input in_f,

output reg div1000_f

);

reg[9:0] div1000_r;

always@(posedge sys_clk)

if(rst) begin

div1000_r<=0;

div1000_f<=1’b0;

end

else begin

div1000_f<=1’b0;

if(in_f) begin

if(div1000_r==999)begin

div1000_r<=0;

div1000_f<=1’b1;

end

else begin

div1000_r<=div1000_r+1;

end

end

end

endmodule

%title插图%num

module div_ms

(

input rst,

input sys_clk,

output ms_f

);

wire us_f;

div_us div_us_inst

(

.rst (rst),

.sys_clk (sys_clk),

.us_f (us_f)

);

div_1000f div_1000f_inst

(

.rst (rst),

.sys_clk (sys_clk),

.in_f (us_f),

.div1000_f (ms_f)

);

endmodule

程序组合:将iv_us.v, div_1000f.v组合成div_ms.v,并添加到相同的项目中。

在axi4_test_v1_0_SW_LED_AXI.v添加div_ms 接口(模块),并对应设置接口信号。

此时的文件的层次结构如下图

%title插图%num

// Add user logic here

wire ms_f;

div_ms div_ms_inst

(

.rst (!S_AXI_ARESETN),

.sys_clk(S_AXI_ACLK),

.ms_f (ms_f)

);

  • 去抖动程序设计:
  • (这里只列出去抖程序,去抖原理请参照FII-PE7030 FPGA 开发与实验手册)
  • 将该程序添加到项目中

module pb_ve(

input sys_clk,

input sys_rst,

input ms_f,

input keyin,

output keyout

);

reg keyin_r;

reg keyout_r;

//push_button vibrating elemination

reg [1:0] ve_key_st;

reg [3:0] ve_key_count;

always@(posedge sys_clk)

keyin_r<=keyin;

always@(posedge sys_clk)

if(sys_rst) begin

keyout_r <=1’b0;

ve_key_count <=0;

ve_key_st <=0;

end

else case(ve_key_st)

0:begin

keyout_r<=1’b0;

ve_key_count <=0;

if(!keyin_r)

ve_key_st <=1;

end

1:begin

if(keyin_r)

ve_key_st <=0;

else begin

if(ve_key_count==10) begin

ve_key_st <=2;

end

else if(ms_f) ve_key_count<=ve_key_count+1;

end

end

2:begin

ve_key_count <=0;

if(keyin_r)

ve_key_st <=3;

end

3:begin

if(!keyin_r)

ve_key_st <=2;

else begin

if(ve_key_count==10) begin

ve_key_st <=0;

keyout_r<=1’b1;

end

else if(ms_f)

ve_key_count<=ve_key_count+1;

end

end

default:;

endcase

assign keyout=keyout_r;

endmodule

    • 修改axi4_test_v1_0_SW_LED_AXI.v中其它相关部分的程序,
    • 添加文件后的层次结构如图5-11

// Add user logic here

wire ms_f;

div_ms div_ms_inst

(

.rst (!S_AXI_ARESETN),

.sys_clk(S_AXI_ACLK),

.ms_f (ms_f)

);

 

 

pb8_ve pb8_ve_test

(

.sys_clk (S_AXI_ACLK),

.sys_rst (!S_AXI_ARESETN),

.ms_f (ms_f),

.keyin (keyin),

.keyout (keyout)

);

 

// User logic ends

// Implement memory mapped register select and read logic generation

// Slave register read enable is asserted when valid address is available

// and the slave is ready to accept the read address.

assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;

always @(*)

begin

// Address decoding for reading registers

case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )

3’h0 : reg_data_out <= slv_reg0;

3’h1 : reg_data_out <= slv_reg1;

3’h2 : reg_data_out <= slv_reg2;

3’h3 : reg_data_out <= slv_reg3;

3’h4 : reg_data_out <= slv_reg4;

3’h5 : reg_data_out <= slv_reg5;

3’h6 : reg_data_out <= slv_reg6;

// 3’h7 : reg_data_out <= slv_reg7;

3’h7 : reg_data_out <= keyout;

default : reg_data_out <= 0;

endcase

end

拓扑结果如下图

%title插图%num

 

`timescale 1 ns / 1 ps、

module axi4_test_v1_0 #

(

// Users to add parameters here

// User parameters ends

// Do not modify the parameters beyond this line

// Parameters of Axi Slave Bus Interface SW_LED_AXI

parameter integer C_SW_LED_AXI_DATA_WIDTH = 32,

parameter integer C_SW_LED_AXI_ADDR_WIDTH = 5,

// Parameters of Axi Slave Bus Interface S_AXI_INTR

parameter integer C_S_AXI_INTR_DATA_WIDTH = 32,

parameter integer C_S_AXI_INTR_ADDR_WIDTH = 5,

parameter integer C_NUM_OF_INTR = 1,

parameter C_INTR_SENSITIVITY = 32’hFFFFFFFF,

parameter C_INTR_ACTIVE_STATE = 32’hFFFFFFFF,

parameter integer C_IRQ_SENSITIVITY = 1,

parameter integer C_IRQ_ACTIVE_STATE = 1

)

(

// Users to add ports here

input [7:0] in_key,

output[7:0] out_led,

output[7:0] out_t,

// User ports ends

// Do not modify the ports beyond this line

// Ports of Axi Slave Bus Interface SW_LED_AXI

input wire sw_led_axi_aclk,

input wire sw_led_axi_aresetn,

input wire [C_SW_LED_AXI_ADDR_WIDTH-1 : 0] sw_led_axi_awaddr,

input wire [2 : 0] sw_led_axi_awprot,

input wire sw_led_axi_awvalid,

output wire sw_led_axi_awready,

input wire [C_SW_LED_AXI_DATA_WIDTH-1 : 0] sw_led_axi_wdata,

input wire [(C_SW_LED_AXI_DATA_WIDTH/8)-1 : 0] sw_led_axi_wstrb,

input wire sw_led_axi_wvalid,

output wire sw_led_axi_wready,

output wire [1 : 0] sw_led_axi_bresp,

output wire sw_led_axi_bvalid,

input wire sw_led_axi_bready,

input wire [C_SW_LED_AXI_ADDR_WIDTH-1 : 0] sw_led_axi_araddr,

input wire [2 : 0] sw_led_axi_arprot,

input wire sw_led_axi_arvalid,

output wire sw_led_axi_arready,

output wire [C_SW_LED_AXI_DATA_WIDTH-1 : 0] sw_led_axi_rdata,

output wire [1 : 0] sw_led_axi_rresp,

output wire sw_led_axi_rvalid,

input wire sw_led_axi_rready,

// Ports of Axi Slave Bus Interface S_AXI_INTR

input wire s_axi_intr_aclk,

input wire s_axi_intr_aresetn,

input wire [C_S_AXI_INTR_ADDR_WIDTH-1 : 0] s_axi_intr_awaddr,

input wire [2 : 0] s_axi_intr_awprot,

input wire s_axi_intr_awvalid,

output wire s_axi_intr_awready,

input wire [C_S_AXI_INTR_DATA_WIDTH-1 : 0] s_axi_intr_wdata,

input wire [(C_S_AXI_INTR_DATA_WIDTH/8)-1 : 0] s_axi_intr_wstrb,

input wire s_axi_intr_wvalid,

output wire s_axi_intr_wready,

output wire [1 : 0] s_axi_intr_bresp,

output wire s_axi_intr_bvalid,

input wire s_axi_intr_bready,

input wire [C_S_AXI_INTR_ADDR_WIDTH-1 : 0] s_axi_intr_araddr,

input wire [2 : 0] s_axi_intr_arprot,

input wire s_axi_intr_arvalid,

output wire s_axi_intr_arready,

output wire [C_S_AXI_INTR_DATA_WIDTH-1 : 0] s_axi_intr_rdata,

output wire [1 : 0] s_axi_intr_rresp,

output wire s_axi_intr_rvalid,

input wire s_axi_intr_rready,

output wire irq

);

wire [7:0] keyout;

// Instantiation of Axi Bus Interface SW_LED_AXI

axi4_test_v1_0_SW_LED_AXI # (

.C_S_AXI_DATA_WIDTH(C_SW_LED_AXI_DATA_WIDTH),

.C_S_AXI_ADDR_WIDTH(C_SW_LED_AXI_ADDR_WIDTH)

) axi4_test_v1_0_SW_LED_AXI_inst (

.in_key (in_key),

.out_led (out_led),

.out_t (out_t),

.S_AXI_ACLK(sw_led_axi_aclk),

.S_AXI_ARESETN(sw_led_axi_aresetn),

.S_AXI_AWADDR(sw_led_axi_awaddr),

.S_AXI_AWPROT(sw_led_axi_awprot),

.

.S_AXI_RREADY(sw_led_axi_rready)

);

// Instantiation of Axi Bus Interface S_AXI_INTR

axi4_test_v1_0_S_AXI_INTR # (

.C_S_AXI_DATA_WIDTH(C_S_AXI_INTR_DATA_WIDTH),

.C_S_AXI_ADDR_WIDTH(C_S_AXI_INTR_ADDR_WIDTH),

.C_NUM_OF_INTR(C_NUM_OF_INTR),

.C_INTR_SENSITIVITY(C_INTR_SENSITIVITY),

.C_INTR_ACTIVE_STATE(C_INTR_ACTIVE_STATE),

.C_IRQ_SENSITIVITY(C_IRQ_SENSITIVITY),

.C_IRQ_ACTIVE_STATE(C_IRQ_ACTIVE_STATE)

) axi4_test_v1_0_S_AXI_INTR_inst (

.S_AXI_ACLK(s_axi_intr_aclk),

.

.keyin (keyout),

.irq(irq)

);

Endmodule

1.深入分析SOC FPGA中断程序,接口参数的使用,注意不要在该文件里修改这些接口参数。后面会介绍如何根据需要修改这些参数。

module axi4_test_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_High : 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

)

1)C_INTR_SENSITIVITY: 每个bit 对应一个中断(INTR)触发类型的设定,0—边沿触发, 1—电平触发

2)C_INTR_ACTIVE_STATE:当C_INTR_SENSITIVITY的某一位设为0时, C_INTR_ACTIVE_STATE对应位表示该位为上升沿还是下降沿触发,0–下降沿,1—上升沿,当C_INTR_SENSITIVITY的某一位设为1时, C_INTR_ACTIVE_STATE对应位表示该位为高电平还是低电平触发,0—低电平,1—高电平,

例如C_INTR_ACTIVE_STATE的第0位为0时,表示该位为边沿触发,而C_INTR_ACTIVE_STATE的第0位则具体到底是上升沿还是下降沿。本程序中C_INTR_SENSITIVITY = 32‘hFFFFFFFF,因此全部为电平触发。注意:程序中的值都是缺省值,具体值是由外部的参数化框设定的。

3)C_IRQ_SENSITIVITY 与C_IRQ_ACTIVE_STATE与上面的类似,只是对应IRQ。

2. 输入输出端口: input [7:0] keyin,

output wire irq (其它没有列出)

信号定义:

//– Signals for Interrupt register space

//————————————————

//– 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 intr_all; //所有中断悬挂寄存器的“或”

reg intr_ack_all; //所有应答信号的“或”

wire s_irq; //中断irq 中间变量

reg intr_all_ff; //

reg intr_ack_all_ff; //

reg aw_en;

// I/O Connections assignments

信号追踪:这里才有从输出信号开始追踪的方式

assign irq = s_irq;

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

//有中断并且全局中断使能为1,设置中断信号

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 //低电平中断

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; //触发器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 //下降沿

.

.

.

assign s_irq = !(s_irq_lvl_ff && (!s_irq_lvl)); //提取下降沿上面这段程序中有两个关键变量 intr_all, intr_ack_all, 下面的程序将追踪这两个变量

always @ ( posedge S_AXI_ACLK)

begin

if ( S_AXI_ARESETN == 1’b0 || intr_ack_all_ff == 1’b1)

begin

intr_all <= 1’b0;

end

else

begin

intr_all <= |reg_intr_pending;

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 <= 1’b0;

end

else

begin

intr_ack_all <= |reg_intr_ack;

end

end

// detects interrupt in any intr input

always @ ( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1’b0)

begin

intr_ack_all_ff <= 1’b0;

intr_all_ff <= 1’b0;

end

else

begin

intr_ack_all_ff <= intr_ack_all;

intr_all_ff <= intr_all;

end

end

//intr_all_ff, intr_ack_all_ff分别比intr_ack_all, intr_ack_all滞后一个时钟

以上部分是针对IRQ的操作。下面的程序将追踪reg_intr_pending和reg_intr_ack

// Interrupt pending register

always @( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1’b0 || reg_intr_ack == 1’b1)

begin

reg_intr_pending <= 1’b0;

end

else

begin

reg_intr_pending <= reg_intr_sts & reg_intr_en;

end

end

// Interrupt acknowledgement register

always @( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1’b0 || reg_intr_ack == 1’b1)

begin

reg_intr_ack <= 1’b0;

end

else if (intr_reg_wren && axi_awaddr[4:2] == 3’h3)

begin

reg_intr_ack <= S_AXI_WDATA;

end

end

//reg_intr_pending是由reg_intr_sts产生的,reg_intr_ack是AXI总线写入的

继续追踪reg_intr_sts寄存器:

// Interrupt status register

always @( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1’b0 || reg_intr_ack == 1’b1)

begin

reg_intr_sts <= 1’b0;

end

else

begin

reg_intr_sts <= det_intr;

end

end

//reg_intr_sts 是由 det_intr产生的;det_intr (interrupt detect)

//———————————————————————

// Hardware interrupt detection

//———————————————————————

 

// 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 == 1’b1)

begin: gen_intr_level_detect

//高电平信号检测

if (C_INTR_ACTIVE_STATE == 1’b1)

begin: gen_intr_active_high_detect

 

always @ ( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1’b0 | reg_intr_ack == 1’b1)

begin

det_intr <= 1’b0;

end

else

begin

if (intr == 1’b1)

begin

det_intr <= 1’b1;

end

end

end

end

//det_intr是由intr信号产生的

// C_INTR_ACTIVE_STATE == 1‘b0,低电平检测

else

begin: gen_intr_active_low_detect

 

always @ ( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1’b0 | reg_intr_ack == 1’b1)

begin

det_intr <= 1’b0;

end

else

begin

if (intr == 1’b0)

begin

det_intr <= 1’b1;

end

end

end

 

end

 

end

else // C_INTR_SENSITIVITY == 1‘b0,边沿检测

begin:gen_intr_edge_detect

wire [C_NUM_OF_INTR-1 :0] intr_edge;

reg [C_NUM_OF_INTR-1 :0] intr_ff; //intr_ff, intr 的寄存器

reg [C_NUM_OF_INTR-1 :0] intr_ff2;

if (C_INTR_ACTIVE_STATE == 1) //上升沿检测

begin: gen_intr_rising_edge_detect

always @ ( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1’b0 || reg_intr_ack == 1’b1)

begin

intr_ff <= 1’b0;

intr_ff2 <= 1’b0;

end

else

begin

intr_ff <= intr;

intr_ff2 <= intr_ff;

end

end

assign intr_edge = intr_ff && (!intr_ff2); //提取上升沿,一个时钟宽度脉冲

always @ ( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1’b0 | reg_intr_ack == 1’b1)

begin

det_intr <= 1’b0;

end

else if (intr_edge == 1‘b1) //上升沿

begin

det_intr <= 1’b1;

end

end

else

begin: gen_intr_falling_edge_detect

always @ ( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1’b0 | reg_intr_ack == 1’b1)

begin

intr_ff <= 1’b1;

intr_ff2 <= 1’b1;

end

else

begin

intr_ff <= intr;

intr_ff2 <= intr_ff;

end

end assign intr_edge = intr_ff2 && (!intr_ff); //intr信号的下降沿提取

always @ ( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1’b0 | reg_intr_ack == 1’b1)

begin

det_intr <= 1’b0;

end

else if (intr_edge == 1’b1)

begin

det_intr <= 1’b1;

end

end

无论电平还是边沿,都是由intr信号产生了det_intr信号

    • 追踪intr信号,下面的程序是向导生成的原始程序,计数器intr_counter==10,intr对应位全部赋值1。这只是个例子程序,也正是我们关心的部分,因此只要修改本部分程序,程序的其它部分完全不要动就可以完成中断(当然也可以把计数器程序注释),修开后的程序如右边所示:

always @ ( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1’b0)

begin

intr <= {C_NUM_OF_INTR{1’b0}};

end

else

begin

if (intr_counter[3:0] == 10)

begin

intr <= {C_NUM_OF_INTR{1’b1}};

end

else

begin

intr <= {C_NUM_OF_INTR{1’b0}};

end

end

end

always @ ( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1’b0)

begin

intr <= {C_NUM_OF_INTR{1’b0}};

end

else

begin

intr <= keyin;

end

end

*注意:C_NUM_OF_INTR的值一定大于等按键的位数,例如按键8位, C_NUM_OF_INTR>=8;

看起来非常有趣,花了那大的篇幅,只要修改几句程序就完成。

如果以上程序没有完全理解不要紧,但一定要明白参数C_INTR_SENSITIVITY, C_INTR_ACTIVE_STATE, C_IRQ_SENSITIVITY及C_IRQ_ACTIVE_STATE的用法。

中断信号生成过程 :

keyin(中断源)->intr->det_intr->reg_intr_pending->reg_intr_sts->

intr_all-> s_irq_lvl -> s_irq->irq

    • AXI 对寄存器读写:
    • 读:

assign intr_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;

always @(*) begin

if ( S_AXI_ARESETN == 1’b0 ) begin

reg_data_out <= 0;

end

else begin

// Address decoding for reading registers

case ( axi_araddr[4:2] )

3’h0 : reg_data_out <= reg_global_intr_en;//offset=0

3’h1 : reg_data_out <= reg_intr_en; //offset=4

3’h2 : reg_data_out <= reg_intr_sts; //offset=8

3’h3 : reg_data_out <= reg_intr_ack; //offset=c

3’h4 : reg_data_out <= reg_intr_pending;//offset=10

default : reg_data_out <= 0;

endcase

if (intr_reg_rden) begin

axi_rdata <= reg_data_out; // register read data

end

end

end

    • 写:

assign intr_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;

generate

 

for(i=0; i<= C_NUM_OF_INTR-1; i=i+1)

begin : gen_intr_reg

 

// Global interrupt enable register

always @( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1’b0) begin

reg_global_intr_en[0] <= 1’b0;

end

else if (intr_reg_wren && axi_awaddr[4:2] == 3’h0) begin

reg_global_intr_en[0] <= S_AXI_WDATA[0];

end

end

 

// Interrupt enable register

always @( posedge S_AXI_ACLK ) begin

if ( S_AXI_ARESETN == 1’b0) begin

reg_intr_en <= 1’b0;

end

else if (intr_reg_wren && axi_awaddr[4:2] == 3’h1) begin

reg_intr_en <= S_AXI_WDATA;

end

end

// Interrupt status register

always @( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1’b0 || reg_intr_ack == 1’b1)

begin

reg_intr_sts <= 1’b0;

end

else

begin

reg_intr_sts <= det_intr;

end

end

 

// Interrupt acknowledgement register

always @( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1’b0 || reg_intr_ack == 1’b1)

begin

reg_intr_ack <= 1’b0;

end

else if (intr_reg_wren && axi_awaddr[4:2] == 3’h3)

begin

reg_intr_ack <= S_AXI_WDATA;

end

end

 

// Interrupt pending register

always @( posedge S_AXI_ACLK )

begin

if ( S_AXI_ARESETN == 1’b0 || reg_intr_ack == 1’b1)

begin

reg_intr_pending <= 1’b0;

end

else

begin

reg_intr_pending <= reg_intr_sts & reg_intr_en;

end

end

 

end

endgenerate

    • 修改parameter:
    • 选中:Package IP标签
    • 修改:C NUM OF ITR 8,这里修改的缺省值,建议不要在这里修改,会引起程序混乱,如要修改,建议在主工程中双击该IP进行修改
    • –>Review and Package
    • Re-Package IP退出临时工程,返回主工程
    • 在主工程中更新修改的IP axi4-test_0(在AXI的使用章节中已经详细介绍了如何使用)

%title插图%num

%title插图%num

%title插图%num

    • 在上图可以看到irq接口信号还没有连接,后续章节中介绍 如何配置SoC 模块实现FPAG到 PS的中断

打开项目目录,删除SDK

Vivado 下:

FileExport Hardware(选中 Include bitstream)

FileLaunch SDK

SDK 下:

File New

选择Empty application

FileNew Source File 如图5-18

文件名“INTR_Main”

 

%title插图%num

%title插图%num

实验程序目标:

    • FPGA端:在上几节的内容可以看出在FPGA端实现类按键的输入,去抖动,中断接口,全局中断的编码等行为,同时定义了8个LED灯作为输出,但是并没有定义LED的行为。
    • 软件端:除了实现PL2PS的中断软件实现外,同时对按键和8个LED的行为进行定义
      1. 按键功能编码:
        1. MENU :软件编码为0x01,
        2. UP :软件编码为0x02,
        3. RETURN :软件编码为0x04,
        4. LEFT :软件编码为0x08,
        5. OK :软件编码为0x10,
        6. RIGHT :软件编码为0x20,
        7. DOWN :软件编码为0x40,
      2. 按键功能:
        1. UP :LED显示加一计数
        2. DOWN :LED显示减一计数
        3. LEFT :LED左移显示
        4. RIGHT :LED右移显示
        5. 其它几个键主要为液晶显示器接口定义的,这里都定义为清零
      3. 软件分为三个文件实现,
        1. 按键中断程序文件
        2. 按键及LED寄存器读写文件
        3. Main主程序文件
  • 主程序:

#include “key_axi_intr.h”

#include “key_axi_mem.h”

int main(void)

{

int xstatus;

int xxx_counter=0;

u32 LED_data;

u32 key_data_last;

u32 i_dly;

u32 base_reg_mem_addr=(u32)KEY_AXI_REG_MEM;

/* SLCR unlock */

Xil_Out32(SLCR_UNLOCK_ADDR, SLCR_UNLOCK_KEY_VALUE);

/* SLCR Enabling Level shifting */

Xil_Out32(SLCR_LVL_SHFTR_EN_ADDR, SLCR_LVL_SHFTR_EN_VALUE);

/* SLCR clearing PL Reset */

Xil_Out32(SLCR_FPGA_RST_CTRL_ADDR, 0x0);

Xil_Out32(SLCR_FPGA_RST_CTRL_ADDR, 0xf);

Xil_Out32(SLCR_FPGA_RST_CTRL_ADDR, 0x0);

/* SLCR lock */

Xil_Out32(SLCR_LOCK_ADDR, SLCR_LOCK_KEY_VALUE);

xstatus = ScuGicInterrupt_Init();

if (xstatus != XST_SUCCESS) {

return XST_FAILURE;

}

print(“Wait for the interrupt trigger \r\n”);

u32 baseaddr_p;

baseaddr_p=(u32)KEY_AXI_INTERRUPT;

KEY_AXI_INTERRUPT_EnableInterrupt((void *) baseaddr_p);

KEY_AXI_REG_MEM_WR((void*)(base_reg_mem_addr),0);

while(1)

{

if (xxx_counter!= xx_counter)

{

xxx_counter=xx_counter;

key_data=KEY_AXI_INTR_mReadReg(baseaddr_p, 0x04); //interrupt pending register

//key_data=KEY_AXI_REG_MEM_RD((void*)(base_reg_mem_addr+0));

xil_printf(“data: %8x\r\n”,key_data);

switch(key_data)

{

case 0x02: LED_data++; break; //UP –0x02

case 0x40: LED_data–; break; //down –0x40

case 0x08://LEFT –0x08

if(key_data!=key_data_last)

LED_data=0x01;

else if((LED_data&0xff)==0x00)

LED_data=0x01;

else LED_data=LED_data<<1;

break;

case 0x20://Right –0x20

if(key_data!=key_data_last)

LED_data=0x80;

else if((LED_data&0xff)==0x00)

LED_data=0x80;

else

LED_data=(LED_data>>1);

break;

default:LED_data=0;

}

key_data_last=key_data;

KEY_AXI_REG_MEM_WR((void*)(base_reg_mem_addr),LED_data);

for(i_dly=0;i_dly<500;i_dly++) {

}

KEY_AXI_INTERRUPT_EnableInterrupt((void *) baseaddr_p);

}

}

return 0;

}

中断子程序:

#include “key_axi_intr.h”

static XScuGic_Config *GicConfig;/* The configuration parameters of the controller */

int xx_counter;

u32 key_data;

void INTERRUPT_Handler(){//void *baseaddr_p

u32 baseaddr;

baseaddr=(u32)KEY_AXI_INTERRUPT;

key_data=KEY_AXI_INTR_mReadReg(baseaddr, 0x10); //interrupt pending register

KEY_AXI_INTERRUPT_DisableInterrupt((void *) baseaddr);

KEY_AXI_INTR_INTERRUPT_ACK( (void *)baseaddr);

xx_counter++;

xil_printf(“interrupt times: %d\r\n”,xx_counter);

//case key_data

}

int ScuGicInterrupt_Init()

{

int Status;

/*

* Initialize the interrupt controller driver so that it is ready to

* use.

* */

xx_counter=0;

Xil_ExceptionInit();

GicConfig = XScuGic_LookupConfig(XPAR_PS7_SCUGIC_0_DEVICE_ID);

if (NULL == GicConfig) {

return XST_FAILURE;

}

Status = XScuGic_CfgInitialize(&InterruptController, GicConfig,

GicConfig->CpuBaseAddress);

if (Status != XST_SUCCESS) {

return XST_FAILURE;

}

/*

* Setup the Interrupt System

* */

/*

* Connect the interrupt controller interrupt handler to the hardware

* interrupt handling logic in the ARM processor.

*/

Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,

(Xil_ExceptionHandler) XScuGic_InterruptHandler,

(void *) &InterruptController);

/*

* Connect a device driver handler that will be called when an interrupt for the device occurs, the device driver handler performs the specific interrupt processing for the device since the xparameters.h file doesnt detect the external interrupts, we have to manually use the IRQ_F2P port numbers ; 91, 90, ect..

*/

Status = XScuGic_Connect(&InterruptController,61,

(Xil_ExceptionHandler)INTERRUPT_Handler,

(void *)&InterruptController);

if (Status != XST_SUCCESS) {

return XST_FAILURE;

}

} XScuGic_Enable(&InterruptController, 61);

/////Enable interrupts in the ARM

Xil_ExceptionEnable();

XScuGic_SetPriorityTriggerType(&InterruptController, 61,0xa0, 3);

return 0;

}

void KEY_AXI_INTERRUPT_EnableInterrupt(void * baseaddr_p)

{

u32 baseaddr;

baseaddr = (u32) baseaddr_p;

//// Enable all interrupt source from user logic.

KEY_AXI_INTR_mWriteReg(baseaddr, 0x4, 0x7f);

////Set global interrupt enable.

KEY_AXI_INTR_mWriteReg(baseaddr, 0x0, 0x1);

}

void KEY_AXI_INTERRUPT_DisableInterrupt(void * baseaddr_p)

{ u32 baseaddr;

baseaddr = (u32) baseaddr_p;

/////Disenable all interrupt source from user logic.

KEY_AXI_INTR_mWriteReg(baseaddr, 0x4, 0x0);

//// Clear global interrupt enable.

KEY_AXI_INTR_mWriteReg(baseaddr, 0x0, 0x0);

}

void KEY_AXI_INTR_INTERRUPT_ACK(void * baseaddr_p)

{ u32 baseaddr;

baseaddr = (u32) baseaddr_p;

//// ACK interrupts on MYIP_WITH_INTERRUPTS.

KEY_AXI_INTR_mWriteReg(baseaddr, 0xc, 0xff);

目前功能已经完全实现,因为底层深入较多,代码量稍大,注释后续需要逐步加上。

实现的功能效果:可以在BM7030的单板上进行流水灯的同时,同时按键会使用中断,改变流水灯的状态,

Posted in SoC, SoC, SoC硬件开发, 教材与教案, 文章

发表评论

相关链接