I remember that when I first searched for a guide on how to write Linux kernel drivers, the first suggestion mentioned was that I should actually avoid writing kernel drivers. I would personally not suggest the same, though the fact remains that writing kernel drivers need some discipline and caution. If you write something that doesn’t work, then you likely end up with kernel panic. This way,  you will not be able to even see the kernel log – “DMESG” and therefore, you may end up compiling a kernel with debug features to help you get over the issue. 

Before we start, I would like to mention, that having a good information source by hand is priceless. My favorite book is the  “Linux Device Drivers Development by John Madieu“. Its good to mention, that writing those drivers requires the knowledge of C language, optionally device tree syntax, at least basic Linux terminal operation capability, knowledge of Vivado tool set and flow, RTL (VHDL / Verilog) and the actual device which you interfacing – this can be a simple AXI TimerAXI DMA or really an arbitrary device with some kind of documentation. Its also worth to mention that an overall knowledge of the System is crucial (IE. Zynq 7000 / ZynqMP ) and their Technical Reference Manuals (TRM – these will be your new best friends).  Also, do not try to write anything complicated until you have basic knowledge of how the kernel drivers work. Therefore, I highly suggest that a simple HelloWorld kernel module is your very first kernel module.  Its also quite handy to have available test board with JTAG at least for RTL debugging (To be honest, I had some problems with my DMA driver and realized that thanks to my stupidity, I forgot to invert one of the signals – causing the drivers to fail the transfers of course … )

In this post, we start with the environment setup and simple HelloWorld example. In fact, I have moved the second part to a new “advanced” post, which is password protected as of now, but if you are interested,see description below the post for getting the unlock key. In the second part, we are going to use the mentioned AXI Timer with interrupt handler and Device Tree modifications and at last, we are going to write and use a custom DMA driver with very simple interrupt handlers. We will also use DMA framework to manage the cache coherency issues, that cannot be solved from the user-space Linux program – IE. we are going to use AXI DMA connected to non-hardware cache coherency port of the ZYNQ 7000 – S_AXI_HP0.

Enclustra Mercury+ XU1

As promised, the first step is to create a HelloWorld.ko kernel module. In order to build and run the module, the following conditions must be met: You have downloaded, compiled and deployed a known Linux kernel. In other words, you are not using petalinux, to create the image.ub for yourself – you have scripted and or build the entire bootable Linux image yourself.If not,you may be interested in this post. The Image.ub should includes the DeviceTreeBlob and Linux Kernel, choice of file system is up to you. Theoretically, the boot.bin containing the platform initialization could be build by the petalinux flow, although I do not recommend mixing any flows and or tools.  If you don’t have a kernel, go ahead and download the latest Xilinx’s! I will be using Vivado 2019.1 along with linux-xlnx version 2019.1. My own compile environment is based on VirtualBox with 64-bit Ubuntu 18.04.5LTS. I have extracted the linux-xlnx package to “/Xilinx/linux-xlnx-xilinx-v2019.1/“.The first thing to do is to create a directory elsewhere, where we create a makefile for the module.


Few more comments on this:

  • ARCH = “arm” is the target for  Zynq7000, for ZynqMP, use “arm64“.
  • CROSS_COMPILE = “arm-linux-gnueabihf-” is the target for Zynq7000, for ZynqMP, use “aarch64-linux-gnu-
  •  Each of the files to be compiled (*.o) needs to have a corresponding (*.c) file in the same directory.

Now onto our first HelloWorld:


Few more comments on the HelloWorld:

After compilation, copy the helloworld.ko to your board/device and load it into the kernel via “insmod helloworld.ko“. You can review that the insert was successful by issuing “lsmod” command, which list all the kernel modules. make sure to check also the output of the “dmesg” command, which should show an entry from the module “Hello World!“. After you have verified this, fell free to unload the module by “rmmod“. After removal, additional entry with “Goodbye World!” should be presented in the kernel log. No additional modifications to the  FPGA / Device Tree were necessary in this example. Congratulations on your first module! :)

If you liked this post and would like to read more on the following topics:

  • Working kernel driver with example code for AXI Timer
  • Working kernel driver with example code for AXI DMA 
  • DMA and cache coherency for kernel drivers
  • Interrupt Handlers
  • Device Tree Modifications
  • Platform Drivers

Feel free to visit the next section of this post HERE. The post is password protected, though its purely because I didnt wanted to make all the code completely public without limitations. Please just send me an email to [email protected] and I will forward you the password at no cost. In case I get hundreds of valid emails per day, I will reconsider this protection :-) 

As always,If you find some bug, or misunderstanding, let me know in the comments belowe, so that I can correct it for all others :)