Radio Interrupt Handler

Hello,

Could someone please point out the code where radio is switched on/off when sending a MAC/UDP packet.

I am expecting something similar to the following code snippet:

/** Turn the MAC layer on. */

int (* on)(void);

/** Turn the MAC layer off. */

int (* off)(int keep_radio_on);

This code is from Contiki.

I am interested in understanding the time it takes for system to wake up from sleep (dormant mode) to receive packets.

Thank you.

Regards,

Navneet Pandey

Hi dear RIOT users,

We are looking to invest in young IoT companies,

we are looking for companies with registered Patents and unique technologies

as well as done few complete business transactions successfully

Proved and solid potentials for growth supported by existing agreements with at least one big industries player

looking forward to hearing from you

best regards

Ahmad Kassar

Hi Navneet Pandey,

Hello,

Could someone please point out the code where radio is switched on/off when sending a MAC/UDP packet.

It is not switched off unless you are using a duty cycling MAC layer such as lwmac or ContikiMAC [1]

I am expecting something similar to the following code snippet:

/** Turn the MAC layer on. */

int (* on)(void);

/** Turn the MAC layer off. */

int (* off)(int keep_radio_on);

This code is from Contiki.

The equivalent call for switching the radio to sleep mode like the contiki on/off calls would be netdev_driver::set [2] with opt=NETOPT_STATE and *value=NETOPT_STATE_SLEEP or NETOPT_STATE_IDLE. Take a look at what happens in the ifconfig shell command when you call it with ifconfig x set state sleep [3]

I am interested in understanding the time it takes for system to wake up from sleep (dormant mode) to receive packets.

I am actually working on some similar performance measurements for a project as well. I will need to measure some various timings related to CPU low power modes and ContikiMAC. Right now I don’t have anything to show, but I can post back when I have some test program. The testing setup will consist of a logic analyzer and using some gpio pins for status.

Best regards, Joakim

References: [1]: https://github.com/RIOT-OS/RIOT/pull/9478 [2]: http://api.riot-os.org/structnetdev__driver.html#ad5c8c409276a702877ad50ad09e87a58 [3]: https://github.com/RIOT-OS/RIOT/blob/master/sys/shell/commands/sc_gnrc_netif.c#L826

Hello Joakim,

Thank you, the information was really helpful. I was exploring the ‘at86rf2xx_netdev.c’ file. I noticed something weird. When I enabled debugging all I got was

  • To Idle state
  • Evt TX_END
  • Evt RX_END

By enabling debugging in pm_layered.c and pm.c, I noticed cortex_sleep(deep) was being called when radio was not doing anything. So why am I not seeing any of the following debug statements being printed (or in other words none of the following cases being invoked).

case NETOPT_STATE_STANDBY:

DEBUG(“STANDBY\n”);

at86rf2xx_set_state(dev, AT86RF2XX_STATE_TRX_OFF);

break;

case NETOPT_STATE_SLEEP:

DEBUG(“To SLEEP\n”);

at86rf2xx_set_state(dev, AT86RF2XX_STATE_SLEEP);

break;

case NETOPT_STATE_IDLE:

DEBUG(“To IDLE\n”);

at86rf2xx_set_state(dev, AT86RF2XX_STATE_RX_AACK_ON);

break;

I think you are mixing up different aspects of low power operation. pm_layered will cause the CPU to go to low power modes when there is no thread running, it will not touch anything other than the CPU. The sleep mode of the radio is the responsibility of a different module. You can control the radio sleep mode manually from the shell using ifconfig x set state sleep, or automatically if you configure a radio duty cycling layer, such as ContikiMAC. If you are running the gnrc_networking example out of the box with no modifications, then you are not using any duty cycling method. This is similar to when contiki is configured with nullrdc instead of TSCH or ContikiMAC. There are two duty cycling modules in the master repo AFAIK: lwmac and gomach. I have never used either of them because of limitations in the implementations which means they are useless for my projects, so I can’t help you with those. The only duty cycling layer which I know anything about is the proposed netdev ContikiMAC module in 1. That module is intended to wake up every 1/8 th of a second and do CCA checks, just like contiki does when running contikimac for the RDC. Sending will strobe the frame according to the rules described in the module API documentation (run make doc locally), or in the original paper on ContikiMAC2. The module in 1 is highly experimental and has only ever been tested on frdm-kw41z and samr21-xpro boards.

/Joakim

Hello Joakim, thanks for an elaborate explanation, I will check those articles out. Before I do, I have a few more queries.

Could you also point out which module is responsible for radio switch on/off, assuming radio only has a binary mode unlike the CPU.

Is it ‘at86rf2xx_netdev’ or something else?

Also, is it possible to keep the radio open always for the duration of an experiment? And may be avoid deep sleep mode as well?

And am I correct when I say that ‘cortex_sleep(deep)’ puts CPU to sleep but doesn’t touch the radio (based on what you replied).

Based on some of the modules on which I enabled debugging:

Following was the pattern: PACKET SENT-> TX_Ends -> pm_set_lowest -> Idle -> pm_set_lowest -> RX_Ends -> PACKET RCVD.

I did not understand the part where CPU moved to idle state before going to sleep again. Did I miss any state in the process (probably did not enable debugging)?

I know these are a lot of questions. I really appreciate that you are answering my question in great detail. Thank you.

Hi again Navneet,

Hello Joakim, thanks for an elaborate explanation, I will check those articles out. Before I do, I have a few more queries.

Could you also point out which module is responsible for radio switch on/off, assuming radio only has a binary mode unlike the CPU.

Is it ‘at86rf2xx_netdev’ or something else?

Yes, look for the NETOPT_STATE handler in that file (_set_state)

Also, is it possible to keep the radio open always for the duration of an experiment? And may be avoid deep sleep mode as well?

What you describe is the default behavior of the gnrc_networking example, no configuration necessary. The radio will be initialized and set to listen for incoming traffic, and will remain turned on until you type ifconfig x set state sleep in the shell. To disable the CPU power savings, the easiest way is to either disable the pm_layered module using DISABLE_MODULE=pm_layered on the command line with your make command, or change the initial pm_blockers to non-zero. The pm blocker initial value is found in cpu_conf.h, or cpu_conf_common.h

And am I correct when I say that ‘cortex_sleep(deep)’ puts CPU to sleep but doesn’t touch the radio (based on what you replied).

Yes, that is correct.

Based on some of the modules on which I enabled debugging:

Following was the pattern: PACKET SENT-> TX_Ends → pm_set_lowest → Idle → pm_set_lowest → RX_Ends → PACKET RCVD.

I did not understand the part where CPU moved to idle state before going to sleep again. Did I miss any state in the process (probably did not enable debugging)?

Yes, you missed the interrupt sent from the radio to the CPU. The CPU is awoken by an interrupt from the radio. The radio has a signal named INT or similar that is connected to an input pin on the CPU. When the radio receives an incoming frame from somewhere, the radio will toggle the interrupt pin to signal that it wants to be serviced by the driver. The interrupt on the CPU is configured to wake the CPU from low power modes. Look for _irq_handler in at86rf2xx_netdev.c. The IRQ handler in that file only posts an event to the gnrc_netif thread telling it that it needs to poll the radio to see what the reason for the interrupt is, this usually means reading some status register in the radio. That last part is performed by the _isr function in at86rf2xx_netdev.c

The first transition from pm set lowest to idle could be a RX Begin event, and the second wake could be the RX End interrupt. The RX Begin interrupt is sent when only the first byte of the frame has been received. The RX End interrupt is sent when the frame has been completely received. Without more info I can only guess.

I know these are a lot of questions. I really appreciate that you are answering my question in great detail. Thank you.

Regards,

Navneet Pandey

Best regards, Joakim

Hello Joakim,

Hello everyone, I have some confusion regarding the process flow of program at the driver level.

I have the following debug logs from at86rf2xx (with some custom logs)

CLIENT - m3-100;[at86rf2xx] at86rf2xx_tx_load

CLIENT - m3-100;[at86rf2xx] PRELOADED at86rf2xx_tx_exec

CLIENT - m3-100;PKT SENT // Represents end of _send method in at86rf2xx_netdev.c

server - m3-101;INTERRUPT

server - m3-101;[at86rf2xx] EVT - ISR CALLED

CLIENT - m3-100;INTERRUPT // Represents _irq_handler method in at86rf2xx_netdev.c if (dev->event_callback) this is TRUE/1

CLIENT - m3-100;[at86rf2xx] EVT - ISR CALLED // represents _isr method being called

CLIENT - m3-100;[at86rf2xx] return to idle state 0x16

server - m3-101;[at86rf2xx] EVT - RX_END

server - m3-101;PKT RECV

server - m3-101;PKT RECV

CLIENT - m3-100;[at86rf2xx] EVT - TX_END

My question here is that,

Firstly, does PKT_SENT i.e. the end of send method from netdev::send mean that data/packet was actually sent?

Secondly, if the answer is yes to previous question, why does INTERRUPT occur after the packet was sent? If answer to the first question is no, then when/where does the packet/data actually gets transmitted?

I am aware that methods such as prepare/load/exec already start the transmission process, hence the dilemma about INTERRUPT being called after these modules/functions.

Thirdly, In the logs (not present in the above log), I observed that the following piece of code never gets executed even though packet transmission starts

if (netdev->event_callback &&

(dev->flags & AT86RF2XX_OPT_TELL_TX_START)) {

DEBUG(“TX STARTED\n”);

netdev->event_callback(netdev, NETDEV_EVENT_TX_STARTED);

}

I really appreciate any help in solving this small confusion. Thank you.

Hi again Navneet,

Hello everyone, I have some confusion regarding the process flow of program at the driver level.

I have the following debug logs from at86rf2xx (with some custom logs)

CLIENT - m3-100;[at86rf2xx] at86rf2xx_tx_load

CLIENT - m3-100;[at86rf2xx] PRELOADED at86rf2xx_tx_exec

CLIENT - m3-100;PKT SENT // Represents end of _send method in at86rf2xx_netdev.c

server - m3-101;INTERRUPT

server - m3-101;[at86rf2xx] EVT - ISR CALLED

CLIENT - m3-100;INTERRUPT // Represents _irq_handler method in at86rf2xx_netdev.c if (dev->event_callback) this is TRUE/1

CLIENT - m3-100;[at86rf2xx] EVT - ISR CALLED // represents _isr method being called

CLIENT - m3-100;[at86rf2xx] return to idle state 0x16

server - m3-101;[at86rf2xx] EVT - RX_END

server - m3-101;PKT RECV

server - m3-101;PKT RECV

CLIENT - m3-100;[at86rf2xx] EVT - TX_END

My question here is that,

Firstly, does PKT_SENT i.e. the end of send method from netdev::send mean that data/packet was actually sent?

The end of the _send method (in at86rf2xx_netdev.c) means that the frame has been loaded in the transceiver buffer and the transceiver is going to send it at the next opportunity. This is also signaled to the upper layers via the netdev->event_callback(netdev, NETDEV_EVENT_TX_STARTED); call in at86rf2xx_tx_exec (at86rf2xx.c). When the transmission is finished, the transceiver will issue an IRQ to the CPU and the CPU will run the IRQ handler. If you look at the _isr method you will find calls to netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); and other similar events. This is the way that the driver tells the upper layers that the transmission has ended for some reason, the type of event signals what the status is, TX_COMPLETE means success, NOACK means that the frame was sent but there was no acknowledgment from the remote side, MEDIUM_BUSY means that there was someone else using the channel and we could not find a free slot before running out of retries (CSMA failure).

Secondly, if the answer is yes to previous question, why does INTERRUPT occur after the packet was sent? If answer to the first question is no, then when/where does the packet/data actually gets transmitted?

The exact behavior depend on which radio you are using, but generally the data will begin transmitting as soon as the transceiver detects that the channel is free if you are using CSMA, which is the default setting on transceivers which support it. There may be a number of retries if the channel is busy so the time that this takes is indeterminate beforehand.

I am aware that methods such as prepare/load/exec already start the transmission process, hence the dilemma about INTERRUPT being called after these modules/functions.

Thirdly, In the logs (not present in the above log), I observed that the following piece of code never gets executed even though packet transmission starts

if (netdev->event_callback &&

        (dev->flags & AT86RF2XX_OPT_TELL_TX_START)) {

        DEBUG("TX STARTED\n");

        netdev->event_callback(netdev, NETDEV_EVENT_TX_STARTED);

    }

Does your application enable the NETOPT_TX_START_IRQ option? (Otherwise the (dev->flags & AT86RF2XX_OPT_TELL_TX_START) part is 0) The example applications in the RIOT repository do not enable this, only LWMAC seem to use it at present.

I really appreciate any help in solving this small confusion. Thank you.

I hope this helps, Joakim