How to run some general initialisation code before running a [[distrbutable]] task?

Technical questions regarding the XTC tools and programming with XMOS.
michaelf
Member++
Posts: 26
Joined: Thu Mar 14, 2024 7:44 pm

How to run some general initialisation code before running a [[distrbutable]] task?

Post by michaelf »

I currently run an i2c_master task in a distributed fashion:

Code: Select all

// declare ports etc

int main() {
  // set up interfaces etc
  
  par {
    on tile[0]: [[distribute]]  i2c_master(i2c_if, NUM_I2C_CLIENTS, p_scl, p_sda, I2C_FAST_MODE_KHZ);
    // more tasks  
  }
}
I'd like to configure the port drive strengths, but I can't find a way to do that whilst keeping the compiler happy and the task distributed.

As a reference, I can just do this when running my spi master on a dedicated core:

Code: Select all

// inside top level par statement within main()

on tile[0]: {
  // for example, reduce drive strength of one of the slave select ports; this function is defined elsewhere
  port_set_drive_strength(p_ss[0], TwomA);

  // set off the task now that we have configured the port(s)
  spi_master_async(
    NUM_SPI_CLIENTS, p_sclk, p_mosi, p_miso, p_ss, NUM_SPI_SLAVES, spi_cb0, spi_cb1
  );
}
Whichever way I try to adjust the syntax, I can't find a way to do that with the I2C task. I sort of want something like this:

Code: Select all

// inside top level par statement within main()

on tile[0]: {
  // do the initial set up once
  port_set_drive_strength(p_scl, TwomA);

  // then set off the task as distrubuted
  [[distribute]]  i2c_master(i2c_if, NUM_I2C_CLIENTS, p_scl, p_sda, I2C_FAST_MODE_KHZ);
}
...but that isn't valid syntax. If I exclude the [[distribute]] attribute, then it tries to occupy a full core.

My current hack is to use assembly to enable me to configure the i2c port from another task:

Code: Select all

// declare ports etc

int main() {
  // set up interfaces etc
  
  par {
    on tile[0]: [[distribute]]  i2c_master(i2c_if, NUM_I2C_CLIENTS, p_scl, p_sda, I2C_FAST_MODE_KHZ);
    
    on tile[0]: {
      // hijack this unrelated task to run initialisation code that uses the i2c ports
      
      unsigned port_pad_ctl = /* configure PORT_PAD_CTL settings */;
      unsigned x;
      asm("ldw %0, dp[p_scl]" : "=r"(x));
      asm volatile ("setc res[%0], %1" :: "r"(x), "r" (port_pad_ctl));    
    
      // then carry on with whatever this task was _actually_ supposed to do
      port_set_drive_strength(p_ss[0], TwomA);

      // set off the task now that we have configured the port(s)
      spi_master_async(
        NUM_SPI_CLIENTS, p_sclk, p_mosi, p_miso, p_ss, NUM_SPI_SLAVES, spi_cb0, spi_cb1
      );
    }
}
Is there a way to do this without the asm hack?