fxload fx2lp dev board
[fx2fw-sdcc] / usbjtag.c
1 /*-----------------------------------------------------------------------------\r
2  * Code that turns a Cypress FX2 USB Controller into an USB JTAG adapter\r
3  *-----------------------------------------------------------------------------\r
4  * Copyright (C) 2005..2007 Kolja Waschk, ixo.de\r
5  *-----------------------------------------------------------------------------\r
6  * Check hardware.h/.c if it matches your hardware configuration (e.g. pinout).\r
7  * Changes regarding USB identification should be made in product.inc!\r
8  *-----------------------------------------------------------------------------\r
9  * This code is part of usbjtag. usbjtag is free software; you can redistribute\r
10  * it and/or modify it under the terms of the GNU General Public License as\r
11  * published by the Free Software Foundation; either version 2 of the License,\r
12  * or (at your option) any later version. usbjtag is distributed in the hope\r
13  * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied\r
14  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15  * GNU General Public License for more details.  You should have received a\r
16  * copy of the GNU General Public License along with this program in the file\r
17  * COPYING; if not, write to the Free Software Foundation, Inc., 51 Franklin\r
18  * St, Fifth Floor, Boston, MA  02110-1301  USA\r
19  *-----------------------------------------------------------------------------\r
20  */\r
21  \r
22 // The firmware version should be regularly updated.\r
23 #define FWVERSION "4.2.0"\r
24 \r
25 #include "isr.h"\r
26 #include "timer.h"\r
27 #include "delay.h"\r
28 #include "fx2regs.h"\r
29 #include "fx2utils.h"\r
30 #include "usb_common.h"\r
31 #include "usb_descriptors.h"\r
32 #include "usb_requests.h"\r
33 \r
34 #include "syncdelay.h"\r
35 \r
36 #include "eeprom.h"\r
37 #include "hardware.h"\r
38 \r
39 //-----------------------------------------------------------------------------\r
40 // Define USE_MOD256_OUTBUFFER:\r
41 // Saves about 256 bytes in code size, improves speed a little.\r
42 // A further optimization could be not to use an extra output buffer at \r
43 // all, but to write directly into EP1INBUF. Not implemented yet. When \r
44 // downloading large amounts of data _to_ the target, there is no output\r
45 // and thus the output buffer isn't used at all and doesn't slow down things.\r
46 \r
47 #define USE_MOD256_OUTBUFFER 1\r
48 \r
49 //-----------------------------------------------------------------------------\r
50 // Global data\r
51 \r
52 typedef bit BOOL;\r
53 #define FALSE 0\r
54 #define TRUE  1\r
55 static BOOL Running;\r
56 static BOOL WriteOnly;\r
57 \r
58 static BYTE ClockBytes;\r
59 static WORD Pending;\r
60 \r
61 #ifdef USE_MOD256_OUTBUFFER\r
62   static BYTE FirstDataInOutBuffer;\r
63   static BYTE FirstFreeInOutBuffer;\r
64 #else\r
65   static WORD FirstDataInOutBuffer;\r
66   static WORD FirstFreeInOutBuffer;\r
67 #endif\r
68 \r
69 #ifdef USE_MOD256_OUTBUFFER\r
70   /* Size of output buffer must be exactly 256 */\r
71   #define OUTBUFFER_LEN 0x100\r
72   /* Output buffer must begin at some address with lower 8 bits all zero */\r
73   xdata at 0xE000 BYTE OutBuffer[OUTBUFFER_LEN];\r
74 #else\r
75   #define OUTBUFFER_LEN 0x200\r
76   static xdata BYTE OutBuffer[OUTBUFFER_LEN];\r
77 #endif\r
78 \r
79 //-----------------------------------------------------------------------------\r
80 \r
81 void usb_jtag_init(void)              // Called once at startup\r
82 {\r
83    WORD tmp;\r
84 \r
85    Running = FALSE;\r
86    ClockBytes = 0;\r
87    Pending = 0;\r
88    WriteOnly = TRUE;\r
89    FirstDataInOutBuffer = 0;\r
90    FirstFreeInOutBuffer = 0;\r
91 \r
92    ProgIO_Init();\r
93 \r
94    ProgIO_Enable();\r
95 \r
96    // Make Timer2 reload at 100 Hz to trigger Keepalive packets\r
97    tmp = 65536 - ( 48000000 / 12 / 100 );\r
98    RCAP2H = tmp >> 8;\r
99    RCAP2L = tmp & 0xFF;\r
100    CKCON = 0; // Default Clock\r
101    T2CON = 0x04; // Auto-reload mode using internal clock, no baud clock.\r
102 \r
103    // Enable Autopointer\r
104    EXTACC = 1;          // Enable\r
105    APTR1FZ = 1;         // Don't freeze\r
106    APTR2FZ = 1;         // Don't freeze\r
107 \r
108    // define endpoint configuration\r
109 \r
110    REVCTL = 3; SYNCDELAY;                                                       // Allow FW access to FIFO buffer\r
111    FIFORESET = 0x80; SYNCDELAY;                                         // From now on, NAK all, reset all FIFOS\r
112    FIFORESET  = 0x02; SYNCDELAY;                                        // Reset FIFO 2\r
113    FIFORESET  = 0x04; SYNCDELAY;                                        // Reset FIFO 4\r
114    FIFORESET  = 0x06; SYNCDELAY;                                        // Reset FIFO 6\r
115    FIFORESET  = 0x08; SYNCDELAY;                                        // Reset FIFO 8\r
116    FIFORESET  = 0x00; SYNCDELAY;                                        // Restore normal behaviour\r
117 \r
118    EP1OUTCFG  = 0xA0; SYNCDELAY;                                        // Endpoint 1 Type Bulk                 \r
119    EP1INCFG   = 0xA0; SYNCDELAY;                                        // Endpoint 1 Type Bulk\r
120 \r
121    EP2FIFOCFG = 0x00; SYNCDELAY;                                        // Endpoint 2\r
122    EP2CFG     = 0xA2; SYNCDELAY;                                        // Endpoint 2 Valid, Out, Type Bulk, Double buffered\r
123 \r
124    EP4FIFOCFG = 0x00; SYNCDELAY;                                        // Endpoint 4 not used\r
125    EP4CFG     = 0xA0; SYNCDELAY;                                        // Endpoint 4 not used\r
126 \r
127    REVCTL = 0; SYNCDELAY;                                                       // Reset FW access to FIFO buffer, enable auto-arming when AUTOOUT is switched to 1\r
128 \r
129    EP6CFG     = 0xA2; SYNCDELAY;                                        // Out endpoint, Bulk, Double buffering\r
130    EP6FIFOCFG = 0x00; SYNCDELAY;                                        // Firmware has to see a rising edge on auto bit to enable auto arming\r
131    EP6FIFOCFG = bmAUTOOUT | bmWORDWIDE; SYNCDELAY;      // Endpoint 6 used for user communicationn, auto commitment, 16 bits data bus\r
132 \r
133    EP8CFG     = 0xE0; SYNCDELAY;                                        // In endpoint, Bulk\r
134    EP8FIFOCFG = 0x00; SYNCDELAY;                                        // Firmware has to see a rising edge on auto bit to enable auto arming\r
135    EP8FIFOCFG = bmAUTOIN  | bmWORDWIDE; SYNCDELAY;      // Endpoint 8 used for user communication, auto commitment, 16 bits data bus\r
136 \r
137    EP8AUTOINLENH = 0x00; SYNCDELAY;                                     // Size in bytes of the IN data automatically commited (64 bytes here, but changed dynamically depending on the connection)\r
138    EP8AUTOINLENL = 0x40; SYNCDELAY;                                     // Can use signal PKTEND if you want to commit a shorter packet\r
139 \r
140    // Out endpoints do not come up armed\r
141    // Since the defaults are double buffered we must write dummy byte counts twice\r
142    EP2BCL = 0x80; SYNCDELAY;                                            // Arm EP2OUT by writing byte count w/skip.=\r
143    EP4BCL = 0x80; SYNCDELAY;\r
144    EP2BCL = 0x80; SYNCDELAY;                                            // Arm EP4OUT by writing byte count w/skip.= \r
145    EP4BCL = 0x80; SYNCDELAY;\r
146    \r
147    // JTAG from FX2 enabled by default\r
148    IOC |= (1 << 7);\r
149    \r
150    // Put the system in high speed by default (REM: USB-Blaster is in full speed)\r
151    // This can be changed by vendor commands\r
152    CT1 &= ~0x02;\r
153    // Put the FIFO in sync mode\r
154    IFCONFIG &= ~bmASYNC;\r
155 }\r
156 \r
157 void OutputByte(BYTE d)\r
158 {\r
159 #ifdef USE_MOD256_OUTBUFFER\r
160    OutBuffer[FirstFreeInOutBuffer] = d;\r
161    FirstFreeInOutBuffer = ( FirstFreeInOutBuffer + 1 ) & 0xFF;\r
162 #else\r
163    OutBuffer[FirstFreeInOutBuffer++] = d;\r
164    if(FirstFreeInOutBuffer >= OUTBUFFER_LEN) FirstFreeInOutBuffer = 0;\r
165 #endif\r
166    Pending++;\r
167 }\r
168 \r
169 //-----------------------------------------------------------------------------\r
170 // usb_jtag_activity does most of the work. It now happens to behave just like\r
171 // the combination of FT245BM and Altera-programmed EPM7064 CPLD in Altera's\r
172 // USB-Blaster. The CPLD knows two major modes: Bit banging mode and Byte\r
173 // shift mode. It starts in Bit banging mode. While bytes are received\r
174 // from the host on EP2OUT, each byte B of them is processed as follows:\r
175 //\r
176 // Please note: nCE, nCS, LED pins and DATAOUT actually aren't supported here.\r
177 // Support for these would be required for AS/PS mode and isn't too complicated,\r
178 // but I haven't had the time yet.\r
179 //\r
180 // Bit banging mode:\r
181 // \r
182 //   1. Remember bit 6 (0x40) in B as the "Read bit".\r
183 //\r
184 //   2. If bit 7 (0x40) is set, switch to Byte shift mode for the coming\r
185 //      X bytes ( X := B & 0x3F ), and don't do anything else now.\r
186 //\r
187 //    3. Otherwise, set the JTAG signals as follows:\r
188 //        TCK/DCLK high if bit 0 was set (0x01), otherwise low\r
189 //        TMS/nCONFIG high if bit 1 was set (0x02), otherwise low\r
190 //        nCE high if bit 2 was set (0x04), otherwise low\r
191 //        nCS high if bit 3 was set (0x08), otherwise low\r
192 //        TDI/ASDI/DATA0 high if bit 4 was set (0x10), otherwise low\r
193 //        Output Enable/LED active if bit 5 was set (0x20), otherwise low\r
194 //\r
195 //    4. If "Read bit" (0x40) was set, record the state of TDO(CONF_DONE) and\r
196 //        DATAOUT(nSTATUS) pins and put it as a byte ((DATAOUT<<1)|TDO) in the\r
197 //        output FIFO _to_ the host (the code here reads TDO only and assumes\r
198 //        DATAOUT=1)\r
199 //\r
200 // Byte shift mode:\r
201 //\r
202 //   1. Load shift register with byte from host\r
203 //\r
204 //   2. Do 8 times (i.e. for each bit of the byte; implemented in shift.a51)\r
205 //      2a) if nCS=1, set carry bit from TDO, else set carry bit from DATAOUT\r
206 //      2b) Rotate shift register through carry bit\r
207 //      2c) TDI := Carry bit\r
208 //      2d) Raise TCK, then lower TCK.\r
209 //\r
210 //   3. If "Read bit" was set when switching into byte shift mode,\r
211 //      record the shift register content and put it into the FIFO\r
212 //      _to_ the host.\r
213 //\r
214 // Some more (minor) things to consider to emulate the FT245BM:\r
215 //\r
216 //   a) The FT245BM seems to transmit just packets of no more than 64 bytes\r
217 //      (which perfectly matches the USB spec). Each packet starts with\r
218 //      two non-data bytes (I use 0x31,0x60 here). A USB sniffer on Windows\r
219 //      might show a number of packets to you as if it was a large transfer\r
220 //      because of the way that Windows understands it: it _is_ a large\r
221 //      transfer until terminated with an USB packet smaller than 64 byte.\r
222 //\r
223 //   b) The Windows driver expects to get some data packets (with at least\r
224 //      the two leading bytes 0x31,0x60) immediately after "resetting" the\r
225 //      FT chip and then in regular intervals. Otherwise a blue screen may\r
226 //      appear... In the code below, I make sure that every 10ms there is\r
227 //      some packet.\r
228 //\r
229 //   c) Vendor specific commands to configure the FT245 are mostly ignored\r
230 //      in my code. Only those for reading the EEPROM are processed. See\r
231 //      DR_GetStatus and DR_VendorCmd below for my implementation.\r
232 //\r
233 //   All other TD_ and DR_ functions remain as provided with CY3681.\r
234 //\r
235 //-----------------------------------------------------------------------------\r
236 \r
237 void usb_jtag_activity(void) // Called repeatedly while the device is idle\r
238 {\r
239    if(!Running) return;\r
240 \r
241    ProgIO_Poll();\r
242    \r
243    if(!(EP1INCS & bmEPBUSY))\r
244    {\r
245       if(Pending > 0)\r
246       {\r
247          BYTE o, n;\r
248 \r
249          AUTOPTRH2 = MSB( EP1INBUF );\r
250          AUTOPTRL2 = LSB( EP1INBUF );\r
251        \r
252          XAUTODAT2 = 0x31;\r
253          XAUTODAT2 = 0x60;\r
254        \r
255          if(Pending > 0x3E) { n = 0x3E; Pending -= n; } \r
256                      else { n = Pending; Pending = 0; };\r
257        \r
258          o = n;\r
259 \r
260 #ifdef USE_MOD256_OUTBUFFER\r
261          APTR1H = MSB( OutBuffer );\r
262          APTR1L = FirstDataInOutBuffer;\r
263          while(n--)\r
264          {\r
265             XAUTODAT2 = XAUTODAT1;\r
266             APTR1H = MSB( OutBuffer ); // Stay within 256-Byte-Buffer\r
267          };\r
268          FirstDataInOutBuffer = APTR1L;\r
269 #else\r
270          APTR1H = MSB( &(OutBuffer[FirstDataInOutBuffer]) );\r
271          APTR1L = LSB( &(OutBuffer[FirstDataInOutBuffer]) );\r
272          while(n--)\r
273          {\r
274             XAUTODAT2 = XAUTODAT1;\r
275 \r
276             if(++FirstDataInOutBuffer >= OUTBUFFER_LEN)\r
277             {\r
278                FirstDataInOutBuffer = 0;\r
279                APTR1H = MSB( OutBuffer );\r
280                APTR1L = LSB( OutBuffer );\r
281             };\r
282          };\r
283 #endif\r
284          SYNCDELAY;\r
285          EP1INBC = 2 + o;\r
286          TF2 = 1; // Make sure there will be a short transfer soon\r
287       }\r
288       else if(TF2)\r
289       {\r
290          EP1INBUF[0] = 0x31;\r
291          EP1INBUF[1] = 0x60;\r
292          SYNCDELAY;\r
293          EP1INBC = 2;\r
294          TF2 = 0;\r
295       };\r
296    };\r
297 \r
298    if(!(EP2468STAT & bmEP2EMPTY) && (Pending < OUTBUFFER_LEN-0x3F))\r
299    {\r
300       WORD i, n = EP2BCL|EP2BCH<<8;\r
301 \r
302       APTR1H = MSB( EP2FIFOBUF );\r
303       APTR1L = LSB( EP2FIFOBUF );\r
304 \r
305       for(i=0;i<n;)\r
306       {\r
307          if(ClockBytes > 0)\r
308          {\r
309             WORD m;\r
310 \r
311             m = n-i;\r
312             if(ClockBytes < m) m = ClockBytes;\r
313             ClockBytes -= m;\r
314             i += m;\r
315 \r
316             /* Shift out 8 bits from d */\r
317          \r
318             if(WriteOnly) /* Shift out 8 bits from d */\r
319             {\r
320                while(m--) ProgIO_ShiftOut(XAUTODAT1);\r
321             }\r
322             else /* Shift in 8 bits at the other end  */\r
323             {\r
324                while(m--) OutputByte(ProgIO_ShiftInOut(XAUTODAT1));\r
325             }\r
326         }\r
327         else\r
328         {\r
329             BYTE d = XAUTODAT1;\r
330             WriteOnly = (d & bmBIT6) ? FALSE : TRUE;\r
331 \r
332             if(d & bmBIT7)\r
333             {\r
334                /* Prepare byte transfer, do nothing else yet */\r
335 \r
336                ClockBytes = d & 0x3F;\r
337             }\r
338             else\r
339             {\r
340                if(WriteOnly)\r
341                    ProgIO_Set_State(d);\r
342                else\r
343                    OutputByte(ProgIO_Set_Get_State(d));\r
344             };\r
345             i++;\r
346          };\r
347       };\r
348 \r
349       SYNCDELAY;\r
350       EP2BCL = 0x80; // Re-arm endpoint 2\r
351    };\r
352 }\r
353 \r
354 //-----------------------------------------------------------------------------\r
355 // Handler for Vendor Requests (\r
356 //-----------------------------------------------------------------------------\r
357 \r
358 unsigned char app_vendor_cmd(void)\r
359 {\r
360     // OUT requests. Pretend we handle them all...\r
361     if ((bRequestType & bmRT_DIR_MASK) == bmRT_DIR_OUT){\r
362         if(bRequest == RQ_GET_STATUS){\r
363             Running = 1;\r
364         };\r
365         return 1;\r
366     }\r
367 \r
368     // IN requests.    \r
369     switch (bRequest){\r
370     case 0x90: // Read EEPROM\r
371         { // We need a block for addr\r
372             BYTE addr = (wIndexL<<1) & 0x7F;\r
373             EP0BUF[0] = eeprom[addr];\r
374             EP0BUF[1] = eeprom[addr+1];\r
375             EP0BCL = (wLengthL<2) ? wLengthL : 2; \r
376         }\r
377         break;\r
378         \r
379     case 0x91: // change USB speed\r
380         if (wIndexL == 0){                      // high speed\r
381             CT1 &= ~0x02;\r
382             EP0BUF[0] = 0x2;\r
383             fx2_renumerate();           // renumerate\r
384         }else{                                          // full speed\r
385             CT1 |= 0x02;\r
386             EP0BUF[0] = 0x1;\r
387             fx2_renumerate();           // renumerate\r
388         }\r
389         EP0BCH = 0; // Arm endpoint\r
390         EP0BCL = 1; // # bytes to transfer\r
391         break;\r
392         \r
393     case 0x92: // change JTAG enable\r
394         if (wIndexL == 0){                      // FX2 is master of JTAG\r
395             IOC |= (1 << 7);\r
396         }else{                                          // external connector is master of JTAG\r
397             IOC &= ~(1 << 7);\r
398         }\r
399         EP0BCH = 0; // Arm endpoint\r
400         EP0BCL = 0; // # bytes to transfer\r
401         break;\r
402         \r
403     case 0x93: // change synchronous/asynchronous mode\r
404         if(wIndexL == 0){           // sync\r
405             IFCONFIG &= ~bmASYNC;\r
406             EP0BUF[0] = 0;\r
407         }else{\r
408             IFCONFIG |= bmASYNC;    // async\r
409             EP0BUF[0] = 1;\r
410         }\r
411         EP0BCH = 0; // Arm endpoint\r
412         EP0BCL = 1; \r
413         break;\r
414     \r
415     case 0x94: // get Firmware version\r
416         {\r
417           int i=0;\r
418           char* ver=FWVERSION;\r
419           while(ver[i]!='\0'){\r
420             EP0BUF[i]=ver[i];\r
421             i++;\r
422           }\r
423           EP0BCH = 0; // Arm endpoint\r
424           EP0BCL = i; \r
425           break;\r
426         }\r
427     default:\r
428         // dummy data\r
429         EP0BUF[0] = 0x36;\r
430         EP0BUF[1] = 0x83;\r
431         EP0BCH = 0;\r
432         EP0BCL = (wLengthL<2) ? wLengthL : 2;\r
433     }\r
434   \r
435   return 1;\r
436 }\r
437 \r
438 //-----------------------------------------------------------------------------\r
439 \r
440 static void main_loop(void)\r
441 {\r
442   while(1)\r
443   {\r
444     if(usb_setup_packet_avail()) usb_handle_setup_packet();\r
445     usb_jtag_activity();\r
446   }\r
447 }\r
448 \r
449 //-----------------------------------------------------------------------------\r
450 \r
451 void main(void)\r
452 {\r
453   EA = 0; // disable all interrupts\r
454 \r
455   usb_jtag_init();\r
456   eeprom_init();\r
457   setup_autovectors ();\r
458   usb_install_handlers ();\r
459 \r
460 \r
461   EA = 1; // enable interrupts\r
462 \r
463   fx2_renumerate(); // simulates disconnect / reconnect\r
464 \r
465   main_loop();\r
466 }\r
467 \r
468 \r
469 \r
470 \r