中断(异常)系统,可以说是计算机体系结构重要的组成部分,也是最复杂的部分。
现在的很多外设,都提供中断处理机制,来减少CPU的一直占用时间。
本小节就通过一个简单的例子,来说明openrisc的中断机制的使用方法。
本小节实现一个wishbone slave模块(mycore),此模块接收其对应的linux driver的指令,产生中断信号。
linux driver注册中断处理程序,记录中断发生的次数。
本实验中,在模块加载时(insmod ip_mkg.ko)时触发一次中断。在测试程序read时触发一次中断。
通过代码内部打印查看结果,通过cat /proc/stat查看结果。
1,通过下面的代码可以看出:
OR最多支持的外部中断线的数目为:31.其中只有(2~31)可以使用。
2,通过下面的代码可以看出:
3,通过下面的综合结果可以看出:
真正使用的为:2,4,6,14,15,16,20,21.共8根。具体如下:
SDC:3根。
USB:2根。
UART:1根。
simple_spi:1根。
eth:1根。
通过下面的linux kernel代码可以看出:
OR的中断线有32根。(与硬件不匹配)。
1>mycore.v
/* * * rill create 2013-05-07 * */ `include "orpsoc-defines.v" module mycore ( intr_o,//interrupt output wb_clk, wb_rst, wb_dat_i, wb_adr_i, wb_sel_i, wb_cti_i, wb_bte_i, wb_we_i, wb_cyc_i, wb_stb_i, wb_dat_o, wb_ack_o, wb_err_o, wb_rty_o ); output reg intr_o; input [addr_width-1:0] wb_adr_i; input wb_stb_i; input wb_cyc_i; input [2:0] wb_cti_i; input [1:0] wb_bte_i; input wb_clk; input wb_rst; input [31:0] wb_dat_i; input [3:0] wb_sel_i; input wb_we_i; output reg [31:0] wb_dat_o; output reg wb_ack_o; output wb_err_o; output wb_rty_o; //external parameters parameter addr_width = 32; parameter mycore_adr = 0; //local regs reg [addr_width-1:0] num_1; reg [addr_width-1:0] num_2; reg [addr_width-1:0] sum; parameter s_idle = 3'b000; parameter s_read = 3'b001; parameter s_write = 3'b010; reg [2:0] state = s_idle; //irq status parameter irq_idle = 3'b000; parameter irq_action = 3'b001; reg [2:0] irq_state = irq_idle; reg [7:0] irq_done;//irq flag assign wb_err_o=0; assign wb_rty_o=0; always @(*)//sum process begin sum = num_1 + num_2; end always @(posedge wb_clk)//interrupt process begin if(wb_rst) begin intr_o <= 1'b0; end else begin case (irq_state) irq_idle: begin intr_o <= 1'b0; if( (32'd1 == num_1) && (irq_done == 8'd0) ) begin irq_state <= irq_action; irq_done <= 8'd1; end else if( (32'd2 == num_1) && (irq_done == 8'd1) ) begin irq_state <= irq_action; irq_done <= 8'd2; end else if( (32'd3 == num_1) && (irq_done == 8'd2) ) begin irq_state <= irq_action; irq_done <= 8'd3; end else if( (32'd4 == num_1) && (irq_done == 8'd3) ) begin irq_state <= irq_action; irq_done <= 8'd4; end else begin irq_state <= irq_idle; end end irq_action: begin intr_o <= 1'b1; irq_state <= irq_idle; end default: begin irq_state <= irq_idle; intr_o <= 1'b0; end endcase end end always @(posedge wb_clk)//wishbone interface begin if(wb_rst) begin state <= s_idle; end else begin case(state) s_idle: begin wb_dat_o <= 1'b0; wb_ack_o <= 1'b0; if(wb_stb_i && wb_cyc_i && wb_we_i) begin state <= s_write; end else if(wb_stb_i && wb_cyc_i && !wb_we_i) begin state <= s_read; end else begin state <= s_idle; end end s_write: begin if(wb_adr_i == {mycore_adr,24'h000000}) begin num_1 <= wb_dat_i; wb_ack_o <= 1'b1; end else if(wb_adr_i == {mycore_adr,24'h000004}) begin num_2 <= wb_dat_i; wb_ack_o <= 1'b1; end else begin //wb_ack_o=1'b0; end state <= s_idle; end s_read: begin if(wb_adr_i=={mycore_adr,24'h000000}) begin wb_dat_o <= num_1; wb_ack_o <= 1'b1; end else if(wb_adr_i=={mycore_adr,24'h000004}) begin wb_dat_o <= num_2; wb_ack_o <= 1'b1; end else if(wb_adr_i=={mycore_adr,24'h000008}) begin wb_dat_o <= sum; wb_ack_o <= 1'b1; end else begin wb_dat_o=0; wb_ack_o <= 1'b1; end state <= s_idle; end default: begin state <= s_idle; end endcase end end endmodule /************** EOF ****************/
1>ip_mkg.c
/* * * rill mkg driver * */ #include <linux/vmalloc.h> #include <linux/slab.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <asm/uaccess.h> /* get_user and put_user */ //#include <linux/clk.h> //#include <linux/ioport.h> #include <asm/io.h> /*ioremap*/ #include <linux/platform_device.h> /*cleanup_module*/ #include <asm-generic/io.h> #include "ip_mkg.h" #include <linux/interrupt.h> volatile int g_irq_test_counter = 0; void __iomem *g_mkg_mem_base = NULL; static int device_open(struct inode *inode, struct file *file) { g_mkg_mem_base = ioremap(MKG_MEM_BASE,MKG_MEM_LEN); if(NULL == g_mkg_mem_base) { printk(KERN_ERR "mkg open ioremap error!\n"); return -1; } else { printk("kernel:mkg open ok!\n"); } return 0; } static int device_release(struct inode *inode, struct file *file) { return 0; } static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset) { /*int ret_val = 0; char * data = NULL; data = (char*)kmalloc(4, GFP_KERNEL); if((ret_val = copy_from_user(new_regs, (struct reg_data*)ioctl_param, sizeof(struct reg_data))) != 0) ioread32(g_mkg_mem_base+length); printk("============read:%d\n",);*/ iowrite32(0x02000000,g_mkg_mem_base); printk("request_irq ==2==!\n"); printk("mkg g_irq_test_counter:%d!\n",(int)g_irq_test_counter); printk("kernel:mkg g_irq_test_counter:%d!\n",(int)g_irq_test_counter); return 1; } static ssize_t device_write(struct file *filp, const char *buffer, size_t count, loff_t *offset) { //iowrite32(2,g_mkg_mem_base); return 1; } long device_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param) { #if 0 int ret_val = 0; unsigned int ret = 0; struct reg_data *new_regs; printk("ioctl======\n"); switch(ioctl_num) { case IOCTL_REG_SET: { new_regs = (struct reg_data*)kmalloc(sizeof(struct reg_data), GFP_KERNEL); if((ret_val = copy_from_user(new_regs, (struct reg_data*)ioctl_param, sizeof(struct reg_data))) != 0) { kfree(new_regs); printk(KERN_ERR " error copy line_datafrom user.\n"); return -1; } //iowrite16(new_regs->value,g_mkg_mem_base+new_regs->addr); kfree(new_regs); } break; case IOCTL_REG_GET: { new_regs = (struct reg_data*)kmalloc(sizeof(struct reg_data), GFP_KERNEL); if((ret_val = copy_from_user(new_regs, (struct reg_data*)ioctl_param, sizeof(struct reg_data))) != 0) { kfree(new_regs); printk(KERN_ERR " error copy line_datafrom user.\n"); return -1; } //ret = ioread16(g_mkg_mem_base+new_regs->addr); kfree(new_regs); return ret; } break; } #endif return -1; } struct file_operations our_file_ops = { .unlocked_ioctl = device_ioctl, .read = device_read, .write = device_write, .open = device_open, .release = device_release, .owner = THIS_MODULE, }; static irqreturn_t ip_mkg_irq(int irq, void *dev) { g_irq_test_counter = g_irq_test_counter+1; return IRQ_HANDLED; } void test(void) { int err = 0; int loop = 0; printk("request_irq...\n"); err = request_irq(MKG_IRQ_INDEX,ip_mkg_irq,0,"ip_mkg",NULL); if(err) { printk("request_irq error!\n"); } else { printk("request_irq ok!\n"); } #if 1 printk("reg test start==\n"); iowrite32(0x11223344,g_mkg_mem_base); iowrite32(0x11223344,g_mkg_mem_base+0x4); for(loop=0;loop<3;loop++) printk("====reg addr==0x%x==reg value:0x%x==\n",loop*4,ioread32(g_mkg_mem_base+4*loop)); #endif iowrite32(0x01000000,g_mkg_mem_base); printk("request_irq ==1==!\n"); printk("mkg g_irq_test_counter:%d!\n",(int)g_irq_test_counter); /*iowrite32(0x02000000,g_mkg_mem_base); printk("request_irq ==2==!\n"); printk("mkg g_irq_test_counter:%d!\n",(int)g_irq_test_counter); iowrite32(0x03000000,g_mkg_mem_base); printk("request_irq ==3==!\n"); printk("mkg g_irq_test_counter:%d!\n",(int)g_irq_test_counter); */ printk("<----ip_mkg test end---->\n"); } int init_module() { int ret_val; int ret; void __iomem *ret_from_request; //=== Allocate character device ret_val = register_chrdev(MAJOR_NUM, DEVICE_NAME, &our_file_ops); if (ret_val < 0) { printk(KERN_ALERT " device %s failed(%d)\n", DEVICE_NAME, ret_val); return ret_val; } ret = check_mem_region(MKG_MEM_BASE, MKG_MEM_LEN); if (ret < 0) { printk(KERN_ERR "mkg check_mem_region bussy error!\n"); return -1; } ret_from_request = request_mem_region(MKG_MEM_BASE, MKG_MEM_LEN, "ip_mkg"); //===ioremap mkg registers g_mkg_mem_base = ioremap(MKG_MEM_BASE,MKG_MEM_LEN); if(NULL == g_mkg_mem_base) { printk(KERN_ERR "mkg ioremap error!\n"); return -1; } else { ;//printk("mkg ioremap addr:%d!\n",(unsigned int)g_mkg_mem_base); } printk("mkg module init done!\n"); test(); return 0; } void cleanup_module() { release_mem_region(MKG_MEM_BASE, MKG_MEM_LEN); unregister_chrdev(MAJOR_NUM, DEVICE_NAME); } MODULE_LICENSE("GPL"); MODULE_AUTHOR("Rill zhen:[email protected]");
2>ip_mkg.h
#ifndef __IP_MKG_H__ #define __IP_MKG_H__ #define MAJOR_NUM 102 #define DEVICE_NAME "ip_mkg" #define MKG_MEM_BASE 0x97000000 #define MKG_MEM_LEN 32 #define MKG_IRQ_INDEX 24 #define IOCTL_REG_SET 0 #define IOCTL_REG_GET 1 struct reg_data { unsigned short addr; int value; }; #endif
3>makefile
# To build modules outside of the kernel tree, we run "make" # in the kernel source tree; the Makefile these then includes this # Makefile once again. # This conditional selects whether we are being included from the # kernel Makefile or not. ifeq ($(KERNELRELEASE),) # Assume the source tree is where the running kernel was built # You should set KERNELDIR in the environment if it's elsewhere KERNELDIR ?= /home/openrisc/soc-design/linux # The current directory is passed to sub-makes as argument PWD := $(shell pwd) modules: make -C $(KERNELDIR) M=$(PWD) modules ARCH=openrisc CROSS_COMPILE=or32-linux- modules_install: make -C $(KERNELDIR) M=$(PWD) modules_install ARCH=openrisc CROSS_COMPILE=or32-linux- clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers .PHONY: modules modules_install clean else # called from kernel build system: just declare what our modules are obj-m := ip_mkg.o endif
1>mkg_test.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { int fd = 0; int read_data = 0; char buffer[10] = {0}; fd =open("/dev/ip_mkg",O_RDWR); if(0 == fd) { printf("file /dev/ip_mkg open error!\n"); return 0; } else { printf("file open ok!\n\n\n"); } read(fd,buffer,1); close(fd); return 0; }
1>进行RTL编码与综合
2>编写linux下的driver
3>编写,编译测试应用程序
4>FPGA板上验证
具体操作步骤请参考:
http://blog.csdn.net/rill_zhen/article/details/8700937
5>需要注意的是,在跑测试程序前要创建设备节点(mknod /dev/ip_mkg c 102 0)
1>代码打印结果,如下图:
3>可见driver给mycore发出了两次指令,mycore也产生了两次中断,中断处理程序也执行了两次。
4>反面验证:除了正面显示实验正确外,还可以从反面进行验证,显示实验正确。
1》将指令发送代码注掉,再测,没有中断产生。
2》修改注册中断号为25,再测,也没有中断产生。
5>需要注意的是:
1》如果连续发送中断指令(就像driver中注释掉的那样),就可能只产生一次中断。
2》发送中断指令后,后面的打印信息,可能不会立即更新(即,counter仍为0)。
通过上述步骤,对openrisc 的外部中断的使用就比较了解了。
这对于SOC的开发又多了一种工作机制,既可以采用轮询,也可以采用中断。
enjoy!