OpenRisc-21-添加自己的IP core到ORSoC并测试
引言
我之前写过一篇类似文章:
http://blog.csdn.net/rill_zhen/article/details/8700937
那篇算是一个比较详细的概述吧,那篇文章把精力主要集中在driver部分,提到ip core的编码时,一笔带过。
这次进一步细化,写一个真的可以work的ip core,加到现有的ORSoC上,结合那篇文章的driver部分,一个真的可以work的东西就诞生了。
本小节实现了一个简单的ipcore:mycore。她的功能也非常简单,实现一个加法运算。
master(CPU)设置mycore的第一个寄存器,和第二个寄存器,mycore将两个寄存器的值相加放在第三个寄存器中,CPU读第三个寄存器来获得计算结果。
这次试验可以看到mycore计算1+2=3。
ok let's go!
21.1 ip core的编码和修改
要想在ORSoC里面加入自己的ipcore(本小节以‘mycore’为例),需要修改三个文件,增加一个文件。
三个需要修改的文件为:arbiter_dbus.v,orpsoc-params.v,orpsoc_top.v
一个需要增加的文件为:mycore.v
如下图:这里需要注意的是,在ORSoC里面共有3个wishbone的arbiter,咱们用的是arbiter_dbus。为什么呢?很简单,instruction那个是取指令用的,很显然不能用;byte那个是8位的,我的是32位的,很显然也不能用。
21.2 概述
一般,添加自己的ipcore到ORSoC,需要三大步:
1>编写符合wishbone interface的ipcore:mycore
2>定义mycore中用到的parameters
3>增加arbiter的slave或者mater接口(本小节是slave)
4>在顶层module例化这个ipcore
当然为了测试验证,还要
5>编写她的driver。
21.3 rtl编码
下面就逐个把需要修改的文件的内容说一下:
1》编写符合wishbone interface的ipcore:mycore.v
/* * * rill create 2013-03-26 * */ `include "orpsoc-defines.v" module mycore ( 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 ); 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 = 2'b000; parameter s_read = 2'b001; parameter s_write = 2'b010; reg [2:0] state = s_idle; assign wb_err_o=0; assign wb_rty_o=0; always @(*) begin sum = num_1 + num_2; end always @(posedge wb_clk) 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 ****************/
2》定义mycore中用到的parameters:修改orpsoc-params.v,共3个地方需要修改,如图:
1>修改1
2>修改2-3
3》增加arbiter的slave或者mater接口(本小节是slave):修改arbiter_dbus.v,共13个地方需要修改,如图:
1>修改1
1>修改2
1>修改3
1>修改4
1>修改5
1>修改6-7
1>修改8
1>修改9
1>修改10
1>修改11
1>修改12
1>修改13
4》在顶层module例化这个ipcore:修改orpsoc_top.v,共4个地方需要修改,如图:
1>修改1
1>修改2
1>修改3
1>修改4
自此,可以通过quartusII进行综合,生成orpsoc_top.svf文件,将其burn到FPGA板子里面。此步请参考:
http://blog.csdn.net/rill_zhen/article/details/8535317
http://blog.csdn.net/rill_zhen/article/details/8558463
21.4 driver
有了硬件电路,想让她工作,还要编写她的driver才行:ip_mkg.c ip_mkg.h Makefile
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 "ip_mkg.h" 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("mkg ioremap addr:%d!\n",(int)g_mkg_mem_base); } 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",);*/ 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, }; int init_module() { int ret_val; int ret; void __iomem *ret_from_request; int loop = 5; //=== 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",g_mkg_mem_base); } printk("mkg module init done!\n"); iowrite32(0x1,g_mkg_mem_base); printk("mkg write1!\n"); iowrite32(0x2,g_mkg_mem_base+4); printk("mkg write2!\n"); while(loop--) printk("======%d======read:%d\n",loop,ioread32(g_mkg_mem_base+4*loop)); 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 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
21.5 测试
然后make生成ip_mkg.ko,并insmod进内核。就可以看到最终结果。如图,可以看到1 + 2 = 3。
21.6 小结
自此,ORSoC就变成一个transformer了,你可以随意的添加自己想要的ipcore,以实现不同的function。
good lock,enjoy!