Зачастую если в устройстве есть программируемая логика, присутствует и процессор/микроконтроллер.
В какой-то момент мне надоело разводить на платах разъем JTAG, он занимает много места на плате и по сути нужен только для разработки. В конечном устройстве он вообще без надобности.
Очень часто для проверки правильности реализации Verilog кода или вообще «посмотреть как сигнальчики бегают» я использую SignalTap II Logic Analizer, штука удобная и наглядная, я думаю многие сразу узнают по изображению:

Но как же отлаживать само устройство и в частности программируемую логику без JTAG?
Я уж молчу о записи прошивок в CPLD на этапе производства.
А у нас есть микроконтроллер.
Возьмем к примеру микроконтроллер STM32F103RCT6 и реализуем в нем USB Byte Blaster. И возьмем CPLD EPM3064.

Разумеется я имею ввиду что микроконтроллер уже подключен к соответствующим выводам программируемой логики, например вот так:
PC12->TDI
PC13->TMS
PC14->TCK
PC15->TDO
USB Устройство начинается с дескриптора, опишем его:
USB Descriptor
/* USB Standard Device Descriptor */ const BYTE USB_DeviceDescriptor[] = { USB_DEVICE_DESC_SIZE, /* bLength */ USB_DEVICE_DESCRIPTOR_TYPE, /* bDescriptorType */ WBVAL(0x0110), /* 1.10 */ /* bcdUSB */ 0x00, // Class Code 0x00, // Subclass code 0x00, // Protocol code USB_MAX_PACKET0, // Max packet size for EP0, see usbcfg.h WBVAL(0x09FB), /* idVendor */ WBVAL(0x6001), /* idProduct */ WBVAL(0x0400), /* 1.00 */ /* bcdDevice */ 0x01, // Manufacturer string index 0x02, // Product string index 0x03, // Device serial number string index 0x01 // Number of possible configurations }; /* USB Configuration Descriptor */ /* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */ const BYTE USB_ConfigDescriptor[] = { /* Configuration Descriptor */ USB_CONFIGUARTION_DESC_SIZE, /* bLength */ USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */ /* Configuration 1 */ WBVAL( /* wTotalLength */ (1*USB_CONFIGUARTION_DESC_SIZE) + (1*USB_INTERFACE_DESC_SIZE) + (2*USB_ENDPOINT_DESC_SIZE) ), 1, // Number of interfaces in this cfg 1, // Index value of this configuration 0, // Configuration string index USB_CONFIG_SELF_POWERED, /*|*/ /* bmAttributes */ USB_CONFIG_POWER_MA(80), /* bMaxPower */ /* Interface Descriptor */ USB_INTERFACE_DESC_SIZE, /* bLength */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ /* Interface 0, Alternate Setting 0, MSC Class */ 0x00, /* bInterfaceNumber */ 0x00, /* bAlternateSetting */ 0x02, /* bNumEndpoints */ 0xFF, // Class code 0xFF, // Subclass code 0xFF, // Protocol code 0, // Interface string index /* Endpoint Descriptor */ /* Bulk In Endpoint */ USB_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_IN(BLST_EP_IN & 0x0F), /* bEndpointAddress */ USB_ENDPOINT_TYPE_BULK, /* bmAttributes */ WBVAL(BLST_MAX_PACKET), /* wMaxPacketSize */ 10, //Interval USB_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_OUT(BLST_EP_OUT & 0x0F), /* bEndpointAddress */ USB_ENDPOINT_TYPE_BULK, /* bmAttributes */ WBVAL(BLST_MAX_PACKET), /* wMaxPacketSize */ 10 //Interval /* Terminator */ ,0 /* bLength */ }; //Language code string descriptor ROM struct{BYTE bLength;BYTE bDscType;WORD string[1];}sd000={ sizeof(sd000),USB_STRING_DESCRIPTOR_TYPE,{0x0409}}; //Manufacturer string descriptor ROM struct{BYTE bLength;BYTE bDscType;WORD string[6];}sd001={ sizeof(sd001),USB_STRING_DESCRIPTOR_TYPE, {'A','l','t','e','r','a'}}; //Product string descriptor ROM struct{BYTE bLength;BYTE bDscType;WORD string[11];}sd002={ sizeof(sd002),USB_STRING_DESCRIPTOR_TYPE, {'U','S','B','-','B','l','a','s','t','e','r'}}; //Serial string descriptor ROM struct{BYTE bLength;BYTE bDscType;WORD string[8];}sd003={ sizeof(sd003),USB_STRING_DESCRIPTOR_TYPE, {'0','0','0','0','0','0','0','0'}}; //Array of configuration descriptors ROM BYTE *ROM USB_CD_Ptr[]= { (ROM BYTE *)&USB_ConfigDescriptor }; //Array of string descriptors ROM BYTE *ROM USB_SD_Ptr[]= { (ROM BYTE *)&sd000, (ROM BYTE *)&sd001, (ROM BYTE *)&sd002, (ROM BYTE *)&sd003 }; /* USB String Descriptor (optional) */ const BYTE *USB_StringDescriptor = (const BYTE *)USB_SD_Ptr;
Далее нам нужно прописать саму «начинку» Byte Blaster’a:
blaster.c
BYTE fifo_wp,fifo_rp; BYTE InFIFO[256]; BYTE nCS = 0; DWORD PacketPos = 0; BYTE InPacket[64]; BYTE OutPacket[128]; BYTE ep1_ready = 1; void EP2CallBack(void) { ProcBlasterData(); } void ProcBlasterData(void) { int bufptr = 0; static BYTE jtag_byte = 0, read = 0, aser_byte = 0; DWORD recv_byte; BYTE acc0, acc1; recv_byte = USB_ReadEP(BLST_EP_OUT, OutPacket); bufptr = 0; if(!recv_byte) return; LED_RD_ON(); do { if (jtag_byte) { if (!read) { do { acc0 = OutPacket[bufptr++]; JTAG_Write(acc0); jtag_byte--; recv_byte--; } while (jtag_byte && recv_byte); } else { do { acc0 = OutPacket[bufptr++]; acc1 = JTAG_RW(acc0); enqueue(acc1); jtag_byte--; recv_byte--; } while (jtag_byte&&recv_byte); } } else if (aser_byte) { if (!read) { do { acc0 = OutPacket[bufptr++]; JTAG_Write(acc0); aser_byte--; recv_byte--; } while (aser_byte&&recv_byte); } else { do { acc0 = OutPacket[bufptr++]; acc1 = ASer_RW(acc0); enqueue(acc1); aser_byte--; recv_byte--; } while (aser_byte&&recv_byte); } } else { do { acc0 = OutPacket[bufptr++]; _bitcopy(bitmask(acc0, 6), read); if (bitmask(acc0, 7)) { //EnterSerialMode LTCK(0); //bug fix if (nCS & 0x8) { //nCS=1:JTAG jtag_byte = acc0 & 0x3F; } else { //nCS=0:ActiveSerial aser_byte = acc0 & 0x3F; } /* Always JTAG Made */ recv_byte--; break; } else { //BitBangMode OUTP(acc0); if (read) { acc1 = 0; if (PADO) acc1 |= 0x02; if (PTDO) acc1 |= 0x01; enqueue(acc1); } recv_byte--; } } while (recv_byte); } } while (recv_byte); /* Disable RD LED */ LED_RD_OFF(); return; } void EP1CallBack(void) { ep1_ready = 1; return; } void OUTP(BYTE b) { unsigned int uiPortState = GPIOC->ODR; /* TCK - 0 TMS - 1 nCE - 2 nCS - 3 x - 5,6 TDI - 4,7 */ if(b & (1 << 3)) /* nCS */ nCS = 0x08; else nCS = 0; if(b & (1 << 0)) /* TCK */ uiPortState |= (1 << 14); else uiPortState &= ~(1 << 14); if(b & (1 << 1)) /* TMS */ uiPortState |= (1 << 13); else uiPortState &= ~(1 << 13); if(b & (1 << 4)) /* TDI */ uiPortState |= (1 << 12); else uiPortState &= ~(1 << 12); GPIOC->ODR = uiPortState; // Nop(); return; } void JTAG_Write(BYTE a) { int i; for(i = 0;i < 8;i ++) { bitcopy(a & (0x01 << i),LTDI); tck(); } } BYTE JTAG_RW(BYTE a) { BYTE bRet=0; int i = 0; for(i = 0;i < 8;i++) { bitcopy(a & (0x01 << i),LTDI); if(PTDO) bRet |= (0x01 << i); tck(); } return bRet; } BYTE ASer_RW(BYTE a) { BYTE bRet=0; int i = 0; for(i = 0;i < 8;i++) { bitcopy(a & (0x01 << 1),LTDI); if(PADO) bRet|= (0x01 << 1); tck(); } return bRet; } extern USB_EP_DATA EP0Data; BYTE bBuffer[10]; void USB_EP0BlasterReq(USB_SETUP_PACKET *SetupPacket) { BYTE bIndex; if (SetupPacket->bmRequestType.BM.Dir == 0) { //0utput //Responce by sending zero-length packet //I don't know if this way is right, but working:) USB_WriteEP(0x80, NULL, 0); return; } if (SetupPacket->bRequest == 0x90) { bIndex = (SetupPacket->wIndex.WB.L << 1) & 0x7E; bBuffer[0] = eeprom_read(bIndex); bBuffer[1] = eeprom_read(bIndex + 1); } else { bBuffer[0] = 0x36; bBuffer[1] = 0x83; } EP0Data.pData = bBuffer; EP0Data.Count = 2; // USB_WriteEP(0x80, bBuffer, 2); return; }
И в финале добавим обработчик в нашу программу. Можно это сделать конечно и в таймере, но для наглядности я добавил функцию обработки в main() в бесконечном цикле.
Смотрим код:
main.c
while (1) { if(USB_Configuration) { if(ep1_ready) { acc0 = fifo_used(); if (62 <= acc0) { //send full packet to host LED_WR_ON(); ep1_ready = 0; dequeue(&InPacket[2], 62); USB_WriteEP(BLST_EP_IN, InPacket, 64); ChargeTimer_ms(10); } else if(acc0 || IsTimerArrived()) { if(acc0) LED_WR_ON(); else LED_WR_OFF(); ep1_ready = 0; if(acc0) dequeue(&InPacket[2], acc0); USB_WriteEP(BLST_EP_IN, InPacket, acc0 + 2); ChargeTimer_ms(10); } } } } // end while
Собираем всё вместе и компилируем и прошиваем, используя вот такой игровой набор:

И подключаем наше устройство к USB.

И это же конечно ничего не значит. Потому что установка происходит только на основании дескриптора устройства, теперь проверим действительно ли это у нас «настоящий» Byte Blaster.
Выбираем:

И запускаем JTAG Chain Debugger:

Работает!!! Можем писать CPLD прям на борту нашего устройства.
К слову сказать если подключить эту реализацию к FPGA, будет доступна запись в FPGA и SignalTap II. У меня всё.
Спасибо за внимание! Успехов в делах и отличного настроения!
ссылка на оригинал статьи https://habr.com/ru/post/534358/
Добавить комментарий