{"action":"created","issue":{"url":"https://api.github.com/repos/openthread/openthread/issues/1533","repository_url":"https://api.github.com/repos/openthread/openthread","labels_url":"https://api.github.com/repos/openthread/openthread/issues/1533/labels{/name}","comments_url":"https://api.github.com/repos/openthread/openthread/issues/1533/comments","events_url":"https://api.github.com/repos/openthread/openthread/issues/1533/events","html_url":"https://github.com/openthread/openthread/issues/1533","id":218050693,"number":1533,"title":"SPINEL - protocol concerns","user":{"login":"DuaneEllis-TI","id":25351219,"avatar_url":"https://avatars1.githubusercontent.com/u/25351219?v=3","gravatar_id":"","url":"https://api.github.com/users/DuaneEllis-TI","html_url":"https://github.com/DuaneEllis-TI","followers_url":"https://api.github.com/users/DuaneEllis-TI/followers","following_url":"https://api.github.com/users/DuaneEllis-TI/following{/other_user}","gists_url":"https://api.github.com/users/DuaneEllis-TI/gists{/gist_id}","starred_url":"https://api.github.com/users/DuaneEllis-TI/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DuaneEllis-TI/subscriptions","organizations_url":"https://api.github.com/users/DuaneEllis-TI/orgs","repos_url":"https://api.github.com/users/DuaneEllis-TI/repos","events_url":"https://api.github.com/users/DuaneEllis-TI/events{/privacy}","received_events_url":"https://api.github.com/users/DuaneEllis-TI/received_events","type":"User","site_admin":false},"labels":[{"id":380297891,"url":"https://api.github.com/repos/openthread/openthread/labels/NCP","name":"NCP","color":"c2e0c6","default":false},{"id":355043116,"url":"https://api.github.com/repos/openthread/openthread/labels/question","name":"question","color":"cc317c","default":true}],"state":"open","locked":false,"assignee":{"login":"darconeous","id":686678,"avatar_url":"https://avatars3.githubusercontent.com/u/686678?v=3","gravatar_id":"","url":"https://api.github.com/users/darconeous","html_url":"https://github.com/darconeous","followers_url":"https://api.github.com/users/darconeous/followers","following_url":"https://api.github.com/users/darconeous/following{/other_user}","gists_url":"https://api.github.com/users/darconeous/gists{/gist_id}","starred_url":"https://api.github.com/users/darconeous/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/darconeous/subscriptions","organizations_url":"https://api.github.com/users/darconeous/orgs","repos_url":"https://api.github.com/users/darconeous/repos","events_url":"https://api.github.com/users/darconeous/events{/privacy}","received_events_url":"https://api.github.com/users/darconeous/received_events","type":"User","site_admin":false},"assignees":[{"login":"darconeous","id":686678,"avatar_url":"https://avatars3.githubusercontent.com/u/686678?v=3","gravatar_id":"","url":"https://api.github.com/users/darconeous","html_url":"https://github.com/darconeous","followers_url":"https://api.github.com/users/darconeous/followers","following_url":"https://api.github.com/users/darconeous/following{/other_user}","gists_url":"https://api.github.com/users/darconeous/gists{/gist_id}","starred_url":"https://api.github.com/users/darconeous/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/darconeous/subscriptions","organizations_url":"https://api.github.com/users/darconeous/orgs","repos_url":"https://api.github.com/users/darconeous/repos","events_url":"https://api.github.com/users/darconeous/events{/privacy}","received_events_url":"https://api.github.com/users/darconeous/received_events","type":"User","site_admin":false}],"milestone":null,"comments":8,"created_at":"2017-03-30T00:50:48Z","updated_at":"2017-03-30T22:14:16Z","closed_at":null,"body":"some issues with the SPINEL protocol need to be addressed, specifically these deal with power management and sleepy devices.\r\n\r\n## Issue 1 - the NCP may be in a deep sleep.\r\n\r\nThe NCP device may be asleep, or in an ultra low power state and not ready to respond quickly  (immediately) to a host that may be transmitting data.\r\n\r\nTypically a GPIO signal from the HOST to wake, or draw the attention of the slave.\r\n\r\nThe simple approach would be to think the SPI-CS signal is the solution. The reason this fails is many SPI hosts will drop the CS to active, and immediately (within ZERO micro seconds) begin transmitting (clocking) data out of the SPI port. \r\n\r\nDepending upon the sleep state of the NCP and the wakup timing requirements the NCP may not be able to respond, and configure it self for the SPI transfer prior to the host transmitting (or perhaps the UART baudclock source needs to be turned back on)\r\n\r\nExamples include:  Interrupt servicing over head at \"slow (ie: 32khz) sleep clock rate\", length of time to re-enable the on chip oscillator, startup the external crystal and walk the internal power regulators back up to operating voltages.\r\n\r\nThis can often be made worse when the SPI interface is operating at 10mhz, or higher (vrs the 32khz sleep clock the NCP is waking up from).\r\n\r\nThe result is the Host & NCP slave can be \"off-by-a-bit\" or two, or three during the transfer and this problem only occurs rarely... and nobody can figure out why or when this occurs.. maybe the chip wakes up slower when it is very cold (think sensor inside a freezer, or a pool sensor in midwinter)\r\n\r\nWhat is missing is a \"NCP-IS-READY\" hand shake back to the host.\r\n(total 2 pins)\r\n\r\n## Issue 2 How fast can the HOST wake up?\r\n\r\nThe NCP may need to wake the host up and tell the host there is data to be sent.\r\n\r\nThis slightly discussed in the SPINEL protocol and repeated here for completeness.\r\n\r\nWhat is not considered is this: A battery powered Linux host might be asleep and in a low power state (example: A cell-phone-data-only device connected to an NCP) the NCP cannot just \"raise the request\" and start transmitting out the UART... that host might also need to \"wake up\" and turn clocks on, enable PLLs and so forth before the UART baudrate clock is operational.\r\n\r\nTypically another GPIO pair (ncp to HOST) is used to indicate this request.\r\ntotal: 4 pins.\r\n\r\n## Issue 3 - Timing (latency) Problems \r\n\r\nCOST PROBLEM - every pin on a chip costs money, or makes a product require a more expensive chip with more pins to provide the solution (this is great for those selling the chip, but bad for buyer of the chip)\r\n\r\nA fundamental truth is this:  Digital signals can cross 3 to 4 inches on a circuit board without problems at relatively high frequency. There is a myth that UARTs are not reliable, that was true when the signal ran across 300 feet of wire from one end of a factory to another end of the factory. Here we are dealing with 3 to 4 inches of signal traces on a circuit board or small flex cable. The state of the art is such that you do not have signal quality problems. The truth is often something else. The problem is often the power up sequences (above) or this race condition, or you are writing very in-efficient C code.\r\n\r\nIn general, one does not want 4 GPIO hand shake pins \r\n\r\n- To solve issue 1  (host -> ncp) requires 2 pins, a request and ack\r\n- To solve issue 2 (ncp -> host) requires 2 pins, a request and ack.\r\n- \r\n\r\nThe economic need is this:\r\n    2 pins UART (txd/rxd)  + 2 GPIO handshake = total 4\r\n    3 pins SPI  (mosi,miso,clock)  + 2 GPIO handshake = total 5\r\n\r\nNotice that the SPI does not use an actual CS signal - in theory it is present, just not hooked up or the GPIO pin is reconfigured in a \"non-SPI-mode\" and is used as a GPIO pin instead. \r\n\r\nIf the slave (NCP) requires this signal, the HOST_REQACK can be used as the SPI_CS signal. The slave will see the SPI_CS assert, then a seemingly long delay before the SPI_CLK starts.\r\n\r\n## Issue 4  - hardware design problem, power management\r\n\r\nSometimes the GPIO signals are also used to enable power to voltage level shifters between different power domains (ie: maybe the HOST uses VDD-IO as 1.2v, but the radio uses 3.3v?) Will enabling the level shift cost another GPIO pin? Or can we reuse one of the HANDSHAKE GPIO signals? The Money answer is simple: An existing GPIO signal needs to be reused reused.\r\n\r\n## The Timing Race Problem that occurs because of issues 1, 2, 3 and 4.\r\n\r\nThe race condition happens because of economizing the number of GPIO signals\r\n\r\nThe race condition is this:\r\n\r\n## At Time 0 - \r\nBoth the Host and the NCP decide \"i-need-to-send-data\"\r\nBoth assert their single GPIO signal at the exact same time.\r\n  \r\nthe host is often a linux machine, the user level IRQ latency is horrible compared to the NCP-microcontroller. Latency is defined as:  NCP-GPIO assert, to HOST-GPIO-Ready assert, or further first bit has transferred over the connection.\r\n\r\n## The RACE is this:\r\n\r\nThe host assumes the slave responded \"OK-READY\" via the GPIO, however the slave is sending \"REQUEST_TO_SEND\"\r\n\r\nDepending upon which packet is \"longer\" - the other side will need to insert \"SPI-STUFFING\" values.\r\n(the UART case does not require stuffing values)\r\n\r\n## Consider the scenario: \r\n\r\n- The host begins transmitting a very tiny packet.\r\n- The slave begins transmitting a very LARGE packet.\r\n- Note: the opposite can also occur.\r\n\r\n## At time HOSTDATA_end\r\n\r\nWhen the host finishes, in the SPI case, the host will \"drop the GPIO\" or stop the SPI clock\r\nThe slave however is still sending data.\r\n\r\n## In the UART case\r\n\r\nThe host can easily tell if the target has sent anything.\r\ndo you have bytes in the UART RX buffer? yes or no.\r\n   \r\nIF YES - determine the length of the message.\r\nAnd if required wait until the end of the message before dropping the handshake pin.\r\n\r\n## In the SPI case\r\n\r\nAssuming the host (linux) is a SPI-MASTER, the NCP will be the SPI-SLAVE role, the side sending the smaller packet must send \"stuffing\" values over the wire.\r\n\r\nOr - maybe the (the race condition is occurring) the NCP slave is sending a response while handling an incoming message.\r\n\r\nBecause we limit to only 1 transfer per handshake..\r\n\r\nAgain, the host needs to examine the first (N) bytes of the SPI transfer and determine\r\nQuestion 1:  Is the slave sending \"stuffing values\"\r\nOR question 2: Is the slave sending a packet?\r\n\r\nThe host needs to examine the partial SPI transfer and determine: Was this stuffing values? or Is this the start of a message?  And if so - how many more SPI clocks are required? Or how long can we wait to turn the power off to the level-shift devices?\r\n\r\nRecall that - we only transfer a single message per handshake, and the message will always start at an appropriate place (where the sender REQACK signal was first asserted).  We never start a message in the middle of, or during the transfer of another message. The transfer then becomes some what atomic, due to the REQACK framing that occurs.\r\n\r\n## Summary\r\n\r\nOnly one transfer is performed per \"handshake\". This means the host must deassert the HOST_REQACK signal at the end of the transfer. (more about this below)\r\n\r\nTo send (HOST -> NCP)\r\n\r\n- GPIO (1) = HOST REQACK, the host asserts this to send data.\r\n- GPIO (2) = SLAVE REQACK, the slave indicates it is ready to receive.\r\n\r\nTo send (SLAVE -> NCP)\r\n\r\n- GPIO (2) = SLAVE REQACK, the slave asserts this indicating it needs to send.\r\n- GPIO (1) = HOST REQACK, the host asserts this indicating it is ready to receive\r\n\r\nFor UARTS - this can be done with the RTS/CTS signal mapping and various IO/CTL operations\r\nBut honestly using the GPIO api is a better solution.\r\n\r\nFor SPI:\r\n\r\n- the host (linux) is always a master (some smartphone chips are really dumb, and cannot operate in slave mode) we can assume these have more resources. \r\n- SPI requires the SCLK, MOSI, MISO - we cannot avoid these.\r\n- \r\n\r\nThe SPI_CS is something we can work around. On the slave side may require the SPI_CS signal to operate correctly. If so - the HOST_REQACK signal can function as a SPI_CS signal.\r\n\r\nWhile the host may generate a SPI_CS - it is not really required or usable (see issues 1 and 2 above) this signal can either be (a) left as a no-connect, or (b) the mode of the HOST_SPI_CS signal could be set to \"GPIO mode\" and controlled via software.\r\n\r\n## FAST TURNAROUND - streaming packets.\r\n\r\nOften in the NCP (micro-controller) this is not an issue, within a few microseconds it is ready to go.\r\n\r\nThe host side, from user space through the kernel and out the GPIO driver is slow. We can use this to our advantage.  \r\n\r\nFirst: Measure latency. \r\n\r\nWrite a simple user space 'pulse-the-gpio' application on Linux and measure fastest time an \r\nThe code needs to perform a WRITE pin (X [toggling the pin]), then READ pin (Y). Measure the period of the wave form, this is 2x the fastest speed the board change a GPIO Pin.  Note: Linux (and windows) user space GPIO is rather slow. We will use this to our advantage.\r\n\r\nSecond: \r\n\r\nPick 50% of that period - the NCP must meet this timing requirement: From END OF DATA to SLAVE_REQACK de-assert - must be completed in that (50%) time window.\r\n\r\nOR - The host must insert a busy wait( as described below )\r\n\r\nThe host at the end of any transfer:\r\n\r\n- step 1 - write the HOST_REQACK pin, \r\n- step 2 - if required insert a short sleep (often this is not required)\r\n- step 3 - read the SLAVE_REQACK pin\r\n- step 4 - determine: Loop for more data, or wait for another interrupt.\r\n- Note: this technique is often found in UART drivers where the hardware supports a FIFO buffer, you will notice the actual hardware IRQ contains a loop - that drains the fifo. That is effectively what we are doing here, the loop draining the transfer buffers.\r\n- \r\n\r\nNote that a Microcontroller based HOST is often much faster at raw GPIO io then a host.\r\nIt does vary greatly due to the architecture of the host chip ( often a 2ghz android device cannot read the GPIO pins at 2ghz... they instead require/use a bus frequency shift to a lower speed IO bus)\r\n\r\n   \r\n\r\n\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/openthread/openthread/issues/comments/290560647","html_url":"https://github.com/openthread/openthread/issues/1533#issuecomment-290560647","issue_url":"https://api.github.com/repos/openthread/openthread/issues/1533","id":290560647,"user":{"login":"darconeous","id":686678,"avatar_url":"https://avatars3.githubusercontent.com/u/686678?v=3","gravatar_id":"","url":"https://api.github.com/users/darconeous","html_url":"https://github.com/darconeous","followers_url":"https://api.github.com/users/darconeous/followers","following_url":"https://api.github.com/users/darconeous/following{/other_user}","gists_url":"https://api.github.com/users/darconeous/gists{/gist_id}","starred_url":"https://api.github.com/users/darconeous/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/darconeous/subscriptions","organizations_url":"https://api.github.com/users/darconeous/orgs","repos_url":"https://api.github.com/users/darconeous/repos","events_url":"https://api.github.com/users/darconeous/events{/privacy}","received_events_url":"https://api.github.com/users/darconeous/received_events","type":"User","site_admin":false},"created_at":"2017-03-30T22:14:16Z","updated_at":"2017-03-30T22:14:16Z","body":"We don't avoid it. Refusals are normal: they are how the slave asserts flow control.\r\n\r\nThe master tries again a dozen (or so) milliseconds later, or we try again immediately if interrupt gets asserted (which it likely would be if the NCP is just waking up).\r\n\r\nThere are two ways the slave can refuse a frame from the master:\r\n\r\n* Passive refusal: Don't wiggle MISO. Leave it in the same state for the duration of CS being asserted.\r\n* Active refusal: Clock out a well-formed 5-byte header on MISO, with a `RECV_LEN` of zero.\r\n\r\n"}}
{"actor":{"display_login":"darconeous"}}