diff --git a/core/net/mac/tsch/README.md b/core/net/mac/tsch/README.md index 02fe0cb05..021bdf346 100644 --- a/core/net/mac/tsch/README.md +++ b/core/net/mac/tsch/README.md @@ -36,6 +36,7 @@ It has been tested on the following platforms: * NXP JN516x (`jn516x`, tested on hardware) * Tmote Sky (`sky`, tested on hardware and in cooja) * Zolertia Z1 (`z1`, tested in cooja only) + * CC2538DK (`cc2538dk`, tested on hardware) This implementation was present at the ETSI Plugtest event in Prague in July 2015, and did successfully inter-operate with all @@ -76,7 +77,7 @@ Orchestra is implemented in: A simple TSCH+RPL example is included under `examples/ipv6/rpl-tsch`. To use TSCH, first make sure your platform supports it. -Currently, `jn516x`, `sky` and `z1` are the supported platforms. +Currently, `jn516x`, `sky`, `z1` and `cc2538dk` are the supported platforms. To add your own, we refer the reader to the next section. To add TSCH to your application, first include the TSCH module from your makefile with: @@ -162,7 +163,7 @@ Finally, one can also implement his own scheduler, centralized or distributed, b ## Porting TSCH to a new platform Porting TSCH to a new platform requires a few new features in the radio driver, a number of timing-related configuration paramters. -The easiest is probably to start from one of the existing port: `jn516x`, `sky`, `z1`. +The easiest is probably to start from one of the existing port: `jn516x`, `sky`, `z1`, `cc2538dk`. ### Radio features required for TSCH diff --git a/core/net/mac/tsch/tsch-conf.h b/core/net/mac/tsch/tsch-conf.h index 3f877884a..8e091a235 100644 --- a/core/net/mac/tsch/tsch-conf.h +++ b/core/net/mac/tsch/tsch-conf.h @@ -175,4 +175,11 @@ #define TSCH_ADAPTIVE_TIMESYNC 0 #endif +/* HW frame filtering enabled */ +#ifdef TSCH_CONF_HW_FRAME_FILTERING +#define TSCH_HW_FRAME_FILTERING TSCH_CONF_HW_FRAME_FILTERING +#else /* TSCH_CONF_HW_FRAME_FILTERING */ +#define TSCH_HW_FRAME_FILTERING 1 +#endif /* TSCH_CONF_HW_FRAME_FILTERING */ + #endif /* __TSCH_CONF_H__ */ diff --git a/core/net/mac/tsch/tsch-slot-operation.c b/core/net/mac/tsch/tsch-slot-operation.c index d04f3b8ae..5f0701895 100644 --- a/core/net/mac/tsch/tsch-slot-operation.c +++ b/core/net/mac/tsch/tsch-slot-operation.c @@ -493,9 +493,11 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) uint8_t ack_hdrlen; frame802154_t frame; +#if TSCH_HW_FRAME_FILTERING /* Entering promiscuous mode so that the radio accepts the enhanced ACK */ NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode); NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode & (~RADIO_RX_MODE_ADDRESS_FILTER)); +#endif /* TSCH_HW_FRAME_FILTERING */ /* Unicast: wait for ack after tx: sleep until ack time */ TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, tsch_timing[tsch_ts_tx_offset] + tx_duration + tsch_timing[tsch_ts_rx_ack_delay] - RADIO_DELAY_BEFORE_RX, "TxBeforeAck"); @@ -514,9 +516,11 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) TSCH_DEBUG_TX_EVENT(); NETSTACK_RADIO.off(); +#if TSCH_HW_FRAME_FILTERING /* Leaving promiscuous mode */ NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode); NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode | RADIO_RX_MODE_ADDRESS_FILTER); +#endif /* TSCH_HW_FRAME_FILTERING */ /* Read ack frame */ ack_len = NETSTACK_RADIO.read((void *)ackbuf, sizeof(ackbuf)); diff --git a/cpu/cc2538/dev/cc2538-rf.c b/cpu/cc2538/dev/cc2538-rf.c index 58b464fc5..52708e0ca 100644 --- a/cpu/cc2538/dev/cc2538-rf.c +++ b/cpu/cc2538/dev/cc2538-rf.c @@ -119,6 +119,25 @@ static const uint8_t magic[] = { 0x53, 0x6E, 0x69, 0x66 }; /** Snif */ #else #define CC2538_RF_AUTOACK 1 #endif +/*--------------------------------------------------------------------------- + * MAC timer + *---------------------------------------------------------------------------*/ +/* Timer conversion */ +#define RADIO_TO_RTIMER(X) ((uint32_t)((uint64_t)(X) * RTIMER_ARCH_SECOND / SYS_CTRL_32MHZ)) + +#define CLOCK_STABLE() do { \ + while ( !(REG(SYS_CTRL_CLOCK_STA) & (SYS_CTRL_CLOCK_STA_XOSC_STB))); \ + } while(0) +/*---------------------------------------------------------------------------*/ +/* Are we currently in poll mode? Disabled by default */ +static uint8_t volatile poll_mode = 0; +/* Do we perform a CCA before sending? Enabled by default. */ +static uint8_t send_on_cca = 1; +static int8_t rssi; +static uint8_t crc_corr; + +void mac_timer_init(void); +uint32_t get_sfd_timestamp(void); /*---------------------------------------------------------------------------*/ static uint8_t rf_flags; static uint8_t rf_channel = CC2538_RF_CHANNEL; @@ -329,6 +348,28 @@ set_frame_filtering(uint8_t enable) } /*---------------------------------------------------------------------------*/ static void +set_poll_mode(uint8_t enable) +{ + poll_mode = enable; + + if(enable) { + mac_timer_init(); + REG(RFCORE_XREG_RFIRQM0) &= ~RFCORE_XREG_RFIRQM0_FIFOP; // mask out FIFOP interrupt source + REG(RFCORE_SFR_RFIRQF0) &= ~RFCORE_SFR_RFIRQF0_FIFOP; // clear pending FIFOP interrupt + nvic_interrupt_disable(NVIC_INT_RF_RXTX); // disable RF interrupts + } else { + REG(RFCORE_XREG_RFIRQM0) |= RFCORE_XREG_RFIRQM0_FIFOP; // enable FIFOP interrupt source + nvic_interrupt_enable(NVIC_INT_RF_RXTX); // enable RF interrupts + } +} +/*---------------------------------------------------------------------------*/ +static void +set_send_on_cca(uint8_t enable) +{ + send_on_cca = enable; +} +/*---------------------------------------------------------------------------*/ +static void set_auto_ack(uint8_t enable) { if(enable) { @@ -395,7 +436,9 @@ off(void) /* Wait for ongoing TX to complete (e.g. this could be an outgoing ACK) */ while(REG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_TX_ACTIVE); - CC2538_RF_CSP_ISFLUSHRX(); + if(!(REG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFOP)) { + CC2538_RF_CSP_ISFLUSHRX(); + } /* Don't turn off if we are off as this will trigger a Strobe Error */ if(REG(RFCORE_XREG_RXENABLE) != 0) { @@ -459,10 +502,6 @@ init(void) set_channel(rf_channel); - /* Acknowledge RF interrupts, FIFOP only */ - REG(RFCORE_XREG_RFIRQM0) |= RFCORE_XREG_RFIRQM0_FIFOP; - nvic_interrupt_enable(NVIC_INT_RF_RXTX); - /* Acknowledge all RF Error interrupts */ REG(RFCORE_XREG_RFERRM) = RFCORE_XREG_RFERRM_RFERRM; nvic_interrupt_enable(NVIC_INT_RF_ERR); @@ -488,6 +527,8 @@ init(void) */ udma_set_channel_src(CC2538_RF_CONF_RX_DMA_CHAN, RFCORE_SFR_RFDATA); } + + set_poll_mode(poll_mode); process_start(&cc2538_rf_process, NULL); @@ -571,9 +612,11 @@ transmit(unsigned short transmit_len) while(RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + ONOFF_TIME)); } - if(channel_clear() == CC2538_RF_CCA_BUSY) { - RIMESTATS_ADD(contentiondrop); - return RADIO_TX_COLLISION; + if(send_on_cca) { + if(channel_clear() == CC2538_RF_CCA_BUSY) { + RIMESTATS_ADD(contentiondrop); + return RADIO_TX_COLLISION; + } } /* @@ -630,8 +673,6 @@ read(void *buf, unsigned short bufsize) { uint8_t i; uint8_t len; - uint8_t crc_corr; - int8_t rssi; PRINTF("RF: Read\n"); @@ -731,14 +772,18 @@ read(void *buf, unsigned short bufsize) flush(); #endif - /* If FIFOP==1 and FIFO==0 then we had a FIFO overflow at some point. */ - if(REG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFOP) { - if(REG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFO) { - process_poll(&cc2538_rf_process); - } else { - CC2538_RF_CSP_ISFLUSHRX(); + if(!poll_mode) { + /* If FIFOP==1 and FIFO==0 then we had a FIFO overflow at some point. */ + if(REG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFOP) { + if(REG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFO) { + process_poll(&cc2538_rf_process); + } else { + CC2538_RF_CSP_ISFLUSHRX(); + } } } + + CC2538_RF_CSP_ISFLUSHRX(); return (len); } @@ -796,6 +841,15 @@ get_value(radio_param_t param, radio_value_t *value) if(REG(RFCORE_XREG_FRMCTRL0) & RFCORE_XREG_FRMCTRL0_AUTOACK) { *value |= RADIO_RX_MODE_AUTOACK; } + if(poll_mode) { + *value |= RADIO_RX_MODE_POLL_MODE; + } + return RADIO_RESULT_OK; + case RADIO_PARAM_TX_MODE: + *value = 0; + if(send_on_cca) { + *value |= RADIO_TX_MODE_SEND_ON_CCA; + } return RADIO_RESULT_OK; case RADIO_PARAM_TXPOWER: *value = get_tx_power(); @@ -806,6 +860,12 @@ get_value(radio_param_t param, radio_value_t *value) case RADIO_PARAM_RSSI: *value = get_rssi(); return RADIO_RESULT_OK; + case RADIO_PARAM_LAST_RSSI: + *value = rssi; + return RADIO_RESULT_OK; + case RADIO_PARAM_LAST_LINK_QUALITY: + *value = crc_corr & LQI_BIT_MASK; + return RADIO_RESULT_OK; case RADIO_CONST_CHANNEL_MIN: *value = CC2538_RF_CHANNEL_MIN; return RADIO_RESULT_OK; @@ -854,13 +914,21 @@ set_value(radio_param_t param, radio_value_t value) return RADIO_RESULT_OK; case RADIO_PARAM_RX_MODE: if(value & ~(RADIO_RX_MODE_ADDRESS_FILTER | - RADIO_RX_MODE_AUTOACK)) { + RADIO_RX_MODE_AUTOACK | + RADIO_RX_MODE_POLL_MODE)) { return RADIO_RESULT_INVALID_VALUE; } set_frame_filtering((value & RADIO_RX_MODE_ADDRESS_FILTER) != 0); set_auto_ack((value & RADIO_RX_MODE_AUTOACK) != 0); + set_poll_mode((value & RADIO_RX_MODE_POLL_MODE) != 0); + return RADIO_RESULT_OK; + case RADIO_PARAM_TX_MODE: + if(value & ~(RADIO_TX_MODE_SEND_ON_CCA)) { + return RADIO_RESULT_INVALID_VALUE; + } + set_send_on_cca((value & RADIO_TX_MODE_SEND_ON_CCA) != 0); return RADIO_RESULT_OK; case RADIO_PARAM_TXPOWER: if(value < OUTPUT_POWER_MIN || value > OUTPUT_POWER_MAX) { @@ -895,6 +963,15 @@ get_object(radio_param_t param, void *dest, size_t size) return RADIO_RESULT_OK; } + + if(param == RADIO_PARAM_LAST_PACKET_TIMESTAMP) { + if(size != sizeof(rtimer_clock_t) || !dest) { + return RADIO_RESULT_INVALID_VALUE; + } + *(rtimer_clock_t*)dest = get_sfd_timestamp(); + return RADIO_RESULT_OK; + } + return RADIO_RESULT_NOT_SUPPORTED; } /*---------------------------------------------------------------------------*/ @@ -950,15 +1027,18 @@ PROCESS_THREAD(cc2538_rf_process, ev, data) PROCESS_BEGIN(); while(1) { - PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL); + /* Only if we are not in poll mode oder we are in poll mode and transceiver has to be reset */ + PROCESS_YIELD_UNTIL((!poll_mode || (poll_mode && (rf_flags & RF_MUST_RESET))) && (ev == PROCESS_EVENT_POLL)); - packetbuf_clear(); - len = read(packetbuf_dataptr(), PACKETBUF_SIZE); + if(!poll_mode) { + packetbuf_clear(); + len = read(packetbuf_dataptr(), PACKETBUF_SIZE); - if(len > 0) { - packetbuf_set_datalen(len); + if(len > 0) { + packetbuf_set_datalen(len); - NETSTACK_RDC.input(); + NETSTACK_RDC.input(); + } } /* If we were polled due to an RF error, reset the transceiver */ @@ -995,8 +1075,10 @@ void cc2538_rf_rx_tx_isr(void) { ENERGEST_ON(ENERGEST_TYPE_IRQ); - - process_poll(&cc2538_rf_process); + + if(!poll_mode) { + process_poll(&cc2538_rf_process); + } /* We only acknowledge FIFOP so we can safely wipe out the entire SFR */ REG(RFCORE_SFR_RFIRQF0) = 0; @@ -1045,4 +1127,44 @@ cc2538_rf_set_promiscous_mode(char p) set_frame_filtering(p); } /*---------------------------------------------------------------------------*/ +uint32_t get_sfd_timestamp(void) +{ + uint64_t sfd, timer_val, buffer; + + REG(RFCORE_SFR_MTMSEL) = (REG(RFCORE_SFR_MTMSEL) & ~RFCORE_SFR_MTMSEL_MTMSEL) | 0x00000000; + REG(RFCORE_SFR_MTCTRL) |= RFCORE_SFR_MTCTRL_LATCH_MODE; + timer_val = REG(RFCORE_SFR_MTM0) & RFCORE_SFR_MTM0_MTM0; + timer_val |= ((REG(RFCORE_SFR_MTM1) & RFCORE_SFR_MTM1_MTM1) << 8); + REG(RFCORE_SFR_MTMSEL) = (REG(RFCORE_SFR_MTMSEL) & ~RFCORE_SFR_MTMSEL_MTMOVFSEL) | 0x00000000; + timer_val |= ((REG(RFCORE_SFR_MTMOVF0) & RFCORE_SFR_MTMOVF0_MTMOVF0) << 16); + timer_val |= ((REG(RFCORE_SFR_MTMOVF1) & RFCORE_SFR_MTMOVF1_MTMOVF1) << 24); + buffer = REG(RFCORE_SFR_MTMOVF2) & RFCORE_SFR_MTMOVF2_MTMOVF2; + timer_val |= (buffer << 32); + + REG(RFCORE_SFR_MTMSEL) = (REG(RFCORE_SFR_MTMSEL) & ~RFCORE_SFR_MTMSEL_MTMSEL) | 0x00000001; + REG(RFCORE_SFR_MTCTRL) |= RFCORE_SFR_MTCTRL_LATCH_MODE; + sfd = REG(RFCORE_SFR_MTM0) & RFCORE_SFR_MTM0_MTM0; + sfd |= ((REG(RFCORE_SFR_MTM1) & RFCORE_SFR_MTM1_MTM1) << 8); + REG(RFCORE_SFR_MTMSEL) = (REG(RFCORE_SFR_MTMSEL) & ~RFCORE_SFR_MTMSEL_MTMOVFSEL) | 0x00000010; + sfd |= ((REG(RFCORE_SFR_MTMOVF0) & RFCORE_SFR_MTMOVF0_MTMOVF0) << 16); + sfd |= ((REG(RFCORE_SFR_MTMOVF1) & RFCORE_SFR_MTMOVF1_MTMOVF1) << 24); + buffer = REG(RFCORE_SFR_MTMOVF2) & RFCORE_SFR_MTMOVF2_MTMOVF2; + sfd |= (buffer << 32); + + return (RTIMER_NOW() - RADIO_TO_RTIMER(timer_val - sfd)); +} +/*---------------------------------------------------------------------------*/ +void mac_timer_init(void) +{ + CLOCK_STABLE(); + REG(RFCORE_SFR_MTCTRL) |= RFCORE_SFR_MTCTRL_SYNC; + REG(RFCORE_SFR_MTCTRL) |= RFCORE_SFR_MTCTRL_RUN; + while(!(REG(RFCORE_SFR_MTCTRL) & RFCORE_SFR_MTCTRL_STATE)); + REG(RFCORE_SFR_MTCTRL) &= ~RFCORE_SFR_MTCTRL_RUN; + while(REG(RFCORE_SFR_MTCTRL) & RFCORE_SFR_MTCTRL_STATE); + REG(RFCORE_SFR_MTCTRL) |= RFCORE_SFR_MTCTRL_SYNC; + REG(RFCORE_SFR_MTCTRL) |= (RFCORE_SFR_MTCTRL_RUN); + while(!(REG(RFCORE_SFR_MTCTRL) & RFCORE_SFR_MTCTRL_STATE)); +} +/*---------------------------------------------------------------------------*/ /** @} */ diff --git a/cpu/cc2538/rtimer-arch.h b/cpu/cc2538/rtimer-arch.h index ddb020af1..45ebc5c5d 100644 --- a/cpu/cc2538/rtimer-arch.h +++ b/cpu/cc2538/rtimer-arch.h @@ -65,6 +65,20 @@ #define RTIMER_ARCH_SECOND 32768 +/* Do the math in 32bits to save precision. + * Round to nearest integer rather than truncate. */ +#define US_TO_RTIMERTICKS(US) ((US) >= 0 ? \ + (((int32_t)(US) * (RTIMER_ARCH_SECOND) + 500000) / 1000000L) : \ + ((int32_t)(US) * (RTIMER_ARCH_SECOND) - 500000) / 1000000L) + +#define RTIMERTICKS_TO_US(T) ((T) >= 0 ? \ + (((int32_t)(T) * 1000000L + ((RTIMER_ARCH_SECOND) / 2)) / (RTIMER_ARCH_SECOND)) : \ + ((int32_t)(T) * 1000000L - ((RTIMER_ARCH_SECOND) / 2)) / (RTIMER_ARCH_SECOND)) + +/* A 64-bit version because the 32-bit one cannot handle T >= 4295 ticks. + Intended only for positive values of T. */ +#define RTIMERTICKS_TO_US_64(T) ((uint32_t)(((uint64_t)(T) * 1000000 + ((RTIMER_ARCH_SECOND) / 2)) / (RTIMER_ARCH_SECOND))) + /** \sa RTIMER_NOW() */ rtimer_clock_t rtimer_arch_now(void); diff --git a/examples/ipv6/rpl-tsch/project-conf.h b/examples/ipv6/rpl-tsch/project-conf.h index 30f638bb4..a725cbba0 100644 --- a/examples/ipv6/rpl-tsch/project-conf.h +++ b/examples/ipv6/rpl-tsch/project-conf.h @@ -67,13 +67,19 @@ #define TSCH_CALLBACK_JOINING_NETWORK tsch_rpl_callback_joining_network #define TSCH_CALLBACK_LEAVING_NETWORK tsch_rpl_callback_leaving_network +/* Needed for CC2538 platforms only */ +/* For TSCH we have to use the more accurate crystal oscillator + * by default the RC oscillator is activated */ +#undef SYS_CTRL_CONF_OSC32K_USE_XTAL +#define SYS_CTRL_CONF_OSC32K_USE_XTAL 1 + /* Needed for cc2420 platforms only */ /* Disable DCO calibration (uses timerB) */ #undef DCOSYNCH_CONF_ENABLED -#define DCOSYNCH_CONF_ENABLED 0 +#define DCOSYNCH_CONF_ENABLED 0 /* Enable SFD timestamps (uses timerB) */ #undef CC2420_CONF_SFD_TIMESTAMPS -#define CC2420_CONF_SFD_TIMESTAMPS 1 +#define CC2420_CONF_SFD_TIMESTAMPS 1 /*******************************************************/ /******************* Configure TSCH ********************/ diff --git a/platform/cc2538dk/contiki-conf.h b/platform/cc2538dk/contiki-conf.h index 9d18ec344..c6f0e2b6a 100644 --- a/platform/cc2538dk/contiki-conf.h +++ b/platform/cc2538dk/contiki-conf.h @@ -40,6 +40,14 @@ typedef uint32_t rtimer_clock_t; #define RTIMER_CLOCK_DIFF(a,b) ((int32_t)((a)-(b))) /** @} */ /*---------------------------------------------------------------------------*/ +#define TSCH_CONF_HW_FRAME_FILTERING 0 + +/* 352us from calling transmit() until the SFD byte has been sent */ +#define RADIO_DELAY_BEFORE_TX ((unsigned)US_TO_RTIMERTICKS(352)) +/* 192us as in datasheet but ACKs are not always received, so adjusted to 250us */ +#define RADIO_DELAY_BEFORE_RX ((unsigned)US_TO_RTIMERTICKS(250)) +#define RADIO_DELAY_BEFORE_DETECT 0 +/*---------------------------------------------------------------------------*/ /** * \name Serial Boot Loader Backdoor configuration *