Sorry, you need to enable JavaScript to visit this website.

Linux /dev/mem accessing switch values

Solved
11 posts / 0 new
mark.mahowald's picture
mark.mahowald
Junior(3)
Linux /dev/mem accessing switch values

After reading a lot of posts it seems like I am on the right track for doing this correctly, but yet it still does not quite work.

Kernel: http://www.wiki.xilinx.com/Zynq+2015.4+Release

I took the zedboard release file, extracted it, and then copied it over to an SD card and booted from that. Seems to work fine.

Tools: Vivado 2015.4 and SDK 2015.4

After that, I create a hardware .bit file using an AXI GPIO IP block to connect to the PS to the switches. I generate the .bit file, export it to the SDK, and then launch the SDK.

Based on the system.hdf file, the axi_gpio_0 is located at 0x41200000. So this is where I assume I need to mmap /dev/mem in order to access the values.

I start a new application project, select Linux, and create the C++ project. Then I program the FPGA after the OS has been loaded from the SD card.

The code I use to try and access the values at the AXI_GPIO address is below. I try to map just one byte because the GPIO register with the values is in the first byte (at least I believe it is...). I have also tried mapping 0xFFFF bytes (which is the end address of the AXI GPIO block) and getpagesize() bytes as well (so 4096 in this case).

Even more aggravating is when I do a bare metal application and de-reference 0x41200000, it accurately reads the switches.

I also tried it as a Linux C application, but that too hangs so it is not C++ versus C related as far as I can tell.

I am using the SDK's TCF to download and run the application. It runs in /tmp/ (not sure if that is important or not...)

#include <iostream>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/fcntl.h>

const unsigned int64_t gpioAddr = 0x41200000;

int devMemSwitchTest()
{

int x = 0;
int fd = 0; fd = open("/dev/mem", O_RDWR | O_SYNC);
volatile unsigned int* addr = NULL;
volatile unsigned int* start = NULL;
off_t offset = gpioAddr & ~(getpagesize()-1);

if (fd == -1)
{
perror("Failed to open /dev/mem: ");
close(fd);
return -1;
}

start = (unsigned int*)mmap(NULL, 1, PROT_READ | PROT_WRITE, MAP_SHARED,
fd, offset);
if (start == MAP_FAILED)
{
perror("ERROR with mmap: ");
munmap((void*)(start), getpagesize());
close(fd);
return -1;
}
addr = (unsigned int*)start + (gpioAddr - offset); //this happens to evaluate to addr = start since it is already page aligned.

//unsigned int y = *((unsigned int*)start); <-- freezes here if not commented out!!

munmap((void*)(start), getpagesize());
close(fd);
return 1;
}

int main() {

std::cout << "Attempting to opend /dev/mem" << std::endl;
devMemSwitchTest();

std::cout << "We made it!!!" << std::endl;
return 1;
}

Thanks in advance for any help provided! This has been driving me crazy for the past two days now...

Mark

phbs's picture
phbs
Junior(1)
The same error

I got the same error Mark, no matter if I try to access the memory address from my AXI4 Lite IP Blocks by using c application or "devmem" command in PetaLinux 2015.4 the OS hangs.

mark.mahowald's picture
mark.mahowald
Junior(3)
Well at least it's not just me!

Wondering if 2015.4 PetaLinux broke something...I'll give it a shot with an earlier version and post back here how it goes (hopefully today, but for sure early next week).

Thanks,

Mark

zedhed's picture
zedhed
Moderator(25)
RE: Linux /dev/mem accessing switch values

Hi Mark,

I had a question about why the unsigned int64_t gpioAddr is needed to be declared as that wide of an integer when we are dealing with a 32-bit memory space on this platform?

I have not tried this out yet, but we use the AXI GPIO blocks in our factory test to do a simple I/O loopback on the FMC connector. This is currently being done under 2015.2 PetaLinux but will be tested under 2015.4 eventually.

For this, two main pieces are needed: the DTS entry for your AXI GPIO block and some sort of mmap function into that PL memory space for the AXI GPIO block:

My DTS entry looks like this for the two AXI GPIO blocks used in the test:

axi_gpio_0: gpio@41200000 {
#gpio-cells = <2>;
compatible = "xlnx,xps-gpio-1.00.a";
gpio-controller ;
reg = <0x41200000 0x10000>;
xlnx,all-inputs = <0x0>;
xlnx,all-inputs-2 = <0x0>;
xlnx,all-outputs = <0x0>;
xlnx,all-outputs-2 = <0x0>;
xlnx,dout-default = <0x00000000>;
xlnx,dout-default-2 = <0x00000000>;
xlnx,gpio-width = <0x12>;
xlnx,gpio2-width = <0x12>;
xlnx,interrupt-present = <0x0>;
xlnx,is-dual = <0x1>;
xlnx,tri-default = <0xFFFFFFFF>;
xlnx,tri-default-2 = <0xFFFFFFFF>;
};
axi_gpio_1: gpio@41210000 {
#gpio-cells = <2>;
compatible = "xlnx,xps-gpio-1.00.a";
gpio-controller ;
reg = <0x41210000 0x10000>;
xlnx,all-inputs = <0x0>;
xlnx,all-inputs-2 = <0x0>;
xlnx,all-outputs = <0x0>;
xlnx,all-outputs-2 = <0x0>;
xlnx,dout-default = <0x00000000>;
xlnx,dout-default-2 = <0x00000000>;
xlnx,gpio-width = <0x12>;
xlnx,gpio2-width = <0x12>;
xlnx,interrupt-present = <0x0>;
xlnx,is-dual = <0x1>;
xlnx,tri-default = <0xFFFFFFFF>;
xlnx,tri-default-2 = <0xFFFFFFFF>;
};

The test code which uses mmap() in pl_gpio_udriver.c is now up on our Github software repository.

https://github.com/Avnet/software/blob/master/petalinux/apps/linux_fmc_l...

This may have broken in 2015.4 PetaLinux but at least it gives you something that is known to have worked in something as recent as 2015.2 PetaLinux.

Regards,

-Kevin

mark.mahowald's picture
mark.mahowald
Junior(3)
DTS Entry

Hey Kevin!

Good catch on the int64_t. I'll knock that down to something that makes more sense.

Thanks for the response! When you talk about the DTS entry, how would I go about making that change? Since everything I used was precompiled I am not sure how to go about changing the DTS. I know what it is and understand what it does, but I do not know how to update it appropriately.

Maybe I will give my code a shot with the 2015.2 version and see if it behaves a little better.

Thanks,

Mark

mark.mahowald's picture
mark.mahowald
Junior(3)
Tried version 2015.2 pre-built

Same issue as before. Also I tried using the devmem command as well and it also freezes. Guessing this is either a DTS issue or I need to build my own version of the boot.bin file that loads the .bit automatically.

Will continue to investigate...

Mark

mark.mahowald's picture
mark.mahowald
Junior(3)
Fixed it

So in case anybody else needs to do this, here is what I had to do (from SDK onwards; in Vivado the steps are adding the appropriate IP block, then building the .bit, then exporting it to SDK, then launching SDK).

By the way, MAKE SURE TO MAKE A COPY OF THE CURRENT SD CARD THAT WORKS!!! Otherwise you can brick the system by having an invalid SD card and make it very hard to re-boot it.

1. Copy the necessary files:

1a. Grab Xilinx release at http://www.wiki.xilinx.com/Zynq+2015.2+Release
1b. Git clone the Xilinx device tree (git clone git://github.com/Xilinx/device-tree-xlnx.git) or just grab the zip file and unpack it.

2. Setup SDK for building a device tree.

2a. Go Xilinx Tools->Repositories
2b. Select New
2c. A browser window will pop up. Point at the root of the directory of 1b (For example: <path to files>\device-tree-xlnx-xilinx-v2015.4)
2d. Hit Apply, then OK

3. Build a new device tree

3a. File->New->Board Support Package
3b. In the pop up window, name the project (for example, LinuxDeviceTree).
3c. In the same window, in the bottom left corner there should be an option for "device_tree". Select that one and click Finish (assuming you exported only one Hardware Platform .bit file from Vivado, it should by default be selected for the BSP).

4. If build automatically is turned off, build the device_tree BSP project you just created.

5. Open the Project folder for the device tree BSP. In there you should see several .dtsi/.dts files. The one you can build and make work for the zedboard is the system.dts (however I am going to explain a different approach that is specific to the Zedboard .dtb that comes from Xilinx's releases; in my opinion this approach is slightly easier).

6. Open pl.dtsi; in there you will see the correct entry for the AXI GPIO IP block in a .dts file. We will use this in a little bit.

7. Outside of SDK, go to where the files from 1a are located.

7a. Find the devicetree.dtb file.
7b. "Reverse" compile this file into a .dts by using the following command: dtc -I dtb -O dts -o devicetree.dts devicetree.dtb (if you need dtc, google on how to locate it; Linux it's very easy, Windows it is a little harder...)
7c. A "devicetree.dts" file should have been created. Open up this file. This is what describes a Zedboard's peripherals to a Linux OS.
7d. From step 6, copy everything starting from "amba_pl" to the end, and paste it right before the last curly brace. The final result should look like the following:

/dts-v1/;

/ {
#address-cells = <0x1>;
#size-cells = <0x1>;
compatible = "xlnx,zynq-zed", "xlnx,zynq-7000";
model = "Zynq Zed Development Board";

chosen {
bootargs = "console=ttyPS0,115200 root=/dev/ram rw earlyprintk";
linux,stdout-path = "/amba/serial@e0001000";
};

aliases {
ethernet0 = "/amba/ethernet@e000b000";
serial0 = "/amba/serial@e0001000";
spi0 = "/amba/spi@e000d000";
};

memory {
device_type = "memory";
reg = <0x0 0x20000000>;
};

cpus {
#address-cells = <0x1>;
#size-cells = <0x0>;

cpu@0 {
compatible = "arm,cortex-a9";
device_type = "cpu";
reg = <0x0>;
clocks = <0x1 0x3>;
clock-latency = <0x3e8>;
cpu0-supply = <0x2>;
operating-points = <0xa2c2b 0xf4240 0x51616 0xf4240>;
};

cpu@1 {
compatible = "arm,cortex-a9";
device_type = "cpu";
reg = <0x1>;
clocks = <0x1 0x3>;
};
};

pmu {
compatible = "arm,cortex-a9-pmu";
interrupts = <0x0 0x5 0x4 0x0 0x6 0x4>;
interrupt-parent = <0x3>;
reg = <0xf8891000 0x1000 0xf8893000 0x1000>;
};

fixedregulator@0 {
compatible = "regulator-fixed";
regulator-name = "VCCPINT";
regulator-min-microvolt = <0xf4240>;
regulator-max-microvolt = <0xf4240>;
regulator-boot-on;
regulator-always-on;
linux,phandle = <0x2>;
phandle = <0x2>;
};

amba {
compatible = "simple-bus";
#address-cells = <0x1>;
#size-cells = <0x1>;
interrupt-parent = <0x3>;
ranges;

adc@f8007100 {
compatible = "xlnx,zynq-xadc-1.00.a";
reg = <0xf8007100 0x20>;
interrupts = <0x0 0x7 0x4>;
interrupt-parent = <0x3>;
clocks = <0x1 0xc>;
};

can@e0008000 {
compatible = "xlnx,zynq-can-1.0";
status = "disabled";
clocks = <0x1 0x13 0x1 0x24>;
clock-names = "can_clk", "pclk";
reg = <0xe0008000 0x1000>;
interrupts = <0x0 0x1c 0x4>;
interrupt-parent = <0x3>;
tx-fifo-depth = <0x40>;
rx-fifo-depth = <0x40>;
};

can@e0009000 {
compatible = "xlnx,zynq-can-1.0";
status = "disabled";
clocks = <0x1 0x14 0x1 0x25>;
clock-names = "can_clk", "pclk";
reg = <0xe0009000 0x1000>;
interrupts = <0x0 0x33 0x4>;
interrupt-parent = <0x3>;
tx-fifo-depth = <0x40>;
rx-fifo-depth = <0x40>;
};

gpio@e000a000 {
compatible = "xlnx,zynq-gpio-1.0";
#gpio-cells = <0x2>;
clocks = <0x1 0x2a>;
gpio-controller;
interrupt-parent = <0x3>;
interrupts = <0x0 0x14 0x4>;
reg = <0xe000a000 0x1000>;
};

i2c@e0004000 {
compatible = "cdns,i2c-r1p10";
status = "disabled";
clocks = <0x1 0x26>;
interrupt-parent = <0x3>;
interrupts = <0x0 0x19 0x4>;
reg = <0xe0004000 0x1000>;
#address-cells = <0x1>;
#size-cells = <0x0>;
};

i2c@e0005000 {
compatible = "cdns,i2c-r1p10";
status = "disabled";
clocks = <0x1 0x27>;
interrupt-parent = <0x3>;
interrupts = <0x0 0x30 0x4>;
reg = <0xe0005000 0x1000>;
#address-cells = <0x1>;
#size-cells = <0x0>;
};

interrupt-controller@f8f01000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <0x3>;
interrupt-controller;
reg = <0xf8f01000 0x1000 0xf8f00100 0x100>;
linux,phandle = <0x3>;
phandle = <0x3>;
};

cache-controller@f8f02000 {
compatible = "arm,pl310-cache";
reg = <0xf8f02000 0x1000>;
arm,data-latency = <0x3 0x2 0x2>;
arm,tag-latency = <0x2 0x2 0x2>;
cache-unified;
cache-level = <0x2>;
};

memory-controller@f8006000 {
compatible = "xlnx,zynq-ddrc-a05";
reg = <0xf8006000 0x1000>;
xlnx,has-ecc = <0x0>;
};

ocmc@f800c000 {
compatible = "xlnx,zynq-ocmc-1.0";
interrupt-parent = <0x3>;
interrupts = <0x0 0x3 0x4>;
reg = <0xf800c000 0x1000>;
};

serial@e0000000 {
compatible = "xlnx,xuartps", "cdns,uart-r1p8";
status = "disabled";
clocks = <0x1 0x17 0x1 0x28>;
clock-names = "uart_clk", "pclk";
reg = <0xe0000000 0x1000>;
interrupts = <0x0 0x1b 0x4>;
};

serial@e0001000 {
compatible = "xlnx,xuartps", "cdns,uart-r1p8";
status = "okay";
clocks = <0x1 0x18 0x1 0x29>;
clock-names = "uart_clk", "pclk";
reg = <0xe0001000 0x1000>;
interrupts = <0x0 0x32 0x4>;
};

spi@e0006000 {
compatible = "xlnx,zynq-spi-r1p6";
reg = <0xe0006000 0x1000>;
status = "disabled";
interrupt-parent = <0x3>;
interrupts = <0x0 0x1a 0x4>;
clocks = <0x1 0x19 0x1 0x22>;
clock-names = "ref_clk", "pclk";
#address-cells = <0x1>;
#size-cells = <0x0>;
};

spi@e0007000 {
compatible = "xlnx,zynq-spi-r1p6";
reg = <0xe0007000 0x1000>;
status = "disabled";
interrupt-parent = <0x3>;
interrupts = <0x0 0x31 0x4>;
clocks = <0x1 0x1a 0x1 0x23>;
clock-names = "ref_clk", "pclk";
#address-cells = <0x1>;
#size-cells = <0x0>;
};

spi@e000d000 {
clock-names = "ref_clk", "pclk";
clocks = <0x1 0xa 0x1 0x2b>;
compatible = "xlnx,zynq-qspi-1.0";
status = "okay";
interrupt-parent = <0x3>;
interrupts = <0x0 0x13 0x4>;
reg = <0xe000d000 0x1000>;
#address-cells = <0x1>;
#size-cells = <0x0>;
is-dual = <0x0>;
num-cs = <0x1>;

flash@0 {
compatible = "n25q128a11";
reg = <0x0>;
spi-tx-bus-width = <0x1>;
spi-rx-bus-width = <0x4>;
spi-max-frequency = <0x2faf080>;
#address-cells = <0x1>;
#size-cells = <0x1>;

partition@qspi-fsbl-uboot {
label = "qspi-fsbl-uboot";
reg = <0x0 0x100000>;
};

partition@qspi-linux {
label = "qspi-linux";
reg = <0x100000 0x500000>;
};

partition@qspi-device-tree {
label = "qspi-device-tree";
reg = <0x600000 0x20000>;
};

partition@qspi-rootfs {
label = "qspi-rootfs";
reg = <0x620000 0x5e0000>;
};

partition@qspi-bitstream {
label = "qspi-bitstream";
reg = <0xc00000 0x400000>;
};
};
};

memory-controller@e000e000 {
#address-cells = <0x1>;
#size-cells = <0x1>;
status = "disabled";
clock-names = "memclk", "aclk";
clocks = <0x1 0xb 0x1 0x2c>;
compatible = "arm,pl353-smc-r2p1";
interrupt-parent = <0x3>;
interrupts = <0x0 0x12 0x4>;
ranges;
reg = <0xe000e000 0x1000>;

flash@e1000000 {
status = "disabled";
compatible = "arm,pl353-nand-r2p1";
reg = <0xe1000000 0x1000000>;
#address-cells = <0x1>;
#size-cells = <0x1>;
};

flash@e2000000 {
status = "disabled";
compatible = "cfi-flash";
reg = <0xe2000000 0x1000>;
#address-cells = <0x1>;
#size-cells = <0x1>;
};
};

ethernet@e000b000 {
compatible = "xlnx,ps7-ethernet-1.00.a";
reg = <0xe000b000 0x1000>;
status = "okay";
interrupts = <0x0 0x16 0x4>;
clocks = <0x1 0xd 0x1 0x1e>;
clock-names = "ref_clk", "aper_clk";
local-mac-address = [00 0a 35 00 00 00];
xlnx,has-mdio = <0x1>;
#address-cells = <0x1>;
#size-cells = <0x0>;
phy-mode = "rgmii-id";
phy-handle = <0x4>;

ethernet-phy@0 {
reg = <0x0>;
linux,phandle = <0x4>;
phandle = <0x4>;
};
};

ethernet@e000c000 {
compatible = "xlnx,ps7-ethernet-1.00.a";
reg = <0xe000c000 0x1000>;
status = "disabled";
interrupts = <0x0 0x2d 0x4>;
clocks = <0x1 0xe 0x1 0x1f>;
clock-names = "ref_clk", "aper_clk";
local-mac-address = [00 0a 35 00 00 00];
xlnx,has-mdio = <0x1>;
#address-cells = <0x1>;
#size-cells = <0x0>;
};

sdhci@e0100000 {
compatible = "arasan,sdhci-8.9a";
status = "okay";
clock-names = "clk_xin", "clk_ahb";
clocks = <0x1 0x15 0x1 0x20>;
interrupt-parent = <0x3>;
interrupts = <0x0 0x18 0x4>;
reg = <0xe0100000 0x1000>;
};

sdhci@e0101000 {
compatible = "arasan,sdhci-8.9a";
status = "disabled";
clock-names = "clk_xin", "clk_ahb";
clocks = <0x1 0x16 0x1 0x21>;
interrupt-parent = <0x3>;
interrupts = <0x0 0x2f 0x4>;
reg = <0xe0101000 0x1000>;
};

slcr@f8000000 {
#address-cells = <0x1>;
#size-cells = <0x1>;
compatible = "xlnx,zynq-slcr", "syscon";
reg = <0xf8000000 0x1000>;
ranges;

clkc@100 {
#clock-cells = <0x1>;
compatible = "xlnx,ps7-clkc";
ps-clk-frequency = <0x1fca055>;
fclk-enable = <0xf>;
clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x", "cpu_3or2x", "cpu_2x", "cpu_1x", "ddr2x", "ddr3x", "dci", "lqspi", "smc", "pcap", "gem0", "gem1", "fclk0", "fclk1", "fclk2", "fclk3", "can0", "can1", "sdio0", "sdio1", "uart0", "uart1", "spi0", "spi1", "dma", "usb0_aper", "usb1_aper", "gem0_aper", "gem1_aper", "sdio0_aper", "sdio1_aper", "spi0_aper", "spi1_aper", "can0_aper", "can1_aper", "i2c0_aper", "i2c1_aper", "uart0_aper", "uart1_aper", "gpio_aper", "lqspi_aper", "smc_aper", "swdt", "dbg_trc", "dbg_apb";
reg = <0x100 0x100>;
linux,phandle = <0x1>;
phandle = <0x1>;
};
};

dmac@f8003000 {
compatible = "arm,pl330", "arm,primecell";
reg = <0xf8003000 0x1000>;
interrupt-parent = <0x3>;
interrupt-names = "abort", "dma0", "dma1", "dma2", "dma3", "dma4", "dma5", "dma6", "dma7";
interrupts = <0x0 0xd 0x4 0x0 0xe 0x4 0x0 0xf 0x4 0x0 0x10 0x4 0x0 0x11 0x4 0x0 0x28 0x4 0x0 0x29 0x4 0x0 0x2a 0x4 0x0 0x2b 0x4>;
#dma-cells = <0x1>;
#dma-channels = <0x8>;
#dma-requests = <0x4>;
clocks = <0x1 0x1b>;
clock-names = "apb_pclk";
};

devcfg@f8007000 {
clock-names = "ref_clk", "fclk0", "fclk1", "fclk2", "fclk3";
clocks = <0x1 0xc 0x1 0xf 0x1 0x10 0x1 0x11 0x1 0x12>;
compatible = "xlnx,zynq-devcfg-1.0";
interrupt-parent = <0x3>;
interrupts = <0x0 0x8 0x4>;
reg = <0xf8007000 0x100>;
};

timer@f8f00200 {
compatible = "arm,cortex-a9-global-timer";
reg = <0xf8f00200 0x20>;
interrupts = <0x1 0xb 0x301>;
interrupt-parent = <0x3>;
clocks = <0x1 0x4>;
};

timer@f8001000 {
interrupt-parent = <0x3>;
interrupts = <0x0 0xa 0x4 0x0 0xb 0x4 0x0 0xc 0x4>;
compatible = "cdns,ttc";
clocks = <0x1 0x6>;
reg = <0xf8001000 0x1000>;
};

timer@f8002000 {
interrupt-parent = <0x3>;
interrupts = <0x0 0x25 0x4 0x0 0x26 0x4 0x0 0x27 0x4>;
compatible = "cdns,ttc";
clocks = <0x1 0x6>;
reg = <0xf8002000 0x1000>;
};

timer@f8f00600 {
interrupt-parent = <0x3>;
interrupts = <0x1 0xd 0x301>;
compatible = "arm,cortex-a9-twd-timer";
reg = <0xf8f00600 0x20>;
clocks = <0x1 0x4>;
};

watchdog@f8005000 {
clocks = <0x1 0x2d>;
compatible = "cdns,wdt-r1p2";
interrupt-parent = <0x3>;
interrupts = <0x0 0x9 0x1>;
reg = <0xf8005000 0x1000>;
timeout-sec = <0xa>;
};

usb@e0002000 {
clocks = <0x1 0x1c>;
compatible = "xlnx,ps7-usb-1.00.a", "xlnx,zynq-usb-1.00.a";
status = "okay";
interrupt-parent = <0x3>;
interrupts = <0x0 0x15 0x4>;
reg = <0xe0002000 0x1000>;
dr_mode = "host";
phy_type = "ulpi";
};

usb@e0003000 {
clocks = <0x1 0x1d>;
compatible = "xlnx,ps7-usb-1.00.a", "xlnx,zynq-usb-1.00.a";
status = "disabled";
interrupt-parent = <0x3>;
interrupts = <0x0 0x2c 0x4>;
reg = <0xe0003000 0x1000>;
};
};

amba_pl {
#address-cells = <0x1>;
#size-cells = <0x1>;
compatible = "simple-bus";
ranges;

gpio@41200000 {
#gpio-cells = <0x2>;
compatible = "xlnx,xps-gpio-1.00.a";
gpio-controller;
reg = <0x41200000 0x10000>;
xlnx,all-inputs = <0x1>;
xlnx,all-inputs-2 = <0x0>;
xlnx,all-outputs = <0x0>;
xlnx,all-outputs-2 = <0x0>;
xlnx,dout-default = <0x0>;
xlnx,dout-default-2 = <0x0>;
xlnx,gpio-width = <0x8>;
xlnx,gpio2-width = <0x20>;
xlnx,interrupt-present = <0x0>;
xlnx,is-dual = <0x0>;
xlnx,tri-default = <0xffffffff>;
xlnx,tri-default-2 = <0xffffffff>;
};
};
};

7e. Recompile the .dtb by using the following command: dtc -I dts -O dtb -o "devicetree.dtb" devicetree.dts

8. Great! Now we have a .dtb that works; but we are sadly still not done. We have to rebuild the boot.bin file as so that the system has the correct .bit file loaded on it. If you do not do this step and just copy the .dtb file onto the SD card with all the original files from 1a, the Zedboard will freeze at "Starting the Kernel". This is due to the fact that there is not actually a driver or valid connection to the PL that matches the .dtb file due to the .bit file being incorrect.

9. The next step is creating a FSBL.

9a. Back in SDK, go to File->New->Application Project
9b. Name the project, select standalone, language as C. Hardware Platform and Processor defaults should be fine.
9c. Hit next, and select the Zynq FSBL template. Then hit finish.

10. If automatically build is disabled, build the FSBL BSP and FSBL projects.

11. You should now have a FSBL elf file in the Debug folder of the application project. Next step is to build the boot.bin file. NOTE: Order of these steps does matter!

11a. Select Xilinx Tools->Create Boot Image
11b. Set an output path for the .bif file (in my case I just pointed it to the SDK directory of my project). Make sure "Create new BIF file" is selected.
11c. Select Add. A window should pop up
11d. For the path, point it at the FSBL .elf file created in step 10. Select "bootloader" for partition type.

11e. Leave everything else blank/to defaults and hit ok.
11f. Next, hit Add again, select "datafile" for partition type, then point it to the .bit file for the project. Hit Ok
11g. Finally, we need to add the u-boot.elf that came with the Xilinx release (see 1a). Hit add, select "datafile" for the partition type, then point it to the .elf file for u-boot (u-boot.elf).
11h. Once those are all done, select "Create Image". This will generate a file called BOOT.bin, which will be in the same directory that you specified for the .bif file in 11b.

12. Now we are ready to load all of these files onto the SD card.

12a. Copy the BOOT.bin file that was created in step 11h to the card.
12b. Copy the devicetree.dtb file that was created in step 7e.
12c. Copy the uImage and uramdisk.image.gz from the Xilinx release folder in step 1a.

After all that, you should be able to plug in the SD card and see it boot up! I know that this is a bit long for a forum post...if there is any way for me to make this into a tutorial somehow please let me know and I will be happy to do that!

Hope others find this useful,

Mark

thomasrepetti's picture
thomasrepetti
Junior(1)
Check that the PL was being configured correctly

Hi Mark,

When the device hangs on a memory-mapped write, it is a possible symptom of unconfigured programmable logic and the fact that AMBA buses are "dumb" (i.e., have no notion of device enumeration). By this, I mean that the OS will always just take the device tree's word for it as to whether a given physical address is actually mapped to any physical memory/device (or in the case of an mmaped /dev/mem write, it will take the user's word for it). Thus, if a memory-mapped peripheral is not actually present in the PL, the host processor will begin an AXI transaction that will never finish (since the bus is disconnected) and the system will hang. This is one of the dangers of using the otherwise excellent bring-up technique of userspace pseudo-drivers.

If you go back to the original system, mount your SD card into the VFS to access the *.bit file. After you pipe the *.bit file into /dev/xdevcfg check to see if the problem is still there. My suspicion is that after this manual configuration step in the original system, you might find the peripheral works as intended. If this is the case, it suggests that the issue was that the PL was not being configured at boot. If this is the case, check the order of the entries in your original boot.bif, and make sure that it goes:

the_ROM_image:
{
[bootloader]fsbl.elf
pl.bit
u-boot.elf
}

If it does not appear in this order, it is possible that the bootloader will pass control to U-Boot or whatever boot system you are using before the PL gets configured, which would then be the root cause of the original failed /dev/mem writes.

Just had an issue with ordering the boot.bif ordering earlier today! Couldn't figure out why the device wasn't getting configured at boot.

-Tom

mark.mahowald's picture
mark.mahowald
Junior(3)
Hey Tom,

Hey Tom,

That was more or less the issue. I needed to rebuild the boot.bin as well as edit the device tree from the original 2015.2 Xilinx release. Once I did those steps it worked like a charm!

Thanks for the explanation of the OS hanging; I was wondering why that kept happening.

Mark

phbs's picture
phbs
Junior(1)
Hey Mark,

Hey Mark,

what other modification did you did in the device tree?

Thank you!

mark.mahowald's picture
mark.mahowald
Junior(3)
Added amba_pl portion to the bottom of the .dts file

After reverse building the Xilinx .dtb file downloaded from one of their webpages, I just added the SDK generated pl.dtsi file to that device tree.

In one of the earlier comments you will see my copy and pasted .dts file. Look for "amba_pl {"; from there on down that is what I had to add.

After that I re-compiled it into a .dtb and then went through the steps of creating a boot.bin file.

Hope that helps!