Source file: /~heha/Mikrocontroller/LEDs/u-wire.zip/osccalASM.S

/* Name: osccalASM.S   v1.1
 * Author: cpldcpu@gmail.com
 * Creation Date: 2013-11-3
 * Update       : 2014-01-4
 * 
 * Tabsize: 4
 * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
 */

/* Calibrate the RC oscillator. Our timing reference is the Start Of Frame
 * signal (a single SE0 bit) repeating every millisecond immediately after
 * a USB RESET. 
 *
 *
 * Benefits:
 *    - Codesize reduced by 90 bytes.
 *    - Improved robustness due to removing timeout from frame length measurement and 
 *      inserted NOP after OSCCAL writes.
 *
 * Changes:
 *    - The new routine performs a combined binary and neighborhood search
 *      in a single loop.
 *      Note that the neighborhood search is necessary due to the quasi-monotonic 
 *      nature of OSCCAL. (See Atmel application note AVR054).
 *    - Inserted NOP after writes to OSCCAL to avoid CPU errors during oscillator
 *      stabilization. 
 *    - Implemented new routine to measure frame time "usbMeasureFrameLengthDecreasing".
 *      This routine takes the target time as a parameter and returns the deviation.
 *    - usbMeasureFrameLengthDecreasing measures in multiples of 5 cycles and is thus
 *      slighly more accurate.
 *    - usbMeasureFrameLengthDecreasing does not support time out anymore. The original
 *      implementation returned zero in case of time out, which would have caused the old
 *      calibrateOscillator() implementation to increase OSSCAL to 255, effictively
 *      overclocking and most likely crashing the CPU. The new implementation will enter
 *      an infinite loop when no USB activity is encountered. The user program should
 *      use the watchdog to escape from situations like this.
 * Update 2014-01-4
 *    - Added an initial sync-state, which will discard the first delay measurement. 
 *      This allows to call this routine before or during the SE0 bus reset without 
 *      corrupting OSCCAL.
 *     - Removed CLI/SEI to allow more flexibility.
 * 
 * 
 * This routine will work both on controllers with and without split OSCCAL range.
 * The first trial value is 128 which is the lowest value of the upper OSCCAL range
 * on Attiny85 and will effectively limit the search to the upper range, unless the
 * RC oscillator frequency is unusually high. Under normal operation, the highest 
 * tested frequency setting is 192. This corresponds to ~20 Mhz core frequency and 
 * is still within spec for a 5V device.
 */
 

#define __SFR_OFFSET 0      /* used by avr-libc's register definitions */
#include "./usbdrv/usbdrv.h"         /* for common defs */

#ifdef __IAR_SYSTEMS_ASM__
/* Register assignments for usbMeasureFrameLengthDecreasing on IAR cc */
/* Calling conventions on IAR:
 * First parameter passed in r16/r17, second in r18/r19 and so on.
 * Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer)
 * Result is passed in r16/r17
 * In case of the "tiny" memory model, pointers are only 8 bit with no
 * padding. We therefore pass argument 1 as "16 bit unsigned".
 */

//Untested

#   define i		r20	
#   define opV		r19
#   define opD		r18
#   define try		r21
#   define stp		r22

#   define cnt16L   r30
#   define cnt16H   r31


#else  /* __IAR_SYSTEMS_ASM__ */ 
/* Register assignments for usbMeasureFrameLength on gcc */
/* Calling conventions on gcc:
 * First parameter passed in r24/r25, second in r22/23 and so on.
 * Callee must preserve r1-r17, r28/r29
 * Result is passed in r24/r25
 */

#   define i		r20	
#   define opV		r19
#   define opD		r18
#   define try		r27
#   define stp		r26
#   define cnt16L   r24
#   define cnt16H   r25
#endif
#   define cnt16    cnt16L

; extern void calibrateOscillatorASM(void);

.global calibrateOscillatorASM
calibrateOscillatorASM:

  ldi   opD, 255

  ldi   try, 128    ; calibration start value
  ldi   stp, 0      ; initial step width=0 for sync phase (first delay is discarded)
  ldi   i, 11       ; 11 iterations (1x sync, 7x binary search, 3x neighbourhood)

usbCOloop:
  out		OSCCAL, try
  
  ; Delay values = F_CPU * 999e-6 / 5 + 0.5
  
#if (F_CPU == 16500000)
  ldi   cnt16L, lo8(3297)
  ldi   cnt16H, hi8(3297)
#elif (F_CPU == 12800000)
  ldi   cnt16L, lo8(2557)
  ldi   cnt16H, hi8(2557)
#elif (F_CPU == 12000000)
  ldi   cnt16L, lo8(2398)
  ldi   cnt16H, hi8(2398)
#elif (F_CPU == 16000000)
  ldi   cnt16L, lo8(3197)
  ldi   cnt16H, hi8(3197)  
#else
	#error "calibrateOscillatorASM: no delayvalues defined for this F_CPU setting"
#endif

usbCOWaitStrobe:            ; first wait for D- == 0 (idle strobe)
    sbic    USBIN, USBMINUS ;
    rjmp    usbCOWaitStrobe ;
usbCOWaitIdle:              ; then wait until idle again
    sbis    USBIN, USBMINUS ;1 wait for D- == 1
    rjmp    usbCOWaitIdle   ;2
usbCOWaitLoop:
#ifdef __AVR_ATtiny10__
    subi cnt16L,1 ;[0] [5]
    sbci cnt16H,0 ;[1] 
#else
    sbiw  cnt16,1 ;[0] [5]
#endif  
    sbic    USBIN, USBMINUS ;[2] 
    rjmp    usbCOWaitLoop   ;[3]

	sbrs	cnt16H, 7		;delay overflow?
	rjmp	usbCOclocktoolow
	sub		try, stp
	neg		cnt16L
	rjmp	usbCOclocktoohigh
usbCOclocktoolow:
	add		try, stp
usbCOclocktoohigh:
	lsr		stp                ; stp = 0 in first iteration (sync)
                           ; stp = 2^x (x=0..6) during binary search, 
                           ; stp = 1 during neighbourhood search                          
                           
	brne	usbCObinarysearch
  ldi   stp, 64            ; stp=64 to initiate binary search. 
                           ; If we are in neighbourhood search (c=1), it is changed to 1 below
  brcc  usbCObinarysearch
	cp		opD, cnt16L
	brcs	usbCOnoimprovement
	in		opV, OSCCAL
	mov		opD, cnt16L
usbCOnoimprovement:
	ldi		stp, 1             ; stp=1 to continue with neighbourhood search
usbCObinarysearch:  
	subi	i, 1
	brne	usbCOloop

	out		OSCCAL, opV
;	nop

  ret

#undef i
#undef opV
#undef opD
#undef try
#undef stp
#undef cnt16
#undef cnt16L
#undef cnt16H

/* ------------------------------------------------------------------------- */
/* ------ Original C Implementation of improved calibrateOscillator -------- */
/* ----------------------   for Reference only ----------------------------- */
/* ------------------------------------------------------------------------- */

#if 0
void    calibrateOscillator(void)
{
	uchar       step, trialValue, optimumValue;
	int         x, targetValue;
	uchar		optimumDev;
	uchar		i,xl;
	
	targetValue = (unsigned)((double)F_CPU * 999e-6 / 5.0 + 0.5);  /* Time is measured in multiples of 5 cycles. Target is 0.999µs */
    optimumDev = 0xff;   
  //  optimumValue = OSCCAL; 
	step=64;
	trialValue = 128;
	
	cli(); // disable interrupts
	
	/*
		Performs seven iterations of a binary search (stepwidth decreasing9
		with three additional steps of a neighborhood search (step=1, trialvalue will oscillate around target value to find optimum)
	*/

	for(i=0; i<10; i++){
		OSCCAL = trialValue;
		asm volatile(" NOP");
	
		x = usbMeasureFrameLengthDecreasing(targetValue);

		if(x < 0)             /* frequency too high */
		{
			trialValue -= step;
			xl=(uchar)-x;
		}
		else                  /* frequency too low */
		{
			trialValue += step;			
			xl=(uchar)x;
		}
		
		/*
			Halve stepwidth to perform binary search. At step=1 the mode changes to neighbourhood search.
			Once the neighbourhood search stage is reached, x will be smaller than +-255, hence more code can be
			saved by only working with the lower 8 bits.
		*/
		
		step >>= 1;
		
		if (step==0)   // Enter neighborhood search mode
		{
			step=1;			
			if(xl <= optimumDev){
				optimumDev = xl;
				optimumValue = OSCCAL;
			}
		}
	}

	OSCCAL = optimumValue;
	asm volatile(" NOP");	
}
#endif
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded