FPGA¶
AXI Memory Access Example¶
The following is a full driver example that exposes a specific 32 bit read only register in an AXI IP to the sysfs
interface.
Warning
This compiles on Ubuntu 20.04 but is untested on hardware.
``// SPDX-License-Identifier: GPL-2.0
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
// Automatically add driver and function name to printk
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) "%s:%s: " fmt "\n", KBUILD_MODNAME, __func__
// Memory
static void __iomem *virt_mem_base;
// Device tree info
static struct resource dt_resource;
#define ADDRESS_BASE phys_to_virt(dt_resource.start)
// sysfs
static struct class *example_class;
static struct device *example_device;
// # "module": the module name
static ssize_t module_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sysfs_emit(buf, "%s\n", KBUILD_MODNAME);
}
static DEVICE_ATTR_RO(module);
// # "my_register": the 32 bit unsigned value at address offset 0
#define ADDRESS_MY_REGISTER ADDRESS_BASE + 0
static ssize_t my_register_show(struct device *dev, struct device_attribute *attr, char *buf) {
return sysfs_emit(buf, "%d\n", readl(ADDRESS_MY_REGISTER));
}
static DEVICE_ATTR_RO(my_register);
static int __init example_init(void)
{
int ret;
// Get device tree information
struct device_node *node = of_find_node_by_name(NULL, "my_axi_ip_name");
if (!node) {
pr_err("failed to get device tree for my_axi_ip_name");
ret = -ENODEV;
goto just_return;
}
ret = of_address_to_resource(node, 0, &dt_resource);
if (ret) {
pr_err("failed to get device tree for my_axi_ip_name (%d)", ret);
goto destroy_device_tree_node;
}
// Get virtual memory
if (!request_mem_region(dt_resource.start, resource_size(&dt_resource), KBUILD_MODNAME))
{
pr_err("failed to request memory region");
ret = -EBUSY;
goto destroy_device_tree_node;
}
virt_mem_base = ioremap(dt_resource.start, resource_size(&dt_resource));
if (!virt_mem_base) {
pr_err("failed to ioremap physical memory region");
ret = -ENOMEM;
goto release_memory;
}
// Create sysfs interface
example_class = class_create(THIS_MODULE, KBUILD_MODNAME);
example_device = device_create(example_class,
NULL,
0,
NULL,
KBUILD_MODNAME);
ret = device_create_file(example_device, &dev_attr_module);
if (ret) {
pr_err("failed to create device file for 'module' (%d)", ret);
goto destroy_device_and_class;
}
ret = device_create_file(example_device, &dev_attr_my_register);
if (ret) {
pr_err("failed to create device file for 'my_register' (%d)", ret);
goto destroy_device_and_class;
}
of_node_put(node);
return 0;
destroy_device_and_class:
device_destroy(example_class, 0);
class_destroy(example_class);
release_memory:
release_mem_region(dt_resource.start, resource_size(&dt_resource));
destroy_device_tree_node:
of_node_put(node);
just_return:
return ret;
}
static void __exit example_exit(void)
{
device_remove_file(example_device, &dev_attr_my_register);
device_remove_file(example_device, &dev_attr_module);
device_destroy(example_class, 0);
class_destroy(example_class);
}
module_init(example_init);
module_exit(example_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("");
MODULE_DESCRIPTION("");
References¶
- ROACH Memory Map
- This uses a character device to expose the memory region for direct read and write.
- It also performs the FPGA bitstream loading itself.