引言
我觉得ORPSoC的关键在于‘P’,即programmable。SoC的有优势就在于只要是满足总线interface的ip,可以实现plug & work。
所以一旦完成前面的工作之后,添加属于自己的ip core到ORPSoC的wishbone总线上,并编写它对应的驱动就成为非常关键的一步。
本小节就做一个简单的例子,来说明需要完成的工作步骤及其中遇到的问题和对应的解决方法。
11.1 编写wishbone为interface的ip core(ip_mkg)
1》这一步请参考:
http://blog.youkuaiyun.com/rill_zhen/article/details/8659788
2》将其中的my_slave_module链接到ORPSoC的wishbone上。
11.2 编写linux下的driver module
代码及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)
- {
- return 0;
- }
- static ssize_t device_write(struct file *filp, const char *buffer, size_t count, loff_t *offset)
- {
- return 0;
- }
- long device_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param)
- {
- int ret_val = 0;
- unsigned int ret = 0;
- struct reg_data *new_regs;
- 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;
- }
- 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;
- //=== 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");
- 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:rillzhen@gmail.com");
2》ip_mkg.h,需要注意的是ip core的基地址是在写verilog HDL时指定的。
- #ifndef __IP_MKG_H__
- #define __IP_MKG_H__
- #define MAJOR_NUM 102
- #define DEVICE_NAME "ip_mkg"
- #define MKG_MEM_BASE 0x10000001
- #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
11.3 遇到的问题
1》当在执行make时会遇到如下警告:__ioremap undefined。
2》在板子上insmod时会遇到如下error:unknown symbol __ioremap。
11.4 解决方法
在arch/openrisc/mm/ioremap.c中添加如下代码:并重新编译kernel。
- #include <linux/module.h>
- EXPORT_SYMBOL(__ioremap);
11.5 小结
实验步骤
0》virtualbox虚拟机unbuntu上安装nfs服务
0.0>确保virtualbox能上网
0.1> apt-get install nfs-kernel-server
0.2>创建nfs共享目录:mkdir /home/openrisc/nfs
0.3>vim /etc/exports,添加如下内容
/home/openrisc/nfs *(rw,sync)
0.4>重启nfs服务
sudo /etc/init.d/nfs-kernel-server restart
1》修改arch/openrisc/mm/ioremap.c
2》cd /home/openrisc/soc-design/linux
3》make ARCH=openrisc defconfig;make生成vmlinux
4》cd 到ip_mkg下,make生成ip_mkg.ko模块文件
5》参考如下链接,在FPGA板子上运行linux(刚刚生成的vmlinux文件)
http://blog.youkuaiyun.com/rill_zhen/article/details/8535317
6》配置virtualbox的ip
sudo ifconfig eth8 192.168.1.101 broadcast 192.168.1.255
7》配置PC机的ip为192.168.1.102
8》板子起来后默认的ip为192.168.1.100,如果不是,则需要配置为同一网段。确保板子能ping通virtualbox。别忘了将板子和PC用网线连起来。
9》板子上执行mkdir nfs,创建本地nfs共享目录
10》挂载NFS:mount -t nfs -o nolock 192.168.1.101:/home/openrisc/nfs /nfs
11》在virtualbox里将ip_mkg.ko copy到nfs共享目录
12》板子上cd nfs
13》执行insmod ip_mkg.ko加载模块,可以通过lsmod检查一下
14》创建设备节点:mknod /dev/ip_mkg c 102 0
15》测试:cat /dev/ip_mkg,看到如下结果:
16》上面的命令确实有些多,如果不想在每次板子起来后手动敲键盘,可以修改一下rootfs的启动脚本文件,这样就不用每次手动输入了,文件路径如下
soc-design/linux/arch/openrisc/support/initramfs/etc/init.d/rcS
\soc-design\linux\arch\openrisc\support\initramfs\这个目录就是用busybox制作的rootfs的源。
16》运行helloworld
16.1>编写hello.c
- #include <stdio.h>
- void main()
- {
- printf("rill helloworld!\n");
- }
16.2>编译: or2-linux-gcc hello.c -o hello
16.3>copy到板子上:cp hello /home/openrisc/nfs
16.4>在板子上cd到/nfs,然后ls可以看到刚copy来的hello文件,最后运行:./hello,可以看到输出: