Этот проект начался с дискуссии, которая развернулась на нашем форуме.
Участник нашего форума Nepston просит помочь реализовать небольшой проект на FPGA Cyclone III. Собственно FPGA стоит на девелопер ките Альтеры Cyclone III FPGA Development Kit.
В самом начале задача стояла вот так: с компьютера передать два числа на плату через USB, там сложить их и передать обратно на ПК. Конечно, это только подготовительный этап к какой-то другой работе - видимо на плате будут отрабатываться сложные алгоритмы и нужно подавать им входные данные и читать выходные. Ну второй этап не начать без первого.
Скажу честно, мне сразу показалось, что задача легкая. Однако почитав описание на имеющуюся у Nepston-а плату как-то энтузиазма поубавилось. Все это действительно довольно странно. Плата девелопер кита вроде бы хорошая:
У нее много возможностей: и большой емкий чип FPGA, и куча интерфейсов: ethernet, flash, graphics, character display, DDR, и т.д. Даже USB интерфейса вроде бы два: есть микросхема FTDI и есть микросхема Cypress. Однако при ближайшем рассмотрении оказалось, что не все так радужно. Простого решения как бы и нет...
Проблема состоит в том, что:
- Чип USB FTDI используется для встроенного на плату програматора UsbBlaster в связке с CPLD MAX II. Использовать его для реализации чистой USB функции нельзя.
- Чип Cypress - это всего лишь USB2 PHY. Чтобы его использовать нужно как-то в Cyclone реализовать USB функцию, базирующуюся на PHY. Самому быстро написать врядли можно. Искать какие-то готовые IP наверное можно, но впереди наверняка долгая отладка. Потребуется сильно вникать во все тонкости USB2.
- Была мысль как-то модифицировать наши Марсоходовские USB проекты и встроить их в Cyclone. Наши проекты без всяких микросхем PHY напрямую управляя сигналамми USB dp и dn реализуют USB1.1 функцию. Но, тут то же проблема - в этом Альтеровском девелопер ките большинство банков выходных контактов имеют напряжение +1,8В, что не подходит для этой идеи.
- Еще мысль - использовать Ethernet вместо USB. Но то же не простое решение.
Так что же делать?
Поизучав всякую документацию я осмелюсь предложить способ связи через UsbBlaster и JTAG. Не могу сказать, что это идеал, но возможно, конкретную проблему связи должно решить.
Делать я буду, конечно, на плате Марсоход, но уверен, что точно так же проект будет работать и на любой другой плате с микросхемами Altera.
Для этого проекта я использую компонент Альтеры Virtual Jtag. Есть специальный документ Altera Virtual JTAG Megafunction User Guide, он описывает этот компонент.
Для создания компонента Virtual Jtag в среде Altera Quartus II через пункт меню Tools вызываем MegaWizard Plugin Manager.
Слева в списке Installed Plug-Ins выбираем Virtual JTAG.
Выбираем Verilog и задаем имя компонента myjtag.
Нажимаем кнопку Next.
Дальше нам нужно задать параметры нашего JTAG контроллера - самое главное выбрать длину регистра IR (Instruction Register). Я делаю его 3х битным. Значит всего я смогу реализовать в своем проекте 8 команд передаваемых через JTAG. В принципе, остальные настройки пожалуй и не важны, можно нажать кнопку Finish - компонент готов.
Теперь создадим Top модуль проекта. Я делаю его на языке Verilog.
Исходный код модуля вот здесь:
//module
//receives BYTE from JTAG and displays it on doard LEDs
//TCL script may read state of board KEYs
module jtag_test(
input wire clock80Mhz,
input wire [3:0]key,
output reg [7:0]led
);
// Signals and registers declared for Virtual JTAG instance
wire tck, tdi;
wire cdr, eldr, e2dr, pdr, sdr, udr, uir, cir, tms;
reg tdo;
wire [2:0]ir_in; //IR command register
// Instantiation of Virtual JTAG
myjtag myjtag_inst(
.tdo (tdo),
.tck (tck),
.tdi (tdi),
.ir_in(ir_in),
.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)
);
//data receiver
//shifts incoming data during PUSH command
reg [7:0]shift_dr_in;
always @(posedge tck)
if(sdr && (ir_in==3'b001) )
shift_dr_in <= { tdi, shift_dr_in[7:1] };
//data receiver
//write received data (during PUSH command) into LED register
always @(posedge tck)
if(udr && (ir_in==3'b001) )
led <= shift_dr_in;
//data sender
reg [7:0]shift_dr_out;
always @(posedge tck)
if(cdr && (ir_in==3'b010) )
//capture data for send during command POP
shift_dr_out <= { key[3:0], key[3:0] };
else
if(sdr && (ir_in==3'b010) )
//shift out data durng command POP
shift_dr_out <= { tdi, shift_dr_out[7:1] };
//pass or bypass data via tdo reg
always @*
begin
case(ir_in)
4'b001: tdo = shift_dr_in [0];
4'b010: tdo = shift_dr_out[0];
default:
tdo = tdi;
endcase
end
endmodule
Модуль имеет выходы led[7:0] и входы key[3:0]. Они будут соответственно назначены к светодиодам и кнопочкам платы Марсоход. Сигнал тактовой частоты clock100Mhz есть в проекте, но не используется. Может он вам понадобится. Сигналы JTAG в модуль явно не поступают, но они будут там, так как мы внутрь установили экземпляр созданного нами в визарде компонента myjtag.
Сигналы ir_in компонента myjtag показывают текущую загруженную инструкцию в регистр IR TAP контроллера виртуального JTAG. Регистр мы определили как 3х битный. Возможно всего 8 команд. Я реализовал две команды:
- команда PUSH = 3'b001, записывает в регистр для светодиодов число. Сможем зажигать их по своему желанию с компьютера.
- команда POP = 3'b010, считывает из устройства состояние кнопочек платы.
Все действие происходит по сигналу тактовой частоты tck - это сигнал с JTAG.
Компонент myjtag дает нам несколько полезных сигналов, которые отображают состояние TAP контроллера Virtual JTAG. Так, по сигналу sdr (и если команда PUSH) мы можем принимать в сдвиговый регистр shift_dr_in входные данные tdi. Когда прием окончен мы узнаем по сигналу udr (update DR). В этот момент мы переписываем принятые данные из сдвигового регистра в регистр led.
Примерно так же работает и передача из платы в ПК. При команде POP и сигнале cdr (capture DR) мы подготавливаем данные для передачи. Я делаю 8 бит данных дублируя 4 бита состояния кнопочек key. А вот при команде POP и sdr данные выдвигаются.
Если команда не PUSH и не POP, то tdo просто подключено к tdi.
Вот как бы и все. Можно компилировать проект и зашивать в плату Марсоход.
Весь проект можно взять здесь:
Ну а что дальше?
Дальше нужно писать программу для ПК для передачи и приема данных в плату через JTAG.
Дело это не простое. Я выбрал для этого язык программирования TCL Scripting.
Других вариантов к сожалению предложить не могу. Все, что я здесь пишу во многом опирается на разные примеры, которые мне удалось разыскать в интернете и на сайте Альтеры. Вот все примеры ведут к TCL.
Посоветую почитать вот этот документ с сайта Альтеры TCL Scripting.
Вот мой 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 {value} {
global device_name usb
open_device -device_name $device_name -hardware_name $usb
if {$value > 256} {
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 1 -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 {} {
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 2 -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
}
proc bin2hex bin {
## No sanity checking is done
array set t {
0000 0 0001 1 0010 2 0011 3 0100 4
0101 5 0110 6 0111 7 1000 8 1001 9
1010 a 1011 b 1100 c 1101 d 1110 e 1111 f
}
set diff [expr {4-[string length $bin]%4}]
if {$diff != 4} {
set bin [format %0${diff}d$bin 0] }
regsub -all .... $bin {$t(&)} hex
return [subst $hex]
}
puts "read board KEYs:"
pop
puts "write board LEDs:"
push 0x55
Этот скрипт ищет устройство программирования UsbBlaster, читает через JTAG порт состояние кнопочек платы Марсоход и выводит на ее светодиоды число 0x55.
Собственно в скрипте для чтения из платы есть функция pop, а для записи в плату есть функция push.
В принципе скрипт может быть сколько угодно сложным, он может читать и писать файлы, передавать много данных и прочее. Нужно изучать писать TCL скрипт. Вы это сделаете сами по вашим потребностям.
Запусатить конкретно этот скрипт можно командой quartus_sta -t send55.tcl.
Здесь send55.tcl - это имя моего скрипта. Запускаем в командной строке и видим:
c:\Altera\marsohod\vjtag>quartus_stp -t send55.tcl
Info: *******************************************************************
Info: Running Quartus II SignalTap II
Info: Version 10.1 Build 197 01/19/2011 Service Pack 1 SJ Web Edition
Info: Copyright (C) 1991-2011 Altera Corporation. All rights reserved.
Info: Your use of Altera Corporation's design tools, logic functions
Info: and other software and tools, and its AMPP partner logic
Info: functions, and any output files from any of the foregoing
Info: (including device programming or simulation files), and any
Info: associated documentation or information are expressly subject
Info: to the terms and conditions of the Altera Program License
Info: Subscription Agreement, Altera MegaCore Function License
Info: Agreement, or other applicable license agreement, including,
Info: without limitation, that your use is for the sole purpose of
Info: programming logic devices manufactured by Altera and sold by
Info: Altera or its authorized distributors. Please refer to the
Info: applicable agreement for further details.
Info: Processing started: Fri Oct 28 10:07:24 2011
Info: Command: quartus_stp -t send55.tcl
*************************
programming cable:
USB-Blaster [USB-0]
read board KEYs:
01110111
write board LEDs:
01010101
Info: Evaluation of Tcl script send55.tcl was successful
Info: Quartus II SignalTap II was successful. 0 errors, 0 warnings
Info: Peak virtual memory: 139 megabytes
Info: Processing ended: Fri Oct 28 10:07:25 2011
Info: Elapsed time: 00:00:01
Info: Total CPU time (on all processors): 00:00:00
c:\Altera\marsohod\vjtag>
Байт состояния кнопочек платы Марсоход принят, видно, что нажата одна кнопка из четырех (код 0111).
Байт установки светодиодов LEDs передан 01010101. На самой плате загораются светодиоды:
В принципе можно усложнять скрипты делая бегущие огни или что-то еще.
Вот так, используя программатор UsbBlaster можно управлять платой через JTAG.
PS: Для реализации этого проекта был использован пример с сайта Альтеры: FPGA Debugging Example with Sources, Probes, and Virtual JTAG Там более сложный пример и сделан он для Cyclone III на VHDL.
Подробнее...