Merge pull request #1550 from thomas-ha/tsch-cc2538-port

Port of TSCH for CC2538
This commit is contained in:
Simon Duquennoy 2016-06-02 21:33:55 +02:00
commit 26bf33c762
7 changed files with 191 additions and 29 deletions

View File

@ -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

View File

@ -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__ */

View File

@ -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));

View File

@ -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);
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);
@ -489,6 +528,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);
rf_flags |= RF_ON;
@ -571,10 +612,12 @@ transmit(unsigned short transmit_len)
while(RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + ONOFF_TIME));
}
if(send_on_cca) {
if(channel_clear() == CC2538_RF_CCA_BUSY) {
RIMESTATS_ADD(contentiondrop);
return RADIO_TX_COLLISION;
}
}
/*
* prepare() double checked that TX_ACTIVE is low. If SFD is high we are
@ -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,6 +772,7 @@ read(void *buf, unsigned short bufsize)
flush();
#endif
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) {
@ -739,6 +781,9 @@ read(void *buf, unsigned short bufsize)
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,8 +1027,10 @@ 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));
if(!poll_mode) {
packetbuf_clear();
len = read(packetbuf_dataptr(), PACKETBUF_SIZE);
@ -960,6 +1039,7 @@ PROCESS_THREAD(cc2538_rf_process, ev, data)
NETSTACK_RDC.input();
}
}
/* If we were polled due to an RF error, reset the transceiver */
if(rf_flags & RF_MUST_RESET) {
@ -996,7 +1076,9 @@ cc2538_rf_rx_tx_isr(void)
{
ENERGEST_ON(ENERGEST_TYPE_IRQ);
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));
}
/*---------------------------------------------------------------------------*/
/** @} */

View File

@ -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);

View File

@ -67,6 +67,12 @@
#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

View File

@ -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
*