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手册)。
单中断操作比较简单,仅需用
-
void myISR( ) __attribute__ ((interrupt_handler));
复制代码
函数声明即可,然后添加用户代码即可
-
void myISR( void )
-
{
-
}
复制代码
未使用函数声明的编译代码
-
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。里面还有一个中断触发条件选项,有上升沿、下降沿、高低电平等。这里选上升沿触发。
<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,将这个信号和用户想要使用的触发中断信号连接起来即可。这里将他和外部按键项链,按键按下触发中断。代码如下
-
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为程序原文件。
|