FPGA registers access from Linux userspace

From ArmadeusWiki
Jump to: navigation, search


There are two methods to access FPGA registers on APF*.

  • For APF9328, APF27 and APF51 registers access are done with a memory bus (parallel).
  • For APF6_SP registers access are done with PCIe

Memory bus



If the fpgaregs package is not already installed on your APF (/usr/bin/fpgaregs), you can select it in the Buildroot menuconfig:

 $ make menuconfig
Package Selection for the target  ---> 
    [*] Hardware handling / blockdevices and filesystem maintenance  ---> 
         [*]   fpgaregs 
 $ make 

Then reflash your rootfs or install it manually.


fpgaregs can be used to do read or write accesses (16 or 32 bits wide) to the FPGA, from Linux userspace/console.

16 bits read
# fpgaregs w <address>

Where <address> is an address relative to FPGA's mapping in hexadecimal value. Example:

# fpgaregs w 0
16 bits write
# fpgaregs w <address> <value>

Where <value> is hexadecimal value to write.

32 bits read
# fpgaregs l <address>
32 bits write
# fpgaregs l <address> <value>

the mmap problem

Note Note: This problem is corrected with latest Toolchains compilation options

First of all, you need to get a file descriptor for /dev/mem using the open() function


Now you have a valid file descriptor to access your FPGA.

The O_SYNC option is recommended to avoid Linux to cache the content of /dev/mem and delay any modification done in this file.

To access fpga register, fpgaregs use the mmap() system call :

void * ptr_fpga;
ptr_fpga = mmap (0, 8192, PROT_READ|PROT_WRITE, MAP_SHARED, ffpga, FPGA_ADDRESS);

Thanks to this function, fpga registers are accessible directly on memory with pointer ptr_fpga. To read and write in 16bits or in 32 bits we will cast the pointer value in unsigned short or unsigned int :

16bits write

        *(unsigned short*)(ptr_fpga+(address)) = (unsigned short)value;


          value = *(unsigned short*)(ptr_fpga+(address));

32 bits write

        *(unsigned int*)(ptr_fpga+(address)) = (unsigned short)value;


          value = *(unsigned int*)(ptr_fpga+(address));

The problem

By default, if the specific arm920t target is not specified, arm-linux-gcc will try to generate compatible read/write for all ARM9 model when it access register in 16bits. Indeed it seems that not all ARM9 have 16bits read/write capabilities (ldrh asm instruction).

As the interface between i.MXL and FPGA on APF9328 has no 8bits read/write capabilities, each 8 bits access is recognized by the FPGA as a 16bits access. So on each 16bits access of the i.MXL, FPGA will process two 16bits access instead of 1. That is a problem when accessing a FIFO for example.

To avoid this painful problem don't forget the -mcpu=arm920t option when compiling fpgaregs for APF9328 and -mcpu=arm926ej-s for APF27.


On APF6_SP FPGA memory is accessed with PCIe, to read/write on FPGA space an example is available with pci_debug.


See pci_debug page to know how to access PCIe address space with a command line tool.