USB PHY Dead?

Technical questions regarding the XTC tools and programming with XMOS.
User avatar
aneves
Experienced Member
Posts: 93
Joined: Wed Sep 16, 2015 2:38 pm

USB PHY Dead?

Post by aneves »

Hi Guys,

I've been using an XU216-512-TQ128 on a custom development board for several months now with no issues whatsoever. My most recent requirement was to try to find a way to reboot the chip via firmware. After trying a few functions I found in this forum and in lib_usb, I can no longer use the USB PHY on my chip. This happened on 2 of my custom development boards. Details below:

Initially, I searched the forum and found this thread:

xcore.com: Reboot/Reset possible?

which suggested using the following:

Code: Select all

void chipReset(void)
{
  unsigned x;
  
  read_sswitch_reg(get_core_id(), 6, x);
  write_sswitch_reg(get_core_id(), 6, x);
}
This did not meet my needs. To be more precise, my needs are to implement DFU functionally. I needed to find a way for the device to simulate being unplugged and being re-plugged so that it can properly switch between normal operation and DFU mode. When I call the above function, my device did not properly reset. So I take it this was merely a "soft" reset.

However, the USB PHY was still functional after testing this function. I could run firmware over xTAG via the development tools and manually cycle the power to the custom development board to boot into previously flashed firmware. In each case, my computer enumerated the custom development as expected.

After researching how to reboot the chip via software further, I began browsing the lib_usb package and I found this function in lib_usb/src/endpoint0/device_reboot.xc

Code: Select all

// Copyright (c) 2016, XMOS Ltd, All rights reserved
#include <xs1.h>
#include <platform.h>
#include <xs1_su.h>
#include "xud.h"


#define XS1_SU_PERIPH_USB_ID 0x1

void do_device_reboot(void)
{
// This hack is for a bug in xTIMEcomposer 13.2.0 where write_periph_32 and read_periph32 do not have the correct settings for use in combinable

  asm(".globl read_periph_32.locnoside;.globl read_periph_32.locnochandec;.globl read_periph_32.locnoglobalaccess;.globl read_periph_32.locnointerfaceaccess;.globl read_periph_32.locnonotificationselect;.weak read_periph_32.locnoside;.weak read_periph_32.locnochandec;.weak read_periph_32.locnoglobalaccess;.weak read_periph_32.locnointerfaceaccess;.weak read_periph_32.locnonotificationselect;.set read_periph_32.locnoside, 1;.set read_periph_32.locnochandec, 1;.set read_periph_32.locnoglobalaccess, 1;.set read_periph_32.locnointerfaceaccess, 1;.set read_periph_32.locnonotificationselect, 1");

  asm(".globl write_periph_32.locnoside;.globl write_periph_32.locnochandec;.globl write_periph_32.locnoglobalaccess;.globl write_periph_32.locnointerfaceaccess;.globl write_periph_32.locnonotificationselect;.weak write_periph_32.locnoside;.weak write_periph_32.locnochandec;.weak write_periph_32.locnoglobalaccess;.weak write_periph_32.locnointerfaceaccess;.weak write_periph_32.locnonotificationselect;.set write_periph_32.locnoside, 1;.set write_periph_32.locnochandec, 1;.set write_periph_32.locnoglobalaccess, 1;.set write_periph_32.locnointerfaceaccess, 1;.set write_periph_32.locnonotificationselect, 1");


  asm(".globl write_sswitch_reg.locnoside;.globl write_sswitch_reg.locnochandec;.globl write_sswitch_reg.locnoglobalaccess;.globl write_sswitch_reg.locnointerfaceaccess;.globl write_sswitch_reg.locnonotificationselect;.weak write_sswitch_reg.locnoside;.weak write_sswitch_reg.locnochandec;.weak write_sswitch_reg.locnoglobalaccess;.weak write_sswitch_reg.locnointerfaceaccess;.weak write_sswitch_reg.locnonotificationselect;.set write_sswitch_reg.locnoside, 1;.set write_sswitch_reg.locnochandec, 1;.set write_sswitch_reg.locnoglobalaccess, 1;.set write_sswitch_reg.locnointerfaceaccess, 1;.set write_sswitch_reg.locnonotificationselect, 1");

  asm(".globl read_sswitch_reg.locnoside;.globl read_sswitch_reg.locnochandec;.globl read_sswitch_reg.locnoglobalaccess;.globl read_sswitch_reg.locnointerfaceaccess;.globl read_sswitch_reg.locnonotificationselect;.weak read_sswitch_reg.locnoside;.weak read_sswitch_reg.locnochandec;.weak read_sswitch_reg.locnoglobalaccess;.weak read_sswitch_reg.locnointerfaceaccess;.weak read_sswitch_reg.locnonotificationselect;.set read_sswitch_reg.locnoside, 1;.set read_sswitch_reg.locnochandec, 1;.set read_sswitch_reg.locnoglobalaccess, 1;.set read_sswitch_reg.locnointerfaceaccess, 1;.set read_sswitch_reg.locnonotificationselect, 1");


  asm(".globl write_sswitch_reg_no_ack.locnoside;.globl write_sswitch_reg_no_ack.locnochandec;.globl write_sswitch_reg_no_ack.locnoglobalaccess;.globl write_sswitch_reg_no_ack.locnointerfaceaccess;.globl write_sswitch_reg_no_ack.locnonotificationselect;.weak write_sswitch_reg_no_ack.locnoside;.weak write_sswitch_reg_no_ack.locnochandec;.weak write_sswitch_reg_no_ack.locnoglobalaccess;.weak write_sswitch_reg_no_ack.locnointerfaceaccess;.weak write_sswitch_reg_no_ack.locnonotificationselect;.set write_sswitch_reg_no_ack.locnoside, 1;.set write_sswitch_reg_no_ack.locnochandec, 1;.set write_sswitch_reg_no_ack.locnoglobalaccess, 1;.set write_sswitch_reg_no_ack.locnointerfaceaccess, 1;.set write_sswitch_reg_no_ack.locnonotificationselect, 1");

  asm(".globl write_node_config_reg.locnoside;.globl write_node_config_reg.locnochandec;.globl write_node_config_reg.locnoglobalaccess;.globl write_node_config_reg.locnointerfaceaccess;.globl write_node_config_reg.locnonotificationselect;.weak write_node_config_reg.locnoside;.weak write_node_config_reg.locnochandec;.weak write_node_config_reg.locnoglobalaccess;.weak write_node_config_reg.locnointerfaceaccess;.weak write_node_config_reg.locnonotificationselect;.set write_node_config_reg.locnoside, 1;.set write_node_config_reg.locnochandec, 1;.set write_node_config_reg.locnoglobalaccess, 1;.set write_node_config_reg.locnointerfaceaccess, 1;.set write_node_config_reg.locnonotificationselect, 1");

  asm(".globl get_local_tile_id.locnoside;.globl get_local_tile_id.locnochandec;.globl get_local_tile_id.locnoglobalaccess;.globl get_local_tile_id.locnointerfaceaccess;.globl get_local_tile_id.locnonotificationselect;.weak get_local_tile_id.locnoside;.weak get_local_tile_id.locnochandec;.weak get_local_tile_id.locnoglobalaccess;.weak get_local_tile_id.locnointerfaceaccess;.weak get_local_tile_id.locnonotificationselect;.set get_local_tile_id.locnoside, 1;.set get_local_tile_id.locnochandec, 1;.set get_local_tile_id.locnoglobalaccess, 1;.set get_local_tile_id.locnointerfaceaccess, 1;.set get_local_tile_id.locnonotificationselect, 1");


#if (XUD_SERIES_SUPPORT == XUD_U_SERIES)
    /* Disconnect from bus */
    unsigned data[] = {4};
    write_periph_32(usb_tile, XS1_SU_PERIPH_USB_ID, XS1_SU_PER_UIFM_FUNC_CONTROL_NUM, 1, data);

    /* Ideally we would reset SU1 here but then we loose power to the xcore and therefore the DFU flag */
    /* Disable USB and issue reset to xcore only - not analogue chip */
    write_node_config_reg(usb_tile, XS1_SU_CFG_RST_MISC_NUM,0b10);
#else
    unsigned int pllVal;
    unsigned int localTileId = get_local_tile_id();
    unsigned int tileId;
    unsigned int tileArrayLength;

    /* Find size of tile array - note in future tools versions this will be available from platform.h */
    asm volatile ("ldc %0, tile.globound":"=r"(tileArrayLength));

    /* Reset all remote tiles */
    for(int i = 0; i< tileArrayLength; i++)
    {
        /* Cannot cast tileref to unsigned! */
        tileId = get_tile_id(tile[i]);

        /* Do not reboot local tile yet! */
        if(localTileId != tileId)
        {
            read_sswitch_reg(tileId, 6, pllVal);
            write_sswitch_reg_no_ack(tileId, 6, pllVal);
        }
    }

    /* Finally reboot this tile! */
    read_sswitch_reg(localTileId, 6, pllVal);

    write_sswitch_reg_no_ack(localTileId, 6, pllVal);
#endif
}

After calling this function I noticed I could not get my custom development board enumerated no matter what firmware - whether the previously flashed "golden image" or something loaded over xTAG - was used. Again, this occured on 2 of my custom development boards. I tried rebooting my computer but my computer still would not enumerated them. I connected the board to a USB analyzer to look at the traffic but there was none. I tried plugging them into a colleague's computer and it still failed to enumerate. The USB PHY has passed on, is no more, has ceased to be!

On the other had, everything else about the chip seems to be OK. I can connect my board to the xTAG adapter and xTimeComposer is able to detect both the xTAG adapter AND the chip it is connected to. The IDE is still able to debug firmware on the target chip. The issue seems limited to only the USB PHY.

While most people would assume this was a hardware failure maybe due to ESD, I find it quite odd that I have been using both boards over the past few months yet I reproduced the exact same failure mode on two custom development boards calling the same function.

So my ultimate question before I go through the trouble of attempting to get these boards reworked is: given the code I posted, is it possible to kill the USB PHY via firmware? In other words, is there a way to disable the USB PHY (which I might have done) and re-enable it? I'm not familiar with assembly, so the first half of the do_device reboot function is unsettling.

Thanks!
User avatar
mon2
XCore Legend
Posts: 1913
Joined: Thu Jun 10, 2010 11:43 am

Post by mon2 »

Can you test with the XMOS USB CDC or USB HID example code on your target board ?

What are the results ?
User avatar
larry
Respected Member
Posts: 275
Joined: Fri Mar 12, 2010 6:03 pm

Post by larry »

is it possible to kill the USB PHY via firmware?
The command you posted issues a PLL reset, and a chip reset. Chip will reset into its default setings and boot as usual. All that happens to USB on the chip is that it gets reset as well.
User avatar
aneves
Experienced Member
Posts: 93
Joined: Wed Sep 16, 2015 2:38 pm

Post by aneves »

I did try running the CDC demo but unfortunately I ran into the same issue where the device is unable to be enumerated by the machine. Looks like I will just have to rework these boards.
larry wrote:The command you posted issues a PLL reset, and a chip reset. Chip will reset into its default setings and boot as usual. All that happens to USB on the chip is that it gets reset as well.
Which command were you referring to? The chip_reset function or the do_device_reboot function? Does that mean one or the ther (or maybe both?) are capable of a complete reset of the chip?

Thanks!
User avatar
larry
Respected Member
Posts: 275
Joined: Fri Mar 12, 2010 6:03 pm

Post by larry »

I guess the answer is yes, what I said applies to both functions. Both essentially do the same - write to switch register 6.
User avatar
larry
Respected Member
Posts: 275
Joined: Fri Mar 12, 2010 6:03 pm

Post by larry »

Both essentially do the same - write to switch register 6.
This is not entirely correct. I am sorry for the confusion. The do_device_reboot function writes to some other registers as well as register 6. None of the writes it makes are expected to have any permanent effect on the USB PHY.

If you are able to run your code again, it would be interesting to use the below GDB script that will print the exact sequence of switch writes that your code makes:

Code: Select all

connect
thread 1
file ./image_n0c0_2.elf
set arch xs2_reva
b write_sswitch_reg_no_ack
commands 1
silent
printf "  write_sswitch_reg_no_ack(0x%x, 0x%x, 0x%x);\n", $r0, $r1, $r2
c
end
b write_sswitch_reg
commands 2
silent
printf "  write_sswitch_reg(0x%x, 0x%x, 0x%x);\n", $r0, $r1, $r2
c
end
b _done
commands 3
silent
end
load 
c
q
To use it, navigate to your XE binary and split it using:

Code: Select all

xobjdump -s
That should give you image_n0c0_2.elf, tile 0's program image. Then run:

Code: Select all

xgdb -x script
where script contains the above.

Change image_n0c0_2.elf in the script to image_n0c1_2.elf if you are calling the reset functions from tile 1 rather than 0
User avatar
aneves
Experienced Member
Posts: 93
Joined: Wed Sep 16, 2015 2:38 pm

Post by aneves »

Thank you very much for the suggestion. I will give it a shot and let you know how it goes.
User avatar
aneves
Experienced Member
Posts: 93
Joined: Wed Sep 16, 2015 2:38 pm

Post by aneves »

Actually, after thinking about it some more I realize that I cannot trigger the device_reboot the same way I was before. The device_reboot function would get called in response to a command sent over USB. Since my USB phy is not working I can't do that.

The best I can do is just create a dummy project where I call device_reboot in main. Would that suffice?
User avatar
aneves
Experienced Member
Posts: 93
Joined: Wed Sep 16, 2015 2:38 pm

Post by aneves »

So I've created a dummy project which calls do_device_reboot in main straight away. I encountered some issues building when I include the entire lib_usb module so I just copied/pasted the do_device_reboot function with some minor modifications.

Here is what main looks like:

Code: Select all

/*
 * Device Reboot.xc
 *
 *  Created on: Jan 5, 2017
 *      Author: Amandio
 */

#include <xs1.h>
#include <platform.h>
#include <xs1_su.h>

// Removed xud dependency
//#include "xud.h"

#define XS1_SU_PERIPH_USB_ID 0x1

void do_device_reboot(void)
{
// This hack is for a bug in xTIMEcomposer 13.2.0 where write_periph_32 and read_periph32 do not have the correct settings for use in combinable

  asm(".globl read_periph_32.locnoside;.globl read_periph_32.locnochandec;.globl read_periph_32.locnoglobalaccess;.globl read_periph_32.locnointerfaceaccess;.globl read_periph_32.locnonotificationselect;.weak read_periph_32.locnoside;.weak read_periph_32.locnochandec;.weak read_periph_32.locnoglobalaccess;.weak read_periph_32.locnointerfaceaccess;.weak read_periph_32.locnonotificationselect;.set read_periph_32.locnoside, 1;.set read_periph_32.locnochandec, 1;.set read_periph_32.locnoglobalaccess, 1;.set read_periph_32.locnointerfaceaccess, 1;.set read_periph_32.locnonotificationselect, 1");

  asm(".globl write_periph_32.locnoside;.globl write_periph_32.locnochandec;.globl write_periph_32.locnoglobalaccess;.globl write_periph_32.locnointerfaceaccess;.globl write_periph_32.locnonotificationselect;.weak write_periph_32.locnoside;.weak write_periph_32.locnochandec;.weak write_periph_32.locnoglobalaccess;.weak write_periph_32.locnointerfaceaccess;.weak write_periph_32.locnonotificationselect;.set write_periph_32.locnoside, 1;.set write_periph_32.locnochandec, 1;.set write_periph_32.locnoglobalaccess, 1;.set write_periph_32.locnointerfaceaccess, 1;.set write_periph_32.locnonotificationselect, 1");


  asm(".globl write_sswitch_reg.locnoside;.globl write_sswitch_reg.locnochandec;.globl write_sswitch_reg.locnoglobalaccess;.globl write_sswitch_reg.locnointerfaceaccess;.globl write_sswitch_reg.locnonotificationselect;.weak write_sswitch_reg.locnoside;.weak write_sswitch_reg.locnochandec;.weak write_sswitch_reg.locnoglobalaccess;.weak write_sswitch_reg.locnointerfaceaccess;.weak write_sswitch_reg.locnonotificationselect;.set write_sswitch_reg.locnoside, 1;.set write_sswitch_reg.locnochandec, 1;.set write_sswitch_reg.locnoglobalaccess, 1;.set write_sswitch_reg.locnointerfaceaccess, 1;.set write_sswitch_reg.locnonotificationselect, 1");

  asm(".globl read_sswitch_reg.locnoside;.globl read_sswitch_reg.locnochandec;.globl read_sswitch_reg.locnoglobalaccess;.globl read_sswitch_reg.locnointerfaceaccess;.globl read_sswitch_reg.locnonotificationselect;.weak read_sswitch_reg.locnoside;.weak read_sswitch_reg.locnochandec;.weak read_sswitch_reg.locnoglobalaccess;.weak read_sswitch_reg.locnointerfaceaccess;.weak read_sswitch_reg.locnonotificationselect;.set read_sswitch_reg.locnoside, 1;.set read_sswitch_reg.locnochandec, 1;.set read_sswitch_reg.locnoglobalaccess, 1;.set read_sswitch_reg.locnointerfaceaccess, 1;.set read_sswitch_reg.locnonotificationselect, 1");


  asm(".globl write_sswitch_reg_no_ack.locnoside;.globl write_sswitch_reg_no_ack.locnochandec;.globl write_sswitch_reg_no_ack.locnoglobalaccess;.globl write_sswitch_reg_no_ack.locnointerfaceaccess;.globl write_sswitch_reg_no_ack.locnonotificationselect;.weak write_sswitch_reg_no_ack.locnoside;.weak write_sswitch_reg_no_ack.locnochandec;.weak write_sswitch_reg_no_ack.locnoglobalaccess;.weak write_sswitch_reg_no_ack.locnointerfaceaccess;.weak write_sswitch_reg_no_ack.locnonotificationselect;.set write_sswitch_reg_no_ack.locnoside, 1;.set write_sswitch_reg_no_ack.locnochandec, 1;.set write_sswitch_reg_no_ack.locnoglobalaccess, 1;.set write_sswitch_reg_no_ack.locnointerfaceaccess, 1;.set write_sswitch_reg_no_ack.locnonotificationselect, 1");

  asm(".globl write_node_config_reg.locnoside;.globl write_node_config_reg.locnochandec;.globl write_node_config_reg.locnoglobalaccess;.globl write_node_config_reg.locnointerfaceaccess;.globl write_node_config_reg.locnonotificationselect;.weak write_node_config_reg.locnoside;.weak write_node_config_reg.locnochandec;.weak write_node_config_reg.locnoglobalaccess;.weak write_node_config_reg.locnointerfaceaccess;.weak write_node_config_reg.locnonotificationselect;.set write_node_config_reg.locnoside, 1;.set write_node_config_reg.locnochandec, 1;.set write_node_config_reg.locnoglobalaccess, 1;.set write_node_config_reg.locnointerfaceaccess, 1;.set write_node_config_reg.locnonotificationselect, 1");

  asm(".globl get_local_tile_id.locnoside;.globl get_local_tile_id.locnochandec;.globl get_local_tile_id.locnoglobalaccess;.globl get_local_tile_id.locnointerfaceaccess;.globl get_local_tile_id.locnonotificationselect;.weak get_local_tile_id.locnoside;.weak get_local_tile_id.locnochandec;.weak get_local_tile_id.locnoglobalaccess;.weak get_local_tile_id.locnointerfaceaccess;.weak get_local_tile_id.locnonotificationselect;.set get_local_tile_id.locnoside, 1;.set get_local_tile_id.locnochandec, 1;.set get_local_tile_id.locnoglobalaccess, 1;.set get_local_tile_id.locnointerfaceaccess, 1;.set get_local_tile_id.locnonotificationselect, 1");

// I am hardcoding the fact that I know this chip is not XUD_U_SERIES
#if 0 //(XUD_SERIES_SUPPORT == XUD_U_SERIES)
    /* Disconnect from bus */
    unsigned data[] = {4};
    write_periph_32(usb_tile, XS1_SU_PERIPH_USB_ID, XS1_SU_PER_UIFM_FUNC_CONTROL_NUM, 1, data);

    /* Ideally we would reset SU1 here but then we loose power to the xcore and therefore the DFU flag */
    /* Disable USB and issue reset to xcore only - not analogue chip */
    write_node_config_reg(usb_tile, XS1_SU_CFG_RST_MISC_NUM,0b10);
#else
    unsigned int pllVal;
    unsigned int localTileId = get_local_tile_id();
    unsigned int tileId;
    unsigned int tileArrayLength;

    /* Find size of tile array - note in future tools versions this will be available from platform.h */
    asm volatile ("ldc %0, tile.globound":"=r"(tileArrayLength));

    /* Reset all remote tiles */
    for(int i = 0; i< tileArrayLength; i++)
    {
        /* Cannot cast tileref to unsigned! */
        tileId = get_tile_id(tile[i]);

        /* Do not reboot local tile yet! */
        if(localTileId != tileId)
        {
            read_sswitch_reg(tileId, 6, pllVal);
            write_sswitch_reg_no_ack(tileId, 6, pllVal);
        }
    }

    /* Finally reboot this tile! */
    read_sswitch_reg(localTileId, 6, pllVal);

    write_sswitch_reg_no_ack(localTileId, 6, pllVal);
#endif
}



int main(void){

    par{

        on tile[0]: do_device_reboot();
        on tile[0]: while(1);
    }
}
When I run the script in xgdb i get the following output and xgdb subsequently crashes.

Ouput:

Code: Select all

C:\Users\Amandio\workspace\Device Reboot\bin>xgdb -x script
GNU gdb (XGDB) Community_14.2.1 (build 15182, Jun-24-2016)
Copyright (C) 2007 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "--host=i686-pc-mingw32 --target=xcore-elf".
For bug reporting instructions, please see: http://www.xmos.com/support.

0xfff004c8 in ?? ()
[Switching to thread 1 (tile[0] core[0])]#0  0xfff004c8 in ?? ()
Breakpoint 1 at 0x40210
Function "write_sswitch_reg" not defined.
Crash:
Image

Any other suggestions?
User avatar
larry
Respected Member
Posts: 275
Joined: Fri Mar 12, 2010 6:03 pm

Post by larry »

It looks like your program isn't using write_sswitch_reg, just write_sswitch_reg_no_ack.

Will you be able to modify the GDB script so it only prints calls to write_sswitch_reg_no_ack? It's complaining, because the script is telling to set a breakpoint on a non-existent function write_sswitch_reg.

The crash of xgdb.exe is a surprise. In case it is a known issue, it would be easier if you could check tools 14.2.3, but I will also try the same and see if I can recreate the crash.