Refactoring a lot of the P5 stuff.
[goodfet] / firmware / apps / pic / pic.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: t -*- */
2 /*! \file dspic33f.c
3
4   \author Scott Livingston
5
6   \brief dsPIC33F programmer application for the GoodFET. Structure
7          and style is somewhat modeled after avr.c
8
9   \date March-May 2010
10 */
11
12
13 #include "platform.h"
14 #include "command.h"
15
16 #include "pic.h"
17
18 #define CYCLE_DELAY() delay_ticks(10);
19
20 //! Handle a PIC command; currently assumes dsPIC33F/PIC24H
21 void pic_handle_fn( uint8_t const app,
22                                         uint8_t const verb,
23                                         uint32_t const len );
24
25 // define the pic app's app_t
26 app_t const pic_app = {
27
28         /* app number */
29         PIC,
30
31         /* handle fn */
32         pic_handle_fn,
33
34         /* name */
35         "PIC",
36
37         /* desc */
38         "\tThe PIC app adds support for programming/debugging\n"
39         "\tdsPIC33F based devices.\n"
40 };
41
42 void pic33f_setup()
43 {
44         // Initialize pins; do NOT begin transaction.
45         P5DIR |= PGC|MCLR;
46         P5REN &= ~(PGC|PGD|MCLR);
47         DIR_PGD_WR; // Initially PGD in write mode
48
49         SET_MCLR;
50         CLR_PGC;
51         CLR_PGD;
52
53         prep_timer(); // What better time than now?
54 }
55
56
57 //! Handle a PIC command; currently assumes dsPIC33F/PIC24H
58 void pic_handle_fn( uint8_t const app,
59                                         uint8_t const verb,
60                                         uint32_t const len )
61 {
62         unsigned int nb; // Number of bytes
63         unsigned int highb, loww; // Used for ICSP commands
64
65         switch (verb) {
66
67         case PIC_DEVID33F:
68                 nb = pic33f_getid();
69                 txdata(app,verb,nb);
70                 break;
71
72         case PIC_SIX33F:
73                 loww = *cmddata;
74                 loww |= (*(cmddata+1)) << 8;
75                 highb = *(cmddata+2);
76                 pic33f_six( highb, loww );
77                 txdata(app,verb,0);
78                 break;
79
80         case PIC_SIXLIST33F:
81                 pic33f_sixlist( len ); // Reply to host is handled by pic33f_sixlist.
82                 break;
83
84         case PIC_REGOUT33F:
85                 loww = pic33f_regout();
86                 *cmddata = loww & 0xff;
87                 *(cmddata+1) = loww >> 8;
88                 txdata(app,verb,2);
89                 break;
90
91         case PIC_RESET33F:
92                 CLR_MCLR;
93                 delay_ms(20);
94                 SET_MCLR;
95                 break;
96
97         case PIC_START33F:
98                 pic33f_connect();
99                 txdata(app,verb,0);
100                 break;
101
102         case PIC_STOP33F:
103                 pic33f_disconnect();
104                 txdata(app,verb,0);
105                 break;
106
107         case PIC_CMDLIST:
108                 pic33f_cmdlist(len); // reply is handled by pic33f_cmdlist
109                 break;
110
111         default:
112                 debugstr( "Verb unimplemented in PIC application." );
113                 txdata(app,NOK,0);
114                 break;
115
116         }
117 }
118
119 void pic33f_transcmd(unsigned char cmd) {
120   int i = 0;
121   DIR_PGD_WR;
122   CLR_PGC;
123   for (i = 0; i < 4; i++) {
124     if (cmd & 0x1)
125       SET_PGD;
126     else
127       CLR_PGD;
128     CYCLE_DELAY();
129     SET_PGC;
130     cmd >>= 1;
131     CYCLE_DELAY();
132     CLR_PGC;
133   }
134   CLR_PGD;
135 }
136
137 void pic33f_trans8( unsigned char byte )
138 {
139         /* We only twiddle the PGD and PGC lines.
140            MCLR is assumed to be in the correct state. */
141         unsigned int i;
142         
143         DIR_PGD_WR; // Write mode
144         for (i = 0; i < 8; i++) {
145                 if (byte & 0x01) {
146                         SET_PGD;
147                 } else {
148                         CLR_PGD;
149                 }
150                 CYCLE_DELAY();
151                 SET_PGC;
152                 byte >>= 1;
153                 CYCLE_DELAY();
154
155                 CLR_PGC;
156                 //CYCLE_DELAY();
157         }
158         CLR_PGD;
159         DIR_PGD_RD; // Read mode
160 }
161
162 void pic33f_trans16( unsigned int word )
163 {
164         pic33f_trans8( word & 0xff );
165         pic33f_trans8( word >> 8 );
166 }
167
168
169 void pic33f_six( unsigned int highb, unsigned int loww )
170 {
171         /* dsPIC33F/PIC24H instructions have width 24 bits, so we use the
172            lower 8 bits of highb and (all 16 bits of) loww to form the
173            instruction.
174
175            Shift in the instruction.  Note that it does not execute until
176            the next 4 clock cycles (which also corresponds to a command
177            receipt time). */
178         pic33f_transcmd(0);
179         pic33f_trans16( loww );
180         pic33f_trans8( highb );
181         DIR_PGD_RD;
182 }
183
184
185 unsigned int pic33f_regout()
186 {       
187         unsigned int i;
188         unsigned int result = 0x0000;
189
190         DIR_PGD_WR;
191         
192         // Shift in command (REGOUT: 0001b).
193         pic33f_transcmd(1);
194
195         // Pump clock for 8 cycles, and switch PGD direction to read.
196         for (i = 0; i < 7; i++) {
197                 SET_PGC;
198                 CYCLE_DELAY();
199                 CLR_PGC;
200                 CYCLE_DELAY();
201         }
202         DIR_PGD_RD;
203
204         /* Now read VISI register (LSb first, as usual).
205        Note that when reading from attached device, data is valid (to
206            be read) on falling clock edges. */
207         for (i = 0; i < 16; i++) {
208                 SET_PGC;
209                 CYCLE_DELAY();
210                 CLR_PGC;
211                 result |= READ_PGD << i;
212                 CYCLE_DELAY();
213         }
214 #if 1
215         /* One last tick apparently is needed here, at least by the
216            dsPIC33FJ128GP708 chip that I am working with. Note that this
217            is not in the flash programming specs. */
218         SET_PGC; 
219         CYCLE_DELAY();
220         CLR_PGC;
221         CYCLE_DELAY();
222 #endif
223
224         return result;
225 }
226
227 void pic33f_cmdlist(unsigned int list_len) {
228         /* commands are written as 4-byte little-endian records, where
229            the low 4 bits of first byte contains the command, and the
230            next three bytes contain the data.
231
232            Currently this only supports the SIX and REGOUT
233            instructions.
234
235            SIX instructions return no data. REGOUT instructions return
236            the 16-bit value read as two bytes, lower byte first.
237            
238            The final two bytes of the response are the 2's complement
239            inverse of the sum of the response words. i.e., if the
240            response is correctly recieved, the sum of the words should
241            be 0.
242
243            This is sent when the goodfet is done running the command
244            list, and is ready for further commands.
245         */
246         
247         unsigned char cmd;
248         unsigned int response_word;
249         unsigned int checksum = 0;
250         int response_count;
251         int i;
252         list_len &= ~3; // truncate to multiple of 4 bytes.
253         if (list_len > CMDDATALEN)
254                 list_len = CMDDATALEN;
255         response_count = 1;
256         for (i = 0; i < list_len; i += 4) {
257                 cmd = cmddata[i];
258                 if (cmd == 0)
259                         continue;
260                 else if (cmd == 1)
261                         response_count ++;
262                 else
263                         goto error;
264         }
265         txhead(PIC, PIC_CMDLIST, response_count << 1);
266
267         for (i = 0; i < list_len; i+= 4) {
268                 cmd = cmddata[i];
269                 if (cmd == 0) {
270                         // SIX command
271                         pic33f_transcmd(0);
272                         pic33f_trans8(cmddata[i+1]);
273                         pic33f_trans8(cmddata[i+2]);
274                         pic33f_trans8(cmddata[i+3]);
275                         
276                 } else if (cmd == 1) {
277                         // REGOUT command
278                         response_word = pic33f_regout();
279                         checksum += response_word;
280                         response_count--;
281                         txword(response_word);
282                 }
283         }
284         txword(~checksum + 1);
285         if (response_count != 1)
286                 debugstr("Response count wrong");
287         return;
288  error:
289         txdata(PIC, NOK, 0);
290 }
291
292 /* This should be replaced by pic33f_cmdlist */
293 void pic33f_sixlist( unsigned int list_len )
294 {
295         unsigned int k;
296         unsigned int instr_loww;
297
298         // Bound to Rx buffer size.
299         if (list_len > CMDDATALEN)
300                 list_len = CMDDATALEN;
301
302         // Run each instruction!
303         for (k = 0; k < list_len-2; k+=3) {
304                 instr_loww = *(cmddata+k);
305                 instr_loww |= (*(cmddata+k+1)) << 8;
306                 pic33f_six( *(cmddata+k+2), instr_loww );
307         }
308
309         // Reply with total number of bytes used from Rx buffer.
310         txdata( PIC, PIC_SIXLIST33F, k );
311 }
312
313
314 /* This is slated to be replaced by pic33f_cmdlist */
315 unsigned int pic33f_getid()
316 {
317         unsigned int result;
318         unsigned int nb = 0;
319
320         pic33f_connect();
321
322         // Read application ID.
323         pic33f_six( 0x04, 0x0200 ); // goto 0x200 (i.e. reset)
324         pic33f_six( 0x00, 0x0000 ); // nop
325         pic33f_six( 0x20, 0x0800 ); // mov #0x80, W0
326         pic33f_six( 0x88, 0x0190 ); // mov W0, TBLPAG
327         pic33f_six( 0x20, 0x7F00 ); // mov #0x7F0, W0
328         pic33f_six( 0x20, 0x7841 ); // mov #VISI, W1
329         pic33f_six( 0x00, 0x0000 ); // nop
330         pic33f_six( 0xBA, 0x0890 ); // TBLRDL [W0], [W1]
331         pic33f_six( 0x00, 0x0000 ); // nop
332         pic33f_six( 0x00, 0x0000 ); // nop
333         result = pic33f_regout();
334         *cmddata = result & 0xff;
335         nb += 1;
336
337         // Read DEVID.
338         pic33f_six( 0x20, 0x0FF0 ); // mov #0xFF, W0
339         pic33f_six( 0x88, 0x0190 ); // mov W0, TBLPAG
340         pic33f_six( 0xEB, 0x0000 ); // clr W0
341         pic33f_six( 0x00, 0x0000 ); // nop
342         pic33f_six( 0xBA, 0x08B0 ); // TBLRDL [W0++], [W1]
343         pic33f_six( 0x00, 0x0000 ); // nop
344         pic33f_six( 0x00, 0x0000 ); // nop
345         result = pic33f_regout();
346         *(cmddata+1) = result & 0xff;
347         *(cmddata+2) = result >> 8;
348         nb += 2;
349
350         // Read hardware revision.
351         pic33f_six( 0xBA, 0x0890 ); // TBLRDL [W0++], [W1]
352         pic33f_six( 0x00, 0x0000 ); // nop
353         pic33f_six( 0x00, 0x0000 ); // nop
354         result = pic33f_regout();
355         *(cmddata+3) = result & 0xff;
356         *(cmddata+4) = result >> 8;
357         nb += 2;
358
359         pic33f_disconnect();
360
361         return nb;
362 }
363
364
365 void pic33f_connect()
366 {
367         unsigned int key_low;
368         unsigned int key_high;
369
370         key_low = ICSP_KEY_LOW;
371         key_high = ICSP_KEY_HIGH;
372
373         pic33f_setup();
374
375         CLR_PGC;
376         delay_us(1);
377         
378         CLR_MCLR;
379         delay_ms(3);
380         SET_MCLR;
381         delay_us(200);
382         CLR_MCLR;
383         delay_us(10);
384         
385         // Enter ICSP key
386         pic33f_trans8( key_low & 0xff );
387         key_low = key_low >> 8;
388         pic33f_trans8( key_low & 0xff );
389         pic33f_trans8( key_high & 0xff );
390         key_high = key_high >> 8;
391         pic33f_trans8( key_high & 0xff );
392
393         delay_us(1);
394         SET_MCLR; // ...and pull MCLR pin back up.
395         delay_ms(25); // Now wait about 25 ms (required per spec!).
396
397         /* The first ICSP command must be a SIX, and further, 9 bits are
398        required before the instruction (to be executed), rather than
399        the typical 4 bits. Thus, to simplify code, I simply load a nop
400        here; hence 33 bits are shifted into the dsPIC33F/PIC24H. */
401         DIR_PGD_WR;
402         CLR_PGD;
403         CLR_PGC;
404         for (key_low = 0; key_low < 33; key_low++) {
405                 SET_PGC;
406                 CYCLE_DELAY();
407                 CLR_PGC;
408                 CYCLE_DELAY();
409         }
410         DIR_PGD_RD;
411
412 }
413
414
415 void pic33f_disconnect()
416 {
417         DIR_PGD_WR;
418         CLR_PGD;
419         CLR_PGC;
420         delay_ms(10);
421         CLR_MCLR;
422 }