From ca4e80551b2144cb08a72a58b6c13c2f88b4b2a0 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 20:06:32 +0200 Subject: dbgu: make debug routines re-entrant --- firmware/src/os/dbgu.c | 305 +++++++++++++++++++++++++++++++------------------ firmware/src/os/dbgu.h | 7 +- firmware/src/os/main.c | 1 - 3 files changed, 193 insertions(+), 120 deletions(-) (limited to 'firmware') diff --git a/firmware/src/os/dbgu.c b/firmware/src/os/dbgu.c index 113208e..d8bf8d1 100644 --- a/firmware/src/os/dbgu.c +++ b/firmware/src/os/dbgu.c @@ -45,18 +45,18 @@ #include #include -//#define DEBUG_UNBUFFERED +/* In case we find that while (); is non interruptible, we may need to + * uncommend this line: */ +// #define ALLOW_INTERRUPT_LOOP asm("nop;nop;nop;nop;nop;nop;nop;nop;") + +#define ALLOW_INTERRUPT_LOOP + +/* DEBUG_BUFFER_SIZE MUST BE A POWER OF 2 */ +#define DEBUG_BUFFER_SIZE (1 << 10) +#define DEBUG_BUFFER_MASK (DEBUG_BUFFER_SIZE - 1) #define USART_SYS_LEVEL 4 /*---------------------------- Global Variable ------------------------------*/ -//*--------------------------1-------------------------------------------------- -//* \fn AT91F_DBGU_Printk -//* \brief This function is used to send a string through the DBGU channel -//*---------------------------------------------------------------------------- -void AT91F_DBGU_Ready(void) -{ - while (!(AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXEMPTY)) ; -} //*---------------------------------------------------------------------------- //* Function Name : Send_reset @@ -69,7 +69,7 @@ static void Send_reset(void) // Acknoledge the interrupt // Mark the End of Interrupt on the AIC AT91C_BASE_AIC->AIC_EOICR = 0; - AT91F_DBGU_Ready(); + while (!(AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXEMPTY)) ; // Jump in reset pfct(); } @@ -92,26 +92,26 @@ static void DBGU_irq_handler(u_int32_t sr) break; case '1': //* info udp_pullup_off(); - AT91F_DBGU_Printk("Set Pull up\n\r"); + AT91F_DBGU_Frame("Set Pull up\n\r"); // Reset Application Send_reset(); break; case '2': - AT91F_DBGU_Printk("Toggling LED 1\n\r"); + AT91F_DBGU_Frame("Toggling LED 1\n\r"); led_toggle(1); break; case '3': - AT91F_DBGU_Printk("Toggling LED 2\n\r"); + AT91F_DBGU_Frame("Toggling LED 2\n\r"); led_toggle(2); break; case '9': - AT91F_DBGU_Printk("Resetting SAM7\n\r"); + AT91F_DBGU_Frame("Resetting SAM7\n\r"); AT91F_RSTSoftReset(AT91C_BASE_RSTC, AT91C_RSTC_PROCRST| AT91C_RSTC_PERRST|AT91C_RSTC_EXTRST); break; default: if (_main_dbgu(value) < 0) - AT91F_DBGU_Printk("\n\r"); + AT91F_DBGU_Frame("\n\r"); break; } // end switch } @@ -148,17 +148,17 @@ void AT91F_DBGU_Init(void) //* open interrupt sysirq_register(AT91SAM7_SYSIRQ_DBGU, &DBGU_irq_handler); - AT91F_DBGU_Printk("\n\r"); - AT91F_DBGU_Printk("(C) 2006-2011 by Harald Welte \n\r" + debugp("\n\r"); + debugp("(C) 2006-2011 by Harald Welte \n\r" "This software is FREE SOFTWARE licensed under GNU GPL\n\r"); - AT91F_DBGU_Printk("Version " COMPILE_SVNREV + debugp("Version " COMPILE_SVNREV " compiled " COMPILE_DATE " by " COMPILE_BY "\n\r\n\r"); - AT91F_DBGU_Printk("\n\rDEBUG Interface:\n\r" + debugp("\n\rDEBUG Interface:\n\r" "0) Set Pull-up 1) Clear Pull-up 2) Toggle LED1 3) " "Toggle LED2\r\n9) Reset\n\r"); - debugp("RSTC_SR=0x%08x\n", rst_status); + debugp("RSTC_SR=0x%08x\n\r", rst_status); } /* @@ -172,32 +172,37 @@ void AT91F_DBGU_Fini(void) // Maybe FIXME, do more? -- Henryk Plötz } -//*---------------------------------------------------------------------------- -//* \fn AT91F_DBGU_Printk -//* \brief This function is used to send a string through the DBGU channel (Very low level debugging) -//*---------------------------------------------------------------------------- -void AT91F_DBGU_Printk(char *buffer) -{ - while (*buffer != '\0') { - while (!AT91F_US_TxReady((AT91PS_USART) AT91C_BASE_DBGU)) ; - AT91F_US_PutChar((AT91PS_USART) AT91C_BASE_DBGU, *buffer++); - } -} - //*---------------------------------------------------------------------------- //* \fn AT91F_DBGU_Frame -//* \brief This function is used to send a string through the DBGU channel +//* \brief This function is used to send a string through the DBGU channel (Very low level debugging) //*---------------------------------------------------------------------------- void AT91F_DBGU_Frame(char *buffer) { - unsigned char len; - - for (len = 0; buffer[len] != '\0'; len++) { + unsigned long intcFlags; + unsigned int len = 0; + while (buffer[len++]) ALLOW_INTERRUPT_LOOP; + len--; + local_irq_save(intcFlags); + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 9; // Disable transfer + if (AT91C_BASE_DBGU->DBGU_TNCR) { + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 8; // Resume transfer + local_irq_restore(intcFlags); + while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXBUFE) == 0) ALLOW_INTERRUPT_LOOP; + local_irq_save(intcFlags); + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 9; // Disable transfer + AT91C_BASE_DBGU->DBGU_TPR = (unsigned)buffer; + AT91C_BASE_DBGU->DBGU_TCR = len; + } else if (AT91C_BASE_DBGU->DBGU_TCR) { + AT91C_BASE_DBGU->DBGU_TNPR = (unsigned)buffer; + AT91C_BASE_DBGU->DBGU_TNCR = len; + } else { + AT91C_BASE_DBGU->DBGU_TPR = (unsigned)buffer; + AT91C_BASE_DBGU->DBGU_TCR = len; } - - AT91F_US_SendFrame((AT91PS_USART) AT91C_BASE_DBGU, - (unsigned char *)buffer, len, 0, 0); - + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 8; // Resume transfer + local_irq_restore(intcFlags); + /* Return ONLY after we complete Transfer */ + while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXBUFE) == 0) ALLOW_INTERRUPT_LOOP; } //*---------------------------------------------------------------------------- @@ -239,105 +244,177 @@ hexdump(const void *data, unsigned int len) } struct dbgu { - char buf[4096]; - char *next_inbyte; - char *next_outbyte; + char buf[DEBUG_BUFFER_SIZE]; + /* Since debugp appears to require to be re-entrant, we need a + * bit more state variables + * in_head Position where incoming *append* characters have + * finished copy to buffer + * in_tail Position where NEW incoming *append* characters + * should COPY to + * out_head Position where the LAST *possibly incomplete* + * dbgu_rb_flush started from + * out_tail Position where the NEXT dbug_rb_flush should + * start from + * The position in the RING should be in this order: + * --> out_tail --> in_head --> in_tail --> out_head --> + */ + volatile unsigned int in_head, in_tail, out_head, out_tail; + + /* flush_stack is to indicate rb_flush is being executed, NOT to + * execute again */ + volatile unsigned int flush_stack; + + /* append_stack is to indicate the re-entrack stack order of the + * current dbgu_append call */ + volatile unsigned int append_stack; }; + static struct dbgu dbgu; void dbgu_rb_init(void) { - memset(dbgu.buf, 0, sizeof(dbgu.buf)); - dbgu.next_inbyte = &dbgu.buf[0]; - dbgu.next_outbyte = &dbgu.buf[0]; -} - -/* pull one char out of debug ring buffer */ -static int dbgu_rb_pull(char *ret) -{ - unsigned long flags; - - local_irq_save(flags); - - if (dbgu.next_outbyte == dbgu.next_inbyte) { - local_irq_restore(flags); - return -1; - } - - *ret = *dbgu.next_outbyte; - - dbgu.next_outbyte++; - if (dbgu.next_outbyte == &dbgu.buf[0]+sizeof(dbgu.buf)) { - //AT91F_DBGU_Printk("WRAP DURING PULL\r\n"); - dbgu.next_outbyte = &dbgu.buf[0]; - } else if (dbgu.next_outbyte > &dbgu.buf[0]+sizeof(dbgu.buf)) { - //AT91F_DBGU_Printk("OUTBYTE > END_OF_BUF!!\r\n"); - dbgu.next_outbyte -= sizeof(dbgu.buf); - } - - local_irq_restore(flags); - - return 0; -} - -static void __rb_flush(void) -{ - char ch; - while (dbgu_rb_pull(&ch) >= 0) { - while (!AT91F_US_TxReady((AT91PS_USART) AT91C_BASE_DBGU)) ; - AT91F_US_PutChar((AT91PS_USART) AT91C_BASE_DBGU, ch); - } + dbgu.in_head = dbgu.in_tail = dbgu.out_head = dbgu.out_tail = 0; + dbgu.flush_stack = dbgu.append_stack = 0; } /* flush pending data from debug ring buffer to serial port */ -void dbgu_rb_flush(void) -{ - __rb_flush(); -} -static void __dbgu_rb_append(char *data, int len) +static void dbgu_rb_flush(void) { - char *pos = dbgu.next_inbyte; - - dbgu.next_inbyte += len; - if (dbgu.next_inbyte >= &dbgu.buf[0]+sizeof(dbgu.buf)) { - AT91F_DBGU_Printk("WRAP DURING APPEND\r\n"); - dbgu.next_inbyte -= sizeof(dbgu.buf); + unsigned long intcFlags; + unsigned int flush_stack, start, len; + if (dbgu.in_head == dbgu.out_tail) + return; + + /* Transmit can ONLY be disabled when Interrupt is disabled. We + * don't want to be interrupted while Transmit is disabled. */ + local_irq_save(intcFlags); + flush_stack = dbgu.flush_stack; + dbgu.flush_stack = 1; + if (flush_stack) { + local_irq_restore(intcFlags); + return; } - - memcpy(pos, data, len); + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 9; // Disable transfer + start = (unsigned)dbgu.buf + dbgu.out_tail; + len = (dbgu.in_head - dbgu.out_tail) & DEBUG_BUFFER_MASK; + if (dbgu.in_head > dbgu.out_tail || dbgu.in_head == 0) { // Just 1 fragmentf + if (AT91C_BASE_DBGU->DBGU_TNCR) { + if (AT91C_BASE_DBGU->DBGU_TNPR + AT91C_BASE_DBGU->DBGU_TNCR == start) { + AT91C_BASE_DBGU->DBGU_TNCR += len; + } else { + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 8; // Resume transfer + local_irq_restore(intcFlags); + while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXBUFE) == 0) ALLOW_INTERRUPT_LOOP; + dbgu.out_head = dbgu.out_tail; // in case we are interrupted, they may need more space + /* Since the ONLY place where Transmit is started is dbgu_rb_flush and AT91F_DBGU_Frame and: + * 1) AT91F_DBGU_Frame always leave the dbgu in a TX completed state + * 2) dbgu_rb is non-reentrant by safeguard at the beginning of this routing + * We can assume that after this INTERRUPTIBLE while loop, there are NO data to be transmitted + */ + local_irq_save(intcFlags); + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 9; // Disable transfer + AT91C_BASE_DBGU->DBGU_TPR = start; + AT91C_BASE_DBGU->DBGU_TCR = len; + } + } else if (AT91C_BASE_DBGU->DBGU_TCR) { + if (AT91C_BASE_DBGU->DBGU_TPR + AT91C_BASE_DBGU->DBGU_TCR == start) { + dbgu.out_head = AT91C_BASE_DBGU->DBGU_TPR - (unsigned)dbgu.buf; + AT91C_BASE_DBGU->DBGU_TCR += len; + } else { + AT91C_BASE_DBGU->DBGU_TNPR = start; + AT91C_BASE_DBGU->DBGU_TNCR = len; + } + } else { + AT91C_BASE_DBGU->DBGU_TPR = start; + AT91C_BASE_DBGU->DBGU_TCR = len; + dbgu.out_head = dbgu.out_tail; + } + } else { // 2 fragments + if ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXBUFE) == 0) { + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 8; // Resume transfer + local_irq_restore(intcFlags); + while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXBUFE) == 0) ALLOW_INTERRUPT_LOOP; + dbgu.out_head = dbgu.out_tail; // in case we are interrupted, they may need more space + local_irq_save(intcFlags); + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 9; // Disable transfer + } + AT91C_BASE_DBGU->DBGU_TPR = start; + AT91C_BASE_DBGU->DBGU_TCR = DEBUG_BUFFER_SIZE - dbgu.out_tail; + AT91C_BASE_DBGU->DBGU_TNPR = (unsigned)dbgu.buf; + AT91C_BASE_DBGU->DBGU_TNCR = dbgu.in_head; + dbgu.out_head = dbgu.out_tail; + } + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 8; // Resume transfer + dbgu.out_tail = dbgu.in_head; + dbgu.flush_stack = 0; + local_irq_restore(intcFlags); } void dbgu_rb_append(char *data, int len) { - unsigned long flags; - int bytes_left; - char *data_cur; + /* Rules: + * 1) ONLY the LOWEST order of dbgu_rb_append CAN update in_head; + * 2) WHEN updateing in_head, always set it to the current in_tail (since all higher order dbgu_rb_append have completed + * 3) ONLY the LOWEST order of dbgu_rb_append MAY call dbgu_rb_flush + */ + unsigned long intcFlags; + unsigned int append_stack, avail, local_head; + if (len <= 0) + return; - local_irq_save(flags); - - bytes_left = &dbgu.buf[0]+sizeof(dbgu.buf)-dbgu.next_inbyte; - data_cur = data; - - if (len > bytes_left) { - AT91F_DBGU_Printk("LEN > BYTES_LEFT\r\n"); - __rb_flush(); - __dbgu_rb_append(data_cur, bytes_left); - len -= bytes_left; - data_cur += bytes_left; + local_irq_save(intcFlags); + append_stack = dbgu.append_stack++; + if (AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXBUFE) + dbgu.out_head = dbgu.out_tail; + avail = (dbgu.out_head - 1 - dbgu.in_tail) & DEBUG_BUFFER_MASK; + local_head = (unsigned)len; + if (local_head > avail) { + while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXBUFE) == 0); + dbgu.out_head = dbgu.out_tail; + avail = (dbgu.out_head - 1 - dbgu.in_tail) & DEBUG_BUFFER_MASK; + if (local_head > avail) { + local_head -= avail; + AT91C_BASE_DBGU->DBGU_TPR = (unsigned)data; + AT91C_BASE_DBGU->DBGU_TCR = local_head; + data += local_head; + len = avail; + } } - __dbgu_rb_append(data_cur, len); - - local_irq_restore(flags); + local_head = dbgu.in_tail; + dbgu.in_tail += len; + dbgu.in_tail &= DEBUG_BUFFER_MASK; + local_irq_restore(intcFlags); + if (dbgu.out_head <= local_head) { + // We may have to wrap around: out_head won't change because NO call to flush will be made YET + avail = DEBUG_BUFFER_SIZE - local_head; + if (avail >= (unsigned)len) { + memcpy(dbgu.buf + local_head, data, (size_t)len); + } else { + memcpy(dbgu.buf + local_head, data, (size_t)avail); + memcpy(dbgu.buf, data + avail, (size_t)(len - avail)); + } + } else { + memcpy(dbgu.buf + local_head, data, len); + } + local_irq_save(intcFlags); + dbgu.append_stack--; + if (!append_stack) + dbgu.in_head = dbgu.in_tail; + local_irq_restore(intcFlags); + if (!append_stack) + dbgu_rb_flush(); } static char dbg_buf[256]; +static int line_num = 0; void debugp(const char *format, ...) { va_list ap; va_start(ap, format); - vsnprintf(dbg_buf, sizeof(dbg_buf)-1, format, ap); + sprintf(dbg_buf, "[%06X] ", line_num++); + vsnprintf(dbg_buf + 9, sizeof(dbg_buf)-10, format, ap); va_end(ap); dbg_buf[sizeof(dbg_buf)-1] = '\0'; diff --git a/firmware/src/os/dbgu.h b/firmware/src/os/dbgu.h index c36ac8c..2a5b59d 100644 --- a/firmware/src/os/dbgu.h +++ b/firmware/src/os/dbgu.h @@ -17,17 +17,14 @@ #define AT91C_DBGU_BAUD 115200 -//#define DEBUGP(x) AT91F_DBGU_Printk(x) - //* ----------------------- External Function Prototype ----------------------- extern const char *hexdump(const void *data, unsigned int len); void AT91F_DBGU_Init(void); void AT91F_DBGU_Fini(void); -void AT91F_DBGU_Printk( char *buffer); -void AT91F_DBGU_Frame( char *buffer); +void AT91F_DBGU_Frame(char *buffer); +#define AT91F_DBGU_Printk(x) AT91F_DBGU_Frame(x) int AT91F_DBGU_Get( char *val); -void dbgu_rb_flush(void); #ifndef __WinARM__ void AT91F_DBGU_scanf(char * type,unsigned int * val); #endif diff --git a/firmware/src/os/main.c b/firmware/src/os/main.c index 968f6ad..363befc 100644 --- a/firmware/src/os/main.c +++ b/firmware/src/os/main.c @@ -77,7 +77,6 @@ int main(void) while (1) { /* Call application specific main idle function */ _main_func(); - dbgu_rb_flush(); /* restart watchdog timer */ wdt_restart(); -- cgit v1.2.3