Use existing OSCCAL as starting point for calibration
[riso-kagaku-clone.git] / libs-device / osccalASM.S
1 /* Name: osccalASM.S
2  * Author: cpldcpu@gmail.com
3  * Creation Date: 2013-11-3
4  * Tabsize: 4
5  * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
6  */
7
8 /* Calibrate the RC oscillator. Our timing reference is the Start Of Frame
9  * signal (a single SE0 bit) repeating every millisecond immediately after
10  * a USB RESET.
11  *
12  *
13  * Benefits:
14  *        - Codesize reduced by 90 bytes.
15  *    - Improved robustness due to removing timeout from frame length measurement and
16  *          inserted NOP after OSCCAL writes.
17  *
18  * Changes:
19  *    - The new routine performs a combined binary and neighborhood search
20  *      in a single loop.
21  *      Note that the neighborhood search is necessary due to the quasi-monotonic
22  *      nature of OSCCAL. (See Atmel application note AVR054).
23  *        - Inserted NOP after writes to OSCCAL to avoid CPU errors during oscillator
24  *      stabilization.
25  *    - Implemented new routine to measure frame time "usbMeasureFrameLengthDecreasing".
26  *              This routine takes the target time as a parameter and returns the deviation.
27  *        - usbMeasureFrameLengthDecreasing measures in multiples of 5 cycles and is thus
28  *          slighly more accurate.
29  *        - usbMeasureFrameLengthDecreasing does not support time out anymore. The original
30  *          implementation returned zero in case of time out, which would have caused the old
31  *      calibrateOscillator() implementation to increase OSSCAL to 255, effictively
32  *      overclocking and most likely crashing the CPU. The new implementation will enter
33  *              an infinite loop when no USB activity is encountered. The user program should
34  *      use the watchdog to escape from situations like this.
35  *
36  * This routine will work both on controllers with and without split OSCCAL range.
37  * The first trial value is 128 which is the lowest value of the upper OSCCAL range
38  * on Attiny85 and will effectively limit the search to the upper range, unless the
39  * RC oscillator frequency is unusually high. Under normal operation, the highest
40  * tested frequency setting is 192. This corresponds to ~20 Mhz core frequency and
41  * is still within spec for a 5V device.
42  */
43
44
45 #define __SFR_OFFSET 0      /* used by avr-libc's register definitions */
46 #include "./usbdrv/usbdrv.h"         /* for common defs */
47
48 #ifdef __IAR_SYSTEMS_ASM__
49 /* Register assignments for usbMeasureFrameLengthDecreasing on IAR cc */
50 /* Calling conventions on IAR:
51  * First parameter passed in r16/r17, second in r18/r19 and so on.
52  * Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer)
53  * Result is passed in r16/r17
54  * In case of the "tiny" memory model, pointers are only 8 bit with no
55  * padding. We therefore pass argument 1 as "16 bit unsigned".
56  */
57
58 //Untested
59
60 #   define i            r20
61 #   define opV          r19
62 #   define opD          r18
63 #   define try          r21
64 #   define stp          r22
65
66 #   define cnt16L   r30
67 #   define cnt16H   r31
68
69
70 #else  /* __IAR_SYSTEMS_ASM__ */
71 /* Register assignments for usbMeasureFrameLength on gcc */
72 /* Calling conventions on gcc:
73  * First parameter passed in r24/r25, second in r22/23 and so on.
74  * Callee must preserve r1-r17, r28/r29
75  * Result is passed in r24/r25
76  */
77
78 #   define i            r20
79 #   define opV          r19
80 #   define opD          r18
81 #   define try          r27
82 #   define stp          r26
83 #   define cnt16L   r24
84 #   define cnt16H   r25
85 #endif
86 #   define cnt16    cnt16L
87
88 ; extern void calibrateOscillatorASM(void);
89
90 .global calibrateOscillatorASM
91 calibrateOscillatorASM:
92
93         cli
94         ldi             opD, 255
95
96         in              try, OSCCAL     ; calibration start value
97         ldi             stp, 64         ; initial step width
98         ldi             i, 10           ; 10 iterations
99
100 usbCOloop:
101
102         out             OSCCAL, try
103         nop
104
105         ; Delay values = F_CPU * 999e-6 / 5 + 0.5
106
107 #if (F_CPU == 16500000)
108         ldi             cnt16L, lo8(3297)
109         ldi             cnt16H, hi8(3297)
110 #elif (F_CPU == 12800000)
111         ldi             cnt16L, lo8(2557)
112         ldi             cnt16H, hi8(2557)
113 #else
114         #error "calibrateOscillatorASM: no delayvalues defined for this F_CPU setting"
115 #endif
116
117 usbCOWaitStrobe:            ; first wait for D- == 0 (idle strobe)
118     sbic    USBIN, USBMINUS ;
119     rjmp    usbCOWaitStrobe ;
120 usbCOWaitIdle:              ; then wait until idle again
121     sbis    USBIN, USBMINUS ;1 wait for D- == 1
122     rjmp    usbCOWaitIdle   ;2
123 usbCOWaitLoop:
124         sbiw    cnt16,1                 ;[0] [5]
125     sbic    USBIN, USBMINUS ;[2]
126     rjmp    usbCOWaitLoop   ;[3]
127
128         sbrs    cnt16H, 7               ;delay overflow?
129         rjmp    usbCOclocktoolow
130         sub             try, stp
131         neg             cnt16L
132         rjmp    usbCOclocktoohigh
133 usbCOclocktoolow:
134         add             try, stp
135 usbCOclocktoohigh:
136         lsr             stp
137         brne    usbCOnoneighborhoodsearch
138         cp              opD, cnt16L
139         brcs    usbCOnoimprovement
140         in              opV, OSCCAL
141         mov             opD, cnt16L
142 usbCOnoimprovement:
143         ldi             stp, 1
144 usbCOnoneighborhoodsearch:
145         subi    i, 1
146         brne    usbCOloop
147
148         out             OSCCAL, opV
149         nop
150         sei
151     ret
152
153 #undef i
154 #undef opV
155 #undef opD
156 #undef try
157 #undef stp
158 #undef cnt16
159 #undef cnt16L
160 #undef cnt16H
161
162 /* ------------------------------------------------------------------------- */
163 /* ------ Original C Implementation of improved calibrateOscillator -------- */
164 /* ----------------------   for Reference only ----------------------------- */
165 /* ------------------------------------------------------------------------- */
166
167 #if 0
168 void    calibrateOscillator(void)
169 {
170         uchar       step, trialValue, optimumValue;
171         int         x, targetValue;
172         uchar           optimumDev;
173         uchar           i,xl;
174
175         targetValue = (unsigned)((double)F_CPU * 999e-6 / 5.0 + 0.5);  /* Time is measured in multiples of 5 cycles. Target is 0.999┬Ás */
176     optimumDev = 0xff;
177   //  optimumValue = OSCCAL;
178         step=64;
179         trialValue = 128;
180
181         cli(); // disable interrupts
182
183         /*
184                 Performs seven iterations of a binary search (stepwidth decreasing9
185                 with three additional steps of a neighborhood search (step=1, trialvalue will oscillate around target value to find optimum)
186         */
187
188         for(i=0; i<10; i++){
189                 OSCCAL = trialValue;
190                 asm volatile(" NOP");
191
192                 x = usbMeasureFrameLengthDecreasing(targetValue);
193
194                 if(x < 0)             /* frequency too high */
195                 {
196                         trialValue -= step;
197                         xl=(uchar)-x;
198                 }
199                 else                  /* frequency too low */
200                 {
201                         trialValue += step;
202                         xl=(uchar)x;
203                 }
204
205                 /*
206                         Halve stepwidth to perform binary search. At step=1 the mode changes to neighbourhood search.
207                         Once the neighbourhood search stage is reached, x will be smaller than +-255, hence more code can be
208                         saved by only working with the lower 8 bits.
209                 */
210
211                 step >>= 1;
212
213                 if (step==0)   // Enter neighborhood search mode
214                 {
215                         step=1;
216                         if(xl <= optimumDev){
217                                 optimumDev = xl;
218                                 optimumValue = OSCCAL;
219                         }
220                 }
221         }
222
223         OSCCAL = optimumValue;
224         asm volatile(" NOP");
225
226         sei(); // enable interrupts
227 }
228 #endif