Доброго всем времени суток.

Случилось у меня в устройстве что свободных пинов 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):

Hidden text
#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.

Всем отличного дня, радости, счастья и пусть получается так как задумано!

С наступающим летом и спасибо за внимание!!!

Комментарии (1)


  1. DrMefistO
    24.05.2022 09:07
    +1

    Сумбурнее некуда!