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  
  }
}
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
  );
}
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);
}
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
      );
    }
}
