Channel/interface limitation between tiles or per function?

Technical discussions around xCORE processors (e.g. xcore-200 & xcore.ai).
Gothmag
XCore Addict
Posts: 129
Joined: Wed May 11, 2016 3:50 pm

Channel/interface limitation between tiles or per function?

Post by Gothmag »

Is there a limit to the number of channels or interfaces 1 function can manage, or a limit to the number of channels/interfaces operating between tiles?

I'm using an explorerkit and I'm running 1 function to check button inputs by masking the appropriate bits from the 4 bit port and it's correctly identifying a button press(verified using printf()), but the other end of the interface(a [[notification]]) never seems to be called. It's similar in set up to the gpio library.

I check the buttons port on tile[0] using a server task, and the client receives a notification when appropriate. I ran the same code I'm using completely isolated and it worked with the server on tile[0].core[0] and client on tile[1].core[0]. On the actual program the server runs on tile[0].core[3] and the client is running on tile[1].core[0]. I've stopped all other tasks on tile 1 and still get nothing. The task that gets the notification has 13 interfaces being passed into it though with 11 of those from tile 1 to tile 0. If I have to I can write another function and define an interface to control it all across tiles using a single interface(should be 1 channel then? I'm not clear on this) but I don't have time right now. I'm well within the tile constraints but I wasn't sure if there was another unwritten limit that's causing my issue.

Code: Select all

/*
 * Slash RC car.xc

 *
 *  Created on: May 20, 2016
 *      Author: Maginnovision
 */

#include <gpio.h>
#include "Interfaces.h"
#include "Ports.h"
#include "ThreadDefinitions.h"

int main() {
    //Interfaces for reading/writing PWM values
    controller_if steering, motor;

    //Interfaces for wheel speed data
    wheel_speed_if leftwheel, rightwheel;
    wheel_handler_if wheels;

    //Interfaces for I2C interfaces
    i2c_master_if imu_i2c[1];
    i2c_slave_callback_if rpi_slave;

    path_follower_if path_following[3];
    path_follower_data_if path_data;

    datalogger_if datalogger;
    datalogger_pathing_if datalogger_pathdata;

    imu_if imu[3];

    input_gpio_if button_if[2];
    output_gpio_if led_if[4];

    par {
        //ESC PWM
        on tile[0].core[0]: read_pwm(motor_input, motor, path_following[0], 0);
        on tile[0].core[0]: write_pwm(motor_output, motor);
        //Steering servo PWM
        on tile[0].core[0]: read_pwm(steering_input, steering, path_following[1], 1);
        on tile[0].core[0]: write_pwm(steering_output, steering);
        on tile[0].core[0]: read_toggle(red_toggle, path_following[2]);

        //IMU server and I2C master
        on tile[0].core[1]: IMU(imu_i2c[0], imu);
        on tile[0]: i2c_master(imu_i2c, 1, imu_scl, imu_sda, 400);

        //Datalogging threads
        on tile[0].core[2]: Datalogger(rpi_slave, datalogger, datalogger_pathdata);
        on tile[0]: i2c_slave(rpi_slave, rpi_scl, rpi_sda, 0x11);

        //Button handler and led handler
        on tile[0].core[3]: input_gpio_with_events(button_if, buttons_port);
        on tile[0].core[4]: output_gpio(led_if, 4, leds_port, null);

        on tile[1].core[0]: path_follower(path_following, imu[2], wheels, datalogger, path_data,
                led_if[0], led_if[1], led_if[2], led_if[3], button_if[0], button_if[1]);
        on tile[1]: pathing_data(path_data, datalogger_pathdata);

        //Wheel speed threads and higher level handler
        on tile[1].core[1]: wheel_speed(left_wheel_a, left_wheel_b, leftwheel, imu[0]);
        on tile[1].core[2]: wheel_speed(right_wheel_a, right_wheel_b, rightwheel, imu[1]);
        on tile[1].core[3]: wheels_handler(leftwheel, rightwheel, wheels);
    }
    return 0;
}
Here is the main, in case it helps.

Code: Select all

[[combinable]]
 void input_gpio_with_events(server input_gpio_if i[2], in port p)
{
    unsigned pval = 0, last_pval = 0;

    p :> pval;

    while (1) {
        select {
        case i[int j].input() -> unsigned result: {
            result = (pval >> j) & 1;
            break;
        }
        case i[int j].event_seen(): {
            delay_milliseconds(50);
            break;
        }
        case p when pinsneq(pval) :> last_pval: {
            if ((pval & 0x01) != (last_pval & 0x01) && (pval & 0x01)) i[0].event();
            if ((pval & 0x02) != (last_pval & 0x02) && (pval & 0x02)) i[1].event();
            pval = last_pval;
            break;
        }
        }
    }
}
And the server.

Code: Select all

 case button1.event(): {
            button1.event_seen();
            current->manual = !current->manual;
            led_green.output(current->manual);
            robot[0].button();
            robot[1].button();
            break;
        }
        case button2.event(): {
            button2.event_seen();
            ++rgbled;
            rgbled_red.output(0); rgbled_green.output(0); rgbled_blue.output(0);
            if (rgbled == 1) rgbled_red.output(1);
            if (rgbled == 2) rgbled_green.output(1);
            if (rgbled == 3) rgbled_blue.output(1);
            if (rgbled == 4) {rgbled = 0; rgbled_red.output(0); rgbled_green.output(0); rgbled_blue.output(0);}
            break;
        }
And the selects for the events.


User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

Post by infiniteimprobability »

Hi,
there shouldn't be a limit in the tools regarding how many parameters you pass in, be them interfaces or whatever.

However, it's worth considering the physical implementation and the device resources.

Interfaces across tiles or physical logical cores (hmm - perhaps we call them threads!?) will always use channels as opposed to combined or distributed tasks on the same logical core which can inline.

It shouldn't make a functional difference (although timing my vary) but when crossing the tile you may see issues. There are only 4 circuits across the switch from tile to tile. Interfaces will setup the path, do the transaction and then free the path. But, depending on usage, it's possible that all 4 circuits may be blocking an additional communication from happening.

What sort of traffic is flowing? Is it big frequent packets or sparse short control data?
henk
Respected Member
Posts: 347
Joined: Wed Jan 27, 2016 5:21 pm

Post by henk »

I would expect the interfaces to use normal channels; that is, you can have as many as you want as long as you don't run out of bandwidth as infiniteimprobability points out.

Can you post the contents of your thread with 13 interfaces specifically where the notification is?
Gothmag
XCore Addict
Posts: 129
Joined: Wed May 11, 2016 3:50 pm

Post by Gothmag »

Typically the information passed is just 32 bits. One does actually pass around 450 bits but that's only called on demand for datalogging. That normally only happens once every 20ms or so normally but it currently won't be running at all since the master i2c device isn't setup and won't be forcing the call.

Once every 5ms 20 bytes is transferred, 2x per 20 ms another 4 bytes is moved with 2 notifications cleared functions called that pass 4 bytes each(control variables).

This was running on a startkit except it was all 1 tile. Most data movement is in tile, the cross tile data is only due to port configuration. As soon as I can get to a computer I can post the large function. I've been writing this to keep data movement to a minimum but keeping threads running as fast as possible. I'm not trying to stream massive amounts of data. If I specify variables to be 8 bit would it actually transfer just 8 bits? I haven't been doing this as much as normal since it's all 32 bit memory but can if it'd help. If this info doesn't help feel free to ignore it until I can add the larger function. Thanks for the responses.
Gothmag
XCore Addict
Posts: 129
Joined: Wed May 11, 2016 3:50 pm

Post by Gothmag »

This is the thread that should be catching the notification from the button handler. The cases in question are within the //'s, I had them included in the first post but this will give more context. I'm not using the second button as of now the code there is just so I know if it's working. I can press the button as much and as often as I want and never receive the notification which is why I wasn't sure if there was some other limitation.

Code: Select all

#include "Path_Follower.h"
#include <stdio.h>

//robot[1] is steering
//robot[0] is motor
[[combinable]]
 void path_follower(server path_follower_if robot[3], client imu_if IMU, client wheel_handler_if Wheels, server datalogger_if datalog,
         client path_follower_data_if path_data, client output_gpio_if led_green, client output_gpio_if rgbled_blue, client output_gpio_if rgbled_green,
         client output_gpio_if rgbled_red, client input_gpio_if button1, client input_gpio_if button2)
{
    delay_seconds(5);

    timer AUTOTIMER, DATATIMER; //Timers
    unsigned int AUTO = 0, DATA = 0, DELAYTIME = 0; //Timer counters
    unsigned reverse_count = 0;
    unsigned rgbled = 0;

    Car * alias current = (Car* alias)calloc(1, sizeof(Car));
    current->manual = 1;

    //Set initial timer values
    AUTOTIMER :> AUTO;
    DATATIMER :> DATA;

    while (1) {
        select {
            //Automatic driving loop
        case !current->manual => AUTOTIMER when timerafter(AUTO + AUTO_INTERVAL) :> void: {
            //If we haven't checked the step do necessary work and mark it
            if (!path_data.checked()) {
                DELAYTIME = 0;
                update_car_data(current, IMU, Wheels);
                path_data.get_step(current);
            }
            //If we have marked a delay add to counter
            if (Delay(current->triggers)) DELAYTIME += DELAY_PERIOD;
            //Update car data and set steering and motor PWM's
            update_car_data(current, IMU, Wheels);

            setsteering(current);
            setspeed(current);
            unsafe {checkgoals(current, path_data);}

            if (path_data.size() == 0) current->manual = 1;
            AUTO += (1000 * MILLISECOND);
            break;
        }
        //Manual driving loop, data updates
        case current->manual => DATATIMER when timerafter(DATA + (5 * MILLISECOND)) :> DATA: {
            if (!path_data.checked()) path_data.get_step(current);
            update_car_data(current, IMU, Wheels);
            break;
        }
        //Update datalogger data
        case datalog.update_data() -> Car data: {
            data = *current;
            break;
        }
        //Update path recording status
        case robot[unsigned i].recording(unsigned record_value): {
            IMU.setrelative_heading();
            Wheels.clear();
            current->manual = 1;
            led_green.output(current->manual);
            current->recording = record_value;

            if (record_value) path_data.clear();
            robot[0].button();
            robot[1].button();
            break;
        }
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
        //If button pressed let control know and switch state
        case button1.event(): {
            button1.event_seen();
            printf("Button 1 seen.\n");
            current->manual = !current->manual;
            led_green.output(current->manual);
            robot[0].button();
            robot[1].button();
            break;
        }
        case button2.event(): {
            button2.event_seen();
            printf("Button 2 seen.\n");
            ++rgbled;
            rgbled_red.output(0); rgbled_green.output(0); rgbled_blue.output(0);
            if (rgbled == 1) rgbled_red.output(1);
            if (rgbled == 2) rgbled_green.output(1);
            if (rgbled == 3) rgbled_blue.output(1);
            if (rgbled == 4) {rgbled = 0; rgbled_red.output(0); rgbled_green.output(0); rgbled_blue.output(0);}
            break;
        }
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
        //notification cleared, reset data
        case robot[unsigned steering].get_button(): {
            Wheels.clear();
            IMU.setrelative_heading();
            break;
        }
        case robot[unsigned i].steeringvalue(unsigned value): {
            current->steer_controller_speed = value / 100;
            break;
        }
        case robot[unsigned i].motorvalue(unsigned value): {
            current->motor_controller_speed = value / 100;
            break;
        }
        //Return the car state
        case robot[unsigned i].manual() -> unsigned retvalue: {
            retvalue = current->manual;
            break;
        }
        //Return actuator speeds for steering and motor
        case robot[unsigned steering].speed() -> unsigned retvalue: {
            //Set retvalue to neutral, in case any breaks occur neutral is returned
            retvalue = neutral;
            if (current->manual) break;
            current->target_speed = path_data.size();

            //Sync AUTO timer so we start calculating PWM values 5ms before next read
            AUTOTIMER :> AUTO;
            //Check status of delay and reverse
            if (DELAYTIME) {
                if (Delay(current->triggers) && DELAYTIME >= 5000) current->triggers ^= (1 << 7);
                if (!Delay(current->triggers)) DELAYTIME = 0; //Reset DELAYTIME if no delays are flagged
                else break;
            }
            if (Reverse(current->triggers) && !steering) {
                if (reverse_count > 0) current->triggers ^= (1 << 6);
                else retvalue = 1600;

                if (reverse_count) reverse_count = 0;
                else reverse_count = 1;
                break;
            }

            //Return intended PWM values in uS
            if (steering) retvalue = current->steer_speed;
            else retvalue = current->motor_speed;
            break;
        }
        }
    }
}
Gothmag
XCore Addict
Posts: 129
Joined: Wed May 11, 2016 3:50 pm

Post by Gothmag »

I decided to poll the button input, within my tile[1] path_follower thread, to see if the interface is working at all. The green led on the board comes on when I press the button which tells me the interface is working but for some reason the notification is never being raised. Since I had both a client and server side interface in this thread both with notifications I removed all elements of it's server interface notification to see if it was some issue there to see if it'd get the button pressed notification and there was no change.

Anybody have an idea what could be going on here? It may seem stupid but buttons are actually required for the project and due to the physical port configuration the threads have to be split over the tiles like they are. I am using version 14.2.0 of studio community version on windows 10. I'm going to try the project in 14.1.2 to see if there is any change in behavior later tonight... Quick update: no change with 14.1.2.
User avatar
larry
Respected Member
Posts: 275
Joined: Fri Mar 12, 2010 6:03 pm

Post by larry »

What happens if you copy your 13-interface task into a new program that only has this task and another task generating a notification on its server interface?

Code: Select all

                 par {
                      path_follower(i, 12 other interfaces)
                      i.notify()
                 }
Gothmag
XCore Addict
Posts: 129
Joined: Wed May 11, 2016 3:50 pm

Post by Gothmag »

Ran the code:

Code: Select all

par {
path_follower(path_following, imu[2], wheels, datalogger, path_data,
                        led_if, button_if);
        button_if[0].event();
        button_if[1].event(); }
This was a new project in the same workspace. Entire program was 1 .xc file containing all the structures and interfaces from the original but none of the actual code except for the path_follower function which had to be slightly modified to run normally(a blocking call was basically halting it). After I got no positive result I altered the case/function that runs every 5ms to run every 5s and print out "Running\n" just to make sure it was and I did get that printing out every 5 seconds, but never the "Event received: %i\n" in the case to receive the event(). The following #includes were also in the .xc file as needed.
#include <platform.h>
#include <timer.h>
#include <lib_dsp.h>
#include <i2c.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

I simplified the function definition by just passing the entire arrays for leds and buttons into the function.

I had also tried earlier raising the notification for the other interface it's the server of and I got each notification and clear once but never again. The function that receives the notification only calls the clear notification function storing the result, an int.

Last night I'd also tried forwarding the pins/ports on tile 0 to the pins/ports on tile 1 using an 8 pin harness and running the threads on tile 1. that also failed to raise the notification.
Gothmag
XCore Addict
Posts: 129
Joined: Wed May 11, 2016 3:50 pm

Post by Gothmag »

Ok so this code prints events received:

Code: Select all

#include <platform.h>
#include <timer.h>
#include <stdio.h>

typedef interface input_gpio_if {
    [[notification]] slave void event();
    [[clears_notification]] void event_seen();
} input_gpio_if;

[[combinable]]
 void path_follower(client input_gpio_if buttons[2])
{
    timer DATATIMER;
    unsigned DATA = 0;

    DATATIMER :> DATA;

    while (1) {
        select {
        case DATATIMER when timerafter(DATA + (5 * 100000000)) :> DATA: {
            printf("Running\n");
            break;
        }
        case buttons[int second_button].event(): {
            printf("Event received: %i\n", second_button);
            break;
        }
        }
    }
}

int main() {
    input_gpio_if button_if[2];

    par {
        path_follower(button_if);
        button_if[0].event();
        button_if[1].event();
    }
    return 0;
}
And this code does not:

Code: Select all

#include <platform.h>
#include <timer.h>
#include <stdio.h>

typedef interface input_gpio_if {
    [[notification]] slave void event();
    [[clears_notification]] void event_seen();
} input_gpio_if;

typedef interface path_follower_if {
    [[notification]] slave void button();
    [[clears_notification]] void get_button();
} path_follower_if;

[[combinable]]
 void path_follower(server path_follower_if robot[3], client input_gpio_if buttons[2])
{
    timer DATATIMER;
    unsigned DATA = 0;

    DATATIMER :> DATA;

    while (1) {
        select {
        case DATATIMER when timerafter(DATA + (5 * 100000000)) :> DATA: {
            printf("Running\n");
            break;
        }
        case buttons[int second_button].event(): {
            printf("Event received: %i\n", second_button);
            break;
        }
        case robot[unsigned steering].get_button(): {
            break;
        }
        }
    }
}

int main() {
    path_follower_if path_following[3];
    input_gpio_if button_if[2];

    par {
        path_follower(path_following, button_if);
        button_if[0].event();
        button_if[1].event();
    }
    return 0;
}
So I'm assuming it's some issue with it being able to both notify and be notified over a certain number of interfaces, because if I change it to this:

Code: Select all

#include <platform.h>
#include <timer.h>
#include <stdio.h>

typedef interface input_gpio_if {
    [[notification]] slave void event();
    [[clears_notification]] void event_seen();
} input_gpio_if;

typedef interface path_follower_if {
    [[notification]] slave void button();
    [[clears_notification]] void get_button();
} path_follower_if;

[[combinable]]
 void path_follower(server path_follower_if robot, client input_gpio_if buttons)
{
    timer DATATIMER;
    unsigned DATA = 0;

    DATATIMER :> DATA;

    while (1) {
        select {
        case DATATIMER when timerafter(DATA + (5 * 100000000)) :> DATA: {
            printf("Running\n");
            break;
        }
        case buttons.event(): {
            printf("Event received\n");
            break;
        }
        case robot.get_button(): {
            break;
        }
        }
    }
}

int main() {
    path_follower_if path_following;
    input_gpio_if button_if;

    par {
        path_follower(path_following, button_if);
        button_if.event();
    }
    return 0;
}
It prints that the event is received again, so it's not just having a server and a client but some number of them together?

EDIT: After trying to see what the numbers were to cause the issue, it's working properly as it is here. I haven't changed anything other than building it a couple times to figure it out. It's even working with 5 of each. I've tried changing, in both the actual project and this test, the optimization level and it's never had an effect on the output.

I've attached a version that is closer to what actually runs that still fails to print in case anyone wants to look at it. Running composer community 14.2.0 on windows 10 with an explorerkit.
Attachments
tester.xc
Fails to print that notifications were received when using an explorerkit.
(7.11 KiB) Downloaded 354 times
tester.xc
Fails to print that notifications were received when using an explorerkit.
(7.11 KiB) Downloaded 354 times
User avatar
larry
Respected Member
Posts: 275
Joined: Fri Mar 12, 2010 6:03 pm

Post by larry »

Gothmag wrote:I've attached a version that is closer to what actually runs that still fails to print in case anyone wants to look at it. Running composer community 14.2.0 on windows 10 with an explorerkit.
Thank you. I think your example can be reduced to:

Code: Select all

#include <xs1.h>
#include <stdio.h>

interface i {
  void f();
  void g();
};

interface j {
  [[notification]] slave void n();
};

void T(server interface i i, client interface j j)
{
  while (1) {
    select {
      case i.f(): break;
      case j.n(): printf("Event received\n"); break;
      case i.g(): break;
    }
  }
}

int main()
{
  interface i i;
  interface j j;
  par {
    T(i, j);
    j.n();
  }
  return 0;
}
This does look like a compiler bug. Just moving the g case up just after the f case makes the notification trigger.

Could you please report this issue under Report a Bug at https://www.xmos.com/support/help?
Post Reply