Доброго всем времени суток.
Случилось у меня в устройстве что свободных пинов CPLD совсем нет, а нужно добавить ещё каплю функционала, а пинов нет.
Только JTAG...
Про INTEL (ALTERA) Virtual JTAG из разработчиков мало кто не слышал, но документации нет, от слова совсем.
Как достучаться до Virtual JTAG из, например, микроконтроллера?
1. Создаем VirtaulJTAG.
...коротко накидаю, чтоб мозг чем попало не забивать...вот, знакомая картинка, я выбрал три бита на адрес.

2. Нарисуем обработчик.
...и вот тут нам таки поможет официальная документация.
Смотрим код:
Код Verilog
module JTAG_COMPANION( // input core version input [7:0]VDR, // Outputs output [15:0]Q, input [15:0]inDAT ); reg regINCMD = 1'b0; reg [7:0]regOUTDAT = 8'h00; reg regRESDAT = 1'b1; // Signals and registers declared for Virtual JTAG instance wire tck, tdi; wire cdr, e1dr, e2dr, pdr, sdr, udr, uir, cir; reg tdo; wire [2:0]ir_in; //IR command register jtag_iov4 jtag_iov4_inst ( .tdo ( tdo ), .ir_in ( ir_in[2:0] ), .tck ( tck ), .tdi ( tdi ), .ir_out (), .virtual_state_cdr (cdr), .virtual_state_e1dr(e1dr), .virtual_state_e2dr(e2dr), .virtual_state_pdr (pdr), .virtual_state_sdr (sdr), .virtual_state_udr (udr), .virtual_state_uir (uir), .virtual_state_cir (cir) ); /* Registers define */ `define VDR_REG_R 3'b100 /* 0 */ `define DAT_REG_R 3'b101 /* 1 */ `define CMD_REG_R 3'b110 /* 2 */ `define RES_REG_W 3'b000 /* 0 cocpu presents register */ `define DAT_REG_W 3'b001 /* 1 */ `define CMD_REG_C 3'b010 /* 2 Clear Command register */ //data receiver //shifts incoming data during PUSH command reg [7:0]shift_dr_in; always @(posedge tck) begin if(sdr) shift_dr_in <= { tdi, shift_dr_in[7:1] }; end /* Command reception from console */ wire CMD_CLR = udr && (ir_in[2:0] == `CMD_REG_C) && tck; wire CMD_REQ = !(REGACC && !nLWR); always @(posedge CMD_REQ or posedge CMD_CLR) begin if(CMD_CLR) begin regINCMD <= 1'h0; end else begin regINCMD <= 1'h1; /* Command processing request */ end end //data receiver always @(posedge tck) begin if(udr) begin case(ir_in[2:0]) `DAT_REG_W: regOUTDAT[7:0] <= shift_dr_in[7:0]; `RES_REG_W: regRESDAT <= shift_dr_in[0]; endcase end end //data sender reg [7:0]shift_dr_out; always @(posedge tck) begin if(cdr) begin case(ir_in[2:0]) /* Read register to jtag out */ `VDR_REG_R: shift_dr_out[7:0] <= VDR[7:0]; `DAT_REG_R: shift_dr_out[7:0] <= inDAT[7:0]; `CMD_REG_R: shift_dr_out[7:0] <= {regINCMD, inDAT[14:8]}; endcase end else if(sdr) begin shift_dr_out[7:0] <= { tdi, shift_dr_out[7:1] }; end end //pass or bypass data via tdo reg always @* begin tdo = shift_dr_out[0]; end endmodule
Достучаться до Virtual JTAG через стандартные средства INTEL (ALTERA) проблем не вызывает.
3. Используем оснастку quartus_stp.exe.
Вот так например: quartus_stp.exe -t send55.tcl (не забываем устройства соединить как положено UsbByteBlaster->Cpld).
send55.tcl
set usb [lindex [get_hardware_names] 0] set device_name [lindex [get_device_names -hardware_name $usb] 0] puts "*************************" puts "programming cable:" puts $usb #IR scan codes: 001 -> push # 010 -> pop proc push {addr value} { global device_name usb open_device -device_name $device_name -hardware_name $usb if {$value > 255} { return "value entered exceeds 8 bits" } set push_value [int2bits $value] set diff [expr {8 - [string length $push_value]%8}] if {$diff != 8} { set push_value [format %0${diff}d$push_value 0] } puts $push_value device_lock -timeout 10000 device_virtual_ir_shift -instance_index 0 -ir_value $addr -no_captured_ir_value device_virtual_dr_shift -instance_index 0 -dr_value $push_value -length 8 -no_captured_dr_value device_unlock close_device } proc pop {addr} { global device_name usb variable x open_device -device_name $device_name -hardware_name $usb device_lock -timeout 10000 device_virtual_ir_shift -instance_index 0 -ir_value $addr -no_captured_ir_value set x [device_virtual_dr_shift -instance_index 0 -length 8] device_unlock close_device puts $x } proc int2bits {i} { set res "" while {$i>0} { set res [expr {$i%2}]$res set i [expr {$i/2}]} if {$res==""} {set res 0} return $res } # Read register VDR_REG_R pop 8 # Write register DAT_REG_W push 1 0x00
Код демонстрирует чтение из регистра 8 и запись в регистр 1 используя USB Byte Blaster и оснастку quartus_stp через VirtualJtag.
А нам нужно чтобы микроконтроллер делал то же самое.
4. Компилируем.
Зашиваем в CPLD, подключаемся через SignalTAP используя к примеру DE0nano чтоб увидеть как работает на аппаратном уровне.
Включаем SignalTAP... и видим вот какую интересную картинку, точнее её часть:

5. Перерисовываем в код.
По отчету SignalTAP видим какие биты меняются, а какие остаются неизменными при изменении параметров передачи. Перерисовываем в код эти интересности и получаем (я надеюсь догадаетесь что делаю функции set_tck и set_tdi, код можно использовать на любом доступном оборудовании которое может управлять выводами GPIO):
vrt_jtag.c
#include "vrt_jtag.h" int pulse_tck_hi(void) { int iOut = (JTAG_GPIO->IDR >> TDO_BIT) & 1; set_tck(1); set_tck(0); return iOut; } int sendBytes(uint8_t bTMS, uint8_t bTDI) { int i; int iRet = 0; for(i = 0;i < 8;i++) { set_tms(bTMS & 0x01); bTMS >>= 1; set_tdi(bTDI & 0x01); bTDI >>= 1; pulse_tck_hi(); if((JTAG_GPIO->IDR >> TDO_BIT) & 1) iRet |= 1 << i; } return iRet; } int jbi_jtag_send_byte(uint8_t bData) { int i; int iRet = 0; for(i = 0;i < 8;i++) { set_tdi(bData & 0x01); bData >>= 1; pulse_tck_hi(); if((JTAG_GPIO->IDR >> TDO_BIT) & 1) iRet |= 1 << i; } return iRet; } void jbi_vrt_jtag_seq(uint8_t addr) { int i; set_tck(0); sendBytes(0xBF, 0xFF); sendBytes(0x00, 0xFF); jbi_jtag_send_byte(0x03); jbi_jtag_send_byte(0xB3); jbi_jtag_send_byte(0x3F); jbi_jtag_send_byte(0xFC); for(i = 0;i < 11; i++) jbi_jtag_send_byte(0xFF); sendBytes(0x3C, 0xFF); // 11h jbi_jtag_send_byte(0xFF); jbi_jtag_send_byte(0xC0); jbi_jtag_send_byte(0xEC); jbi_jtag_send_byte(0x0F); for(i = 0;i < 12; i++) jbi_jtag_send_byte(0xFF); sendBytes(0x0F, 0xFF);// 22h jbi_jtag_send_byte(0x3F); jbi_jtag_send_byte(0x30); jbi_jtag_send_byte(0xFB); jbi_jtag_send_byte(0xC3); for(i = 0;i < 11; i++) jbi_jtag_send_byte(0xFF); sendBytes(0xC0, 0xFF);// 32h sendBytes(0x01, 0xFF);// 33h jbi_jtag_send_byte(0x07); jbi_jtag_send_byte(0x66); jbi_jtag_send_byte(0x7F); jbi_jtag_send_byte(0xF8); for(i = 0;i < 11; i++) jbi_jtag_send_byte(0xFF); sendBytes(0x78, 0xFF);// 43h sendBytes(0x00, 0xFF);// 44h sendBytes(0x9C, 0x7F);// 45h sendBytes(0x07, 0xDF);// 46h sendBytes(0xC0, 0x81);// 47h sendBytes(0x01, 0x07);// 48h for(i = 0;i < 7; i++) jbi_jtag_send_byte(0x00); sendBytes(0x3C, 0xF8);// 50h sendBytes(0x00, 0x0C);// 51h for(i = 0;i < 16; i++) sendBytes(0x0E, 0x3C); sendBytes(0x1E, 0x7C);// 62h sendBytes(0x00, 0x07);// 63h /* IR Reg */ sendBytes(0x07, 0x1E | (addr << 5));// 64h /* 07, DE mask 0xE0*/ sendBytes(0x1E, 0x7E | ((addr >> 3) & 1));// 65h /* 7E, 7F mask 0x01*/ sendBytes(0x00, 0x06);// 66h return; } void jtag_vrt_reg_set(uint8_t addr, uint8_t data) { int i; jbi_vrt_jtag_seq(addr); /* DR Reg */ sendBytes(0x07, 0x1E | (data << 5));// 67h /* 07, FE-1E mask 0xE0*/ sendBytes(0xF0, 0xE0 | (data >> 3));// 68h /* F0, F0-EF mask 0x1F*/ sendBytes(0x0F, 0x0F);// 69h /* 1F, 1F*/ for(i = 0;i < 6; i++) pulse_tck_hi(); return; } int jtag_vrt_reg_get(uint8_t addr) { int i, iRet; addr |= BIT_READ; jbi_vrt_jtag_seq(addr); /* DR Reg */ iRet = (sendBytes(0x07, 0x1E) >> 4) & 0xF;// 67h iRet |= (sendBytes(0x00, 0x00) << 4) & 0xF0;// 68h sendBytes(0xF0, 0xEA);// 69h sendBytes(0x0F, 0x0F);// 6Ah for(i = 0;i < 6; i++) pulse_tck_hi(); return iRet; } uint8_t GetCOREVersion(void) { return jtag_vrt_reg_get(VDR_REG_R); }
6. Что нам это дало?
Мы можем общаться с нашим CPLD ядром используя выводы JTAG которые в конечном устройстве, у нас, нужны были только для записи прошивки ядра CPLD.
Всем отличного дня, радости, счастья и пусть получается так как задумано!
С наступающим летом и спасибо за внимание!!!