• 0
theAsker

Program code on PetaLinux

Question

Hello!

Here is another newbie question from me!

I am having a running hardware project from Vivado, and I also debugged bare metal code with SDK. Both was running perfectly.

5afad87e1734e_Screenshotfrom2018-05-1514-49-33.png.cdf78f1b17b7b8eff313ad35024847b8.png

Now I also got a PetaLinux (v2017.4) run on my Zybo Z7-20. So far so good.

First I thought, that I could only paste the c code on PetaLinux, compile it and let it run. But of course it didn't worked because I used bare metal code.

I wrote already that I have to add drivers to the device tree.

Here are my problems/questions:

1) I wrote something the following files: system-user.dtsi, zynq-7000.dtsi, system-top.dts and system-conf.dtsi

As I wrote I am only allowed to change stuff in the system-user.dtsi file. But when I compare the driver code from your Zybo Z7-20 and the stuff I found in the internet. It looks completly different. So where and what do I have add? I wrote that I have to enable the kernel configuration (where and how)? Am I doing this with petalinux-config -c kernel, is it also possible that when I use my own hardwaretarget for PetaLinux creating , that the enabeling of the drivers happened already?

2) I want to send the voltage values over ethernet, so do I need one or two drivers (XADC-driver or XADC + ethernet-driver) for it and also a gpio driver, right? Is there code for Zybo Z7-20 somewhere existing, or where can I find some code that is fitting? I made more trys but never the petalinux-build was successfully.

Thanks again for your help and time, it is always totally helpful! A+ for your job, helping a totally newbie ;-)

greetings,

Share this post


Link to post
Share on other sites

25 answers to this question

Recommended Posts

  • 0

hello,

 

1)

- 1st question: as you already figured you can define your device tree in a single file. See system-user.dtsi as reference in the petalinux platforms from digilent. I am a beginner, too but I think it is better to place it all in one file. Once your projects are more complex you can use several files. Instead of system-user.dtsi you can use your own file. You can change this in a file located in the directory where you place these files (meta_user/user_dtb/ something like that)

- 2nd question: to change the kernel configuration use the petalinux-tools. I don't know what you want to change, though

- 3rd question: have you had a look at UG1156?

- 4th question: no, this is usually not the case. You have to configure rootfs

 

2) 

- 1st question: yes (one driver for each is highly recommended ^^)

- 2nd question: http://www.wiki.xilinx.com/Linux+Drivers

Share this post


Link to post
Share on other sites
  • 0

To expand a little further on the XADC questions, first you need to add the following to system-user.dtsi:

&xadc_wiz_0 {
	clocks = <&clkc 15>;
	xlnx,channels {
		#address-cells = <1>;
		#size-cells = <0>;
		channelJA4@7 { /* vaux6 */
			reg = <7>;
		};
		channelJA2@8 {
			reg = <8>;
		};
		channelJA1@15 {
			reg = <15>;
		};
		channelJA3@16 {
			reg = <16>;
		};
	};
};

That should get the Xilinx XADC driver properly loaded. I'm also assuming that you are using the base-linux vivado project, or that you included the XADC wizard in your own block diagram (configured in the same way as base-linux). 

You can then cat the "raw" value in the /sys/ folder for the corresponding channel in the XADC driver folder (sorry, I don't have the entire path on hand). That will display the current voltage on the command line as a value from 0-4096 (corresponding to the voltages from 0v-1v). If you are needing to read the voltage in a custom program, just open and read the same value using standard file APIs. 

For sending data like this over a network, I'd recommend using the low-level socket APIs, they are the easiest to get working for stuff like this in my experience.

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)

Thanks for your answers, they are making the stuff more clear for me.

But I still have some questions about the XADC:

The driver you send is made for 4 Channels right? So in my case I only have to the register 7 (vaux6) and register 15 (vaux14). I configured the XADC to simultaneous access, but I read that this functions is not available for simultaneous access. Is there an other way to solve this problem?

Could I add a XADC module and map it, and read from some registers the voltage values from the simultaneous access? (in this case, I won't need a driver, right?)

I also have questions about the GPIO:

Is there also a drive like @sbobrowicz posted for XADC existing for GPIO? Is the reading/writing of the GPIO-ports the same than, like XADC? I found this one. Is this correct. It is looking too simple...

&axi_gpio_button {
	compatible = "generic-uio";
};

thanks a lot, and greetings,

Edited by theAsker

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)

yes, you can use UIO drivers for GPIO. These are standard drivers for Linux. There's an API you can use to read and write from UIO devices. I think the petalinux platform from Digilent is using it, too. Make sure the memory address in your device tree matches your address mapping. This is probably defined in some include file, in your case. Without using includes, this will do:

&axi_gpio_button {
    compatible = "generic-uio";
    reg = <0x43c00000 0x10000>;
};

(for some reason, my syntax highlighting is not working :( )

Edited by theUltimateSource

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)

Thank you for your answer! I will add this.

Can you tell me, where in /sys/bux/iio/devices/iio\:device0 I can find the external voltages I want to measure.

Because in this path I can find the following:

5afe95e0217da_Screenshotfrom2018-05-1810-58-24.png.6e468585e3d4c534b460ee4d47842551.png

My driver is looking like this:

&xadc_wiz_0 {
	clocks = <&clkc 15>;
	xlnx,channels {
		#address-cells = <1>;
		#size-cells = <0>;
		channelJA4@7 { /*Vaux6*/
			reg = <7>;
		};
		channelJA1@15 { /*Vaux14*/
			reg = <15>;
		};
	};
};

The voltage files are only counted from 0 to 7, looks a bit strange for me.

Edited by theAsker

Share this post


Link to post
Share on other sites
  • 0

I don't see the correct voltages in your snapshot... you might be looking at the driver for the XADC controller in the Zynq PS, which is not what you want. Does another device exist, perhaps /sys/bus/iio/devices/iio:device1 

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)

No there isn't an other path existing. The iio:device0 is the internal XADC. But there is not another iio:device appearing. Should be a problem with the device tree I think. But I don't know what I have to add to user-system.dtsi, or somewhere else. Maybe you can help me here. I added a comment in the code below:

/include/ "system-conf.dtsi"
/ {
	chosen {
        bootargs = "console=ttyPS0,115200 earlyprintk uio_pdrv_genirq.of_id=generic-uio";
		/*do I have to add here something, and if yes what?*/
    };
};

&xadc_wiz_0 {
  clocks = <&clkc 15>;
  xlnx,channels {
    #address-cells = <1>;
    #size-cells = <0>;
    channelJA4@7 {
      reg = <7>;
    };
    channelJA1@15 {
      reg = <15>;
    };
  };
};

&axi_gpio_rgb_led {
  compatible = "generic-uio";
};

&axi_gpio_button {
  compatible = "generic-uio";
};

&axi_gpio_segment {
  compatible = "generic-uio";
};     

The Xilinx_ADC_Driver in petalinux-config > industriell I/O is also enable. And the naming of the xadc_wiz_0 should also be correct, when you compare the posts above!

I also thought about a missing rootfs driver configuration. But I find nowhere informations about it, if I forget something. Only that the xilinx-xadc-driver has to be enabled. and it is.

Do you know what could be the problem?

Edited by theAsker

Share this post


Link to post
Share on other sites
  • 0

Hello!

I was able to fix the problem. I only had to add an interrupt to the bloc diagram and also to the device driver.

But now I have a now problem. The voltages I read are not correct. And I don't know why. In the attachment you can see my code for testing.

Is someone having a plan, why I am having this effect?

My driver is here:

&xadc_wiz_0 {
  interrupts = <0 53 4>;
  interrupt-parent = <0x1>;
  clocks = <&clkc 15>;
  xlnx,channels {
    #address-cells = <1>;
	#size-cells = <0>;
	channelJA4@7 {
	  reg = <7>;
	};
	channelJA1@15 {
	  reg = <15>;
	};
  };
};

My constraints are:

set_property -dict { PACKAGE_PIN N15   IOSTANDARD LVCMOS33 } [get_ports { Vaux14_0_v_p }]; #IO_L21P_T3_DQS_AD14P_35 Sch=JA1_R_p
#set_property -dict { PACKAGE_PIN L14   IOSTANDARD LVCMOS33 } [get_ports { ja[1] }]; #IO_L22P_T3_AD7P_35 Sch=JA2_R_P             
#set_property -dict { PACKAGE_PIN K16   IOSTANDARD LVCMOS33 } [get_ports { ja[2] }]; #IO_L24P_T3_AD15P_35 Sch=JA3_R_P            
set_property -dict { PACKAGE_PIN K14   IOSTANDARD LVCMOS33 } [get_ports { Vaux6_0_v_p }]; #IO_L20P_T3_AD6P_35 Sch=JA4_R_P             
set_property -dict { PACKAGE_PIN N16   IOSTANDARD LVCMOS33 } [get_ports { Vaux14_0_v_n }]; #IO_L21N_T3_DQS_AD14N_35 Sch=JA1_R_N        
#set_property -dict { PACKAGE_PIN L15   IOSTANDARD LVCMOS33 } [get_ports { ja[5] }]; #IO_L22N_T3_AD7N_35 Sch=JA2_R_N             
#set_property -dict { PACKAGE_PIN J16   IOSTANDARD LVCMOS33 } [get_ports { ja[6] }]; #IO_L24N_T3_AD15N_35 Sch=JA3_R_N            
set_property -dict { PACKAGE_PIN J14   IOSTANDARD LVCMOS33 } [get_ports { Vaux6_0_v_n }]; #IO_L20N_T3_AD6N_35 Sch=JA4_R_N             

The configuration of the XADC is:

AXI4Lite, channel_sequencer, AXI4 Stream Interface is false, Timing Mode is continuous, DCLK Frequency is 100MHz, Sequencer Mode is continuous, channel averaging ist none and enable external mux is also false.

xadcread.cpp

Share this post


Link to post
Share on other sites
  • 0

@theAsker

I didn't know the interrupt was required for the XADC wizard, thanks for providing that info.

FYI, since you are using petalinux, the interrupt properties should automatically be generated. After you add the interrupt to the block diagram and run petalinux-config --get-hw-description again, the interrupt properties should get automatically generated. So you should be able to remove the interrupts and interrupt-parent properties from your node in system-user.dtsi. The reason I'm mentioning this is that if you are setting the interrupts property incorrectly, that could be causing your problem.

It looks like you are trying to access the correct file. Do you get the expected value when you run the following from the command line:

cat /sys/bus/iio/devices/iio\:device1/in_voltage8_vaux6_raw

FYI, the voltages are differential, so the command above will give you the voltage across Pin 4 and Pin 10. When you set Pin 4 to GND and Pin 10 to GND, it should return ~0, and when you set Pin 4 to 1V (or higher) and Pin 10 to GND you should get ~4096. Verifying the command above will help determine if the issue is with how you are reading the file node in your program.

Share this post


Link to post
Share on other sites
  • 0

I get now the correct voltages. I had one mistake in the XADC configuration. But with the help of another post of you @sbobrowicz in an other thread, I was able to fix the problem.

I have two more questions:

1) If I want to safe the voltage values in a txt-file I am not able to read all values. I think the problem is, that my code works to slowly, or the linux system is to slowly.

Is there a way, to make the linux more quickly. Or are you having a hint how to write all values (with 1 MSPS) to a textfile, to send it afterwards?

2) There is a file called sample_frequency int the path /sys/bus/iio/devices/iio\:device1/ . I think this is the place to change the sample frequency. The value is very, very big. What is the value I have to calculate to change the sampling frequency to 320kSPS?

 

thanks for a lot of help!

Share this post


Link to post
Share on other sites
  • 0

@theAsker

Your questions are starting to cross the verge into "general linux" questions about the Industrial I/O (iio) interface. I'll take a crack, but you might get better answers on a more general linux forum. 

1) Are you printing anything to the terminal in your loop? This is the usual suspect when it comes slow-downs... keep in mind every message has to be sent over 115200 baud UART. If not, you could try printing time stamps and log messages to a log file to determine where your slow-down is occurring. You should probably already be reading a time value to attempt to control the sample rate of your loop.

Linux is non-deterministic, but you should still be able to accomplish what you are trying to do. If you think you are seeing too much variability in the sample time, you could look into the real-time Linux kernel patches (PREEMPT_RT)

2) You should check out the iio documentation in the Documentation folder of the Linux source. Or you can find some documentation for it online. Also, maybe you can set the sample rate in the Vivado block diagram by configuring the XADC Wizard

Share this post


Link to post
Share on other sites
  • 0

Thank you for your Answer!

Are you having an c/c++ - example, how I can read the external voltages controlled via the interrupt with my petalinux?

Share this post


Link to post
Share on other sites
  • 0

I'm a little confused here... it seems like your xadcread.cpp is already an example of that. I don't have any similar code myself, if that's what you're asking.

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)

Now I am also a bit confused. Cause I don't know which xadcread.cpp file you mean.

To explain my problem a bit more:

At the moment I read a voltage with the following code:

myvoltageIR.open("/sys/bus/iio/devices/iio\:device1/in_voltage9_vaux14_raw");
myvoltageIR >> voltage;
myvoltageIR.close();

But know I want to do this only, when I get an interrupt from XADC.

I also see, that there is an interrupt available: Cause if I do the following:

$ cat /proc/interrupt
\snip
46:	0	0	GIC-0	85	Level	43c00000.xadc_wiz
\snip

My problem now is, how do I build a c++ code/problem, that reads that interrupt and only reads the voltage, when an interrupt is available.

Edited by theAsker

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)

The important thing here is: I want an event driven sampling.

so always when a new sample value is there, i want that it will be read!

And it is absolutly not clear for me how to do it! I am really a newbie to this and I am feeling lost....

 

update question:

in the path: /sys/bus/iio/devices/trigger1/

there is uevent. Is this my trigger? or when is the value inside uevent not 0! What is this uevent?

Edited by theAsker

Share this post


Link to post
Share on other sites
  • 0

I'm really not going to be of much help here, I have not done what you are trying to do before. I think you should start by reading the Xilinx XADC linux driver documentation:

http://www.wiki.xilinx.com/xadc

That page also has a demo app packed inside a rootfs image that seems to be a great demo of what you are trying to do. I couldn't find the source, but you could try reaching out to Xilinx support and the Xilinx forum to request it. I imagine the Linux team would be fine with sharing it.

Share this post


Link to post
Share on other sites
  • 0

Hi! Thank you for your answer!

I read now something and found out, that the interrupt from the xadc block is not as i expected.

Do you have an idea, how I can (write a c++ code) read the external voltages so that i don't miss a value?

Thought about adding in the block design a clock devider and then connect it via UIO to use it then as an interrupt?

Do you have another idea, or how do you solve it, getting really all voltages read, you want?

Share this post


Link to post
Share on other sites
  • 0

Hello, I have some questions about application code of linux, for reading the external voltages.

1) At the moment I use the c++ commands: file.read("path"); then file >> array; and at least file.close();

It there a much more gentil way to solve it. For example mmaping the registers directly to PS.

I thought about using the "GPIO" mapping function for reading ports: *((volatile unsigned *)(gpio_base + offset));

Is this possible. And if yes, how will i figure out the gpio_base (of course I mean here the xadc_base) and the necessary offset?

2) Is there the possibility to do the same with EOS (interrupt if the converting worked)?

 

thanks for your help!

Share this post


Link to post
Share on other sites
  • 0

You should not use mmap or try to access the XADC drivers directly from user space, just use the IIO files.

Here is the IIO documentation: https://01.org/linuxgraphics/gfx-docs/drm/driver-api/iio/core.html#industrial-i-o-devices

You should try accessing /dev/iio:deviceX as described in that document. Turns out the kernel should ensure the adc is sampled at a constant rate and then inserted into a buffer that can be read using that file.  The sampling_frequency file in /sys/bus/ also seems to control the sampling rate (not sure if you can change it or not for the Xilinx driver).

There is complete documentation there, and also some third party libraries that might help you out (see libiio). 

Share this post


Link to post
Share on other sites
  • 0
Posted (edited)

Thanks for the response!

About working with /dev/iio:deviceX it isn't writte so much down. And reading from files is in my application to slow.

So I am back at my solution way with mapping from /dev/mem.

So for it is working correctly. I am possible to map.

But if i let the code run. I only get 0 for voltages.

I read something, that you have to disable the driver. I tried it both ways, but nothing happend.

Maybe you could have a look over my code and you're seeing my mistake right now.

#include <stdio.h>
#include <stdlib.h>

#define XADC_START_ADDR		0x43C00000
#define XADC_END_ADDR		0x43C0FFFF
#define XADC_SIZE		(XADC_END_ADDR - XADC_START_ADDR)

#define	UINT16			unsigned short
#define UINT32			unsigned long


int main ()
{
	int fd = open("/dev/mem", O_RDWR|O_SYNC);
	UINT16 test;
	if (fd == -1)
	{
		printf("Error: no open /dev/mem\n");
		return -1;
	}
	
	char *xadc_addr = (char *)mmap(0, XADC_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, XADC_START_ADDR);
	if (xadc_addr == MAP_FAILED)
	{
		printf("Error: no mmap\n");
		return -1;
	}

  	//here could be the problem, but if I add the enable, i get the error: "segmentation fault"
	//enable: JTAG DRP (for measure external voltages)
	//*((volatile UINT32 *)(xadc_addr + 0x02)) = 0x0001;
	if(*((volatile UINT32 *)(xadc_addr + 0x02)) == 0x0001)
	{
		printf("Error: enable for external voltagemessurement");
		return -1;
	}
	
	//continuous sampling mode in config_register0
	*((volatile UINT16 *)(xadc_addr + 0x40)) |= 0x0400;
	if (*((volatile UINT16 *)(xadc_addr + 0x40)) == 0x0400)
	{
		printf("continuous sampling mode\n");
		return -1;
	}
	//UINT16 volatile* pReg = (UINT16 volatile*)(xadc_addr + 0x40);
	//UINT16 val = *pReg;
	//printf("Register 40 is %u\n", val);

	//simultaneous mode in config_register1
	*((volatile UINT16 *)(xadc_addr + 0x41)) |= 0x4000;
	if (*((volatile UINT16 *)(xadc_addr + 0x41)) == 0x4000)
	{
		printf("Error: no configuration of simultaneous mode\n");
		return -1;
	}

	//enable calibration
	*((volatile UINT16 *)(xadc_addr + 0x48)) |= 0x0001;
	if (*((volatile UINT16 *)(xadc_addr + 0x41)) == 0x0001)
	{
		printf("Error: enable calibration\n");
		return -1;
	}

	//ADC powered up and ADC-Clock = DCLK/2 in config_register2
	*((volatile UINT16 *)(xadc_addr + 0x42)) |= 0x0400;
	if (*((volatile UINT16 *)(xadc_addr + 0x42)) == 0x0400)
	{
		printf("Error: ADC powered up and ADC-Clock = DCLK/2\n");
		return -1;
	}

	//enable channel 6 and 14 for simultaneous mode
	*((volatile UINT16 *)(xadc_addr + 0x49)) |= 0x4000;
	if (*((volatile UINT16 *)(xadc_addr + 0x49)) == 0x4000)
	{
		printf("Error: simultaneous mode for channel 6 and 14\n");
		return -1;
	}

	//enable flags
	*((volatile UINT16 *)(xadc_addr + 0x3F)) |= 0x0400;
	if(*((volatile UINT16 *)(xadc_addr + 0x3F)) == 0x0400)
	{
		printf("Error: enable JTAG_XADC\n");
		return -1;
	}

	//Read: channel 6
	unsigned int val1 = *((volatile UINT16 *)(xadc_addr + 0x16));
	printf("channel 6: %u\n", val1);


	printf("successful test\n");

	close(fd);

	return 0;
}

507742163_Screenshotfrom2018-06-1507-27-29.png.a1fa6ddb4563b042b48c9315707fdc8f.png

 

thanks for all help!

Edited by theAsker

Share this post


Link to post
Share on other sites
  • 0

@theAsker

Please provide more information about what you mean when you say you cannot write to the XADC registers. Are you receiving an error?

Also, I'm not certain your addresses in the code above are correct. For one, in the commented out line where you try to enable the core, you write to address 0x02, which doesn't make sense since the registers are 32-bits (they should all be 4-byte aligned: 0x00,0x04,0x08,0x0C...)

Share this post


Link to post
Share on other sites
  • 0

I want to map the XADC from user space.

I use for this:

#define START_ADDRESS_XADC	0x43C00000

int fd = open("/dev/mem", O_RDWR|O_SYNC);
char *ptr = (char *)mmap(NULL, 0x10000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, START_ADDRESS_XADC);

when I want to write now to the registers like:

ptr[ 0x40 ] = 0x8200;

And I read the address before writing, it returns a 0. After writing, also a 0.

So I think, a don't have permissions to write to this address.

Why and how do I fix that?

Share this post


Link to post
Share on other sites
  • 0

Try printing the values of fd and ptr to see if open and mmap are returning successfully.

Also, you should see the XADC wizard documentation: https://www.xilinx.com/support/documentation/ip_documentation/xadc_wiz/v3_3/pg091-xadc-wiz.pdf . The register space section of that document indicates that 0x40 is not a valid address on the AXI Lite interface. You are likely trying to access the DRP registers, which have been mapped to addresses starting at 0x300. 

Also, you should probably cast ptr to (volatile uint32_t *) or (volatile unsigned long *) in order to read the entire 32-bit register. See this example for a good way to do this rather than using array notation: http://www.wiki.xilinx.com/Linux+User+Mode+Pseudo+Driver

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now