Use 128rfa1 interrupts instead of timing.
Generalize DEBUGFLOW macro for use in multiple routines.
This commit is contained in:
parent
b55dddcd38
commit
577215deca
4 changed files with 158 additions and 117 deletions
|
@ -58,7 +58,13 @@
|
|||
* which communicates directly with the contiki core MAC layer.
|
||||
* It is optimized for speed at the expense of generality.
|
||||
*/
|
||||
|
||||
#include "contiki-conf.h"
|
||||
#if DEBUGFLOWSIZE
|
||||
extern uint8_t debugflowsize,debugflow[DEBUGFLOWSIZE];
|
||||
#define DEBUGFLOW(c) if (debugflowsize<(DEBUGFLOWSIZE-1)) debugflow[debugflowsize++]=c
|
||||
#else
|
||||
#define DEBUGFLOW(c)
|
||||
#endif
|
||||
|
||||
|
||||
/*============================ INCLUDE =======================================*/
|
||||
|
@ -814,118 +820,88 @@ volatile char rf230interruptflag;
|
|||
#endif
|
||||
|
||||
#if defined(__AVR_ATmega128RFA1__)
|
||||
/* The atmega128rfa1 has individual interrupts for the integrated radio */
|
||||
/* Whichever are enabled by the RF230 driver must be present even if not used! */
|
||||
/* The atmega128rfa1 has individual interrupts for the integrated radio'
|
||||
* Whichever are enabled by the RF230 driver must be present even if not used!
|
||||
*/
|
||||
/* Received packet interrupt */
|
||||
ISR(TRX24_RX_END_vect)
|
||||
{
|
||||
INTERRUPTDEBUG(11);
|
||||
/* Received packet interrupt */
|
||||
/* Buffer the frame and call rf230_interrupt to schedule poll for rf230 receive process */
|
||||
// if (rxframe.length) break; //toss packet if last one not processed yet
|
||||
if (rxframe[rxframe_tail].length) INTERRUPTDEBUG(42); else INTERRUPTDEBUG(12);
|
||||
|
||||
#ifdef RF230_MIN_RX_POWER
|
||||
/* Discard packets weaker than the minimum if defined. This is for testing miniature meshes.*/
|
||||
/* Save the rssi for printing in the main loop */
|
||||
/* Get the rssi from ED if extended mode */
|
||||
#if RF230_CONF_AUTOACK
|
||||
// rf230_last_rssi=hal_subregister_read(SR_ED_LEVEL);
|
||||
rf230_last_rssi=hal_register_read(RG_PHY_ED_LEVEL);
|
||||
#endif
|
||||
// if (rf230_last_rssi >= RF230_MIN_RX_POWER) {
|
||||
|
||||
/* Buffer the frame and call rf230_interrupt to schedule poll for rf230 receive process */
|
||||
/* Is a ram buffer available? */
|
||||
if (rxframe[rxframe_tail].length) {DEBUGFLOW('0');} else DEBUGFLOW('1');
|
||||
|
||||
#ifdef RF230_MIN_RX_POWER
|
||||
/* Discard packets weaker than the minimum if defined. This is for testing miniature meshes */
|
||||
/* This does not prevent an autoack. TODO:rfa1 radio can be set up to not autoack weak packets */
|
||||
if (rf230_last_rssi >= RF230_MIN_RX_POWER) {
|
||||
#else
|
||||
if (1) {
|
||||
#endif
|
||||
// DEBUGFLOW('2');
|
||||
hal_frame_read(&rxframe[rxframe_tail]);
|
||||
rxframe_tail++;if (rxframe_tail >= RF230_CONF_RX_BUFFERS) rxframe_tail=0;
|
||||
rf230_interrupt();
|
||||
#ifdef RF230_MIN_RX_POWER
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/* Preamble detected, starting frame reception */
|
||||
ISR(TRX24_RX_START_vect)
|
||||
{
|
||||
INTERRUPTDEBUG(10);
|
||||
/* Save RSSI for this packet if not in extended mode, scaling to 1dB resolution */
|
||||
// DEBUGFLOW('3');
|
||||
/* Save RSSI for this packet if not in extended mode, scaling to 1dB resolution */
|
||||
#if !RF230_CONF_AUTOACK
|
||||
rf230_last_rssi = 3 * hal_subregister_read(SR_RSSI);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/* PLL has locked, either from a transition out of TRX_OFF or a channel change while on */
|
||||
ISR(TRX24_PLL_LOCK_vect)
|
||||
{
|
||||
// DEBUGFLOW('4');
|
||||
}
|
||||
|
||||
/* PLL has unexpectedly unlocked */
|
||||
ISR(TRX24_PLL_UNLOCK_vect)
|
||||
{
|
||||
DEBUGFLOW('5');
|
||||
}
|
||||
/* Flag is set by the following interrupts */
|
||||
extern volatile uint8_t rf230_interruptwait;
|
||||
|
||||
#if 0
|
||||
HAL_RF230_ISR() //for reference, for now
|
||||
/* Wake has finished */
|
||||
ISR(TRX24_AWAKE_vect)
|
||||
{
|
||||
/*The following code reads the current system time. This is done by first
|
||||
reading the hal_system_time and then adding the 16 LSB directly from the
|
||||
hardware counter.
|
||||
*/
|
||||
// uint32_t isr_timestamp = hal_system_time;
|
||||
// isr_timestamp <<= 16;
|
||||
// isr_timestamp |= HAL_TICK_UPCNT(); // TODO: what if this wraps after reading hal_system_time?
|
||||
// isr_timestamp /= HAL_US_PER_SYMBOL; /* Divide so that we get time in 16us resolution. */
|
||||
// isr_timestamp &= HAL_SYMBOL_MASK;
|
||||
|
||||
uint8_t interrupt_source;
|
||||
|
||||
INTERRUPTDEBUG(1);
|
||||
|
||||
/*Read Interrupt source.*/
|
||||
interrupt_source = IRQ_STATUS;
|
||||
|
||||
/*Handle the incomming interrupt. Prioritized.*/
|
||||
if (interrupt_source & (1>>RX_START)){
|
||||
INTERRUPTDEBUG(10);
|
||||
/* Save RSSI for this packet if not in extended mode, scaling to 1dB resolution */
|
||||
#if !RF230_CONF_AUTOACK
|
||||
rf230_last_rssi = 3 * hal_subregister_read(SR_RSSI);
|
||||
#endif
|
||||
|
||||
} else if (interrupt_source & (1<<RX_END)){
|
||||
INTERRUPTDEBUG(11);
|
||||
/* Received packet interrupt */
|
||||
/* Buffer the frame and call rf230_interrupt to schedule poll for rf230 receive process */
|
||||
// if (rxframe.length) break; //toss packet if last one not processed yet
|
||||
if (rxframe[rxframe_tail].length) INTERRUPTDEBUG(42); else INTERRUPTDEBUG(12);
|
||||
|
||||
#ifdef RF230_MIN_RX_POWER
|
||||
/* Discard packets weaker than the minimum if defined. This is for testing miniature meshes.*/
|
||||
/* Save the rssi for printing in the main loop */
|
||||
#if RF230_CONF_AUTOACK
|
||||
// rf230_last_rssi=hal_subregister_read(SR_ED_LEVEL);
|
||||
rf230_last_rssi=hal_register_read(RG_PHY_ED_LEVEL);
|
||||
#endif
|
||||
// if (rf230_last_rssi >= RF230_MIN_RX_POWER) {
|
||||
if (1) {
|
||||
#endif
|
||||
hal_frame_read(&rxframe[rxframe_tail]);
|
||||
rxframe_tail++;if (rxframe_tail >= RF230_CONF_RX_BUFFERS) rxframe_tail=0;
|
||||
rf230_interrupt();
|
||||
// trx_end_callback(isr_timestamp);
|
||||
#ifdef RF230_MIN_RX_POWER
|
||||
}
|
||||
#endif
|
||||
|
||||
} else if (interrupt_source & (1<<TX_END)){
|
||||
INTERRUPTDEBUG(13);
|
||||
;
|
||||
} else if (interrupt_source & (1<<PLL_UNLOCK)){
|
||||
INTERRUPTDEBUG(14);
|
||||
;
|
||||
} else if (interrupt_source & (1<<PLL_LOCK)){
|
||||
INTERRUPTDEBUG(15);
|
||||
// hal_pll_lock_flag++;
|
||||
;
|
||||
} else {
|
||||
INTERRUPTDEBUG(99);
|
||||
;
|
||||
}
|
||||
// DEBUGFLOW('6');
|
||||
rf230_interruptwait=0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Transmission has ended */
|
||||
ISR(TRX24_TX_END_vect)
|
||||
{
|
||||
// DEBUGFLOW('7');
|
||||
rf230_interruptwait=0;
|
||||
}
|
||||
|
||||
/* Frame address has matched ours */
|
||||
extern volatile uint8_t rf230_pending;
|
||||
ISR(TRX24_XAH_AMI_vect)
|
||||
{
|
||||
// DEBUGFLOW('8');
|
||||
rf230_pending=1;
|
||||
}
|
||||
|
||||
/* CCAED measurement has completed */
|
||||
ISR(TRX24_CCA_ED_DONE_vect)
|
||||
{
|
||||
DEBUGFLOW('4');
|
||||
rf230_interruptwait=0;
|
||||
}
|
||||
|
||||
#else /* defined(__AVR_ATmega128RFA1__) */
|
||||
/* Separate RF230 has a single radio interrupt and the source must be read from the IRQ_STATUS register */
|
||||
HAL_RF230_ISR()
|
||||
|
|
|
@ -166,10 +166,9 @@ uint8_t rf230_calibrate;
|
|||
uint8_t rf230_calibrated; //for debugging, prints from main loop when calibration occurs
|
||||
#endif
|
||||
|
||||
/* Track flow through driver, see contiki-raven-main.c for example of use */
|
||||
//#define DEBUGFLOWSIZE 128
|
||||
/* Track flow through mac, rdc and radio drivers, see contiki-raven-main.c for example of use */
|
||||
#if DEBUGFLOWSIZE
|
||||
uint8_t debugflowsize,debugflow[DEBUGFLOWSIZE];
|
||||
extern uint8_t debugflowsize,debugflow[DEBUGFLOWSIZE];
|
||||
#define DEBUGFLOW(c) if (debugflowsize<(DEBUGFLOWSIZE-1)) debugflow[debugflowsize++]=c
|
||||
#else
|
||||
#define DEBUGFLOW(c)
|
||||
|
@ -186,7 +185,11 @@ static unsigned long total_time_for_transmission, total_transmission_len;
|
|||
static int num_transmissions;
|
||||
#endif
|
||||
|
||||
static uint8_t volatile pending;
|
||||
#if defined(__AVR_ATmega128RFA1__)
|
||||
volatile uint8_t rf230_interruptwait;
|
||||
#endif
|
||||
|
||||
uint8_t volatile rf230_pending;
|
||||
|
||||
/* RF230 hardware delay times, from datasheet */
|
||||
typedef enum{
|
||||
|
@ -217,7 +220,7 @@ static int rf230_transmit(unsigned short len);
|
|||
static int rf230_send(const void *data, unsigned short len);
|
||||
|
||||
static int rf230_receiving_packet(void);
|
||||
static int pending_packet(void);
|
||||
static int rf230_pending_packet(void);
|
||||
static int rf230_cca(void);
|
||||
|
||||
uint8_t rf230_last_correlation,rf230_last_rssi,rf230_smallest_rssi;
|
||||
|
@ -231,7 +234,7 @@ const struct radio_driver rf230_driver =
|
|||
rf230_read,
|
||||
rf230_cca,
|
||||
rf230_receiving_packet,
|
||||
pending_packet,
|
||||
rf230_pending_packet,
|
||||
rf230_on,
|
||||
rf230_off
|
||||
};
|
||||
|
@ -320,6 +323,7 @@ static bool radio_is_sleeping(void)
|
|||
static void
|
||||
radio_reset_state_machine(void)
|
||||
{
|
||||
if (hal_get_slptr()) DEBUGFLOW('"');
|
||||
hal_set_slptr_low();
|
||||
delay_us(TIME_NOCLK_TO_WAKE);
|
||||
hal_subregister_write(SR_TRX_CMD, CMD_FORCE_TRX_OFF);
|
||||
|
@ -495,6 +499,15 @@ on(void)
|
|||
#endif
|
||||
|
||||
if (hal_get_slptr()) {
|
||||
#if defined(__AVR_ATmega128RFA1__)
|
||||
rf230_interruptwait=1;
|
||||
ENERGEST_ON(ENERGEST_TYPE_LED_RED);
|
||||
PORTE|=(1<<PE1); //ledon
|
||||
hal_set_slptr_low();
|
||||
while (rf230_interruptwait) {}
|
||||
// rf230_waitidle(); //?
|
||||
}
|
||||
#else
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
// DEBUGFLOW('0');
|
||||
|
@ -505,6 +518,7 @@ on(void)
|
|||
SREG=sreg;
|
||||
}
|
||||
rf230_waitidle();
|
||||
#endif
|
||||
|
||||
#if RF230_CONF_AUTOACK
|
||||
// radio_set_trx_state(is_promiscuous?RX_ON:RX_AACK_ON);
|
||||
|
@ -523,7 +537,6 @@ static void
|
|||
off(void)
|
||||
{
|
||||
RF230_receive_on = 0;
|
||||
|
||||
#ifdef RF230BB_HOOK_RADIO_OFF
|
||||
RF230BB_HOOK_RADIO_OFF();
|
||||
#endif
|
||||
|
@ -540,6 +553,8 @@ off(void)
|
|||
radio_reset_state_machine();
|
||||
#if RADIOSLEEPSWHENOFF
|
||||
/* Sleep Radio */
|
||||
ENERGEST_OFF(ENERGEST_TYPE_LED_RED);
|
||||
// PORTE&=~(1<<PE1); //ledoff
|
||||
hal_set_slptr_high();
|
||||
// DEBUGFLOW('d');
|
||||
delay_us(TIME_SLEEP_TO_TRX_OFF); //?
|
||||
|
@ -800,6 +815,7 @@ void rf230_warm_reset(void) {
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static uint8_t buffer[RF230_MAX_TX_FRAME_LENGTH+AUX_LEN];
|
||||
|
||||
static int
|
||||
rf230_transmit(unsigned short payload_len)
|
||||
{
|
||||
|
@ -819,10 +835,22 @@ rf230_transmit(unsigned short payload_len)
|
|||
/* If radio is sleeping we have to turn it on first */
|
||||
/* This automatically does the PLL calibrations */
|
||||
if (hal_get_slptr()) {
|
||||
#if defined(__AVR_ATmega128RFA1__)
|
||||
if (radiowason) DEBUGFLOW('e');
|
||||
radiowason=0;
|
||||
// DEBUGFLOW('j');
|
||||
ENERGEST_ON(ENERGEST_TYPE_LED_RED);
|
||||
// PORTE|=(1<<PE1); //ledon
|
||||
rf230_interruptwait=1;
|
||||
hal_set_slptr_low();
|
||||
while (rf230_interruptwait) {}
|
||||
#else
|
||||
hal_set_slptr_low();
|
||||
DEBUGFLOW('j');
|
||||
delay_us(TIME_SLEEP_TO_TRX_OFF);
|
||||
delay_us(TIME_SLEEP_TO_TRX_OFF);
|
||||
#endif
|
||||
|
||||
} else {
|
||||
#if RADIO_CONF_CALIBRATE_INTERVAL
|
||||
/* If nonzero, do periodic calibration. See clock.c */
|
||||
|
@ -922,6 +950,8 @@ rf230_transmit(unsigned short payload_len)
|
|||
if(radiowason) {
|
||||
DEBUGFLOW('l');
|
||||
on();
|
||||
} else {
|
||||
off();
|
||||
}
|
||||
|
||||
#if RF230_CONF_TIMESTAMPS
|
||||
|
@ -1207,7 +1237,7 @@ if (RF230_receive_on) {
|
|||
TIMETABLE_TIMESTAMP(rf230_timetable, "interrupt");
|
||||
#endif /* RF230_TIMETABLE_PROFILING */
|
||||
|
||||
pending = 1;
|
||||
rf230_pending = 1;
|
||||
|
||||
#if RADIOSTATS //TODO:This will double count buffered packets
|
||||
RF230_receivepackets++;
|
||||
|
@ -1247,7 +1277,7 @@ PROCESS_THREAD(rf230_process, ev, data)
|
|||
TIMETABLE_TIMESTAMP(rf230_timetable, "poll");
|
||||
#endif /* RF230_TIMETABLE_PROFILING */
|
||||
|
||||
pending = 0;
|
||||
rf230_pending = 0;
|
||||
|
||||
packetbuf_clear();
|
||||
len = rf230_read(packetbuf_dataptr(), PACKETBUF_SIZE);
|
||||
|
@ -1520,25 +1550,31 @@ rf230_cca(void)
|
|||
{
|
||||
uint8_t cca=0;
|
||||
uint8_t radio_was_off = 0;
|
||||
uint8_t volatile saved_sreg = SREG;
|
||||
|
||||
/* If the radio is locked by an underlying thread (because we are
|
||||
being invoked through an interrupt), we preted that the coast is
|
||||
clear (i.e., no packet is currently being transmitted by a
|
||||
neighbor). */
|
||||
if(locked) {
|
||||
DEBUGFLOW('1');
|
||||
return 1;
|
||||
DEBUGFLOW('|');
|
||||
// return 1; rf230 hangs on occasion?
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Don't allow interrupts! */
|
||||
//#if !defined(__AVR_ATmega128RFA1__)
|
||||
cli();
|
||||
//#endif
|
||||
/* Turn radio on if necessary. If radio is currently busy return busy channel */
|
||||
/* This may happen when testing radio duty cycling with RADIOALWAYSON */
|
||||
|
||||
if(RF230_receive_on) {
|
||||
if (hal_get_slptr()) {
|
||||
if (hal_get_slptr()) { //should not be sleeping!
|
||||
DEBUGFLOW('<');
|
||||
return 0;
|
||||
goto busyexit;
|
||||
} else {
|
||||
if (!rf230_isidle()) {DEBUGFLOW('2');return 0;}
|
||||
if (!rf230_isidle()) {DEBUGFLOW('2');goto busyexit;}
|
||||
}
|
||||
} else {
|
||||
DEBUGFLOW('3');
|
||||
|
@ -1552,21 +1588,34 @@ rf230_cca(void)
|
|||
|
||||
/* Start the CCA, wait till done, return result */
|
||||
/* Note reading the TRX_STATUS register clears both CCA_STATUS and CCA_DONE bits */
|
||||
#if defined(__AVR_ATmega128RFA1__)&&0
|
||||
rf230_interruptwait=1;
|
||||
hal_subregister_write(SR_CCA_REQUEST,1);
|
||||
sei();
|
||||
while (rf230_interruptwait) {}
|
||||
// while ((cca & 0x80) == 0 ) {//TODO:why does this hang the 128rfa1?
|
||||
cca=hal_register_read(RG_TRX_STATUS);
|
||||
// }
|
||||
#else
|
||||
hal_subregister_write(SR_CCA_REQUEST,1);
|
||||
delay_us(TIME_CCA);
|
||||
while ((cca & 0x80) == 0 ) {
|
||||
// while ((cca & 0x80) == 0 ) { //hangs 128rfa1
|
||||
cca=hal_register_read(RG_TRX_STATUS);
|
||||
}
|
||||
// }
|
||||
#endif
|
||||
|
||||
if(radio_was_off) {
|
||||
rf230_off();
|
||||
}
|
||||
// if (cca & 0x40) {/*DEBUGFLOW('3')*/;} else {pending=1;DEBUGFLOW('4');}
|
||||
// if (cca & 0x40) {/*DEBUGFLOW('3')*/;} else {rf230_pending=1;DEBUGFLOW('4');}
|
||||
if (cca & 0x40) {
|
||||
// DEBUGFLOW('5');
|
||||
SREG=saved_sreg;
|
||||
return 1;
|
||||
} else {
|
||||
// DEBUGFLOW('6');
|
||||
busyexit:
|
||||
SREG=saved_sreg;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -1576,12 +1625,12 @@ rf230_receiving_packet(void)
|
|||
{
|
||||
uint8_t radio_state;
|
||||
if (hal_get_slptr()) {
|
||||
DEBUGFLOW('7');
|
||||
DEBUGFLOW('=');
|
||||
} else {
|
||||
radio_state = hal_subregister_read(SR_TRX_STATUS);
|
||||
if ((radio_state==BUSY_RX) || (radio_state==BUSY_RX_AACK)) {
|
||||
DEBUGFLOW('8');
|
||||
pending=1;
|
||||
// DEBUGFLOW('8');
|
||||
// rf230_pending=1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -1589,10 +1638,10 @@ rf230_receiving_packet(void)
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
pending_packet(void)
|
||||
rf230_pending_packet(void)
|
||||
{
|
||||
if (pending) DEBUGFLOW('9');
|
||||
return pending;
|
||||
if (rf230_pending) DEBUGFLOW('@');
|
||||
return rf230_pending;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#if RF230_CONF_SNEEZER && JACKDAW
|
||||
|
|
|
@ -69,7 +69,11 @@
|
|||
/* RF230 does not support RX_START interrupts in extended mode, but it seems harmless to always enable it. */
|
||||
/* In non-extended mode this allows RX_START to sample the RF rssi at the end of the preamble */
|
||||
//#define RF230_SUPPORTED_INTERRUPT_MASK ( 0x0C ) //disable RX_START
|
||||
#if defined(__AVR_ATmega128RFA1__)
|
||||
#define RF230_SUPPORTED_INTERRUPT_MASK ( 0xFF )
|
||||
#else
|
||||
#define RF230_SUPPORTED_INTERRUPT_MASK ( 0x0F )
|
||||
#endif
|
||||
|
||||
#define RF230_MIN_CHANNEL ( 11 )
|
||||
#define RF230_MAX_CHANNEL ( 26 )
|
||||
|
|
|
@ -77,9 +77,18 @@
|
|||
#define TIFR TIFR1
|
||||
#endif
|
||||
|
||||
/* Track flow through rtimer interrupts*/
|
||||
#if DEBUGFLOWSIZE&&0
|
||||
extern uint8_t debugflowsize,debugflow[DEBUGFLOWSIZE];
|
||||
#define DEBUGFLOW(c) if (debugflowsize<(DEBUGFLOWSIZE-1)) debugflow[debugflowsize++]=c
|
||||
#else
|
||||
#define DEBUGFLOW(c)
|
||||
#endif
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#if defined(TCNT3) && RTIMER_ARCH_PRESCALER
|
||||
ISR (TIMER3_COMPA_vect) {
|
||||
DEBUGFLOW('/');
|
||||
ENERGEST_ON(ENERGEST_TYPE_IRQ);
|
||||
|
||||
/* Disable rtimer interrupts */
|
||||
|
@ -96,14 +105,17 @@ ISR (TIMER3_COMPA_vect) {
|
|||
rtimer_run_next();
|
||||
|
||||
ENERGEST_OFF(ENERGEST_TYPE_IRQ);
|
||||
DEBUGFLOW('\\');
|
||||
}
|
||||
|
||||
#elif RTIMER_ARCH_PRESCALER
|
||||
#warning "No Timer3 in rtimer-arch.c - using Timer1 instead"
|
||||
ISR (TIMER1_COMPA_vect) {
|
||||
DEBUGFLOW('/');
|
||||
TIMSK &= ~((1<<TICIE1)|(1<<OCIE1A)|(1<<OCIE1B)|(1<<TOIE1));
|
||||
|
||||
rtimer_run_next();
|
||||
DEBUGFLOW('\\');
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -191,7 +203,7 @@ rtimer_arch_schedule(rtimer_clock_t t)
|
|||
uint8_t sreg;
|
||||
sreg = SREG;
|
||||
cli ();
|
||||
|
||||
DEBUGFLOW(':');
|
||||
#ifdef TCNT3
|
||||
/* Set compare register */
|
||||
OCR3A = t;
|
||||
|
|
Loading…
Reference in a new issue