How do I use the same clock block in two different cores? Topic is solved

If you have a simple question and just want an answer.
Post Reply
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

How do I use the same clock block in two different cores?

Post by infiniteimprobability »

I would like to use the same clock block to clock ports in two different cores. However the compiler gives me a "parallel usage violation" error. How can I do this?



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

Post by infiniteimprobability »

Sharing clockblocks is OK to do in the architecture (actually the hidden clockblock number 0 aka the 100MHz ref clock does this), however the compiler by default prevents any resource from being used in differnt cores, even though in this case it is safe.

Using the tools 13 features (unsafe pointers) you can have an alias of the clock block and pass it to two different cores. Please make sure they are on the same tile! Only channel/interface calls traverse tiles..nothing else is shared across tiles.

Finally, don't try this with ports or timers unless you know what you are doing - it is dangerous to share these across cores without locks to ensure two instructions accessing the same resource don't find themselves in the pipeline at the same time (exception)..

Here's the code example showing how to use the same clockblock :

 

#include <xs1.h>

buffered out port:32 p_1 = XS1_PORT_1A;
buffered out port:32 p_2 = XS1_PORT_1B;
 
clock clk = XS1_CLKBLK_1;
 
void task1(buffered out port:32 p_clocked, clock * unsafe clk_p){
  unsafe{
    configure_out_port_no_ready(p_clocked, *clk_p, 0);
  }
  while(1){
    p_clocked <: 0xaaaaaaaa;
  }
}
 
void task2(buffered out port:32 p_clocked, clock * unsafe clk_p){
  unsafe {
    configure_out_port_no_ready(p_clocked, *clk_p, 0);
  start_clock(*clk_p);
  }
  while(1){
    p_clocked <: 0xf0f0f0f0;
  }
}
 
int main(void){
  stop_clock(clk);
  configure_clock_ref(clk, 17); //About 3MHz
 
  unsafe {
 
    clock * unsafe clk_ptr1 = &clk;
    clock * unsafe clk_ptr2 = &clk;
 
    par{
      task1(p_1, clk_ptr1);
      task2(p_2, clk_ptr1);
    }
  }//unsafe region
return 0;
}
ffomich
Experienced Member
Posts: 119
Joined: Mon Sep 15, 2014 1:32 pm

Post by ffomich »

Hi infiniteimprobability,
please clarify 2 moments:

1.

Code: Select all

void task1(buffered out port:32 p_clocked, clock * unsafe clk_p){ 
  unsafe{ 
    configure_out_port_no_ready(p_clocked, *clk_p, 0); 
  } 

Code: Select all

void task2(buffered out port:32 p_clocked, clock * unsafe clk_p){ 
  unsafe { 
    configure_out_port_no_ready(p_clocked, *clk_p, 0); 
  start_clock(*clk_p); <- must be only in task2() ?
  } 
2.

Code: Select all

int main(void){ 
  ...
    par{ 
      task1(p_1, clk_ptr1); 
      task2(p_2, clk_ptr1); <- must be clk_ptr2 here?
    } 
 ...
}
DemoniacMilk
XCore Addict
Posts: 191
Joined: Tue Jul 05, 2016 2:19 pm

Post by DemoniacMilk »

Ill just randomly add my 2 cents here if oyu dont mind.

1) Starting the clock block once is enough, its the same ressource after all, so you don't need to start it twice

2) I think you could just declare one unsafe pointer and pass it to both tasks.
He was talking about aliasing pointers earlier, I havent really used those, so I dont know what the compiler allows in that case.

EDIT: Tested, on using alias pointers you have to use ptr1 and ptr2 as parameters. However, i end up getting an error "../src/00_clkShare.xc:49:18: error: use of alias pointer `clk_ptr2' declared outside of par block" (and a similar error for clt_ptr1)
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

Post by infiniteimprobability »

task2(p_2, clk_ptr1); <- must be clk_ptr2 here?
Yes - looks like I accidentally passed in the same pointer to both tasks (typo). However this worked so two pointers apparently are not required.

DemoniacMilk's other comments look pretty fair.
User avatar
CousinItt
Respected Member
Posts: 360
Joined: Wed May 31, 2017 6:55 pm

Post by CousinItt »

It may be a bit late in the day, but it is possible to share a constant clock block without tears.

It could be useful for configuring ports, but it's fairly limited, For example it wouldn't work if one of the shared instances needs to do something to the clock, like start it.

This code compiles without complaint (using 14.4.1 tools):

Code: Select all

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

on tile[0]: in port pa = XS1_PORT_1A;
on tile[0]: in port pb = XS1_PORT_1B;

on tile[0]: clock bclk = XS1_CLKBLK_1;

void clock_test_1(in port p, const clock clk)
{
   configure_in_port(p, clk);
}

void clock_test_2(in port p, const clock clk)
{
   configure_in_port(p, clk);
}

void tile_0_tasks(void)
{
   par
   {
      clock_test_1(pa, bclk);
      clock_test_2(pb, bclk);
   }
}

int main(void)
{
   par
   {
      on tile[0]: tile_0_tasks();
   }

   return 0;
}
User avatar
fabriceo
XCore Addict
Posts: 181
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hi CousinItt
I m using a single clock block for 4 Spdif_rx tasks, but I had to read the clock_block with an assembly statement (ldw %0,dp[clk_blk]) and pass the result as an unsigned value to each tasks... this was during my early days on xmos where I had very much difficulty to understand all the constrains linked to concurrent task and exclusive resource allocation...
User avatar
CousinItt
Respected Member
Posts: 360
Joined: Wed May 31, 2017 6:55 pm

Post by CousinItt »

Thanks Fabriceo, your method looks like a better compromise than declaring unsafe all functions that use a clock block just to share it.
User avatar
Ross
XCore Expert
Posts: 962
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

The easiest thing to do is configure the ports/clocks before the par

Code: Select all

void main()
{
   configure_in_port(p0, clk);
   configure_in_port(p1, clk);
   par
   {
       task0(p0);
       task1(p1);
   }
}
Post Reply