Understanding port inputs Topic is solved

Technical discussions around xCORE processors (e.g. xcore-200 & xcore.ai).
DemoniacMilk
XCore Addict
Posts: 191
Joined: Tue Jul 05, 2016 2:19 pm

Understanding port inputs

Post by DemoniacMilk »

I start to doubt that i know how port inputs work on the xcore.

From what I see on my scope, pins are sampled on the falling and output on the rising edge.

I am trying to initiate a word transfer by sending a sync one clock cycle right before the word is being transmitted. Timing diagram attached.
xcorePreSync.png
(8.64 KiB) Not downloaded yet
xcorePreSync.png
(8.64 KiB) Not downloaded yet
For transmitting, I am able to generate the desired timings. Output pins/ports are shortened with input pins/ports, and the inputs are driven by a clock block, that is output on one of the output ports.

Receiveing doesnt work tho, although I can see, that the values arent completely off. They just seem to be shifted, but im not sure why.

How I am trying to read the values is:

Code: Select all

while(1){
        [[ordered]]
        select{

            case portInfsync when pinsneq(uiPinValue) :> uiPinValue:
                /* frame sync just changed, did it transition to high? -> another 16 bit word is on its way!
                 * Continous clock! frame sync arrives one clock cycle BEFORE the first data bit.
                 * flush the buffer to put it in an empty/defined state - but only if we're not 
                 * currently receiving */
                if (uiPinValue){
                    if(!uiBytesExpected){
                        clearbuf(portInData);
                    }
                    uiBytesExpected += 2;   // we expect two more bytes for each frame sync
                }
                break;
            case uiBytesExpected => portInData :> uiData: // Read from buffer if more bytes are expected
                // send to rx_processing thread for buffering
                rxServerProcessing_c <: (unsigned char)uiData;
                uiBytesExpected--;   // reduce the expected remaining number of bytes
                break;
        } // select
    } // while
Basically, every time the sync signal transitions to high, we expect 2 more bytes to be sent. If were not in a transition already, then we want to set the buffer to a known/empty state, as the clock is running continously, so the port is deserializing continously as well, but there is no need for the previous/invalid data and I need a fixed starting point.

What I expected this to do:
Data is being sampled on all ports on the rising edge.
This means, portInData has another bit being shifted in, while sync might (or might not) transition to high. For latter, the CPU spends some time checking, if we are transmitting transmitting already, and if not, the portInData buffer is flushed, throwing away all sampled bits including the bit that was just sampled. Because we got another sync signal, we expect 2 more bytes.
In the next sample cycle, our first data bit is sampled. In this case, the input port is buffered with 8 bits, so 7 cycles later, the first byte has been received.
Another 8 bits later, the second byte has finished sampling AND the next sync signal arrives. Here i wonder:

what happens?

Both events (port transition and port buffer full) fire at the same time, as both ports are sampled at the same time?
If this is the case, can I be sure that the higher priority case is always executed first?
Is my approach reasonable for what I am trying to achieve?


View Solution
User avatar
Ross
XCore Expert
Posts: 962
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

Please can you include the code that includes the declaration/initialisation of your ports?

Also, what freq is the clock and fsync signals expected to be roughly?
DemoniacMilk
XCore Addict
Posts: 191
Joined: Tue Jul 05, 2016 2:19 pm

Post by DemoniacMilk »

Please can you include the code that includes the declaration/initialisation of your ports?
Sure!

Port/clock initilization is:

Code: Select all

out buffered port:32 portOutData  = on tile[0]: XS1_PORT_1L; // D35
out buffered port:32 portOutfsync = on tile[0]: XS1_PORT_1N; // D37
out buffered port:1  portOutClock = on tile[0]: XS1_PORT_1M;  // D36
in  buffered port:8  portInData   = on tile[0]: XS1_PORT_1J;  // D25  // CONFLICTS WITH ETHERNET USAGE
in  port             portInfsync  = on tile[0]: XS1_PORT_1O;  // D38
in  port             portInClock  = on tile[0]: XS1_PORT_1P;  // D39
clock clock0  = on tile[0]: XS1_CLKBLK_1;
clock clock1  = on tile[0]: XS1_CLKBLK_2;
clock clockIn = on tile[0]: XS1_CLKBLK_3;
Also, what freq is the clock and fsync signals expected to be roughly?
Clock: minimum of 25 MHz, aiming for 50 MHz.
Fsync: 'High' for one clock cycle, at maximum once every 16 clock cycles.

The mein function looks like this:

Code: Select all

    ssc_interface_single ssc_tx_if;
    ssc_interface_single ssc_rx_if; // placeholder, no RX interface needed yet

    par{
        on tile[0]: ssc_test(ssc_tx_if);

        on tile[0]: ssc_single(ssc_tx_if, ssc_rx_if,
                        portOutData, portOutClock, portOutfsync,
                        portInData, portInClock, portInfsync,
                        clock0, clock1, clockIn);

    }
and starts the ssc_single() thread that does port/clock setup, then starts an RX, TX, and RX_processing thread. The RX thread was shown in the OP and sends sampled data to the RX_processing thread via a streaming channel, where the data is buffered and analyzed for start/end flags/validity.
ssc_test() is periodically sending out a predefined array of chars, by using the ssc_tx_if to send data to the TX thread, which are then stuffed into 32 Bit integers and, together with the sync signal, output on ports. The three output ports (clk, data, sync) are connected to the respective input ports.


EDIT: I changed the in port for the sync signal to be in buffered port:1, because I don't know how a port behaves that is not buffered (might be sampled by directly by the CPU?).
The effect remains the same. All results are shifted by one bit. Instead of F0F0.. i receive 8787..

EDIT2: when is the port buffer actually flushed? immediately or when the next sample would be taken?
The programming guide states: "The recommended way to synchronise to a rising edge is to clear the buffer using the standard library function clearbuf and then perform an input." I guess this is refering to CPU synchronization, as the CPU blocks until it can output the data?
User avatar
larry
Respected Member
Posts: 275
Joined: Fri Mar 12, 2010 6:03 pm

Post by larry »

I think that sentence from the programming guide about using clearbuf is meant for relatively slow I/O, where you have many instructions per application clock cycle and can make assumptions about the amount of time until the next clock cycle.

If you are running faster, say 50MHz, and only have a handful of instructions per clock cycle, clearbuf is no longer useful, because you don't know when exactly it will be executed with respect to the application clock.

You'll be better off using timed I/O. Take a timestamp of the sync pulse and input data from the buffered at +8 cycles later. What happens with a timed input on that buffered data port is that the last 8 bits up to given time are returned. Subsequent inputs are aligned to the first one, so just keep doing plain inputs to get remaining data.

Code: Select all

                portInfsync when pinseq(1) :> void @ t;
                portInData @ (t + 8) :> x0;
                portInData :> x1;
                portInData :> x2;
Note that it might need to +7 or +9 rather than +8, depending on implementation details that I can't remember right now.

The advantage of using timed I/O compared to clearbuf and plain inputs is that as long as code is executed before all 8 bits have gone, data input will be aligned to sync. With plain inputs you could be off by a number of bits depending on when exactly code is executed after sync.
DemoniacMilk
XCore Addict
Posts: 191
Joined: Tue Jul 05, 2016 2:19 pm

Post by DemoniacMilk »

So this allows me to read the input at any time?
I wasn't sure about that cause I thought the shift register has to be "full" (as in some counter indicates we read another n bits on an n-buffered port). If i can read out the shift register at any given point, then i actually dont have an problem. Will give it a try tomorrow! Thank you
User avatar
larry
Respected Member
Posts: 275
Joined: Fri Mar 12, 2010 6:03 pm

Post by larry »

DemoniacMilk wrote:So this allows me to read the input at any time?
I wasn't sure about that cause I thought the shift register has to be "full" (as in some counter indicates we read another n bits on an n-buffered port). If i can read out the shift register at any given point, then i actually dont have an problem. Will give it a try tomorrow! Thank you
Timed input moves a fresh lot from shift register (register that runs continuously and does the deserialisation) to transfer register, so you always get transfer register full at the right time.
DemoniacMilk
XCore Addict
Posts: 191
Joined: Tue Jul 05, 2016 2:19 pm

Post by DemoniacMilk »

Okay that is awesome.

Thinking about it: not moving new data to the transfer register for a timestamped read would render the function useless, so my question was actually a bit stupid. Thank you for your help.
DemoniacMilk
XCore Addict
Posts: 191
Joined: Tue Jul 05, 2016 2:19 pm

Post by DemoniacMilk »

Based on your help I am currently trying to change the receive thread posted in the opening post to use timestamped inputs instead of clearbuf(), but I am having some difficulties. I would like to keep the strcuture kind of similar to this:

Code: Select all

    while(1){
        [[ordered]]
        select{
            case portInfsync when pinseq(0x1) :> void @ uiTimestampSync:
                /* frame sync high, another 16 bit word is on its way!
                 * frame sync arrives one clock cycle BEFORE the first data bit. */
                if((uiTimestampSyncPrev - uiTimestampSync) < 16)
                    break;

                uiTimestampSyncPrev = uiTimestampSync;

                if(!uiBytesExpected){
                    // .:A:.
                }
                uiBytesExpected += 2;   // we expect two more bytes for each frame sync
                break;

            case uiBytesExpected => portInData  :> uiData: // Read a set of bytes if more bytes are expected
                rxServerProcessing_c <: uiData;
                uiBytesExpected--;
                break;
        } // select
    } // while
Where .:A:. is the code section I am not sure about. At this point I know im not within a transmission (the sync signal is sent before every 16-bit word, so it is possible that sync signal and the last bit of the previous word are received at the same time). If im not within a transmit, I have to sync my data input, what I used to do with a slow clearbuf().

As suggested by larry, I tried to remove clearbuf() and use timestamps instead. At .:A:. I thought about reading portInData immediately or when it should store the first byte, but I dont understand how I could do that, as the port counters for sync and data have different values. Can I read an input ports timestamp at any time or only when performing an input? Does portInData:>void; always block? Or does it block only if I try to read multiple values in a row and no new data is available compared to my previous read? Or does this never block, but only triggers an event wheneverthe shift regsiter is "full"? Is there a way to force a read (transfer to transmit register) immediately, without knowing the current timestamp?
User avatar
larry
Respected Member
Posts: 275
Joined: Fri Mar 12, 2010 6:03 pm

Post by larry »

What I forgot to add in my original suggestion was that sync and data ports will need to have their timestamps synchronised. For that, they just need to run off the same clock block. When a clock block starts, all ports attached to it have their times reset (simultaneously).
DemoniacMilk
XCore Addict
Posts: 191
Joined: Tue Jul 05, 2016 2:19 pm

Post by DemoniacMilk »

Ah okay so they are synchronized already.
I did a small test:

Code: Select all

    case portInfsync when pinseq(0x1) :> void @ uiTimestampSync:
         portInData :> void @ uiFirst;
         break;
and realized that uiFirst was smaller than uiTimestampSync. Does port :> var @ time return the timestamp of when the data was transfered to the transfer register instead of when the transfer register was read?
Post Reply