AVR Instruction decoding...

Go To Last Post
34 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I recently read zbaird's "Assembly Language Programming Using The AVRButterfly. " While I didn't actually work through the experiments that Chuck provided in the book with an actual Butterfly, I did study the code and, for the most part, got the jest of the intent.

However, what reading Chuck's book did do was inspire me to play with assembly language on the AVR. I do have about 20 years experience with assembly language on the MC6802 and MC68Hc11 MCU's so, I wasn't exactly starting out from the beginning.

What I did is purchased an Ardunio kit from SmileyMicros, not for the programming features afforded with the Arduino methodology but, rather, because of the Arduino's small board size. The though is that, because of the Arduino's small size, it more easily enables me to mess around with assembly experiments while at work

To date, I have written assembly code to talk to my PC notebook over the USB communications connection. I've also written an interrupted TIMER1 handler and routines to send text and ANSI terminal codes to the terminal, allowing me to place the text anywhere on the PC's screen - using HyperTerminal, of course.

So then, having laid out the ground work, my question is this...

During one of the many debugging efforts, I found myself having trouble deciphering the instruction code and exactly what the HEX code represented.

As always, my first approach is to read the pertinent literature. So I got out the AVR Instruction Set manual - doc0856. What I found is that the instructions seem to be split across the two bytes of each word. As and example:

; Early on in the program I assign a general working register as:
.def acca = r20
;
;
;
; Later in the program:
0000a6 e478 ldi USART0_Data, 'H'

And the assembly users manual description of the Load Register Immediate instruction:

Description: 
Loads an 8 bit constant directly to register 16 to 31. 

Operation:
          Rd <- K

 Syntax:           Operands:            Program  Counter:

LDI Rd,K    16 ≤ d ≤ 31, 0 ≤ K ≤ 255       PC <- PC + 1
 

16-bit Opcode:    1110 KKKK dddd KKKK

Where in the high byte:
1110 is the actual instruction and the high niggle (KKKK) is the upper nibble of the immediate data

and in the second byte:
The high nibble holds dddd, the destination register, followed by the lower nibble of the immediate data (KKKK) in the low nibble of the second byte.

0000a6 e478 ldi USART0_Data, 'H'

In fact, 0x48 (the second and fourth nibbles in the instruction) does equal 'H'

This seems odd to me. Every other MCU I've worked with, the entire instruction filled the entire byte while the immediate data filled the entire next byte.

Is this scheme particular to the AVR or, is this an inherent difference between Von Neumann and Harvard architectures?

JS, you should be proud of me!!!

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

EDIT TO ADD:
I've updated the code listing to show the most recent changes and additions.

I removed the confusing comments related to the interrupt vector handling table.

In addition, I added code to "Lock Down " the MCU, in the event that interrupt vector space is violated.

For those who might be curious or, for those who might want to mess around with HyperTerminal and ANSI ESC codes, I offer the following code listing...

First, my goal was to communicate with HyperTerminal - easy enough.
My second goal was to use no memory data space - only the 32 registers.
And the third was that I only wanted to "push " and "pop " only where absolutely needed - specifically, in the Timer1 ISR handler.

The Timer1 interrupt handler toggles the LED on the Arduino board.

In addition, there are several time delays that I've been experimenting with in the Timer1 interrupt handler. In fact, the concept is used in another project, but using 'C' code, hense, the names of the delay counters that I used.

Some may think these goals flawed but, they are what they are.

Before listing the assembly code, I present a few macros. These were created in order NOT to have to type in the same sequence over and over. In addition, they seem to make the program a bit more reader friendly.

; Macros.h

/***********************--- Macro definitions go here ---************************/

.macro SetDelay
			; Set delay value for the selected delay counter
			ldi acca, low(@0) 					; Load Z register low
			sts	@1, acca

			ldi acca, high(@0+1)					; Load Z register high
			sts @1+1, accb						;Store data in address @1
.endmacro

.macro SetAttributes

; fc = foreground color, bc = background color, m = mode
;	   				ESC, "[fc;bc;im", NULL
;		Example:		ESC, "[37;40;1m", NULL
;				normal mode, fc = white, bc = black, high intensity

			ldi		ZL, low(@0<<1)		; Get "ClearScreen" message
			ldi		ZH, high(@0<<1)
			rcall	USART0_PutString	
.endmacro

.macro GoTo_XY
			ldi		X_Pos, @0
			ldi		Y_Pos, @1
			rcall	USART0_PutCursor
.endmacro

You will notice that I assigned all used registers to a label. My reasoning for this is that, names have more meaning to me then numbers and, as such, it's less likely that I'll get names confused, then is the case with numbers.

I chose "acca " and "accb " due to my many years use of the MC68HC11.

I feel that the bulk of the program is adaquately documented - save the "Init " and "MAIN " sections. But even still, there is enough information to decipher the intent.

Your comments are welcome...

/*------------------------------------------------------------------------------*/
/**************************** -- USART_COMMS.asm -- *****************************/
/*------------------------------------------------------------------------------*/
/*
	This program was written by:
								Carl W Livingston
								3265 Echo Valley lane
								Bethel, Ohio 45106
								(513) 403-2182
								microcarl@roadrunner.com
								AVRFreak member: microcarl
*/
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/******************** --- Include Selection / Assignments --- *******************/
/*------------------------------------------------------------------------------*/

; Include the AVR definitions in the AVRStudio part library

.nolist
;.include	
;.include	
.include	
;.include	
.list

.include	"macros.h"
.listmac

; Clock Frequency: Internal 18.432000MHz

/*------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/***********************--- Global equate assignments ---************************/
/*------------------------------------------------------------------------------*/

; ANSI escape codes
.equ NULL				= '\0'			; NULL
.equ CR					= '\r'			; Carrage return
.equ LF					= '\n'			; Line feed
.equ ESC				= 0x1B			; Escape
.equ CSI				= 0x9B			; Alternate escape
.equ Ctrl_D				= 0x04
.equ Ctrl_F				= 0x06

; BAUD rate settings for Fosc = 18.432000Mhz
.equ _2400				= 0x01DF		; 479	decimal
.equ _4800				= 0x00EF		; 239	decimal
.equ _9600				= 0x0077		; 119	decimal
.equ _14_4K				= 0x004F		; 79	decimal
.equ _19_2K				= 0x0033		; 51	decimal
.equ _28_8K				= 0x0027		; 39	decimal
.equ _38_4K				= 0x001D		; 29	decimal
.equ _57_6K				= 0x0013		; 19	decimal
.equ _76_8K				= 0x000E		; 14	decimal
.equ _115_2K			= 0x0009		; 9		decimal
.equ _230_4K			= 0x0004		; 4		decimal


.equ SysDlyDone			= 0				; Bit position 1, -> 0x01
.equ LcdDlyDone			= 1				; Bit position 2, -> 0x02
.equ KbdDlyDone			= 2				; Bit position 3, -> 0x04

.equ SentinelEnable		= 7				; Bit position 7, -> 0x80
.equ SentinelTime		= 100			; 100mS
.equ SysDlyTime			= 100			; 100mS
.equ LcdDlyTime			= 100			; 100mS
.equ KbdDlyTime			= 100			; 100mS

.equ LED				= PORTB5		; Pin 13 on the Arduino board
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/************************* --- Set Up Memory Vectors --- ************************/
/*------------------------------------------------------------------------------*/

.equ INT_VECTORS_START = 0x0000

;NOTE:
	; INT_VECTORS_SIZE is defined in the  definition file

.equ PROGRAM_CODE_START = INT_VECTORS_SIZE ; Find program code starting address
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/********************* --- Global register assignments --- **********************/
/*------------------------------------------------------------------------------*/

.def SysDlyCntrL		= r10
.def SysDlyCntrH		= r11

.def LcdDlyCntrL		= r12
.def LcdDlyCntrH		= r13

.def KbdDlyCntrL		= r14
.def KbdDlyCntrH		= r15

.def SentinelCtnr_low	= r16
.def SentinelCtnr_high	= r17

.def DelayStatus		= r18			; Global timer status - up to 8 delays

.def X_Pos				= r20			; HyperTerminal 'X' position
.def Y_Pos				= r21			; HyperTerminal 'Y' position

.def USART0_Status		= r22
.def USART0_Data		= r23

.def acca				= r24			; General purpose register/accumulator
.def accb				= r25			; General purpose register/accumulator
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/******************** --- Global data space assignments --- *********************/
/*------------------------------------------------------------------------------*/

.dseg
.org 0x100

//	Nothing assigned yet.  RAM FREE !!!
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/******************** --- Interrupt vector address table --- ********************/
/*------------------------------------------------------------------------------*/

.cseg

.org INT_VECTORS_START

jmp MCU_INIT			; RESET interrupt
jmp IV_INT0				; External Interrupt Request 0
jmp IV_INT1				; External Interrupt Request 1
jmp IV_PCI0				; Pin Change Interrupt Request 0
jmp IV_PCI1				; Pin Change Interrupt Request 0
jmp IV_PCI2				; Pin Change Interrupt Request 1
jmp IV_WDT				; Watchdog Time-out Interrupt
jmp IV_OC2A				; Timer/Counter2 Compare Match A
jmp IV_OC2B				; Timer/Counter2 Compare Match A
jmp IV_OVF2				; Timer/Counter2 Overflow
jmp IV_ICP1				; Timer/Counter1 Capture Event
jmp timer1_compa_isr	; Timer/Counter1 Compare Match A
jmp IV_OC1B				; Timer/Counter1 Compare Match B
jmp IV_OVF1				; Timer/Counter1 Overflow
jmp IV_OC0A				; TimerCounter0 Compare Match A
jmp IV_OC0B				; TimerCounter0 Compare Match B
jmp IV_OVF0				; Timer/Couner0 Overflow
jmp IV_SPI				; SPI Serial Transfer Complete
jmp IV_URXC				; USART Rx Complete
jmp IV_UDRE				; USART, Data Register Empty
jmp IV_UTXC				; USART Tx Complete
jmp IV_ADCC				; ADC Conversion Complete
jmp IV_ERDY				; EEPROM Ready
jmp IV_ACI				; Analog Comparator
jmp IV_TWI				; Two-wire Serial Interface
jmp IV_SPMR				; Store Program Memory Read
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/********************** --- MCU hardware initialization --- *********************/
/*------------------------------------------------------------------------------*/
; Start building code imediately after the interrupt vector address table

.org PROGRAM_CODE_START
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/********************** --- Start of Program data space --- *********************/
/*------------------------------------------------------------------------------*/

; Microcontroller initialization sequence

MCU_INIT:
			cli								; Disable all global interrupts 

			; Set stack pointer
			ldi		acca, low(RAMEND)		
			out		SPL, acca 
			ldi		acca, high(RAMEND) 
			out		SPH, acca

			; Set PORTB5 to output for Arduino project board LED, pin 13
			ldi		acca, (1<<LED)
			out		DDRB, acca

			; Set up the comms consol
			rcall	USART0_Init				; Set up USART0 for 230.4K Baud, 8N1
			SetAttributes Text_Normal		
			SetAttributes Cursor_Off
			SetAttributes BakGrnd_Black
			SetAttributes Text_Brite
			SetAttributes Clr_Screen
			SetAttributes Cursor_Off

			; Get the "Hello" message
			SetAttributes Text_Magenta
			SetAttributes Cursor_Home
			ldi		ZL, low(Msg_Hello<<1)	
			ldi		ZH, high(Msg_Hello<<1)
			rcall	USART0_PutString

			; Get the "Name" message
			SetAttributes Text_Cyan
			ldi		ZL, low(Msg_Name<<1)	
			ldi		ZH, high(Msg_Name<<1)
			rcall	USART0_PutString

			; Get the LED Sentinel Control
			ldi		acca, low(SentinelTime) ; Load SentinelCtnr
			mov		SentinelCtnr_low, acca
			ldi		acca, high(SentinelTime)
			mov		SentinelCtnr_high, acca	

			; Set the system delay time
			ldi		acca, low(SysDlyTime)	; Load system delay counter
			mov		SysDlyCntrL, acca
			ldi		acca, high(SysDlyTime)
			mov		SysDlyCntrH, acca

			; Set the LCD delay time
			ldi		acca, low(LcdDlyTime)	; Load LCD delay counter
			mov		LcdDlyCntrL, acca
			ldi		acca, high(LcdDlyTime)
			mov		LcdDlyCntrH, acca

			; Set the Keyboard delay time
			ldi		acca, low(KbdDlyTime)	; Load keyboard delay counter
			mov		KbdDlyCntrL, acca
			ldi		acca, high(KbdDlyTime)
			mov		KbdDlyCntrH, acca

			rcall	Timer1_Init				; Set up Timer1 for CTC mode -> no output
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/*************************** --- Main Program Loop --- **************************/
/*------------------------------------------------------------------------------*/

; Main program loop

MAIN:
			rcall USART0_GetChar			; Check for character in RX_Data

			cpi 	USART0_Data, 's'		; Turn on sentinel timer ?
			breq	SNTL_ON
			rjmp	NextCheck

SNTL_ON:	ori DelayStatus, (1<<SentinelEnable)
			SetAttributes Text_Green
			GoTo_XY	33, 12

			ldi		ZL, low(Msg_SntlOn<<1)	; Get "Sentinel On" message
			ldi		ZH, high(Msg_SntlOn<<1)
			rcall	USART0_PutString
			rjmp	MAIN

NextCheck:	
			cpi 	USART0_Data, 'c'		; Turn off sentinel timer?
			breq	SNTL_OFF
			rjmp	MAIN

SNTL_OFF:	andi DelayStatus, ~(1<<SentinelEnable)
			SetAttributes Text_Yellow
			GoTo_XY 33, 12

			ldi		ZL, low(Msg_SntlOff<<1)	; Get "Sentinel Off" message
			ldi		ZH, high(Msg_SntlOff<<1)
			rcall	USART0_PutString

			cbi		PORTB, LED				; Turn off the sentinel LED

			ldi ZL, low(SentinelTime)		; Load 1000mS into Sentenel
			ldi ZH, high(SentinelTime)

			mov		SentinelCtnr_low, ZL	; Save to SystemDelayCounter
			mov		SentinelCtnr_high, ZH

			rjmp MAIN
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/************************* --- USART Initialization --- *************************/
/*------------------------------------------------------------------------------*/

; USART0 initialization

; Registers used:
;					acca				->	r24
;
; On Entry:
;					No data passed

; On exit:
;					No data passed


USART0_Init:
			; Set USART0 BAUD rate
			ldi		acca, low(_230_4K)			; BAUD rate = 230.4K @ 18.432 Mhz clock
			sts		UBRR0L, acca			; Store in low BAUD rate register
			ldi		acca, high(_230_4K)		
			sts		UBRR0H, acca			; Clear high BAUD rate register
	
			; Enable the USART transmitter and receiver
			ldi		acca, (1<<RXEN0) | (1<<TXEN0)
			sts		UCSR0B, acca
		
			; Set comms parameters ---> BITS = 8, PARITY = none, STOP = 1
			ldi		acca, (1<<UCSZ01) | (1<<UCSZ00)
			sts		UCSR0C, acca

			ret
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/************************ --- USART0 Output Handler --- *************************/
/*------------------------------------------------------------------------------*/

; USART0 character transmit

; Registers used:
;					USART0_Status		->	r22
;					USART0_Data			->	r23

; On Entry:
;					USART0_Data holds byte to be transmitted

; On exit:
;					No data passed

USART0_PutChar:

TX_NotRdy:	
			lds		USART0_Status, UCSR0A	; Get USART transmit status
			sbrs	USART0_Status, UDRE0	; Check for transmit data register ready
			rjmp	TX_NotRdy				; Transmitter isn't ready, try again
			sts		UDR0, USART0_Data		; Transmitter is ready, send the character

			ret								; Return to caller 
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/************************* --- USART0 Input Handler --- *************************/
/*------------------------------------------------------------------------------*/

; USART0 character receive

; Registers used:
;					USART0_Status		->	r22
;					USART0_Data			->	r23

; On Entry:
;					No data passed

; On exit:
;					USART0_Data holds received byte

USART0_GetChar:

RX_NotRdy:	
			lds		USART0_Status, UCSR0A	; Get USART receive status
			sbrs	USART0_Status, RXC0		; Check for receive character complete
			rjmp	RX_NotRdy				; No character has been received, so exit
			lds		USART0_Data, UDR0		; A character is in the USART data register

			ret								; Return to caller
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/************************ --- Consol Cursor Handler --- *************************/
/*------------------------------------------------------------------------------*/

; Function name:						->	USART0_PutCursor

; Moves the consol cursor to position 'X', 'Y'
; X being rows (top to bottom) and Y being collums (left to rignt)

; Registers used:
;					acca				->	r24

;					USART_Data			->	r23

;					X_Pos				->	r20
;					Y_Pos				->	r21

;					USART0_Putchar		->	Called routine	
;					USART0_ByteToAscii	->	Called routine	

; On entry:
;					Dara to be sent added to string is located in X_Pos and Y_Pos

;On exit:
					;No data passed

USART0_PutCursor:
			ldi		USART0_Data, ESC
			rcall	USART0_PutChar

			ldi		USART0_Data, '['
			rcall	USART0_PutChar

			mov		acca, Y_Pos
			rcall	USART0_ByteToAscii

			ldi		USART0_Data, ';'
			rcall	USART0_PutChar

			mov		acca, X_Pos
			rcall	USART0_ByteToAscii


			ldi		USART0_Data, 'H'
			rcall	USART0_PutChar

			ret
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/*********************** --- Byte to ASCII Conversion --- ***********************/
/*------------------------------------------------------------------------------*/

; Function name:						->	USART0_ByteToAscii:

; Convert single byte to 3 ASCII digits and send to terminal

; Registers used:
;					acca				->	r24
;					accb 				->	r25

;					USART0_Data			->	r23

;					USART0_PutChar		->	Called routine	

; On entry:
;					Number to be converted to ASCII is in acca

; On exit:
;					No data passed

; Example:

; IF:
;		ASCII '0' = 0x30 and Hundreds = 1, then 0x30 + 1 = 0x31 or, ASCII '1'
; AND IF:
;		ASCII '0' = 0x30 and Tens = 3, then 0x30 + 3 = 0x33 or, ASCII '3'
; AND IF:
;		ASCII '0' = 0x30 and Units = 5, then 0x30 + 5 = 0x35 or, ASCII '5'

; THEN:
;		The ASCII string sent to the consol in the above example represents 135 decimal

USART0_ByteToAscii:
			ldi		USART0_Data, '0'		// ASCII 0 = 0x30
Hundreds:
			cpi		acca, 100				// Can 100 be subtracted and not go negative?
			brlt	Done100
			inc		USART0_Data				// Increment Most significant digit
			ldi		accb, 100				
			sub		acca, accb				// Subtract 100 from incoming number
			brge	Hundreds
Done100:	
			rcall	USART0_PutChar			// Send hundreds digit to the consol
    
TensCheck:	
			ldi		USART0_Data, '0'		// ASCII 0 = 0x30
Tens:
			cpi		acca, 10				// Can 10 be subtracted and not go negative?
			brlt	Done10
			inc		USART0_Data				// Increment middle digit
			ldi		accb, 10					
			sub		acca, accb				// Subtract 10 from incoming number
			brge	Tens
Done10:
			rcall	USART0_PutChar			// Send tens digit to the consol

Units:
			ldi		accb,	'0'				// ASCII 0 = 0x30
			add		acca, accb				// add ASCII 0 and Units    
			mov		USART0_Data, acca
			rcall	USART0_PutChar			// Send Units digit to the consol
			ret		
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/************************* --- USART0 String Handler --- ************************/
/*------------------------------------------------------------------------------*/

; Function name:						->	USART0_PutString

; Send a string of NULL terminated bytes to USART0

; Registers used:
;					USART0_Data			->	r23
;					Z	 				->	r30:r31:

;					USART0_PutChar		->	Called routine	


; On entry:
;					Z holds address of data string to be sent to the consol

; On exit:
;					No data passed

USART0_PutString:
			lpm		USART0_Data, Z+
			cpi		USART0_Data, '\0'
			breq	MsgExit
			rcall	USART0_PutChar
			rjmp	USART0_PutString
MsgExit:	ret
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/************************* --- Timer1 Initialization --- ************************/
/*------------------------------------------------------------------------------*/

; Function name:			Timer1_Init

; Initialize Timer1 for CTC mode

; Registers used:
;							acca		->	r24

; On entry:
;					Number to be converted to ASCII is in acca

; On exit:
;					No data passed

; TIMER1 Initialization Parameters:
;									Osc = 18.432000 MHz
;									Prescale = 64
;									WGM: 0) Normal, TOP=0xFFFF -> CTC mode
;									Desired value: 1mSec
;									Actual value:  0.997mSec (0.3%)

Timer1_Init:
			lds acca, TIMSK1
			cbr acca, (1<<OCIE1A)							; Disable OCR1A interrupt
			sts	TIMSK1, acca

			ldi	acca, (1<<COM1A1) | (1<<COM1A0)				; Set Timer1 to no output
			sts TCCR1A, acca

			ldi	acca, (1<<CS11) | (1<<CS10) | (1<<WGM12)	; /64, CTC mode
			sts	TCCR1B, acca

			ldi acca, 0x01
			sts	OCR1AH, acca								; Set OCR1A to 1mS
			ldi acca, 0x1F
 			sts	OCR1AL,acca

			ldi acca, 0xFE									; Clear timer1 counter
			sts TCNT1H, acca
			ldi acca, 0xE1
			sts TCNT1L, acca

			; Disable all delays
			andi DelayStatus, ~(1<<SentinelEnable)
			ori  DelayStatus, (1<<SysDlyDone) | (1<<LcdDlyDone) | (1<<KbdDlyDone)

			SetAttributes Text_Yellow
			GoTo_XY 33, 12
			ldi		ZL, low(Msg_SntlOff<<1)	; Get "Sentinel Off" message
			ldi		ZH, high(Msg_SntlOff<<1)
			rcall	USART0_PutString

			lds acca, TIMSK1								; Enablet OCR1A interrupt
			sbr acca, (1<<OCIE1A)
			sts	TIMSK1, acca

			sei												; Enable global interrupts
			ret 
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/
/************************ --- Timer 1 Interrupt Service --- *********************/
/*------------------------------------------------------------------------------*/

; Function name:							->	timer1_compa_isr

; Timer1 interrupt handler

; Registers used:
;					SysDlyCntrH				->	r10
;					SysDlyCntrL				->	r11

;					LcdDlyCntrH				->	r12
;					LcdDlyCntrL				->	r13

;					KbdDlyCntrH				->	r14
;					KbdDlyCntrL				->	r15

;					SentinelCtnr_low		->	r16
;					SentinelCtnr_High		->	r17

;					DelayStatus				->	r18

;					acca					->	r24
;					accb					->	r25

;					ZL						->	r30
;					ZH						->	r31

; On entry:
;					acca, accb, ZL and ZH are preserved

; On exit:
;					All registers listed above are modified

; TIMER1 Initialization Parameters:
;									Osc = 18.432000 MHz
;									Prescale = 64
;									WGM: 0) Normal, TOP=0xFFFF -> CTC mode
;									Desired value: 1mSec
;									Actual value:  0.997mSec (0.3%)

timer1_compa_isr:
			push ZL
			push ZH
			push acca
			push accb
			sei

		/*----------------------------------------------------------------------*/

SysDly:		
			sbrs	DelayStatus, SysDlyDone			; System delay done set?
			rjmp	LcdDly							; no delay is currently active

			mov		ZH, SysDlyCntrH					; Get the System Delay Counter
			mov		ZL, SysDlyCntrL
			sbiw	ZH:ZL, 1						; Decrement System Delay Counter by 1
			brne	Exit_SysDly						; System Delay Counter zero?
			ori		DelayStatus, (1<<SysDlyDone)	; Yes! Set the System delay done

Exit_SysDly:	
			mov		SysDlyCntrH, ZH					; Save the System Delay Counter
			mov		SysDlyCntrL, ZL

		/*----------------------------------------------------------------------*/

LcdDly:		
			sbrs	DelayStatus, LcdDlyDone			; Lcd delay done flag set?
			rjmp	KbdDly							; no delay is currently active

			mov		ZH, LcdDlyCntrH					; Get the Lcd Delay Counter
			mov		ZL, LcdDlyCntrL
			sbiw	ZH:ZL, 1						; Decrement Lcd Delay Counter by 1
			brne	Exit_LcdDly						; Lcd Delay Counter zero?
			ori		DelayStatus, (1<<LcdDlyDone)	; Yes! Set the Lcd delay done

Exit_LcdDly: 
			mov		LcdDlyCntrH, ZH					; Save the Lcd Delay Counter
			mov		LcdDlyCntrL, ZL

		/*----------------------------------------------------------------------*/

KbdDly:
			sbrs	DelayStatus, KbdDlyDone	; Kbd delay done flag set?
			rjmp	Sentinel						; no delay is currently active

			mov		ZH, KbdDlyCntrH					; Get the Kbd Delay Counter
			mov		ZL, KbdDlyCntrL
			sbiw	ZH:ZL, 1						; Decrement Kbd Delay Counter by 1
			brne	Exit_KbdDly						; Lcd Delay Counter zero?
			ori		DelayStatus, (1<<KbdDlyDone)	; Yes! Set the Kbd delay done

Exit_KbdDly: 
			mov		KbdDlyCntrH, ZH					; Save the Kbd Delay Counter
			mov		KbdDlyCntrL, ZL

		/*----------------------------------------------------------------------*/

; LED Sentinel Control			

Sentinel:
			sbrs	DelayStatus, SentinelEnable
			rjmp	Exit_OC1

			mov		ZL, SentinelCtnr_low		; Decrement SentenelCounter
			mov		ZH, SentinelCtnr_high
			sbiw	ZH:ZL, 1
			brne	Sntl_Exit

			in		acca, PORTB					; Toggle PORTB5
			ldi		accb, (1<<LED)
			eor		acca, accb
			out		PORTB, acca

			ldi		ZL, low(SentinelTime)		; Get Sentenel time interval
			ldi		ZH, high(SentinelTime)		; Sentinal is an LED on PORTB5

Sntl_Exit:	
			mov		SentinelCtnr_low, ZL		; Save the SystemDelayCounter
			mov		SentinelCtnr_high, ZH

Exit_OC1:	
			pop accb
			pop acca
			pop	ZH
			pop ZL
			reti
/*------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------*/ 
/******************* --- Errant Interrupt Vector Handling --- *******************/ 
/*------------------------------------------------------------------------------*/

; The following are the addresses of unused interrupt handlers that simply fall
; through to a code sequence called Fail_Safe_Halt.  In the event of an errant
; interrupt vector access, the tight loop ; prevents further random program
; execution.  If enabled and in the likelihood that the WDT catches the program
; runaway first, it should cause a jump here, after shutting itself down.

IV_INT0:				; External Interrupt Request 0
IV_INT1:				; External Interrupt Request 1
IV_PCI0:				; Pin Change Interrupt Request 0
IV_PCI1:				; Pin Change Interrupt Request 0
IV_PCI2:				; Pin Change Interrupt Request 1
IV_WDT:					; Watchdog Time-out Interrupt
IV_OC2A:				; Timer/Counter2 Compare Match A
IV_OC2B:				; Timer/Counter2 Compare Match A
IV_OVF2:				; Timer/Counter2 Overflow
IV_ICP1:				; Timer/Counter1 Capture Event
IV_OC1B:				; Timer/Counter1 Compare Match B
IV_OVF1:				; Timer/Counter1 Overflow
IV_OC0A:				; TimerCounter0 Compare Match A
IV_OC0B:				; TimerCounter0 Compare Match B
IV_OVF0:				; Timer/Couner0 Overflow
IV_SPI:					; SPI Serial Transfer Complete
IV_URXC:				; USART Rx Complete
IV_UDRE:				; USART, Data Register Empty
IV_UTXC:				; USART Tx Complete
IV_ADCC:				; ADC Conversion Complete
IV_ERDY:				; EEPROM Ready
IV_ACI:					; Analog Comparator
IV_TWI:					; Two-wire Serial Interface
IV_SPMR:				; Store Program Memory Read

Fail_Safe_Halt:
				cli					; Turn off all interrupts, including the Sentinel timer
				cbi	PORTB, LED		; Turn off the sentinel LED
Fail_Safe_Loop:
				jmp	Fail_Safe_Loop	; Something stinks in Denmark!  Shut it down!!! 


/*------------------------------------------------------------------------------*/
/********************** --- Start of text messaging space --- *********************/
/*------------------------------------------------------------------------------*/
;Note:
	;The extra NULL zeros are for padding because the assembler complains

/** System Messages **/
Msg_Hello:
.db "Hello!", CR, LF, LF, NULL
Msg_Name:
.db "Carl W. Livingston", CR, LF, LF, NULL
Msg_SntlOn:
.db "Sentinel On ", CR, LF, NULL, 0
Msg_SntlOff:
.db "Sentinel Off", CR, LF, NULL, 0

/** ANSII Control Strings **/
Clr_Screen:
.db ESC, "[2J", NULL, 0
Cursor_Home:
.db ESC, "[H", NULL
Cursor_On:
.db	ESC, "?25h", NULL
Cursor_Off:
.db ESC, "?25l", NULL
Cursor_Save:
.db ESC, "[s", NULL
Cursor_Restore:
.db ESC, "[u", NULL
Cursor_Hide:
.db ESC, "[fm", NULL, 0

Text_Hide:
.db ESC, "[8m", NULL, 0	
Text_Brite:
.db ESC, "[1m", NULL, 0
Text_Normal:
.db ESC, "[0m", NULL, 0
Text_Blink:
.db ESC, "[5m", NULL, 0
Text_UnderLine:
.db ESC, "[4m", NULL, 0
Text_Reverse:
.db ESC, "[7m", NULL, 0

/** Foreground Text Color Control Strings **/
Text_Black:
.db ESC, "[30m", NULL
Text_Red:
.db ESC, "[31m", NULL
Text_Green:
.db ESC, "[32m", NULL
Text_Yellow:
.db ESC, "[33m", NULL
Text_Blue:
.db ESC, "[34m", NULL
Text_Magenta:
.db ESC, "[35m", NULL
Text_Cyan:
.db ESC, "[36m", NULL
Text_White:
.db ESC, "[37m", NULL

/** Background Text Color Control Strings **/
BakGrnd_Black:
.db ESC, "[40m", NULL
BakGrnd_Red:
.db ESC, "[41m", NULL
BakGrnd_Green:
.db ESC, "[42m", NULL
BakGrnd_Yellow:
.db ESC, "[43m", NULL
BakGrnd_Blue:
.db ESC, "[44m", NULL
BakGrnd_Magenta:
.db ESC, "[45m", NULL
BakGrnd_Cyan:
.db ESC, "[46m", NULL
BakGrnd_White:
.db ESC, "[47m", NULL
/*------------------------------------------------------------------------------*/

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Last Edited: Wed. Oct 13, 2010 - 05:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
JS, you should be proud of me!!!
Very proud of you!! Anything to stop you shooting people :lol:
Quote:
I chose "acca " and "accb "
You are better served by R24 and R25 for this purpose as you can do some 16 bits stuff with them, similar to the HC11 D register. ie ADIW, SBIW.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

js wrote:
Very proud of you!! Anything to stop you shooting people :lol:

Now who said I was shooting people? Though, you'd never really know!

js wrote:
microcarl wrote:
I chose "acca " and "accb... "
You are better served by R24 and R25 for this purpose as you can do some 16 bits stuff with them, similar to the HC11 D register. ie ADIW, SBIW.

Thanks, JC. I'll make the changes.

So looking the expanded use of r24 and r25, the only two extended instructions that you can use on them is ADIW and SBIW. I suppose you can't pre increment and post decrement r24 and r25, either?

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Last Edited: Tue. Oct 12, 2010 - 02:30 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Carl,

I, too, am impressed! Nice job! You are YEARS ahead of me!

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
This seems odd to me. Every other MCU I've worked with, the entire instruction filled the entire byte while the immediate data filled the entire next byte.

Is this scheme particular to the AVR or, is this an inherent difference between Von Neumann and Harvard architectures?

Probably nothing to do with harvard/von neumann but more likely to do with the core architecture of the AVR. It may have been that they could save some gates doing it that way. I think this is a question that could be put to the guys that designed the AVR.
Aren't we lucky we have assemblers to hide this from us?

The HC11 et al were nice cpus to program in assembler.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
Quote:
This seems odd to me. Every other MCU I've worked with, the entire instruction filled the entire byte while the immediate data filled the entire next byte.

Is this scheme particular to the AVR or, is this an inherent difference between Von Neumann and Harvard architectures?

Probably nothing to do with harvard/von neumann but more likely to do with the core architecture of the AVR. It may have been that they could save some gates doing it that way. I think this is a question that could be put to the guys that designed the AVR.
Aren't we lucky we have assemblers to hide this from us?

The HC11 et al were nice cpus to program in assembler.

Thanks, Kartman.

When I first moved over to the AVR, I wanted to play with assembly. But when I started looking at the complexities (read, rules) of the different instructions and how much more complex they were then with the MC68HC11, I decided to back off and stick with 'C', which hides all of the nuances of dealing with the different memory spaces available.

To be sure, I'm still struggling... But it's getting easier.

One thing I am realizing is that, with good planning, very little RAM memory space is required. I figure that the only real need I’ll have for RAM data space is when I build changing numeric data, such as when converting multi-byte integers to an ASCII representations that will then get sent out to the consol.

Also, committing to register assignments for frequently used variables, such as “USART0_Status “ or “SysDlyCntr “ (both frequently accessed in my experimentation), the code is a lot more efficient, then that of pulling a memory location into a register, performing the required operation on the data, and then sending that modified data back out to RAM space, just modify the dedicated register and be done with it - one instruction - as opposed to three instructions, when using RAM data space

Anyway, thanks!

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Last Edited: Tue. Oct 12, 2010 - 03:16 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
the code is a lot more efficient

The last time Carl was seen, he was wearing red and climbing over the fence into the C bulls' pen.

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

zbaird wrote:
Quote:
the code is a lot more efficient

The last time Carl was seen, he was wearing red and climbing over the fence into the C bulls' pen.

Actually, you should know, as you were the last AVRFreak to actually see me...

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

one of the goals of the RISC architecture was to minimise memory accesses as they are expensive in terms of time (load/store). Therefore you have a number of registers to store intermediate values. Then comes the problem of managing what you keep in the registers and what you keep out in ram! The use of 'volatile' in C is evidence of this. In a HC11, you might be able to keep a pointer or 2 in the X,Y regs but A was vitually guaranteed to get trashed and B less so, so the C compiler was compelled to always store stuff in ram.

With the AVR, I use the C compiler's rules for register allocation. Makes things easy if I need to combine asm with C and is at least some concrete rules to work from.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
...use the C compiler's rules for register allocation.

Kartman,

Are these compiler rules specific to a particular compiler, meaning, every compiler manufacture uses different conventions or, are these rules generally universal or compliant across complier manufacturers?

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
you can't pre increment and post decrement r24 and r25, either?
No, but you can with the other top 6 registers making up X, Y and Z.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Every other MCU I've worked with, the entire instruction filled the entire byte while the immediate data filled the entire next byte.
Those MCU's likely had 8 bit instructions. They need to load the opcode and decipher it to see if there are more bytes that need to be loaded for that operation. The AVR has 16 bit instructions all loaded in the same clock cycle, so it really doesn't matter where the bits of the immediate value are. If you look at other opcodes, bits 4-8 (or a subset thereof) are always used for the destination register when there is one, and 0-3 and 9 are used for the source register.
Quote:
Are these compiler rules specific to a particular compiler
Yes, though there will undoubtedly be similarities since the top 6 are the pointer registers and the bottom 8 can't use the immediate opcodes. This limits the compiler writer's options somewhat.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
the bottom 8 can't use the immediate opcodes
Make it bottom 16. :)

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

He lives in Canada. It's the exchange rate.

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well I went the other way around - AVR first, with assembly and then C. Then comes along HC11, with C and then assembly.

In my opinion the opcode bit order is irrelevant as long as it works. If I have to disassemble something, well there are disassemblers in AVR Studio for example so I don't have to look at the hex bytes and decipher their meaning.

And there is a big difference between RISC machines with 16-bit opcode bus and CISC machines with 8-bit bus for code and data.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Carl,

Just to add that rather than pouring through the opcode manual to find the opcode bit patterns I always find the list on this page clear and concise:

http://sourceware.org/binutils/d...

Quote:

     1001010010001000   clc
     1001010011011000   clh
     1001010011111000   cli
     1001010010101000   cln
     1001010011001000   cls
     1001010011101000   clt
     1001010010111000   clv
     1001010010011000   clz
     1001010000001000   sec
     1001010001011000   seh
     1001010001111000   sei
     1001010000101000   sen
     1001010001001000   ses
     1001010001101000   set
     1001010000111000   sev
     1001010000011000   sez
     100101001SSS1000   bclr    S
     100101000SSS1000   bset    S
     1001010100001001   icall
     1001010000001001   ijmp
     1001010111001000   lpm     ?
     1001000ddddd010+   lpm     r,z
     1001010111011000   elpm    ?
     1001000ddddd011+   elpm    r,z
     0000000000000000   nop
     1001010100001000   ret
     1001010100011000   reti
     1001010110001000   sleep
     1001010110011000   break
     1001010110101000   wdr
     1001010111101000   spm
     000111rdddddrrrr   adc     r,r
     000011rdddddrrrr   add     r,r
     001000rdddddrrrr   and     r,r
     000101rdddddrrrr   cp      r,r
     000001rdddddrrrr   cpc     r,r
     000100rdddddrrrr   cpse    r,r
     001001rdddddrrrr   eor     r,r
     001011rdddddrrrr   mov     r,r
     100111rdddddrrrr   mul     r,r
     001010rdddddrrrr   or      r,r
     000010rdddddrrrr   sbc     r,r
     000110rdddddrrrr   sub     r,r
     001001rdddddrrrr   clr     r
     000011rdddddrrrr   lsl     r
     000111rdddddrrrr   rol     r
     001000rdddddrrrr   tst     r
     0111KKKKddddKKKK   andi    d,M
     0111KKKKddddKKKK   cbr     d,n
     1110KKKKddddKKKK   ldi     d,M
     11101111dddd1111   ser     d
     0110KKKKddddKKKK   ori     d,M
     0110KKKKddddKKKK   sbr     d,M
     0011KKKKddddKKKK   cpi     d,M
     0100KKKKddddKKKK   sbci    d,M
     0101KKKKddddKKKK   subi    d,M
     1111110rrrrr0sss   sbrc    r,s
     1111111rrrrr0sss   sbrs    r,s
     1111100ddddd0sss   bld     r,s
     1111101ddddd0sss   bst     r,s
     10110PPdddddPPPP   in      r,P
     10111PPrrrrrPPPP   out     P,r
     10010110KKddKKKK   adiw    w,K
     10010111KKddKKKK   sbiw    w,K
     10011000pppppsss   cbi     p,s
     10011010pppppsss   sbi     p,s
     10011001pppppsss   sbic    p,s
     10011011pppppsss   sbis    p,s
     111101lllllll000   brcc    l
     111100lllllll000   brcs    l
     111100lllllll001   breq    l
     111101lllllll100   brge    l
     111101lllllll101   brhc    l
     111100lllllll101   brhs    l
     111101lllllll111   brid    l
     111100lllllll111   brie    l
     111100lllllll000   brlo    l
     111100lllllll100   brlt    l
     111100lllllll010   brmi    l
     111101lllllll001   brne    l
     111101lllllll010   brpl    l
     111101lllllll000   brsh    l
     111101lllllll110   brtc    l
     111100lllllll110   brts    l
     111101lllllll011   brvc    l
     111100lllllll011   brvs    l
     111101lllllllsss   brbc    s,l
     111100lllllllsss   brbs    s,l
     1101LLLLLLLLLLLL   rcall   L
     1100LLLLLLLLLLLL   rjmp    L
     1001010hhhhh111h   call    h
     1001010hhhhh110h   jmp     h
     1001010rrrrr0101   asr     r
     1001010rrrrr0000   com     r
     1001010rrrrr1010   dec     r
     1001010rrrrr0011   inc     r
     1001010rrrrr0110   lsr     r
     1001010rrrrr0001   neg     r
     1001000rrrrr1111   pop     r
     1001001rrrrr1111   push    r
     1001010rrrrr0111   ror     r
     1001010rrrrr0010   swap    r
     00000001ddddrrrr   movw    v,v
     00000010ddddrrrr   muls    d,d
     000000110ddd0rrr   mulsu   a,a
     000000110ddd1rrr   fmul    a,a
     000000111ddd0rrr   fmuls   a,a
     000000111ddd1rrr   fmulsu  a,a
     1001001ddddd0000   sts     i,r
     1001000ddddd0000   lds     r,i
     10o0oo0dddddbooo   ldd     r,b
     100!000dddddee-+   ld      r,e
     10o0oo1rrrrrbooo   std     b,r
     100!001rrrrree-+   st      e,r
     1001010100011001   eicall
     1001010000011001   eijmp


It makes it very easy to see how the AVR's instruction decode works. As for other CPU's that pack the data parts of an operation into the opcode. This is very common (always the case?) in RISC:

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Cliff.

I put that table in a file for future use...

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, the original question seemed to be why aren't the instruction blocks arranged to fit within 8-bit byte boundaries? I think that the answer is a simple "not needed"!

In many micros, the basic code memory is structured as 8-bit bytes. In the AVR, the fundamental code memory structure is 16-bit words. The machine actually has to go through some extra effort to read data tables out, byte-wise. So, there is no particular reason to constrain fields within instructions to byte boundaries. Nor, any particular benefit to doing so, either.

If there is no particular benefit to arranging things to fit byte boundaries, then you can arrange things in what ever way strikes your fancy.

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
.org INT_VECTORS_START

; Unused interrupts are commented out, makeing assigned interrupts more visible
; First interrupt vector is the RESET Startup handler

   jmp MCU_INIT         ; RESET interrupt
   jmp IV_INT0            ; External Interrupt Request 0
   jmp IV_INT1            ; External Interrupt Request 1
   jmp IV_PCI0            ; Pin Change Interrupt Request 0
   jmp IV_PCI1            ; Pin Change Interrupt Request 0
   jmp IV_PCI2            ; Pin Change Interrupt Request 1
   jmp IV_WDT            ; Watchdog Time-out Interrupt
   jmp IV_OC2A            ; Timer/Counter2 Compare Match A
   jmp IV_OC2B            ; Timer/Counter2 Compare Match A
   jmp IV_OVF2            ; Timer/Counter2 Overflow
   jmp IV_ICP1            ; Timer/Counter1 Capture Event
   jmp timer1_compa_isr   ; Timer/Counter1 Compare Match A
   jmp IV_OC1B            ; Timer/Counter1 Compare Match B
   jmp IV_OVF1            ; Timer/Counter1 Overflow
   jmp IV_OC0A            ; TimerCounter0 Compare Match A
   jmp IV_OC0B            ; TimerCounter0 Compare Match B
   jmp IV_OVF0            ; Timer/Couner0 Overflow
   jmp IV_SPI            ; SPI Serial Transfer Complete
   jmp IV_URXC            ; USART Rx Complete
   jmp IV_UDRE            ; USART, Data Register Empty
   jmp IV_UTXC            ; USART Tx Complete
   jmp IV_ADCC            ; ADC Conversion Complete
   jmp IV_ERDY            ; EEPROM Ready
   jmp IV_ACI            ; Analog Comparator
   jmp IV_TWI            ; Two-wire Serial Interface
   jmp IV_SPMR            ; Store Program Memory Read

Your statement about commenting out unused vectors concerns me. If you do in fact comment out one of these jmp statements you will throw off the remainder of the vector table. Further down in your code you actually comment out vectors that are used, from the catch-all vector table you made. A little confusing when you come back in a year and want to change something. Maybe revise your comments a little?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, the scheme is one used in the book I read and it made sense to me. After all, all of the interrupt vectors need to go somewhere and that is the purpose of the vector table at the end of the code area.

The second vector table at the end of the listing is a "Catch-All " for unused interrupt vectors to have a controlled place to go. If it is true that the actual interrupt vectors should be assigned, then to what?

Maybe it would be better stated that the latter vector table be called “Interrupt Handler Destinations Address Table. ” So that when a new interrupt handler is created for some particular hardware and the interrupt starting address is placed in the proper vector, the appropriate interrupt handler destination address in the “Catch-All “list gets commented out of that table.

If there is a better way to word the scheme or even the prevention of errant interrupts, I'm open to suggestions.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The implementation is fine, the comment about commenting out unused entries is the concern. Simply removing that comment would help prevent someone altering the vector table at a later date.

On the smaller AVR's I find it easier just to use a single table. Then it's pretty obvious what's going on. The 2-byte table would need nop's or org statements to pad it out.

;------ Interrupt Vactor Table --------------------------

        .cseg
        .org 0

        rjmp Start                      ;Reset Handler
        reti                            ;IRQ0 Handler
        reti                            ;IRQ1 Handler
        reti                            ;PCINT0 Handler
        reti                            ;PCINT1 Handler
        reti                            ;PCINT2 Handler
        reti                            ;Watchdog Timer Handler
        reti                            ;Timer2 Compare A Handler
        reti                            ;Timer2 Compare B Handler
        reti                            ;Timer2 Overflow Handler
        reti                            ;Timer1 Capture Handler
        reti                            ;Timer1 Compare A Handler
        reti                            ;Timer1 Compare B Handler
        rjmp Timer1                     ;Timer1 Overflow Handler
        rjmp Timer0                     ;Timer0 Compare A Handler
        reti                            ;Timer0 Compare B Handler
        reti                            ;Timer0 Overflow Handler
        reti                            ;SPI Transfer Complete Handler
        reti                            ;USART, RX Complete Handler
        reti                            ;USART, UDR Empty Handler
        reti                            ;USART, TX Complete Handler
        reti                            ;ADC Conversion Complete Handler
        reti                            ;EEPROM Ready Handler
        rjmp Acompare                   ;Analog Comparator Handler
        reti                            ;2-wire Serial Interface Handler
        reti                            ;Store Program Memory Ready Handler

Now if you really want a catch-all that does something, then your first approach would work better since it points to a location to handle unplanned interrupts, rather then just doing a reti.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is why Atmel went to the trouble of defining the address of every vector in every *def.inc file. Use .org and you never have positioning problems even when moving from an RJMP to JMP model (such as mega48 to mega168)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
If there is a better way to word the scheme or even the prevention of errant interrupts, I'm open to suggestions.

If you get that, there are somthing wrong with your chip! That would be the same as makeing a RAM check on the chip!
It's is a commen pracis to get all unused interrupts to get to a forever loop so the watchdog can do the ugly cleaning.

But just to make it clear a AVR jump to the interrupt place, it's not just a vector as on many other cpu's.

There is no harm done useing the unused interrupt places for code. I often only use one timer interrupt so I just start the interrupt code in the table (no rjmp or jump) that way I save 2 (or 3)clk in the interrupt. It adds up when you do 100KHz interrups :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Carl, I think you misunderstood what I was talking about, and I also mentioned my concern in my email to you. This is taken from the code for the book:

	.cseg			; what follows is flash code
	.org	0x0000

; this is the full set of ATmega 169 interrupt vectors, even though
; we're only going to use 2.  putting them all in is safer, but we
; could have coded this more sparsely:
;   .org   0x0000                ; address of first vector
;          jmp     main          ; reset handler
;   .org   OC0addr               ; address of timer 0 compare vector
;                                ; from the ATmega 169's include file
;          jmp     i_tim0_comp   ; timer 0 compare handler

	jmp	main		; reset comes here
	jmp	i_ext_int0	; IRQ0 handler
	jmp	i_pcint0	; PCINT0 handler
	jmp	i_pcint1	; PCINT1 handler
	jmp	i_tim2_comp	; Timer 2 compare handler
	jmp	i_tim2_ovf	; Timer 2 overflow handler
	jmp	i_tim1_capt	; Timer 1 capture handler
	jmp	i_tim1_compa	; Timer 1 compare A handler
	jmp	i_tim1_compb	; Timer 1 compare B handler
	jmp	i_tim1_ovf	; Timer 1 overflow handler
	jmp	i_tim0_comp	; Timer 0 compare handler
	jmp	i_tim0_ovf	; Timer 0 overflow handler
	jmp	i_spi_stc	; SPI transfer complete handler
	jmp	i_usart_rxc	; USART Rx complete handler
	jmp	i_usart_dre	; USART UDR empty handler
	jmp	i_usart_txc	; USART Tx complete handler
	jmp	i_usi_start	; USI start condition handler
	jmp	i_usi_ovflo	; USI overflow handler
	jmp	i_ana_comp	; analog comparator handler
	jmp	i_adc		; ADC conversion complete handler
	jmp	i_ee_rdy	; EEPROM ready handler
	jmp	i_spm_rdy	; SPM ready handler
	jmp	i_lcd_eof	; LCD start of frame handler

; the following are the unused interrupt handlers, all of which refer
; to the single reti (return from interrupt) instruction
i_ext_int0:			; IRQ0 handler
i_pcint0:			; PCINT0 handler
i_pcint1:			; PCINT1 handler
i_tim2_comp:			; Timer 2 compare handler
i_tim2_ovf:			; Timer 2 overflow handler
i_tim1_capt:			; Timer 1 capture handler
i_tim1_compa:			; Timer 1 compare A handler
i_tim1_compb:			; Timer 1 compare B handler
i_tim1_ovf:			; Timer 1 overflow handler
i_tim0_ovf:			; Timer 0 overflow handler
i_spi_stc:			; SPI transfer complete handler
i_usart_rxc:			; USART Rx complete handler
i_usart_dre:			; USART UDR empty handler
i_usart_txc:			; USART Tx complete handler
i_usi_start:			; USI start condition handler
i_usi_ovflo:			; USI overflow handler
i_ana_comp:			; analog comparator handler
i_adc:				; ADC conversion complete handler
i_ee_rdy:			; EEPROM ready handler
i_spm_rdy:			; SPM ready handler
i_lcd_eof:			; LCD start of frame handler
	reti			; resume what you were doing

I put in all the vectors, then don't touch them. I remove the target address from the common reti when I want to define a new "live" vector, and put some code with it. Otherwise the unused vectors are all trapped.

As others have said, commenting out or including vectors without using the individual .orgs on them is quite risky.

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Chuck,

Well, I guess I did miss something there...

But let me ask this: If there is an errant interrupt vector access, why? Maybe it's a programmed misdirection. The machine crashes. If there is hardware failure and an errant vector jump – CRASH – but to where?

Now the way I see it, and I gave this a lot of thought while at work (more like play) last night. Discounting any the concern over the confusing comment in my code, what ever your approach, a miss-step into interrupt vector space will be catastrophic, and more then likely without recovery. Yes, using a watchdog timer may provide the ability to send a message, kill all I/O or even perform a re-boot. But in the end, an errant interrupt vector jump is a catastrophic. In fact, even with a WDT, I can imagine scenarios where the damage (literally) can be done before a shutdown could take place.

More to the point… Nearly every controller I design controls machinery - dangerous machinery - even when used as intended. As such, I've always struggled with the concept of MCU run away and have found it a very difficult concept to contend with.

On one design I did, not only did I have the internal WDT mechanism and associated software active, I also had a hardware WDT, simply because I don't trust either failsafe - they are both prone to failure.

There are some that will make the statement that "The odds are incalculable" that both failsafe systems would simultaneously fail. But then I ask, would you rely on my device as if your (or your wife’s or children’s lives) life depended on it, literally?

Yes, my code comments are/were confusing, but the dummy list recovery methodology is also flawed, as well, if for no other reason then, once directed to the dummy vector table, what memory location (interrupted program or otherwise) does the program stack actually return the PC register too. Does it not have to be assumed that, if an errant entry into the vector space was made, the stack might RETI to the last subroutine called, or was that just some data that was passed to the stack via push and pops, and will it then become a misinterpreted RETI destination? That RETI could be more catastrophic then just putting a tight loop at the end of the dummy vector table.

/*------------------------------------------------------------------------------*/ 
/******************* --- Errant Interrupt Vector Handling --- *******************/ 
/*------------------------------------------------------------------------------*/

; The following are the addresses of unused interrupt handlers that simply fall through to a code
; sequence called Fail_Safe_Halt.  In the event of an errant interrupt vector access, the tight loop
; prevents further errant program execution.  If enabled and in the likelihood that the WDT catches
; the program runaway first, it should cause a jump here, after shutting itself down.

IV_INT0:					; External Interrupt Request 0
IV_INT1:					; External Interrupt Request 1
.
.
.
IV_TWI:					; Two-wire Serial Interface
IV_SPMR:					; Store Program Memory Read

Fail_Safe_Halt:
		cli				; Turn off all interrupts, including the Sentinel timer
		cbi	PORTB, LED		; Turn off the sentinel LED
Fail_Safe_Loop:
jmp	Fail_safe_Loop	; Something stinks in Denmark!  Shut it down!!!

Holding in a tight loop until the WDT can take possible corrective action provides a better alternative, I think, then jumping off to a relatively obscure location into the vast unknown. And if the WDT can’t recover from the tight loop, any further possibility of random program execution is eliminated by the lock-down and the prevention of further errant program execution.

I could carry this on to infinity, but the larger issue isn’t a confusing comment in the code. The real issue is how to adequately handle failures of the MCU in a true “Failsafe ” manor – if there really is such a thing?

EDIT TO ADD:

To be sure... The fact that the original code posting did include a Sentinel monitor, it shows that my thinking was already aimed at addressing system failure with more robust "Failsafe " mindset.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The common error seen here is when the programmer sets either too many or the the wrong IE bits before the SEI leading to a jump to a non-existent interrupt handler. The best thing is to probably trap this then trace the source but the benign way is just to RETI from any vectors not being used so that all that happens are a few wasted cycles.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I could carry this on to infinity, but the larger issue isn’t a confusing comment in the code. The real issue is how to adequately handle failures of the MCU in a true “Failsafe ” manor – if there really is such a thing?


You don't that's why emergency stop's bypass the controller. You design the hardware in a way that it's safe if you take the power.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:
Quote:
I could carry this on to infinity, but the larger issue isn’t a confusing comment in the code. The real issue is how to adequately handle failures of the MCU in a true “Failsafe ” manor – if there really is such a thing?


You don't that's why emergency stop's bypass the controller. You design the hardware in a way that it's safe if you take the power.

Maybe in Denmark, every E-STOP works, but unlikely.

E-STOPs, while necessary, are human driven. E-STOPs won't safeguard against unattended processes where the machinery, itself, must be safeguarded.

An E-STOP is typically a safeguard against the symptom and not necessarily the prevention of "Root Cause " of some unsuspected failure.

Will an E_STOP serve any purpose in say, a home furnace controller that just happens to malfunction in a runaway state while you are sleeping?

Will an E-STOP help in a refrigeration system where the controller fails, possibly causing venting of Anhydrous Ammonia into the atmosphere of a building populated with human workers?

The circumstances are nearly endless, where an E-STOP simply doesn't solve the problem.

Besides, if the manufacturers in Denmark (or any other country, for that matter) are like many manufacturers here in the United States, when an E-STOP turns up defective, it is simply by-passed. Al-be-it, while the intent is to eventually fix the malfunctioning E-STOP at a later date, the discipline to go back and make that repair is often lost in forgetfulness and neglect – until someone gets hurt.

E-STOP safeguards, in themselves, are NOT infallible. And for an E-STOP to be effective, it actually NEEDS to be actuated to be effective, which, in many cases, panic seems to obviate that necessity!

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are two things that irk the living daylights out of me:

The first is the "Seasoned ” engineer who has never been out on a manufacturing floor, except, possibly, on his initial employment interview. So you call the guy and describe a given problem and his reply is "Well, I've never seen that before! " Well, of course he/she hasn't seen the problem before! He/she never gets out into the real world to see the problems they create for everyone else to deal with!

The second issue in the production manager who is so "Hell Bent " on meeting production quotas that, he/she will put employees in hazardous situations. Later, when someone does get hurt, he/she point their finger at the guy who just wanted to do his/her job, but wasn't allowed to.

I see this type of thing every day, and Ive seen it at nearly every company that I've worked for!!!

The “Best Practice “ is to design in real "Fail Safe " mechanisms - at the initial concept, E-STOPS being one and built-in system safeguards, as well.

And lastly, "Best Practice " might just dictate that the production manager be lashed to a post somewhere and horse whip him/her when ever he/she puts profit and quotas above human safety!

So my interest in handling errant program code is much more then a passing curiosity. My interest in proper fault handling is very, very personal, based on a lifetime of observation and experience in with real world failure issues...

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

this goes on to a larger issue, like the use of assert in higher-end mcus vs. lower-end mcus (AVR/PIC/8051/etc.). Very rare you see the use of asserts at the lower-end.

and you see more wide spread use of asserts on higher-end chips - it is standard in CMSIS for example.

I code for high reliability computing and quite frankly I am always amazed at the resistance and ignorance I see out of the 8-bitter community towards error handling / fault trapping (non-existent for practical purposes)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My experience with E-Stops in U.S. and EU are not all that different
But I see a big difference between north and south of us.
First I have only lived in the Seattle area, but I was once on at job in Port Arthur in Texas, and sorry but that is not the center of the universe.
We where working on renovating an old NOAA vessel, that had been dead for more that 10 years and sold to some money people from Seattle. And let’s say that what happened on that boat would not have been tolerated in Washington. (One dead welder!).
Our job was the monitoring and control of the engine room etc. so we had to go thru a lot of safety of the operation, and not just stupid E-stops. A boat has to have backup for almost everything, and be able to sail without power etc.
And fire safty is big! (what to start and stop )

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was working in Romania some years ago as maintenance electrician. There were some big stamping machines which in order to operate, you needed to close the safety door, push two separate buttons far enough each other in order to use both hands, and press a pedal with your foot. No electrician dare to bypass those buttons or the door switch.

When you transport or store a gun, you have to keep it unloaded, or with the barrel detached.

Any industry has its own safety rules based on many years of experience, tragic accidents, lawsuits etc.

Lets say you have a product with a microcontroller and a half bridge power supply and you want it UL certified. They don't ask you about how safe your firmware is, or if can happen your micro to drive both mosfets in the same time, but they assume your micro is faulty and will make a short between drain and source at both transistors. If your traces get melted and some paint get fire, your product fail. If the electrolytic caps explode, it fail also.

A gas based water heater which has a microcontroller to control the temperature has a completely separate circuit with a safety over temperature switch which if trigger, will cut all gas entering the device. It doesn't matter what safety firmware or microcontroller you have, if you don't add these extra safety measures, you will not pass inspections.

So, I am not sure how far is worth to dig about what happen if your micro end up in a wrong interrupt vector table and how to overcome it.

Just write good code, and keep the micro under its limits (frequency, voltage, currents).

George.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

NASA likes high reliability also. The Shuttle uses five processors in a four plus one configuration. Wiki Space Shuttle . My friends at NASA have always talked about 386 CPUs, but this article notes that NASA considers these to be embedded systems, running a special purpose IBM processor.

Many medical systems have built-in redundancy.

Eventually it becomes a question of who is watching the watcher...

JC