Add note about another tested PIC target: PIC24FJ64GA002
[goodfet] / firmware / apps / pic / dspic33f.c
1 /*! \file dspic33f.c
2
3   \author Scott Livingston
4
5   \brief dsPIC33F programmer application for the GoodFET. Structure
6          and style is somewhat modeled after avr.c
7
8   \date March-May 2010
9 */
10
11
12 #include "apps.h"
13 #include "platform.h"
14 #include "command.h"
15
16 #include "dspic33f.h"
17
18
19 void pic33f_setup()
20 {
21         // Initialize pins; do NOT begin transaction.
22         P5DIR |= PGC|MCLR;
23         P5REN &= ~(PGC|PGD|MCLR);
24         DIR_PGD_WR; // Initially PGD in write mode
25
26         SET_MCLR;
27         CLR_PGC;
28         CLR_PGD;
29
30         prep_timer(); // What better time than now?
31 }
32
33
34 //! Handle a PIC command; currently assumes dsPIC33F/PIC24H
35 void pichandle( unsigned char app,
36                                 unsigned char verb,
37                                 unsigned long len )
38 {
39         unsigned int nb; // Number of bytes
40         unsigned int highb, loww; // Used for ICSP commands
41
42         switch (verb) {
43
44         case PIC_DEVID33F:
45                 nb = pic33f_getid();
46                 txdata(app,verb,nb);
47                 break;
48
49         case PIC_SIX33F:
50                 loww = *cmddata;
51                 loww |= (*(cmddata+1)) << 8;
52                 highb = *(cmddata+2);
53                 pic33f_six( highb, loww );
54                 txdata(app,verb,0);
55                 break;
56
57         case PIC_SIXLIST33F:
58                 pic33f_sixlist( len ); // Reply to host is handled by pic33f_sixlist.
59                 break;
60
61         case PIC_REGOUT33F:
62                 loww = pic33f_regout();
63                 *cmddata = loww & 0xff;
64                 *(cmddata+1) = loww >> 8;
65                 txdata(app,verb,2);
66                 break;
67
68         case PIC_RESET33F:
69                 CLR_MCLR;
70                 delay_ms(20);
71                 SET_MCLR;
72                 break;
73
74         case PIC_START33F:
75                 pic33f_connect();
76                 txdata(app,verb,0);
77                 break;
78
79         case PIC_STOP33F:
80                 pic33f_disconnect();
81                 txdata(app,verb,0);
82                 break;
83
84         default:
85                 debugstr( "Verb unimplemented in PIC application." );
86                 txdata(app,NOK,0);
87                 break;
88
89         }
90 }
91
92
93 void pic33f_trans8( unsigned char byte )
94 {
95         /* We only twiddle the PGD and PGC lines.
96            MCLR is assumed to be in the correct state. */
97         unsigned int i;
98         
99         DIR_PGD_WR; // Write mode
100         i = 1;
101         while (i & 0xff) {
102                 if (byte & i) {
103                         SET_PGD;
104                 } else {
105                         CLR_PGD;
106                 }
107                 delay_ticks(10);
108                 SET_PGC;
109                 delay_ticks(10);
110
111                 CLR_PGC;
112                 delay_ticks(10);
113                 i = i << 1;
114         }
115         CLR_PGD;
116         DIR_PGD_RD; // Read mode
117 }
118
119 void pic33f_trans16( unsigned int word )
120 {
121         pic33f_trans8( word & 0xff );
122         pic33f_trans8( word >> 8 );
123 }
124
125
126 void pic33f_six( unsigned int highb, unsigned int loww )
127 {
128         /* dsPIC33F/PIC24H instructions have width 24 bits, so we use the
129            lower 8 bits of highb and (all 16 bits of) loww to form the
130            instruction.
131
132            Shift in the instruction.  Note that it does not execute until
133            the next 4 clock cycles (which also corresponds to a command
134            receipt time). */
135         unsigned int i;
136         DIR_PGD_WR;
137         CLR_PGD;
138         CLR_PGC;
139         for (i = 0; i < 4; i++) {
140                 SET_PGC;
141                 delay_ticks(10);
142                 CLR_PGC;
143                 delay_ticks(10);
144         }
145         pic33f_trans16( loww );
146         pic33f_trans8( highb );
147         DIR_PGD_RD;
148 }
149
150
151 unsigned int pic33f_regout()
152 {       
153         unsigned int i;
154         unsigned int result = 0x0000;
155
156         DIR_PGD_WR;
157         
158         // Shift in command (REGOUT: 0001b).
159         SET_PGD;
160         delay_ticks(10);
161         SET_PGC;
162         delay_ticks(10);
163         CLR_PGC;
164         delay_ticks(10);
165         
166         CLR_PGD;
167         delay_ticks(10);
168         for (i = 0; i < 3; i++) {
169                 SET_PGC;
170                 delay_ticks(10);
171                 CLR_PGC;
172                 delay_ticks(10);
173         }
174
175         // Pump clock for 8 cycles, and switch PGD direction to read.
176         for (i = 0; i < 7; i++) {
177                 SET_PGC;
178                 delay_ticks(10);
179                 CLR_PGC;
180                 delay_ticks(10);
181         }
182         DIR_PGD_RD;
183
184         /* Now read VISI register (LSb first, as usual).
185        Note that when reading from attached device, data is valid (to
186            be read) on falling clock edges. */
187         for (i = 0; i < 16; i++) {
188                 SET_PGC;
189                 delay_ticks(10);
190                 CLR_PGC;
191                 result |= READ_PGD << i;
192                 delay_ticks(10);
193         }
194
195         /* One last tick apparently is needed here, at least by the
196            dsPIC33FJ128GP708 chip that I am working with. Note that this
197            is not in the flash programming specs. */
198         SET_PGC; 
199         delay_ticks(10);
200         CLR_PGC;
201         delay_ticks(10);
202
203         return result;
204 }
205
206
207 void pic33f_sixlist( unsigned int list_len )
208 {
209         unsigned int k;
210         unsigned int instr_loww;
211
212         // Bound to Rx buffer size.
213         if (list_len > CMDDATALEN)
214                 list_len = CMDDATALEN;
215
216         // Run each instruction!
217         for (k = 0; k < list_len-2; k+=3) {
218                 instr_loww = *(cmddata+k);
219                 instr_loww |= (*(cmddata+k+1)) << 8;
220                 pic33f_six( *(cmddata+k+2), instr_loww );
221         }
222
223         // Reply with total number of bytes used from Rx buffer.
224         txdata( PIC, PIC_SIXLIST33F, k );
225 }
226
227
228 unsigned int pic33f_getid()
229 {
230         unsigned int result;
231         unsigned int nb = 0;
232
233         pic33f_connect();
234
235         // Read application ID.
236         pic33f_six( 0x04, 0x0200 ); // goto 0x200 (i.e. reset)
237         pic33f_six( 0x00, 0x0000 ); // nop
238         pic33f_six( 0x20, 0x0800 ); // mov #0x80, W0
239         pic33f_six( 0x88, 0x0190 ); // mov W0, TBLPAG
240         pic33f_six( 0x20, 0x7F00 ); // mov #0x7F0, W0
241         pic33f_six( 0x20, 0x7841 ); // mov #VISI, W1
242         pic33f_six( 0x00, 0x0000 ); // nop
243         pic33f_six( 0xBA, 0x0890 ); // TBLRDL [W0], [W1]
244         pic33f_six( 0x00, 0x0000 ); // nop
245         pic33f_six( 0x00, 0x0000 ); // nop
246         result = pic33f_regout();
247         *cmddata = result & 0xff;
248         nb += 1;
249
250         // Read DEVID.
251         pic33f_six( 0x20, 0x0FF0 ); // mov #0xFF, W0
252         pic33f_six( 0x88, 0x0190 ); // mov W0, TBLPAG
253         pic33f_six( 0xEB, 0x0000 ); // clr W0
254         pic33f_six( 0x00, 0x0000 ); // nop
255         pic33f_six( 0xBA, 0x08B0 ); // TBLRDL [W0++], [W1]
256         pic33f_six( 0x00, 0x0000 ); // nop
257         pic33f_six( 0x00, 0x0000 ); // nop
258         result = pic33f_regout();
259         *(cmddata+1) = result & 0xff;
260         *(cmddata+2) = result >> 8;
261         nb += 2;
262
263         // Read hardware revision.
264         pic33f_six( 0xBA, 0x0890 ); // TBLRDL [W0++], [W1]
265         pic33f_six( 0x00, 0x0000 ); // nop
266         pic33f_six( 0x00, 0x0000 ); // nop
267         result = pic33f_regout();
268         *(cmddata+3) = result & 0xff;
269         *(cmddata+4) = result >> 8;
270         nb += 2;
271
272         pic33f_disconnect();
273
274         return nb;
275 }
276
277
278 void pic33f_connect()
279 {
280         unsigned int key_low;
281         unsigned int key_high;
282
283         key_low = ICSP_KEY_LOW;
284         key_high = ICSP_KEY_HIGH;
285
286         pic33f_setup();
287
288         CLR_PGC;
289         delay_us(1);
290         
291         CLR_MCLR;
292         delay_ms(3);
293         SET_MCLR;
294         delay_us(200);
295         CLR_MCLR;
296         delay_us(10);
297         
298         // Enter ICSP key
299         pic33f_trans8( key_low & 0xff );
300         key_low = key_low >> 8;
301         pic33f_trans8( key_low & 0xff );
302         pic33f_trans8( key_high & 0xff );
303         key_high = key_high >> 8;
304         pic33f_trans8( key_high & 0xff );
305
306         delay_us(1);
307         SET_MCLR; // ...and pull MCLR pin back up.
308         delay_ms(25); // Now wait about 25 ms (required per spec!).
309
310         /* The first ICSP command must be a SIX, and further, 9 bits are
311        required before the instruction (to be executed), rather than
312        the typical 4 bits. Thus, to simplify code, I simply load a nop
313        here; hence 33 bits are shifted into the dsPIC33F/PIC24H. */
314         DIR_PGD_WR;
315         CLR_PGD;
316         CLR_PGC;
317         for (key_low = 0; key_low < 33; key_low++) {
318                 SET_PGC;
319                 delay_us(1);
320                 CLR_PGC;
321                 delay_us(1);
322         }
323         DIR_PGD_RD;
324
325 }
326
327
328 void pic33f_disconnect()
329 {
330         DIR_PGD_WR;
331         CLR_PGD;
332         CLR_PGC;
333         delay_ms(10);
334         CLR_MCLR;
335 }