cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
joancab
Advisor
Advisor
345 Views
Registered: ‎05-11-2015

Acess IP core Axi Lite port from Linux

Jump to solution

I have a custom IP core with an AXI lite port for its configuration. 

Vivado creates drivers files, I have used them in bare metal and it's fine.

Now in Linux I'm lost. From the header file, there are functions that are not grayed out with the __linux__ define, so I assume they can be used in linux, but they cannot as they try to access the physical memory. I'm talking about the

X<function>_Get_<parameter>   and   X<function>_Set_<parameter>

functions. 

So I suppose the way is to map the physical address to a virtual one and read/ write there, but I could not find an example or a clue of which functions to use.

Tags (3)
0 Kudos
1 Solution

Accepted Solutions
rfs613
Scholar
Scholar
197 Views
Registered: ‎05-28-2013

I'd suggest checking the UIO entry in the device tree, specifically the "reg" attribute. It should list the physical starting address, and the length. In your case I suspect there will be 4 sets of values. However from the screenshot provided, it seems the address and length are all 0. Which in turn causes the mmap() to fail, returning -1, which becomes 0xFFFFFFFFFFFFFFFF when cast to a 64-bit pointer.

View solution in original post

0 Kudos
8 Replies
rfs613
Scholar
Scholar
313 Views
Registered: ‎05-28-2013

The usual approaches include the following, listed roughly in order of increasing complexity:

  • using "devmem" utility to read/write the physical address -- a good starting point, and sufficient to set a couple of registers on startup, etc.
  • using UIO driver (in the kernel)  -- essentially this takes care of the virt/phys mappings, giving your code direct access to your registers.
  • writing a real kernel driver that provides a suitable API to userspace (you get to pick the API)
  • using DMA or similar to transfer data

Which one to pick depends on your needs (how much data are you moving?) and your background (eg. if you know python, you probably won't enjoy writing kernel driver in C). I would encourage starting simple and moving to more complex solutions if necessary. Don't be fooled by claims that the code will write itself; invariably this only works in limited cases, and the results are often pretty horrible.

0 Kudos
joancab
Advisor
Advisor
308 Views
Registered: ‎05-11-2015

Probably devmem or UIO will do, I'm not going to mess with the kernel and don't need a DMA to read/ write discrete parameters.

I have used custom cores with bare metal software enough times to feel comfortable. Vivado creates h and c files, then there are functions to initialize, start, stop, reset, read and write. It's all well done.

Now with Linux that doesn't work. The generated files have #ifdef __linux__ pragmas but the apparently available functions won't work under linux (they try to read/ write physical memory from user space), so that's broken, to start with.

I didn't think of devmem, probably the simplest. So simple that I wonder why linux versions of the read and write functions that are 1-line for bare metal are not included. Vivado creates a <function>_linux.c file, so it looks like the included functions are not going to work and the necessary functions are not included.

0 Kudos
joancab
Advisor
Advisor
237 Views
Registered: ‎05-11-2015

devmem is a CLI command, need proper software (I know apps can invoke commands but isn't it just over convoluted?), so UIO seems to be the way here.

0 Kudos
joancab
Advisor
Advisor
211 Views
Registered: ‎05-11-2015

Apparently, when the linux function X<function>_Initialize is executed, it maps the IP core addresses to virtual addresses:

int XInterleaver_Initialize(XInterleaver *InstancePtr, const char* InstanceName) {
	....

    // NOTE: slave interface 'Control' should be mapped to uioX/map0
    InstancePtr->Control_BaseAddress = (u64)mmap(NULL, InfoPtr->maps[0].size, PROT_READ|PROT_WRITE, MAP_SHARED, InfoPtr->uio_fd, 0 * getpagesize());
    assert(InstancePtr->Control_BaseAddress);

    // NOTE: slave interface 'Param' should be mapped to uioX/map1
    InstancePtr->Param_BaseAddress = (u64)mmap(NULL, InfoPtr->maps[1].size, PROT_READ|PROT_WRITE, MAP_SHARED, InfoPtr->uio_fd, 1 * getpagesize());
    assert(InstancePtr->Param_BaseAddress);

    InstancePtr->IsReady = XIL_COMPONENT_IS_READY;

    return XST_SUCCESS;
}

Nevertheless I'm getting this, what produces a segmentation error:

joancab_0-1620131502263.png

 

0 Kudos
rfs613
Scholar
Scholar
198 Views
Registered: ‎05-28-2013

I'd suggest checking the UIO entry in the device tree, specifically the "reg" attribute. It should list the physical starting address, and the length. In your case I suspect there will be 4 sets of values. However from the screenshot provided, it seems the address and length are all 0. Which in turn causes the mmap() to fail, returning -1, which becomes 0xFFFFFFFFFFFFFFFF when cast to a 64-bit pointer.

View solution in original post

0 Kudos
joancab
Advisor
Advisor
193 Views
Registered: ‎05-11-2015

Mmm, I defined them in system-user.dtsi as:

	interleaver_0{
		compatible="generic-uio";
		reg = <0x80000000 0x10000 0x80010000 0x10000>;
	};

And I just noticed that in pl.dtsi they have 64-bit addresses:

	amba_pl: amba_pl@0 {
		#address-cells = <2>;
		#size-cells = <2>;
		compatible = "simple-bus";
		ranges ;
		interleaver_0: interleaver@80000000 {
			clock-names = "ap_clk";
			clocks = <&zynqmp_clk 71>;
			compatible = "xlnx,interleaver-0.06";
			interrupt-names = "interrupt";
			interrupt-parent = <&gic>;
			interrupts = <0 89 4>;
			reg = <0x0 0x80000000 0x0 0x10000 0x0 0x80010000 0x0 0x10000>;
			xlnx,s-axi-control-addr-width = <0x5>;
			xlnx,s-axi-control-data-width = <0x20>;
			xlnx,s-axi-param-addr-width = <0x7>;
			xlnx,s-axi-param-data-width = <0x20>;

Actually, I think I didn't need to re-specify the reg line.

0 Kudos
joancab
Advisor
Advisor
181 Views
Registered: ‎05-11-2015

Yep, now I got it, it was the reg line wanting 64-bit values

joancab_0-1620137952705.png

 

0 Kudos
rfs613
Scholar
Scholar
172 Views
Registered: ‎05-28-2013

Glad you got it sorted. By the way, there is a clue in pl.dtsi file. It says #address-cells = <2> which means that each address is made up of 2 cells (each cell being a 32-bit integer). Likewise for the length, these are 64-bit values.