El propósito de el siguiente DigiKey Coffee Cup demo es demonstrar como se puede implementar un USB Virtual JTAG interface en el Intel® Cyclone® 10 LP FPGA con un proceso de 60nm optimizado para bajo consumo de potencia eléctrica,
Este Intel® Cyclone® 10 LP Kit de evaluación de bajo costo es una plataforma fácil de usar para obtener una evaluación de las características del Intel® Cyclone® 10 LP FPGA
Las cualidades importantes del Intel® Cyclone® 10 LP FPGA kit de evuluación son:
• Intel Cyclone 10 LP FPGA (10CL025, U256 package)
• Intel Enpirion® EN5329QI/EN5339QI - 2A/3A PowerSoC Low Profile Synchronous
Buck DC-DC Converter with Integrated Inductor
• Intel Enpirion EP5358xUI 600 mA PowerSoC DC-DC Step-Down Converters with
Integrated Inductor
• Intel XWAY PHY11G Gigabit Ethernet PHY PEF7071
• Intel MAX® 10 FPGA 10M08SAU169C8G (Embedded Intel FPGA Download Cable II
and System Management)
Programming and Configuration
• Embedded Intel FPGA Download Cable II (JTAG)
• Optional JTAG direct through 10-pin header
• Active Serial x1 configuration from EPCQ or EPCQ-A flash
Memory Devices
• 128 megabit (Mb) 8-bit HyperRAM with HBMC (Hyperbus Memory Controller) IP
provided by Synaptic Labs
• Flash memory:
— Rev A1 board: 64 Mb EPCQ
— Rev A2 board: 128 Mb EPCQ-A
Communication Ports
• One Gigabit Ethernet (GbE) RJ-45 port
• One 2x20 GPIO Expansion Header
• One Arduino UNO R3 type connectors
• One 12-pin Digilent Pmod compatible connector
Clock Circuits
• Silicon Labs Si510 50 MHz crystal oscillator
• Silicon Labs Si5351 clock generator with programmable frequency GUI
Power Supply
• USB Y-cable (USB Type-A to mini Type-B) for both on-board Intel FPGA Download
Cable II and 5 V power supply from USB port
• Supplemental 5 V DC power adapter option (5 V power adapter and cord are not
included in the kit)
Por favor proceda a instalar el FPGA software de desarrolllo localizado en este link.
Despues de completar la instalación del Quartus Lite (y usando el vjtag IP core desde el Quartus Project) se procede a usar el siguiente archivo de definición del projecto blink.tcl
# File: blink.tcl
# Load Quartus Prime Tcl Project package
package require ::quartus::project
set need_to_close_project 0
set make_assignments 1
# Check that the right project is open
if {[is_project_open]} {
if {[string compare $quartus(project) "blink"]} {
puts "Project blink is not open"
set make_assignments 0
}
} else {
# Only open if not already open
if {[project_exists blink]} {
project_open -revision blink blink
} else {
project_new -revision blink blink
}
set need_to_close_project 1
}
# Make assignments
if {$make_assignments} {
set_global_assignment -name FAMILY "Cyclone 10 LP"
set_global_assignment -name DEVICE 10CL025YU256I7G
set_global_assignment -name TOP_LEVEL_ENTITY "blink"
set_global_assignment -name ORIGINAL_QUARTUS_VERSION 24.1STD.0
set_global_assignment -name PROJECT_CREATION_TIME_DATE "20:12:02 November, 19 2025"
set_global_assignment -name LAST_QUARTUS_VERSION "24.1std.0 Lite Edition"
set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files
set_global_assignment -name MIN_CORE_JUNCTION_TEMP "-40"
set_global_assignment -name MAX_CORE_JUNCTION_TEMP 100
set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 1
set_global_assignment -name VERILOG_FILE blink.v
set_global_assignment -name VHDL_FILE jtag.vhd
set_global_assignment -name VERILOG_FILE vjtag/synthesis/vjtag.v -library vjtag
set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top
set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top
set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
set_global_assignment -name SDC_FILE SDC1.sdc
set_location_assignment PIN_L14 -to LED0
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LED0
set_location_assignment PIN_K15 -to LED1
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LED1
set_location_assignment PIN_J14 -to LED2
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LED2
set_location_assignment PIN_J13 -to LED3
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LED3
set_location_assignment PIN_E1 -to clk
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to clk
set_location_assignment PIN_E15 -to KEY0
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to KEY0
set_location_assignment PIN_F14 -to KEY1
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to KEY1
set_location_assignment PIN_C11 -to KEY2
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to KEY2
set_location_assignment PIN_D9 -to KEY3
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to KEY3
set_location_assignment PIN_M16 -to DIP0
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DIP0
set_location_assignment PIN_A8 -to DIP1
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DIP1
set_location_assignment PIN_A9 -to DIP2
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DIP2
set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top
# Commit assignments
export_assignments
# Close project
if {$need_to_close_project} {
project_close
}
}
El archivo blink.tcl configura los archivos HDL relevantes al projecto, y las señales que aplican al universo externo del Intel® Cyclone® 10 LP FPGA. En este VJTAG demo se usan los siguientes LEDs (LED0 and LED1) como salidas y el KEY3 como un reset de entrada asincronica en el Intel® Cyclone® 10 LP FPGA evaluation kit. También hay que incluir el archivo de restricciones temporales SDC1.sdc en el directorio del projecto,
# inform Quartus that the clk port brings a 50MHz clock into our design so
# that timing closure on our design can be analyzed
create_clock -name clk -period "50MHz" [get_ports clk]
# inform Quartus that the LED output port has no critical timing requirements
# it’s a single output port driving an LED, there are no timing relationships
# that are critical for this
set_false_path -from * -to [get_ports LED]
Ahora se procede a crear el archivo blink.v (verilog HDL modus operandi) el cuál es el archivo principal así,
// Digikey Coffee Cup Virtual JTAG interface demonstration for Cyclone 10 board
module blink (
input wire clk, // 50MHz input clock
input wire DIP0, // input DIP SWITCH 0
input wire DIP1, // input DIP SWITCH 1
input wire DIP2, // input DIP SWITCH 2
input wire KEY0, // input KEY SWITCH 0
input wire KEY1, // input KEY SWITCH 1
input wire KEY2, // input KEY SWITCH 2
input wire KEY3, // input KEY SWITCH 3
output wire LED0, // LED0 output
output wire LED1, // LED1 output
output wire LED2, // LED2 output
output wire LED3 // LED3 output
);
//=======================================================
// vJTAG
//=======================================================
wire tdi, tdo, tck, v_cdr, v_sdr, v_udr;
wire [3:0] ir_in;
vjtag u0 (
.tdi (tdi), // jtag.tdi
.tdo (tdo), // .tdo
.ir_in (ir_in), // .ir_in
.ir_out (), // .ir_out
.virtual_state_cdr (v_cdr), // .virtual_state_cdr
.virtual_state_sdr (v_sdr), // .virtual_state_sdr
.virtual_state_e1dr (), // .virtual_state_e1dr
.virtual_state_pdr (), // .virtual_state_pdr
.virtual_state_e2dr (), // .virtual_state_e2dr
.virtual_state_udr (v_udr), // .virtual_state_udr
.virtual_state_cir (), // .virtual_state_cir
.virtual_state_uir (), // .virtual_state_uir
.tck (tck) // tck.clk
);
//=======================================================
// jtag control
//=======================================================
reg d0, d1;
wire bypass_state, write_state, read_state;
jtag #(.JTAG_BYPASS("0000"),.JTAG_READ("0001"),.JTAG_WRITE("0010")) jtag_inst (
//Reset
.aclr(~KEY3),
//vJTAG interface
.tdi(tdi),
.tdo(tdo),
.ir_in(ir_in),
.v_cdr(v_cdr),
.v_sdr(v_sdr),
.v_udr(v_udr),
.tck(tck),
//leds
.d0(LED0), .d1(LED1)
);
endmodule
También hemos desarrollado en DigiKey en tiempo de Planck el archivo VHDL Virtual JTAG correspondiente,
-- DigiKey Coffee Cup Virtual JTAG
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
-- This JTAG controller
entity jtag is
generic (
-- virtual JTAG instruction register width
VJTAG_IR_WIDTH : natural := 4;
-- user defined IR Instructions
JTAG_BYPASS : std_logic_vector(3 downto 0) := "0000";
JTAG_READ : std_logic_vector(3 downto 0) := "0001";
JTAG_WRITE : std_logic_vector(3 downto 0) := "0010";
PARALLEL_SIZE: natural := 2;
WRITE_PARALLEL_SIZE: natural := 2
);
port (
-- virtual JTAG Control Interface --
-- virtual JTAG cLock
tck : in std_logic;
-- virtual JTAG input data
tdi : in std_logic;
-- virtual JTAG output data
tdo : out std_logic;
-- virtual JTAG asynchronous clear
aclr : in std_logic;
-- virtual JTAG instruction register used to command write and read commands from host
ir_in : in std_logic_vector(VJTAG_IR_WIDTH-1 downto 0);
-- virtual JTAG input shift data register (sdr) state discrete
v_sdr : in std_logic;
-- virtual JTAG input update data register (udr) state discrete
v_udr : in std_logic;
-- virtual JTAG input capture data register (cdr) state discrete
v_cdr : in std_logic;
-- collect data from other module
data : in std_logic_vector(PARALLEL_SIZE-1 downto 0);
-- output discrete control
d0, d1: out std_logic
);
end jtag;
architecture arch of jtag is
-- JTAG State Machine
-- state names
type state_t is (JTAG_BYPASS_STATE, JTAG_WRITE_STATE, JTAG_READ_STATE);
-- state signals
signal state, next_state : state_t;
--registers
signal DR0 : std_logic_vector(1 downto 0);
signal DR1 : std_logic_vector(PARALLEL_SIZE-1 downto 0);
signal DR2 : std_logic_vector(WRITE_PARALLEL_SIZE-1 downto 0);
begin
-- state machine
-- combinational logic
fsm : process (state)
begin
next_state <= state;
case state is
-- jtag bypass state
when JTAG_BYPASS_STATE =>
tdo <= DR0(0);
if ir_in = JTAG_BYPASS then
next_state <= JTAG_BYPASS_STATE;
elsif ir_in = JTAG_READ then
next_state <= JTAG_READ_STATE;
elsif ir_in = JTAG_WRITE then
next_state <= JTAG_WRITE_STATE;
end if;
-- jtag write state
when JTAG_WRITE_STATE =>
tdo <= DR2(0);
if ir_in = JTAG_BYPASS then
next_state <= JTAG_BYPASS_STATE;
elsif ir_in = JTAG_READ then
next_state <= JTAG_READ_STATE;
elsif ir_in = JTAG_WRITE then
next_state <= JTAG_WRITE_STATE;
end if;
-- jtag read state
when JTAG_READ_STATE =>
tdo <= DR1(0);
if ir_in = JTAG_BYPASS then
next_state <= JTAG_BYPASS_STATE;
elsif ir_in = JTAG_READ then
next_state <= JTAG_READ_STATE;
elsif ir_in = JTAG_WRITE then
next_state <= JTAG_WRITE_STATE;
end if;
end case;
end process;
-- latch the next state
process (tck, aclr)
begin
if aclr = '1' then
state <= JTAG_BYPASS_STATE;
elsif rising_edge(tck) then
state <= next_state;
end if;
end process;
-- sequential logic
process (tck, aclr)
begin
if aclr = '1' then
DR1(PARALLEL_SIZE-1 downto 0) <= (DR1'range => '0');
DR2(WRITE_PARALLEL_SIZE-1 downto 0) <= (DR2'range => '0');
elsif rising_edge(tck) then
-- jtag bypass
if state = JTAG_BYPASS_STATE then
if v_sdr = '1' then
DR0(1 downto 0) <= tdi & DR0(1); --shift right to bypass using DR0 2 bit register
end if;
end if;
-- jtag read
if state = JTAG_READ_STATE then
if v_cdr = '1' then
DR1 <= data; --capture data
end if;
if v_sdr = '1' then
DR1(PARALLEL_SIZE-1 downto 0) <= tdi & DR1(PARALLEL_SIZE-1 downto 1) ; --shift right to read using DR1 register
end if;
end if;
-- jtag write
if state = JTAG_WRITE_STATE then
if v_sdr = '1' then
DR2(WRITE_PARALLEL_SIZE-1 downto 0) <= tdi & DR2(WRITE_PARALLEL_SIZE-1 downto 1) ; --shift right to write using DR2 register
end if;
end if;
end if;
end process;
-- update do from DR2 register after PARALLEL_SIZE-1 samples and reset counter afterwards
-- sequential logic
update_dr1: process (tck, v_udr)
variable counter: natural range 0 to WRITE_PARALLEL_SIZE-1;
begin
if (ir_in = JTAG_WRITE and v_udr = '1') then
counter := counter + 1;
if counter=WRITE_PARALLEL_SIZE-1 then
d0 <= DR2(0);
d1 <= DR2(1);
counter := 0;
end if;
end if;
end process;
end architecture arch;
Finalmente, estamos listos para construir los HDLs y completar el proceso de síntesis y programación en el Field Programmable Gate Array canvas de arte como sigue,
digikey_coffee_cup # quartus_sh -t blink.tcl
En esta etapa del demo, se procede a realizar una compilación completa,
digikey_coffee_cup # quartus_sh --flow compile blink
Finalmente se programa el FPGA como se muestra a continuación (En este momento se asume que el FPGA dev kit esta conectado vía el cable de la interfáz USB conectado a la computadora del usuario,
digikey_coffee_cup # quartus_pgm -m jtag -o "p;output_files/blink.sof"
Despues de que se completen una colección de procesos típicos, entonces el terminal debe de mostrar lo siguiente si no hay ningún problema en el proceso,
.............
Info: Command: quartus_pgm -m jtag -o p;output_files/blink.sof
Info (213045): Using programming cable "Cyclone 10 LP Evaluation Kit [1-1]"
Info (213011): Using programming file output_files/blink.sof with checksum 0x0014BE09 for device 10CL025YU256@1
Info (209060): Started Programmer operation at Wed Nov 19 22:54:29 2025
Info (209016): Configuring device index 1
Info (209017): Device 1 contains JTAG ID code 0x020F30DD
Info (209007): Configuration succeeded -- 1 device(s) configured
Info (209011): Successfully performed operation(s)
Info (209061): Ended Programmer operation at Wed Nov 19 22:54:30 2025
Info: Quartus Prime Programmer was successful. 0 errors, 0 warnings
Info: Peak virtual memory: 292 megabytes
Info: Processing ended: Wed Nov 19 22:54:30 2025
Info: Elapsed time: 00:00:01
Info: Total CPU time (on all processors): 00:00:01
Ahora estamos listos para usar el siguiente archivo llamado jtag.tcl para comunicarse con el FPGA desde la computadora del usuario vía el “soft” alias interfáz Virtual JTAG (Emulando el hard JTAG de la vieja escuela),
# DigiKey Coffee Cup VJTAG Demo for Altera Cyclone 10 LP
#Write to JTAG
proc LEDSON {} {
device_virtual_ir_shift -instance_index 0 -ir_value 2 -no_captured_ir_value
set send_data "11"
device_virtual_dr_shift -dr_value $send_data -instance_index 0 -length 2
}
#Write to JTAG
proc LEDSOFF {} {
device_virtual_ir_shift -instance_index 0 -ir_value 2 -no_captured_ir_value
set send_data "00"
device_virtual_dr_shift -dr_value $send_data -instance_index 0 -length 2
}
#VJTAG
set usb [lindex [get_hardware_names] 0]
set device_name [lindex [get_device_names -hardware_name $usb] 0]
puts "Connected with: "
puts $device_name
open_device -hardware_name $usb -device_name $device_name
device_lock -timeout 10000
puts "Now JTAG 6 MHZ Clock Frequency"
after 1000
while {1} {
LEDSOFF
after 1000
LEDSON
after 1000
}
#JTAG Bypass = 0
device_virtual_ir_shift -instance_index 0 -ir_value 0 -no_captured_ir_value
after 1000
#Close device
catch {device_unlock}
catch {close_device}
Ahora podemos ver el LED0 y el LED1 parpadeando una vez por segundo en el kit del Intel® Cyclone® 10 LP FPGA.en el siguiente video,
Los LEDS verdes, LED2 and LED3 se dejaron flotando en este demo y han sido subidos ( se quedaron prendidos en el demo), si se desea cambiar esto, para apagarlos, entonces se incluyen estas dos lineas en el archivo de blink.v de esta manera,
assign LED2 = 1'b0;
assign LED3 = 1'b0;
En este video se puede observar como se puede escribir a el FPGA y a los correspondientes LED0 y LED1 vIa la interfaz USB VJTAG desde la computadora del usuario. Existe la capacidad de leer los valores del FPGA tambien, pero este demo solo muetra la funcionalidad de escribir. Esta sesion introductiva sirve como un demo para desarollar aplicaciones que pueden usar el interfaz de USB VJTAG para propositos de desarollo. El Intel® Cyclone® 10 LP FPGA evaluation kit en una excelente, portable, kit de desarrollo de bajo costo para muchas aplicaciones de baja potencia y esta disponible enDigiKey. Como una nota de interes el protocolo “Boundary Scan JTAG interface” (IEEE 1149.1) ademas de ser un metodo para programar, probar, con propositos de desarrollo, analisis de falla en circuitos integrados de alta densidad de puertos miniaturizados, se puede usar para detectar falsificacion de estos circuitos integrados en muchos sistemas. Que tenga un buen dia.
This article is also available in english here.
Este artículo esta disponible en ingles aquí.
