Jump to content
ELFORUM - Forumul electronistilor

Debugger Arduino


Guest Razvan Marinovici

Recommended Posts

Guest Razvan Marinovici

Salut,

 

Nu am sa incep cu povestea, vreau doar sa spun ca am inceput sa lucrez la o extensie pentru a face debug pe Arduino din Atmel Studio. Nu ma refer la debugger extern, ma refer la software debugger. Sunt unele limitari dar pentru multe proiecte cred ca este suficient.

 

As avea nevoie de ajutor de la voi, unu pentru teste, doi pentru ajutor la development, extensia va fi free deci nu va ganditi la castiguri.

 

https://www.youtube.com/watch?v=p_YvdBPmX_E - Ram change / debug / variable change

https://www.youtube.com/watch?v=HBGGGCVuaxc - EEPROM change - single step / continue running .....

https://www.youtube.com/watch?v=6AgQ8sfcUJk - Aplicatie de debug externa AS.

 

Daca e cineva interesat va rog sa imi spuneti, si vedem cum facem sa expun codul si sa adaug useri la repo.

 

 

 

Link to comment
  • Replies 4
  • Created
  • Last Reply

Top Posters In This Topic

  • Liviu M

    1

  • nico_2010

    1

Top Posters In This Topic

Salut,

curajos proiect :), felicitari!

La partea de munca nu ma bag, dar am o idee despre modul de colaborare - iti faci cont pe github.com si urci proiectul acolo.

Daca cineva vrea sa contribuie, il cloneaza, face modificarile si ti le trimite sa le aplici prin pull request-uri.

In modul asta pastrezi controlul asupra a ce intra in proiect.

Asta daca proiectul ramane deschis. Se poate si closed, da' e ceva mai mult de scris si de pe tableta..

Link to comment
Guest Razvan Marinovici

Multumesc,

Ideea de la care am plecat, este urmatoarea, am un Arduino si un USB, vreau sa fac debug, optiunile ar fi
- Visual Micro (am licenta), personal nu imi place, prea complicat, si nu poti face debug in adevaratul sens. + este prea mare (ma refer la marimea codului)
- GDB - la fel prea mare pentru mine, cam 4k flash si mult ram.

Cerintele pe care le-am pus de la inceput au fost

- USB to Arduino si atat pentru folosire
- Configurabil (folosesc strict ce am nevoie), impact asupra spatiului folosit (flash si ram)
- Marimea codului cat mai mica
- RAM ocupat cat mai putin.
- Posibilitatea de a avea serverul de debug in bootloader.

Dupa parerea mea am reusit partial. Posibilitatea de a avea cel putin citire variabile (ram) in doar 360 bytes (flash) si 7 RAM este ok, poate fi inclus in optiboot.

 

Codul este pe GIT privat momentan, vroiam sa stiu daca sunt persoane care doresc sa ajute, in caz negativ il voi face public cand il termin complet pentru prima versiune. Lucrez la el in timpul liber de aici si nevoia de ajutor.

 

Are 5 proiecte in el

- Codul nativ (serverul de debug din Arduino)

- Clientul pentru debug (librarie pentru comunicarea cu serverul din Arduino)

- Aplicatie Consola

- Extensia din AS

- Aplicatia pentru debug externa AS

 

Este de lucru cam la toate cate putin

- codul nativ momentan este continut in trei fisiere header. (doua pentru debug si unul pentru printf)

- As vrea sa il descompun in fisiere separate pentru fiecare device in parte ("Atmega328", "Atmega168" .... )

- Unele valori sunt hardcoded cum ar fi viteza UART

- printf momentan este inclus mereu, ar trebui sa il incadrez in define's si sa adaug optiune la build pentru el.

- Nu are suport pentru Error Check (viteza UART este 500k daca aleg alta viteza va fi problematic), aici e mai complicat, daca vreau suport pentru CRC am 3 optiuni

- Salvez tot buffer-ul si calculez un CRC pe el inainte sa il folosesc, problemele aici sunt: ce marime de buffer e suficienta (pot scrie toata memoria RAM asta inseamna un buffer cel putin la fel de mare ca marimea RAM-ului, nu are sens), daca aleg o marime de buffer mai mica tot e problematic pentru ca este in conflict cu una din cerinte

- Procesez cate un byte sau 2, 3 pe care ii validez inainte sa trimit alte date.

- Extensia, cam 90% gata, mai am exceptii din cand in cand trebuie sa vad de unde vin (e complicat tare sistemul)

- Foloseste simulatorul pentru a face debug, practic suprascriu / monitorizez datele din simulator si las AS + Simulatorul sa afiseze / proceseze datele afisate, e complicat de controlat toate 3 lucruri in mod coerent (Serverul de Debug Arduino, Simulatorul, Serverul de Debug din AS)

- Trebuie sa includ codul nativ in proiect, astfel incat la install sa poata fi folosit.

- Aplicatia externa pentru debug, aici mai am de lucru la procesarea fisierului ELF si a datelor DWARF pentru debug.

- Aplicatia consola, a fost folosita initial pentru teste, ar fi un nice to have nu tin neaparat la ea

 

Toate proiectele sunt in C# / .NET 4.6 (in afara de codul nativ pentru Arduino)

 

Mai jos este codul nativ pe care as vrea sa il despart in functionalitatea de server si implementarile specifice pentru device-uri.

/*
 * debug.h
 *
 * Created: 2/27/2017 10:38:31 AM
 *  Author: razvanm
 */ 


#ifndef DEBUG_H_
#define DEBUG_H_

#include <stdint.h>
#include <avr/io.h>
#include <avr/common.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#include "debug_defs.h"

#define DEBUG_PROTOCOL_VERSION		0x01

#ifdef __cplusplus
extern "C" {
#endif
// This should be used to call the debug function
//	It will force the compiler to place the PC on stack and not optimize the call
//	otherwise we could be in a situation where we are breaking in method_a, yet the compiler
//	has optimized the stack to return directly to the calling method

//	Normal stack
//	PC - Parent
//	PC - method_a
//	Debug method 

//	Optimized stack - we can't tell we were called from method_a
//	PC - Parent - method_a - optimized away
//	Debug method - return directly to parent
#ifdef DEBUG
	#define DEBUG_BRK	asm volatile ("call dbg_brk");
#else
	#define DEBUG_BRK
#endif

#ifdef __cplusplus
}
#endif

#ifndef EEWE
	#define EEWE (1)
#endif

#define	AVR8_SWINT_PIN		(PORTD2)
#define AVR8_SWINT_INTMASK	(INT0)
#define AVR8_INT_SOURCE		INT0_vect

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//	This assumes the following for now
//	- Clock - 16Mhz
//	- 16 Bit PC / STACK register sizes
//	- 16 Bit pointers
//	- At most 64k flash
//  ---------------
//	- Basically an ATmega328
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////


//	MINIMIZE_RAM will force inline everything, so we don't have to push / pop registers between calls
//			it will however increase the flash size, we might even figure out a way to optimize the ram structure ????
//	MINIMIZE_FLASH will use the stack to push / pop registers when needed, if the stack is full, this will cause issues.
#define MINIMIZE_FLASH

//////////////////////////////////////////////////////////////////////////
// THE CAPABILITIES WE WANT TO HAVE IN THE DEBUGGER
//	By default we only have "Read ram" without saving the registers
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
//	Enable the functionality to change the RAM memory
//CAPS_RAM_WRITE

//////////////////////////////////////////////////////////////////////////
//	Enable high speed UART (500000) this maps perfectly with 4,8,16,20Mhz crystals
//		it also allows us to read / write fast from / to memory
//CAPS_UART_HIGH_SPEED

//////////////////////////////////////////////////////////////////////////
//	Save the registers (R0...R31), the return PC address (from stack) and the stack pointer
//CAPS_SAVE_CTX

//////////////////////////////////////////////////////////////////////////
//	Enable flash read so we can read the program from memory
//CAPS_FLASH_READ

//////////////////////////////////////////////////////////////////////////
//	Enable flash write so we can change the program memory (AVR's support this only from bootloader)
//CAPS_FLASH_WRITE

//////////////////////////////////////////////////////////////////////////
//	Enable EEPROM read
//CAPS_EEPROM_READ

//////////////////////////////////////////////////////////////////////////
// Enable EEPROM write
//CAPS_EEPROM_WRITE

//////////////////////////////////////////////////////////////////////////
//	Enable code execution / method invocation, not done for now
//CAPS_EXECUTE

//////////////////////////////////////////////////////////////////////////
//	Enable single step support
//CAPS_SINGLE_STEP

//////////////////////////////////////////////////////////////////////////
//	Read the debug structure information in cases where we don't have access
//		to the compiled program (elf file). For example when the debugger is 
//		included in the bootloader, we don't have the bootloader file at our disposal
//		we need to know the location of the debug context, the structure will be based
//		on the CAPS enabled and retrieved when the debugging starts
//CAPS_DBG_CTX_ADDR


#if defined(MINIMIZE_FLASH) && defined(MINIMIZE_RAM)
#error	"Cannot minimize flash an ram at the same time, please choose only one"
#endif

#define INLINE	static inline
#if defined(MINIMIZE_RAM)
	#define ATTRIBUTES	__attribute__((always_inline, optimize("-Os")))
#elif defined(MINIMIZE_FLASH)
	#define ATTRIBUTES    __attribute__ ((optimize("-Os")))
#else
	#define ATTRIBUTES    __attribute__ ((optimize("-Os")))
#endif

#ifndef _BV
	#define _BV(v) (1 << (v))
#endif

#ifdef CAPS_DISABLE_TIMERS
	#define CAPS_FLAG_DISABLE_TIMERS _BV(CAPS_DISABLE_TIMERS_BIT)
	#ifndef CAPS_SAVE_POWER_REG
		#define CAPS_SAVE_POWER_REG
	#endif
#else
	#define CAPS_FLAG_DISABLE_TIMERS 0
#endif

#ifdef CAPS_SINGLE_STEP
	#define CAPS_FLAG_SINGLE_STEP _BV(CAPS_SINGLE_STEP_BIT)
	#ifndef CAPS_SAVE_CTX
		#define CAPS_SAVE_CTX
	#endif
#else
	#define CAPS_FLAG_SINGLE_STEP 0
#endif

#ifdef CAPS_UART_HIGH_SPEED
	//////////////////////////////////////////////////////////////////////////
	//	 BAUD PRESCALLER Used by the debug functionality, this will make 500k communication
	//		TODO Calculate this based on the CPU frequency, for now assume 16Mhz
	#define BAUD_PRESCALLER 1
	#define CAPS_FLAG_UART_HIGH_SPEED _BV(CAPS_UART_HIGHSPEED_BIT)
#else
	#define CAPS_FLAG_UART_HIGH_SPEED 0
#endif

#ifdef CAPS_SAVE_CTX
	#ifndef CAPS_DBG_CTX_ADDR
		#define CAPS_DBG_CTX_ADDR
	#endif
	#define CAPS_FLAG_SAVE_CTX _BV(CAPS_SAVE_CONTEXT_BIT)
#else
	#define CAPS_FLAG_SAVE_CTX 0
#endif

#ifdef CAPS_RAM_WRITE
	#define CAPS_FLAG_RAM_WRITE _BV(CAPS_RAM_W_BIT)
#else
	#define CAPS_FLAG_RAM_WRITE 0
#endif

#ifdef CAPS_FLASH_READ
	#define CAPS_FLAG_FLASH_READ	_BV(CAPS_FLASH_R_BIT)
#else
	#define CAPS_FLAG_FLASH_READ	0
#endif

#ifdef CAPS_FLASH_WRITE
	#define CAPS_FLAG_FLASH_WRITE _BV(CAPS_FLASH_W_BIT)
#else
	#define CAPS_FLAG_FLASH_WRITE	0
#endif

#ifdef CAPS_EEPROM_READ
	#define CAPS_FLAG_EEPROM_READ _BV(CAPS_EEPROM_R_BIT)
#else
	#define CAPS_FLAG_EEPROM_READ	0
#endif

#ifdef CAPS_EEPROM_WRITE
	#define CAPS_FLAG_EEPROM_WRITE _BV(CAPS_EEPROM_W_BIT)
#else
	#define CAPS_FLAG_EEPROM_WRITE	0
#endif

#ifdef CAPS_EXECUTE
	#define CAPS_FLAG_EXECUTE _BV(CAPS_EXECUTE_BIT)
#else
	#define CAPS_FLAG_EXECUTE 0
#endif

#ifdef CAPS_DBG_CTX_ADDR
	#define CAPS_FLAG_DBG_CTX_ADDR	_BV(CAPS_DBG_CTX_ADDR_BIT)
#else
	#define CAPS_FLAG_DBG_CTX_ADDR 0
#endif

//////////////////////////////////////////////////////////////////////////
//	The debug capabilities we have
#define CAPS_0	_BV(CAPS_RAM_R_BIT) | CAPS_FLAG_RAM_WRITE | CAPS_FLAG_FLASH_READ | CAPS_FLAG_FLASH_WRITE | CAPS_FLAG_EEPROM_READ | CAPS_FLAG_EEPROM_WRITE | CAPS_FLAG_EXECUTE | CAPS_FLAG_DBG_CTX_ADDR
#define CAPS_1	CAPS_FLAG_UART_HIGH_SPEED | CAPS_FLAG_SAVE_CTX | CAPS_FLAG_SINGLE_STEP | CAPS_FLAG_DISABLE_TIMERS

#define DBG_FLAG_EXECUTING			0
#define DBG_FLAG_UART_HIGH_SPEED	1
#define DBG_SINGLE_STEP_ISR			2
#define DBG_WILL_SINGLE_STEP		3

#define INTERRUPT_FLAG_BIT			7

//////////////////////////////////////////////////////////////////////////
//	Debug data sent when entering debug, so we know we are debugging and what capabilities we have
const uint8_t DEBUG_INFO[] PROGMEM = {DEBUG_COM_KEY_0, DEBUG_COM_KEY_1, DEBUG_COM_KEY_2, DEBUG_COM_KEY_3, DEBUG_COM_KEY_4, DEBUG_PROTOCOL_VERSION, SIGNATURE_0, SIGNATURE_1, SIGNATURE_2, CAPS_0, CAPS_1, DEBUG_COM_DATA_FILLER};

typedef struct {
	uint8_t l;
	uint8_t h;
} st_16bit_struct;

typedef union {
	st_16bit_struct d8;
	uint16_t d16;
} tu_uint16;

typedef union {
	st_16bit_struct nibbles;
	uint16_t addr;
	uint8_t* ptr;
} tu_puint8;

typedef struct {
	tu_uint16 size;
	tu_puint8 buff;
} t_mem_op_8;

#ifdef CAPS_UART_HIGH_SPEED
typedef struct {
	uint8_t ubrrh;
	uint8_t ubrrl;
	uint8_t ucsrc;
	uint8_t ucsrb;
} t_uart_regs;
#endif

#if defined(CAPS_EEPROM_READ) || defined(CAPS_EEPROM_WRITE)
typedef struct {
	uint16_t eear;
	#ifdef CAPS_EEPROM_WRITE
		uint8_t eedr;
	#endif 
} t_eeprom_regs;
#endif

#ifdef CAPS_SAVE_CTX
typedef struct {
	uint8_t _registers[32];
	tu_puint8 _stack_ptr;
	tu_uint16 _return_addr;
} t_state_ctx;
#endif

#ifdef CAPS_SAVE_POWER_REG
typedef struct {
	uint8_t prr;
} t_power_regs;
#endif

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//  DEBUG CONTEXT STRUCTURE, Define everything we need here
//		so we know at build time how much ram we need, also the compiler can optimize the memory access with a base pointer and offset
//	It is important that the registers are in the first part of the structure
//	We are doing asm with offset loads, and the largest offset is 64
//	So the registers must be in the first 64 bytes of the structure
typedef struct {
//////////////////////////////////////////////////////////////////////////
//	Save registers / PC / stack pointer
#ifdef CAPS_SAVE_CTX
	t_state_ctx registers;
#endif
//////////////////////////////////////////////////////////////////////////
//	High speed UART
#ifdef CAPS_UART_HIGH_SPEED
	t_uart_regs uart_regs;
#endif
//////////////////////////////////////////////////////////////////////////
//	EEprom functionality
#if defined(CAPS_EEPROM_READ) || defined(CAPS_EEPROM_WRITE)
	t_eeprom_regs eeprom_regs;
#endif
//////////////////////////////////////////////////////////////////////////
//	Execute support
#if defined(CAPS_EXECUTE) || defined(CAPS_UART_HIGH_SPEED) || defined(CAPS_SINGLE_STEP)
	uint8_t ctx_state;
#endif
#ifdef CAPS_SAVE_POWER_REG
	t_power_regs power_regs;
#endif
	t_mem_op_8 mem_op;
	uint8_t tmp_u8;
	uint8_t status_reg;
	uint8_t watchdog;
} t_dbg_context;

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// We don't need the context to be initialized, we will initialize what we need manually
//   this way we give the user an option to remove the initialization code generated by GCC if they need that
__attribute__((section(".noinit")))
t_dbg_context dbg_context;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

__attribute__((used, optimize("-Os")))
void dbg_init(){
	dbg_context.ctx_state = 0;
}

#ifdef CAPS_SAVE_POWER_REG
INLINE
ATTRIBUTES
void dbg_save_power_reg(){
	dbg_context.power_regs.prr = PRR;
}
INLINE
ATTRIBUTES
void dbg_restore_power_reg(){
	PRR = dbg_context.power_regs.prr;
}
#endif

#ifdef CAPS_DISABLE_TIMERS
INLINE
ATTRIBUTES
void dbg_disable_timers(){
	PRR &= ~(_BV(PRTIM0) | _BV(PRTIM1) | _BV(PRTIM2));
}
#endif

#ifdef CAPS_SINGLE_STEP
INLINE
ATTRIBUTES
void dbg_enable_single_step(){
	EICRA &= ~(_BV(ISC01) | _BV(ISC00));
	DDRD |= _BV(AVR8_SWINT_PIN);		/* set pin to output mode */
	EIFR |= _BV(AVR8_SWINT_INTMASK);	/* clear INTx flag */
	EIMSK |= _BV(AVR8_SWINT_INTMASK);	/* enable INTx interrupt */
	PORTD &= ~_BV(AVR8_SWINT_PIN);		/* make sure the pin is low */
}

INLINE
ATTRIBUTES
void dbg_disable_single_step(){
	EIMSK &= ~_BV(AVR8_SWINT_INTMASK);
}
#endif

//////////////////////////////////////////////////////////////////////////
#ifdef CAPS_UART_HIGH_SPEED

INLINE 
ATTRIBUTES
void dbg_init_uart(){
	UBRR0H = (uint8_t)(BAUD_PRESCALLER>>8);
	UBRR0L = (uint8_t)(BAUD_PRESCALLER);
	UCSR0B = (1<<RXEN0)|(1<<TXEN0);
	UCSR0C = ((1<<UCSZ00)|(1<<UCSZ01));	
}

INLINE
ATTRIBUTES
void dbg_save_uart_state(){
	dbg_context.uart_regs.ubrrh = UBRR0H;
	dbg_context.uart_regs.ubrrl = UBRR0L;
	dbg_context.uart_regs.ucsrb = UCSR0B;
	dbg_context.uart_regs.ucsrc = UCSR0C;
}

INLINE 
ATTRIBUTES
void dbg_restore_uart_state(){
	UBRR0H = dbg_context.uart_regs.ubrrh;
	UBRR0L = dbg_context.uart_regs.ubrrl;
	UCSR0B = dbg_context.uart_regs.ucsrb;
	UCSR0C = dbg_context.uart_regs.ucsrc;
}

#endif
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
#ifdef CAPS_SAVE_CTX
//////////////////////////////////////////////////////////////////////////
//	 Save registers

//// Register save inspired from https://www.codeproject.com/articles/1037057/debugger-for-arduino
INLINE
ATTRIBUTES
void dbg_save_registers(){
asm volatile (	"sts	%[regs]+31, r31			\n\t" // R31
				"in		r31, __SREG__			\n\t"
				"cli							\n\t"
				"sts	%[statusaddr], r31		\n\t" // SREG
				"sts	%[regs]+30, r30			\n\t" // R30
				// Save registers from 0 to 27 inclusive
				"ldi	r30, lo8(%[regs])		\n\t" // load with regs ptr
				"ldi	r31, hi8(%[regs])		\n\t" // load with regs ptr Z
				"std	z+29, r29				\n\t"
				"std	z+28, r28				\n\t"
				"std	z+27, r27				\n\t"
				"ldi	r28, 0					\n\t"
				"ldi	r29, 0					\n\t"
			"1:	 ld		r27, y+					\n\t"
				"st		z+, r27					\n\t"
				"cpi	r28, 27					\n\t"
				"brne	1b						\n\t"
				// Save PC
				"pop	r15						\n\t" // Return ADDR HIGH
				"std	z+35-27, r15			\n\t"
				"pop	r16						\n\t" // Return ADDR LOW
				"std	z+34-27, r16			\n\t"
				"push	r16						\n\t" // Return ADDR LOW
				"push	r15						\n\t" // Return ADDR HIGH
				// Save stack
				"in		r15, __SP_L__			\n\t"
				"std	z+32-27, r15			\n\t"				
				"in		r15, __SP_H__			\n\t"
				"std	z+33-27, r15			\n\t"
				// And finally clear r1, the compiler expects this to be 0
				"clr	__zero_reg__			\n\t"
				::	[statusaddr] "i" (&dbg_context.status_reg),
					[regs] "i" (&dbg_context.registers)
		);
}

INLINE
ATTRIBUTES
void dbg_restore_registers(){
asm volatile (	"ldi	r30, lo8(%[regs])		\n\t" // load with regs ptr
				"ldi	r31, hi8(%[regs])		\n\t" // load with regs ptr Z
				// Restore the registers from 0 to 27 (inclusive)
				"ldi	r28, 0					\n\t"
				"ldi	r29, 0					\n\t"
			"1:	 ld		r27, z+					\n\t"
				"st		y+, r27					\n\t"
				"cpi	r28, 27					\n\t"
				"brne	1b						\n\t"
				// Restore registers from 28 to 31 and the SREG register
				"ld		r27, z					\n\t"
				"ldd	r28, z+28-27			\n\t"
				"ldd	r29, z+29-27			\n\t"
				"ldd	r30, z+30-27			\n\t"
				"lds	r31, %[statusaddr]		\n\t"
				"out	__SREG__, r31			\n\t"
				"lds	r31, %[regs]+31			\n\t"
				::	[statusaddr] "i" (&dbg_context.status_reg),
					[regs] "i" (&dbg_context.registers)
				);
}
#endif
//////////////////////////////////////////////////////////////////////////

#if defined(CAPS_EEPROM_READ) || defined(CAPS_EEPROM_WRITE)

#ifdef CAPS_EEPROM_READ
INLINE
ATTRIBUTES
uint8_t dbg_eeprom_read(uint16_t addr){
	/* Wait for completion of previous write */
	while(EECR & (1<<EEWE));
	/* Set up address register */
	EEAR = addr;
	/* Start eeprom read by writing EERE */
	EECR |= (1<<EERE);
	/* Return data from Data Register */
	return EEDR;
}
#endif

#ifdef CAPS_EEPROM_WRITE
INLINE
ATTRIBUTES
void dbg_eeprom_write(uint16_t addr, uint8_t data){
	/* Wait for completion of previous write */
	while(EECR & (1<<EEWE));
	/* Set up address and Data Registers */
	EEAR = addr;
	EEDR = data;
	/* Write logical one to EEMPE */
	EECR |= (1<<EEMPE);
	/* Start eeprom write by setting EEWE */
	EECR |= (1<<EEWE);
}
#endif
#endif

INLINE
ATTRIBUTES
void dbg_enter_debug(){

#ifdef CAPS_SAVE_POWER_REG
	dbg_save_power_reg();
#endif

#ifndef CAPS_SAVE_CTX
	// Disable interrupts, we don't need / want any
	dbg_context.status_reg = SREG;
	cli();
#else
	// Disable the interrupts
	// Save the registers (we are doing a `context switch`)
	dbg_save_registers();
#endif
}

INLINE
ATTRIBUTES
void dbg_leave_debug(){

#ifdef CAPS_SAVE_POWER_REG
	dbg_restore_power_reg();
#endif

#ifdef CAPS_SAVE_CTX
	//	Context switch again
	dbg_restore_registers();
#else
	// Restore the status registers, this will enable interrupts if they were 
	//	enabled when we entered debug
	SREG = dbg_context.status_reg;
#endif
}

INLINE
ATTRIBUTES
void dbg_disable_watchdog(){
	dbg_context.watchdog = WDTCSR;
	// Disable watchdog
	WDTCSR ^= ~_BV(WDE);
}

INLINE
ATTRIBUTES
void dbg_restore_watchdog(){
	WDTCSR = dbg_context.watchdog;
}

uint8_t dbg_get_ch(){
	while(!(UCSR0A & (1<<RXC0)));
	return UDR0;
}

void dbg_put_ch(uint8_t data){
	while(!(UCSR0A & (1<<UDRE0)));
	UDR0 = data;
}

INLINE
ATTRIBUTES
void dbg_read_mem_op(){
	// 2 bytes - the address
	dbg_context.mem_op.buff.nibbles.h = dbg_get_ch();
	dbg_context.mem_op.buff.nibbles.l = dbg_get_ch();
	// Next 2 bytes the size to read
	dbg_context.mem_op.size.d8.h = dbg_get_ch();
	dbg_context.mem_op.size.d8.l = dbg_get_ch();	
}

INLINE
ATTRIBUTES
void dbg_send_info(){
	for(dbg_context.tmp_u8 = 0; dbg_context.tmp_u8 < sizeof(DEBUG_INFO); ++dbg_context.tmp_u8){
		dbg_put_ch(pgm_read_byte_near(DEBUG_INFO + dbg_context.tmp_u8));
	}
}

INLINE
ATTRIBUTES
void dbg_disable_interrupts(){
	dbg_context.status_reg &= ~_BV(INTERRUPT_FLAG_BIT);
}

#ifdef CAPS_SAVE_CTX
__attribute__ ((naked, optimize("-Os")))
#else
__attribute__ ((optimize("-Os")))
#endif
void dbg_brk() {

	dbg_enter_debug();

#ifdef CAPS_DISABLE_TIMERS
	dbg_disable_timers();
#endif

#ifdef CAPS_EXECUTE
	if (dbg_context.ctx_state & _BV(DBG_FLAG_EXECUTING)){
		dbg_leave_debug();
		asm volatile("ret \n\t");
	}
	else dbg_context.ctx_state |= _BV(DBG_FLAG_EXECUTING);
#endif

#ifdef CAPS_SINGLE_STEP
if (dbg_context.ctx_state & _BV(DBG_SINGLE_STEP_ISR)){
	dbg_context.ctx_state &= ~_BV(DBG_SINGLE_STEP_ISR);
	dbg_disable_single_step();
}
#endif

//////////////////////////////////////////////////////////////////////////
// Disable the Watchdog
	dbg_disable_watchdog();

//////////////////////////////////////////////////////////////////////////
// Notify we reached a debug point
	dbg_send_info();

	while(1){
		dbg_context.tmp_u8 = dbg_get_ch();
		if (dbg_context.tmp_u8 == DEBUG_REQ_CONTINUE){
			break;
		} else if (dbg_context.tmp_u8 == DEBUG_REQ_READ_RAM){
			dbg_read_mem_op();
			while(dbg_context.mem_op.size.d16--){
				dbg_put_ch(*dbg_context.mem_op.buff.ptr++);
			}
		}
	// Everything bellow is optional depending on how big we want the debug code to be
	//		and what functionality we want to / can have
	#ifdef CAPS_RAM_WRITE
	else if (dbg_context.tmp_u8 == DEBUG_REQ_WRITE_RAM){
		// Normally we should check to see where we are writing to
		//		If we are writing to any SAVED registers
		//			we want to update the saved data and not the live one
		//			however this can be more easily handled in the debug server than on the client
		//			this way the code on the client is small, fast and clean.
		dbg_read_mem_op();
		while(dbg_context.mem_op.size.d16--){
			*dbg_context.mem_op.buff.ptr++ = dbg_get_ch();
		}
	}
	#endif
	#ifdef CAPS_UART_HIGH_SPEED
		else if (dbg_context.tmp_u8 == DEBUG_REQ_UART_HIGH_SPEED){
			// Save the UART configuration
			dbg_save_uart_state();

			// Initialize debug communication
			dbg_init_uart();
			dbg_context.ctx_state |= _BV(DBG_FLAG_UART_HIGH_SPEED);
		}
	#endif
	#ifdef CAPS_DBG_CTX_ADDR
		// This is needed in case we don't have the elf file,
		//		and we don't know where the context is located in memory
		//		and what size it has
		else if (dbg_context.tmp_u8 == DEBUG_REQ_GET_CTX_ADDR){
			dbg_put_ch(((uint16_t)&dbg_context) >> 8);
			dbg_put_ch(((uint16_t)&dbg_context) & 0xFF);
			uint16_t size = sizeof(dbg_context);
			dbg_put_ch(size >> 8);
			dbg_put_ch(size & 0xFF);
		}
	#endif
	#ifdef CAPS_FLASH_READ
		else if (dbg_context.tmp_u8 == DEBUG_REQ_READ_FLASH){
			dbg_read_mem_op();
			while(dbg_context.mem_op.size.d16--){
				dbg_put_ch(pgm_read_byte(dbg_context.mem_op.buff.addr++));
			}
		} 
	#endif
	#ifdef CAPS_FLASH_WRITE
		else if (dbg_context.tmp_u8 == DEBUG_REQ_WRITE_FLASH){
			// nothing for now, AVR's only supports write to flash from bootloader
			//	since we are not part of it, we can't write to flash
			break;
		} 
	#endif
	#ifdef CAPS_EEPROM_READ
		else if (dbg_context.tmp_u8 == DEBUG_REQ_READ_EEPROM){
			dbg_read_mem_op();
			while(dbg_context.mem_op.size.d16--){
				dbg_put_ch(dbg_eeprom_read(dbg_context.mem_op.buff.addr++));
			}
		} 
	#endif
	#ifdef CAPS_EEPROM_WRITE
		else if (dbg_context.tmp_u8 == DEBUG_REQ_WRITE_EEPROM){
			dbg_read_mem_op();
			dbg_eeprom_write(dbg_context.mem_op.buff.addr, dbg_get_ch());
		}
	#endif
	#ifdef CAPS_SINGLE_STEP
		else if (dbg_context.tmp_u8 == DEBUG_REQ_SINGLE_STEP){
			dbg_enable_single_step();
			dbg_context.ctx_state |= _BV(DBG_WILL_SINGLE_STEP);
			// Exit the read loop and return to caller
			break;
		}
	#endif
	#ifdef CAPS_EXECUTE
		else if (dbg_context.tmp_u8 == DEBUG_REQ_EXECUTE){
			// Nothing for now, we should call methods from here
			//	TODO: Figure out the how we receive / setup parameters, 
			//	and how we are going to send data back after we do the call
			//	Also how do we handle strings sent as parameters, we need to setup a memory region and copy the string there?
			break;
		}
	#endif
	}

#ifdef CAPS_UART_HIGH_SPEED
	//	RESTORE the UART registers
	if (dbg_context.ctx_state & _BV(DBG_FLAG_UART_HIGH_SPEED)){
		dbg_restore_uart_state();
		dbg_context.ctx_state &= ~_BV(DBG_FLAG_UART_HIGH_SPEED);
	}
#endif

#ifdef CAPS_EXECUTE
	// Clear execute flag
	dbg_context.ctx_state &= ~_BV(DBG_FLAG_EXECUTING);
#endif

	// Restore the watchdog register
	dbg_restore_watchdog();

#ifdef CAPS_SINGLE_STEP
	if (dbg_context.ctx_state & _BV(DBG_SINGLE_STEP_ISR)){
		if (dbg_context.ctx_state & _BV(DBG_WILL_SINGLE_STEP)){
			// Do not clear the ISR state just return from the current ISR
			dbg_context.ctx_state &= ~_BV(DBG_WILL_SINGLE_STEP);
		}else{
			// We will not single step again
			//		clear the flag and return from ISR
			dbg_context.ctx_state &= ~_BV(DBG_SINGLE_STEP_ISR);
		}
		dbg_disable_interrupts();
		dbg_leave_debug();
		asm volatile ("reti \n\t");
	}else if (dbg_context.ctx_state & _BV(DBG_WILL_SINGLE_STEP)){
		dbg_context.ctx_state &= ~_BV(DBG_WILL_SINGLE_STEP);
		dbg_context.ctx_state |= _BV(DBG_SINGLE_STEP_ISR);
		dbg_disable_interrupts();
		dbg_leave_debug();
		asm volatile ("reti \n\t");
	}
#endif
	// Leave debug and set back the status register
	//		this will re-enable interrupts if needed
	dbg_leave_debug();
	asm volatile ("ret \n\t");
}

#ifdef CAPS_SINGLE_STEP
	ISR ( AVR8_INT_SOURCE, ISR_BLOCK ISR_NAKED ){
		asm volatile ("jmp dbg_brk");
	}
#endif

// Used for interrupt handling, instead of a reset we can enter debug and see what happened
//__attribute__((used, naked))
//void __vector_default(){
	//dbg_brk();
//}

#endif /* DEBUG_H_ */
Link to comment
  • 1 month later...
Guest Razvan Marinovici

Dupa o pauza destul de lunga (nu am mai apucat sa fac nimic la el), am reinceput.

 

Am facut public codul pe GIT. Este work in progress, asa ca va rog fara prea multe comentarii legat de structura proiectelor.

 

https://github.com/MRazvan/ASAVRSD

 

Sunt 5 proiecte

- Codul pentru Arduino

- Codul pentru Atmel Studio

- Codul pentru o aplicatie consola

- Codul pentru o aplicatie stand alone

- Codul pentru serverul de debug.

 

 

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now



×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.Terms of Use si Guidelines