Device tree hacking

Upgrading to a newer version is always recommended, as it brings bug fixes and improvements to the code. However, sometimes upgrading is not smooth and things break, making the process often frustrating. Such was the case when I upgraded this design running smoothly in 2019.2 to the latest Petalinux 2020.1. The standalone device tree generator tool was used to detect and fix the issue.

Device Tree Generator tool

Petalinux uses this tool under the hood to generate the device tree source (dts) from the device description file (xsa) and compile it to a device tree blob (dtb). Debugging device tree issues under Petalinux is time consuming, and it’s made much easier using a standalone flow, such as explained in this useful Xilinx wiki post: Quick guide to debugging device tree generator issues

The makefile in the guide calls the xsct tool, which imports the Vivado-exported project (.xsa) and parses its components while generating the device tree source. The xsct tool runs this script to accomplish that:

proc build_dts {xsa} {
  hsi::open_hw_design $xsa
    hsi::set_repo_path ./repo
    set procs [hsi::get_cells -filter {IP_TYPE==PROCESSOR}]
    puts "Targeting [lindex $procs 0]"
    hsi::create_sw_design device-tree -os device_tree -proc [lindex $procs 0]
    hsi::generate_target -dir my_dts
    hsi::close_hw_design [hsi::current_hw_design]
}

This process would normally go without issue, unless it detects some problem in the actual design, such as an interrupt line not properly connected.

The device tree generator runs through the list of devices in the design and calls their dedicated Tcl script. Each script has access to the whole design description and configuration, which gives great flexibility to generate their particular device tree node. For instance, for each of the gigabit Ethernet MACs (GEM) it runs the emacps/data/emacps.tcl script, passing as parameter the instance name, i.e. “psu_ethernet_0”.

Unfortunately, in Petalinux 2020.1 a change to this script introduced an unexpected design setting. In this case, the script parses the design expecting only one instance of the PCS/PMA IP in the design, but this design counts with three, triggering the following error:

expected integer but got "0x09 0x0A 0x0B"
ERROR: [Hsi 55-1545] Problem running tcl command ::sw_emacps::generate : expected integer but got "0x09 0x0A 0x0B"
    while executing
"format %x $val"
    (procedure "::sw_emacps::generate" line 111)
    invoked from within
"::sw_emacps::generate psu_ethernet_0"
ERROR: [Hsi 55-1442] Error(s) while running TCL procedure generate()
generate_target failed

Basically, the statement “get_cells ... IP_NAME == gig_ethernet_pcs_pma” returns a list when more than one instance is present, but the rest of the code always assumes one instance. Technically, the previous code didn’t generate correct nodes either, setting all nodes to is-internal-pcspma if any pcspma IP was present and set to sgmii. It just didn’t error out.

set is_pcspma [get_cells -hier -filter {IP_NAME == gig_ethernet_pcs_pma}]
if {![string_is_empty ${is_pcspma}] && $phymode == 2} {
    # if eth mode is sgmii and no external pcs/pma found
    hsi::utils::add_new_property $drv_handle "is-internal-pcspma" boolean ""
}

The fix involves picking out the PCSPMA IP that was connected to the GEM being analyzed (i.e. drv_handle argument), from psu_ethernet_0 thru 3. For this, the last character of the GEM instance is used to index the MDIO_ENET port, and using it to check whether a PCSPMA is connected to such port.

set ip_id [string index $drv_handle end]
set connected_ip [hsi::utils::get_connected_stream_ip $zynq_periph "MDIO_ENET$ip_id"]

Admittedly, this fix is limited if multiple PCSPMA IP are sharing the MDIO line, but that’s currently the case for any MDIO sharing.

The name of the connected IP is then compared in a loop to all the PCSPMA instances, and the matched IP is extracted and processed as usual.

set ip_name [get_property NAME $connected_ip]
set all_pcspma [get_cells -hier -filter {IP_NAME == gig_ethernet_pcs_pma}]
foreach ip $all_pcspma {
  if {[get_property NAME $ip] == $ip_name} {
    set is_pcspma $ip
      break
  }

The last fix involves correctly handling the phy-mode case, when there is no PCSPMA on that particular GEM port and the mode is set to sgmii, such that generates the is-internal-pcspma property.

if {[llength $is_pcspma]} {
  <...>
} else {
  if {$phymode == 2} {
  # if eth mode is sgmii and no external pcs/pma found
    hsi::utils::add_new_property $drv_handle "is-internal-pcspma" boolean ""
  }

This post was based on my forum post reporting the problem Petalinux 2020.1 fails to generate default device tree, but works in 2019.2 and includes the patch.

Conclusion

The standalone device tree generator is a nifty tool to handle device tree issues, in a practical and time saving way. In this post, I showed how it was used to handle and workaround a limitation in the latest version of Petalinux.



Petalinux notes

Petalinux tools provide a reference Linux distribution tailored for Xilinx embedded processors, such as Microblaze™ and Zynq™ devices. It’s a...

Previous
Linux Userspace Memory & I/O

In this post we'll be looking at how Linux handles accesses to memory devices and I/O from userspace, and comparing...

Next