EmbeddedRelated.com

High speed serial and port control for logic analysis

March 22, 2013 Coded in C for the Atmel AVR32

These functions allow high speed serial and port control for debugging code without too much machine cycle overhead.

TAG compiles down to one instruction per bit. SET, CLR and TOG compile down to one instruction each. These are all for static output.

textOut, which is a little slower, allows for dynamic data output.

Small tags can be placed at the start of functions where a single pin on a logic analyzer shows a debug trace without impacting the throughput.

#ifndef TESTPOINTS_H
#define TESTPOINTS_H

#include "gpio.h"

class TpHw
{

public:

//===================================================================================
// TOG, CLR, SET macros provide a means to quickly clear, set or toggle a pin.
// Each macro compiles into a single machine instruction when optimized. 
// usage is: 
//			TOG(AVR32_PIN10);
//===================================================================================

	#define TOG(pin) {											\
	volatile avr32_gpio_port_t *gpio_pin = &AVR32_GPIO.port[pin >> 5];	\
		gpio_pin->ovrt = 1 << (pin & 0x1F); } 

	#define CLR(pin) {											\
	volatile avr32_gpio_port_t *gpio_pin = &AVR32_GPIO.port[pin >> 5];	\
		gpio_pin->ovrc = 1 << (pin & 0x1F); } 

	#define SET(pin) {											\
	volatile avr32_gpio_port_t *gpio_pin = &AVR32_GPIO.port[pin >> 5];	\
		gpio_pin->ovrs = 1 << (pin & 0x1F); } 
		
//===================================================================================
// TAG provides a means to insert static real time serial data to be displayed on a 
// logic analyzer. The TAG macro should be optimized for a baud rate that matches 
// the instruction cycle time of the compiler. Each letter compiles into 10 
// machine instructions when optimized. Use 8 data bits, no parity and one stop bit. 
// usage is:
//			TAG(AVR32_PIN10, 'H', 'E', 'L', 'L', 'O');
//===================================================================================

	// this macro builds the bit
	#define BT(pin, letter, bit) {											\
		volatile avr32_gpio_port_t *gpio_pin = &AVR32_GPIO.port[pin >> 5];	\
		if (letter & bit)													\
			gpio_pin->ovrs = 1 << (pin & 0x1F);								\
		else																\
			gpio_pin->ovrc = 1 << (pin & 0x1F); } 
 
	// this macro builds the letter
	#define LETTER(pin, letter) {		\
		BT(pin, letter, 0x00);			\
		BT(pin, letter, 0x01);			\
		BT(pin, letter, 0x02);			\
		BT(pin, letter, 0x04);			\
		BT(pin, letter, 0x08);			\
		BT(pin, letter, 0x10);			\
		BT(pin, letter, 0x20);			\
		BT(pin, letter, 0x40);			\
		BT(pin, letter, 0x80);			\
		BT(pin, 0xff, 0xff);			}
	 
	// these build the tag
	#define LETTER1(pin, letter)      LETTER(pin, letter)
	#define LETTER2(pin, letter, ...) LETTER1(pin, letter) LETTER1(pin, __VA_ARGS__)
	#define LETTER3(pin, letter, ...) LETTER1(pin, letter) LETTER2(pin, __VA_ARGS__)
	#define LETTER4(pin, letter, ...) LETTER1(pin, letter) LETTER3(pin, __VA_ARGS__)
	#define LETTER5(pin, letter, ...) LETTER1(pin, letter) LETTER4(pin, __VA_ARGS__)
	#define LETTER6(pin, letter, ...) LETTER1(pin, letter) LETTER5(pin, __VA_ARGS__)
	#define LETTER7(pin, letter, ...) LETTER1(pin, letter) LETTER6(pin, __VA_ARGS__)
	#define LETTER8(pin, letter, ...) LETTER1(pin, letter) LETTER7(pin, __VA_ARGS__)
	 
	// these calculate the number of arguments passed
	#define ARGS_N(x1, x2, x3, x4, x5, x6, x7, x8, N, ...) N
	#define RSEQ_N()         8, 7, 6, 5, 4, 3, 2, 1, 0
	#define NARG_N(...)      ARGS_N(__VA_ARGS__)
	#define NARG(...)        NARG_N(__VA_ARGS__, RSEQ_N())
	 
	// these build the macro to start with (T1 - T8) base on number of arguments
	#define PASTE(a, b)      a ## b
	#define XPASTE(a, b)     PASTE(a, b)
	#define T_(pin, M_, ...) M_(pin, __VA_ARGS__)
	#define TAG(pin, ...)    T_(pin, XPASTE(LETTER, NARG(__VA_ARGS__)), __VA_ARGS__) 

//=====================================================================================
// textOut provides a means to insert dynamic real time serial data to be displayed 
// on a logic analyzer. The code below should be optimized for a baud rate that matches 
// the instruction cycle time of the compiler by stuffing no ops where appropriate 
// to balance the bit output. Use 8 data bits, no parity and one stop bit. This 
// gives a character that should take 55 cycles and doesn't take any time to set up. 
// usage is:
//			TpHw::textOut(AVR32_PIN_PA22, "HELLO");
//=====================================================================================

	static void serialOut(uint32_t pin, uint8_t value)
	{
		volatile avr32_gpio_port_t *gpio_pin = &AVR32_GPIO.port[pin >> 5];
		volatile uint32_t out = 1 << (pin & 0x1F);

		// start bit
		gpio_pin->ovrc = out;
		asm("nop"); 
	               
		// bit 0
		if (value & (1 << 0))
		{
			gpio_pin->ovrs = out;	  
		} 
		else
		{
			gpio_pin->ovrc = out;	  
			asm("nop"); 
		}
	          
		// bit 1
		if (value & (1 << 1))
		{
			gpio_pin->ovrs = out;	  
		}
		else
		{
			gpio_pin->ovrc = out;	  
			asm("nop"); 
		}
	    
		// bit 2
		if (value & (1 << 2))
		{
			gpio_pin->ovrs = out;	  
		}
		else
		{
			gpio_pin->ovrc = out;	  
			asm("nop"); 
		}

		// bit 3
		if (value & (1 << 3))
		{
			gpio_pin->ovrs = out;	  
		}
		else
		{
			gpio_pin->ovrc = out;	  
			asm("nop"); 
		}

		// bit 4
		if (value & (1 << 4))
		{
			gpio_pin->ovrs = out;	  
		}
		else
		{
			gpio_pin->ovrc = out;	  
			asm("nop"); 
		}

		// bit 5
		if (value & (1 << 5))
		{
			gpio_pin->ovrs = out;	  
		}
		else
		{
			gpio_pin->ovrc = out;	  
			asm("nop"); 
		}

		// bit 6
		if (value & (1 << 6))
		{
			gpio_pin->ovrs = out;	  
		}
		else
		{
			gpio_pin->ovrc = out;	  
			asm("nop"); 
		}

		// bit 7
		if (value & (1 << 7))
		{
			gpio_pin->ovrs = out;	  
		}
		else
		{
			gpio_pin->ovrc = out;	  
			asm("nop"); 
		}
	      
		// stop bit
		asm("nop");
		asm("nop");
		asm("nop");
		
		gpio_pin->ovrs = out;	  
	      
	}

	static void textOut(uint32_t pin, char *text)
	{
	   while (*text != 0)
	   {
		  serialOut(pin, *text++);
	   }
	}

};

#endif