UserReadHIDButtons : read port fails Topic is solved

Technical questions regarding the XTC tools and programming with XMOS.
bonelli
Member++
Posts: 19
Joined: Wed Feb 03, 2021 9:06 pm

UserReadHIDButtons : read port fails

Post by bonelli »

Hello,

I successfully ported the USB audio SW to my custom board. Working with linux and windows with a single stereo output port, 192kHz/24b with a TI DAC.

I'm trying to make the hid function to work. I have some switches connected on tile 0 and declared as follow:

Code: Select all

in port  p_sw1  = on tile[0] : XS1_PORT_4C;  // PREV, SRC, NEXT, ON
in port  p_sw2  = on tile[0] : XS1_PORT_4E;  // PLAY, DST, VOL-, VOL+
in UserReadHIDButtons, the following code does not work:

Code: Select all

    unsigned int sw1, sw2;
    p_sw1 :> sw1;
    p_sw2 :> sw2;
I have random values in sw1 and sw2.

I'm suspecting that UserReadHIDButtons is executed on tile 1, when ports are located on tile 0. Quite strange, because :
- When accessing a port located on tile 1 in this function, the compiler warns
- It didn't warn accessing port on tile 0

=> How can I know the location of a function (tile 0/1) ?

Also, I found in the documentation :
A port is initially OFF with its pins in a high impedance state. Before it is used,
it must be configured to determine the way it interacts with its pins, and set
ON, which also has the effect of starting the port
ok, so how to configure the port ? I'm using an output port without any config, and it works... Basic examples of port usage on the web don't do any config. I found some init functions but only for a more complex usage with clock.

Finally, does someone makes this adaptation of eclipse working ?
- CTRL+H regularly fails
- CTRL+clic almost never works
- right clic + open call hierarchy and other usefull things don't works.
Very bad to debug a spaghetti plate when even the main() is hidden and not visible in the project explorer tab.

Thank you
Regards
View Solution
User avatar
Ross
Verified
XCore Legend
Posts: 1080
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

What do you have XUD_TILE set to? 0 or 1?

I assume you don't mean fully random numbers in those variables, you should only see the lower 4 bits being set.
Technical Director @ XMOS. Opinions expressed are my own
bonelli
Member++
Posts: 19
Joined: Wed Feb 03, 2021 9:06 pm

Post by bonelli »

XUD_TILE = 1.

I'm reading true random 32 bits values, with not only 4 lower bits set. But you are asking for XUD_TILE location, so UserReadHIDButtons may be on tile 1. And I'm reading a port on tile 0 from code on tile 1. This may be the source for my bug, but it's quite strange that the compiler didn't warns about that.

Now I have to learn a lot about the channels/interfaces/... to see how I could transfer data between tiles.
User avatar
fabriceo
XCore Addict
Posts: 222
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hi Bonelli;
welcome in this journey :)

reading your questions, I d recommend you use xscope and printf inside userreadhid to check whats happen exactly.
FYI, a function is not dedicated to a tile at compilation time. The linker is creating dedicated cpu code for each tile and will potentially put a copy of the function code in that cpu code if it is required. a function can exist in the 2 tile domain but has a single existence in your source code. if you want to trace where the function is executed, you can use this :
printf("core %d:%d ", get_local_tile_id() & 1, get_logical_core_id() )

basic output/input port configuration is made automatically by compiler, you don't need to care about it in XC.
but if you want to be in full control you can use some predefined macros like "set_port_drive"

to report your tile0 port value to the function readhid in tile1, you will need to create a specific I/O task in tile0, declared in main.xc, with a basic interface doing read/write on the local ports. then this interface has to be included in the main.xc and passed as a parameter to buffer() thread, via usb_audio_core(), and then to userreadhid...
you will loose some hairs for sure, but it works (I ve done that for exchanging parameters between a DSP task running on tile 0 and USB host via endpoint0 running on tile 1...)
You could also use another approach, which is to transmit the value of the local I/O port as an additional data word between Audio.xc (DoSampleTransfer) and Decouple.xc(handle_audio_request), but this is another story not for the fainthearted :)

for eclipse, I m using all the shortcut you mentioned and they all work, I m on OSX with 14.4.1 and java 8. Player One Try Again
bonelli
Member++
Posts: 19
Joined: Wed Feb 03, 2021 9:06 pm

Post by bonelli »

fabriceo wrote: Tue Apr 18, 2023 9:11 am I d recommend you use xscope and printf inside userreadhid to check whats happen exactly.
good idea. printf confirms that UserReadHIDButtons() is located on tile 1.
to report your tile0 port value to the function readhid in tile1, you will need to create a specific I/O task in tile0, declared in main.xc, with a basic interface doing read/write on the local ports. then this interface has to be included in the main.xc and passed as a parameter to buffer() thread, via usb_audio_core(), and then to userreadhid...
you will loose some hairs for sure, but it works
Not a cause for concern, still having a lot :)
bonelli
Member++
Posts: 19
Joined: Wed Feb 03, 2021 9:06 pm

Post by bonelli »

fabriceo wrote: Tue Apr 18, 2023 9:11 am for eclipse, I m using all the shortcut you mentioned and they all work, I m on OSX with 14.4.1 and java 8. Player One Try Again
Really?
You can "open call hierarchy" for UserReadHIDButtons () to see where it is called?
bonelli
Member++
Posts: 19
Joined: Wed Feb 03, 2021 9:06 pm

Post by bonelli »

A lot of hours later, I'm still blocked to read a port. Master grade + 10y+ experience killed by this extra-terrestrial CPU architecture. I still don't understand what is really going on at hw level when playing with select, port, event and tmr.

I have 8 switches, connected to 2x 4 bits ports on tile 0.

I successfully implemented the idea proposed by fabriceo:
to report your tile0 port value to the function readhid in tile1, you will need to create a specific I/O task in tile0, declared in main.xc, with a basic interface doing read/write on the local ports. then this interface has to be included in the main.xc and passed as a parameter to buffer() thread, via usb_audio_core(), and then to userreadhid...
But using a task permanently pooling the switches. Quite dirt. As a second step I opened the "XMOS Programming Guide" / prog. ex. / Handling button presses.
=> This example has not been tested by xmos. The debounce time is 50s, not 50ms, quite a lot. Easy to correct, but it proves that this code is not tested. Once corrected, does the whole code is really doing what we expect ?

I'm now using 2 tasks, based on the above example, each one handling a 4 bits port with 10ms debouncing and sending the sw state to a channel.

Then, if I try to replace my polling function by this well written button handler code, and nothing works anymore.
- USB device is not recognized
- I'm doing printf inside the button handler tasks : Each SW task works for 2 button presses. After, nothing reacts. solved

Honestly, I would be cool - and safe - if we can have global variables, writable by only 1 task, and readable by all, instead of imbricating all tasks with channels just for passing a global parameter driven by a switch.

I'm infinitely playing with task imbrication and chan / streaming chan but nothing want to work as expected.

Any help, advice, or validated documentation appreciated :)
Last edited by bonelli on Sun Jun 11, 2023 8:34 pm, edited 1 time in total.
bonelli
Member++
Posts: 19
Joined: Wed Feb 03, 2021 9:06 pm

Post by bonelli »

Problem found, but not resolved.

Code: Select all

void UserReadHIDButtons(unsigned char hidData[],  streaming chanend c_params) // Called every 4ms - Tile 1
{

    unsigned int params;
    printf("UserReadHIDButtons...\n");
    c_params :> params; // Paused here until chan is empty
    printf("UserReadHIDButtons : params=0x%X\n", params);
With input port in polling mode, a task is continuously sending data to the channel and everything woks fine.
With input port in interrupt mode, the task handling the port is only filling the channel when the port change. Consequently the UserReadHIDButtons enters and wait for c_params until is is written in the other task.
If I push several time quickly on the buttons, the function is not paused for a too long time and the USB audio device is visible from the computer.

Using a select statement resolved the problem. Finally, I'm note sure to understand the difference between streaming and normal channels.
Last edited by bonelli on Sun Jun 11, 2023 8:50 pm, edited 1 time in total.
User avatar
fabriceo
XCore Addict
Posts: 222
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hi Bonelli,
seems you are almost there, with some hiccups... you could try creating a new project with just 2 tasks (no usb) and make this work before integrating into usb_audio ?
Otherwise, as I told you there is another method if you are in a hurry to deliver your program for your project, you can make something easier.
insert a word transfer in the procedure DoSampleTransfer in audio.xc
with an instruction outuint(c_out, myPorts32bitword);
just before the return 0; at the end of this procedure.
you can read your 2 ports and combine them in the myPorts32bitword just before your outuint.
As the audio task is running on tile 0 you will have no problem to access your port from here.

now, to get the value into tile 1 and fill a global variable that you can use anywhere in tile 1 (with asm or with xcptr.h):
declare : int g_myTile0Port; somewhere in decouple.xc
put the following instruction in the middle of handle_audio_request() :
g_myTile0Port = inuint(c_mix_out);
just before the instruction
sampsToWrite--;

this way you create a transfer of your two 4 bits ports value between the 2 tiles as part of the Audio communication flow (which runs at frequency fs).
All the tasks on tile 1 can access your global variable g_myTile0Port, but you have to manage debounce on tile 1 in this case.

otherwise, don't be stressed by using a dedicated task on tile 0 for I/O. Such task could also be "combined" with others (not with audio task) and you have plenty of tasks available.

it is not easy to understand the select mechanism.
in fact when the compiler see the select, it prepares for each cases, all the internal registers (ports, timer...) to generate an event (=interrupt) when the condition is reached.
If your select contains a default statement then it will loop infinitely to execute the default statement, until an event (case) is happening.
if there is no default statement, the task just in standby until an event (case) happen.

you can play with task and just a while loop without select. this might help as this is more conventional programing (free rtos, Arduino...) but very soon you need to check some timing and what I do is just to call a getTime macro and compare timer difference by myself.
the getTime is :
static inline int getTime() { //same as timer t; t :> tmp; but doesn't require a formal timer declaration
int tmp;
asm volatile("gettime %0":"=r"(tmp)); // lecture generic timer
return tmp;
}

good luck
User avatar
fabriceo
XCore Addict
Posts: 222
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

you cannot "hang" in waiting c_params.
one way to sort it is to "send" something to your IO task on tile 0 from UserReadHIDButtons (for example a Control Token)
then you add a case in your select to react on reception of this CT and you send back the "params" in this case.

this way your task on tile 0 is always listening to a potential request and will reply in some nanoseconds. Thus the USB driver is not stall.