Доброго всем времени суток.
Случилось у меня в устройстве что свободных пинов 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.
Всем отличного дня, радости, счастья и пусть получается так как задумано!
С наступающим летом и спасибо за внимание!!!
DrMefistO
Сумбурнее некуда!