This is an interesting discussion..
We had some experience of something similar where we observed a unreliable reset in a multi-chip system (only worked about 75% of the time so not a good customer experience).
Firstly, the absolutely safe way of doing it is indeed using the RST line. This is nice and clean and will be the most robust but uses extra HW, albeit an I/O and some simple components.
Doing it all using the PLL register write way needs careful tuning, and needs to be done in a kind of reverse spanning tree way. As hkr87 said - do it too quickly and you will cut off packets in flight to the furthest away nodes so they won’t be reset. Too slow, and links can come up connected to an out of synch or to floating lines, possibly causing link lockup. So this is a definitely a case for Goldilocks engineering of getting it just right.
We found that a delay of about a microsecond was enough time to let packets get there, but ensure that the end node wasn’t too far ahead after reboot. This is related to the PLL settle time.
However to complicate things, depending on the network and usage, there may not be a switch path available immediately, which could throw timings due to blocking. You should ideally ensure in the application that a path is always available, however unwinding all of the channel communications to ensure this may be quite fiddly if the paths are heavily used.
To work around this, we wrote a split write_sswitch_no_ack function into an open and write/close pair primatives. The open blocks until the path is available and reserves it thereafter. Once this is done, the write/close can be used, safe in the knowledge that the message will get through quickly and that the delay timings will be observed.
Attached is the code to do this in a 1 x L16 and 1 x L8 system.
Code: Select all
#define _chkct(a,b) {__asm__ __volatile__ ("chkct res[%0], %1": : "r" (a) , "r" (b));}
#define _outct(a,b) {__asm__ __volatile__ ("outct res[%0], %1": : "r" (a) , "r" (b));}
#define _outuchar(a,b) {__asm__ __volatile__ ("outt res[%0], %1": : "r" (a) , "r" (b));}
#define _outuint(a,b) {__asm__ __volatile__ ("out res[%0], %1": : "r" (a) , "r" (b));}
#define _inuint_byref(a,b) {__asm__ __volatile__ ("in %0, res[%1]": "=r" (b) : "r" (a));}
#define _sync(a) {__asm__ __volatile__ ("syncr res[%0]": : "r" (a));}
static inline unsigned _getchanend(unsigned otherside) {
unsigned cend;
__asm__ __volatile__ ("getr %0, 2"
: "=r" (cend));
__asm__ __volatile__ ("setd res[%0], %1"
: : "r" (cend), "r" (otherside) );
return cend;
}
static inline void _freechanend(unsigned cend) {
__asm__ __volatile__ ("freer res[%0]"
: /* no output */
: "r" (cend));
}
void wait_us(int us)
{
int time_now;
timer t;
t :> time_now;
t when timerafter(time_now + us * 100) :> void;
}
{unsigned, unsigned} _write_sswitch_no_ack_open(unsigned tile_id){
unsigned c_write, dst_addr;
dst_addr = XS1_RES_TYPE_CONFIG | (XS1_CT_SSCTRL << XS1_CHAN_ID_CHANNUM_SHIFT) | (tile_id << XS1_CHAN_ID_PROCESSOR_SHIFT);
c_write = _getchanend(dst_addr);
//Start of packet with command token to request write to switch. Reserves route
_outct(c_write, XS1_CT_WRITEC);
return {c_write, dst_addr};
}
void _write_sswitch_no_ack_write_close(unsigned c_write, unsigned dst_addr, unsigned reg, unsigned data){
unsigned rtn_addr;
//Calculate return address, dest node tile_id, channel end 0xff (will get junked by device so stops ack getting through)
rtn_addr = dst_addr >> 8;
rtn_addr |= 0xff;
//Send return address (3 bytes)
_outuchar(c_write, rtn_addr >> 16);
_outuchar(c_write, rtn_addr >> 8);
_outuchar(c_write, rtn_addr);
//Send bottom 8b of reg
_outuchar(c_write, reg >> 8);
//Send bottom 8b of reg
_outuchar(c_write, reg)
//Send data
_outuint(c_write, data);
//Send end of packet
_outct(c_write, XS1_CT_END);
//Free channel end
_freechanend(c_write);
}
////////////////////////////////////////////////////
///RESET FUNCTION//////////////////////////////////
///MUST BE CALLED ON TILE 0///////////////////////
/////////////////////////////////////////////////
unsigned c_write, dst_addr, pll_val, id[3];
//Free channel end here if needed
id[0] = get_tile_id(tile[0]);
id[1] = get_tile_id(tile[1]);
id[2] = get_tile_id(tile[2]);
read_sswitch_reg(id[2], 6, pll_val); //read the pll register
{c_write, dst_addr} = _write_sswitch_no_ack_open(id[2]); //Open channels to tile2 sswitch first
wait_us(10000);
_write_sswitch_no_ack_write_close(c_write, dst_addr, 6, pll_val); //write reset val to tile 2
wait_us(1);
read_sswitch_reg(id[1], 6, pll_val);
write_sswitch_reg(id[1], 6, pll_val); //Reset tile 1
wait_us(1);
read_sswitch_reg(id[0], 6, pll_val);
write_sswitch_reg(id[0], 6, pll_val); //Reset tile 0