Discussion:
JTAG to Wishbone bridge for debugging in Vivado Tcl console
(too old to reply)
Wojciech M. Zabołotny
2018-12-30 16:32:22 UTC
Permalink
Archive-name: jtag2wb
Submitted-by: ***@gmail.com
Last-modified: 2018-12-10

This is the code of the JTAG to Wishbone bridge for Xilinx FPGAs.
This is the free code published as PUBLIC DOMAIN or under Creative
Commons CC0 license.
I do not provide warranty of any kind. If you use that code, you
do it on your own risk.

The Tcl code offers just three functions:

bus_init
must be called to get access to the controller
(Please note, that you may need to adapt this procedure
so that your JTAG programmer is correctly found).

bus_write address value
Writes to the register under specific address.

bus_read address
Reads the value from the register under specific address
and returns it.

This is a "quick&dirty" implementation. so probably the code
may be significantly improved or corrected.
It worked and appeared to be useful for me, so I decided to share it.

With best regards,
Wojtek

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.15.2).
# To extract the files from this archive, save it to some FILE, remove
# everything before the '#!/bin/sh' line above, then type 'sh FILE'.
#
lock_dir=_sh02322
# Made on 2018-12-30 17:21 CET by <***@wzab>.
# Source directory was '/tmp/jtag2wb'.
#
# Existing files will *not* be overwritten, unless '-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 2329 -rw-r--r-- jtag2wb.tcl
# 10251 -rw-r--r-- jtag2wb.vhd
#
MD5SUM=${MD5SUM-md5sum}
f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'`
test -n "${f}" && md5check=true || md5check=false
${md5check} || \
echo 'Note: not verifying md5sums. Consider installing GNU coreutils.'
if test "X$1" = "X-c"
then keep_file=''
else keep_file=true
fi
echo=echo
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=
locale_dir=
set_echo=false

for dir in $PATH
do
if test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
case `$dir/gettext --version 2>&1 | sed 1q` in
*GNU*) gettext_dir=$dir
set_echo=true
break ;;
esac
fi
done

if ${set_echo}
then
set_echo=false
for dir in $PATH
do
if test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
set_echo=true
break
fi
done

if ${set_echo}
then
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
fi
IFS="$save_IFS"
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null
then if (echo -n test; echo 1,2,3) | grep n >/dev/null
then shar_n= shar_c='
'
else shar_n=-n shar_c= ; fi
else shar_n= shar_c='\c' ; fi
f=shar-touch.$$
st1=200112312359.59
st2=123123592001.59
st2tr=123123592001.5 # old SysV 14-char limit
st3=1231235901

if touch -am -t ${st1} ${f} >/dev/null 2>&1 && \
test ! -f ${st1} && test -f ${f}; then
shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'

elif touch -am ${st2} ${f} >/dev/null 2>&1 && \
test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'

elif touch -am ${st3} ${f} >/dev/null 2>&1 && \
test ! -f ${st3} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$2 "$8"'

else
shar_touch=:
echo
${echo} 'WARNING: not restoring timestamps. Consider getting and
installing GNU '\''touch'\'', distributed in GNU coreutils...'
echo
fi
rm -f ${st1} ${st2} ${st2tr} ${st3} ${f}
#
if test ! -d ${lock_dir} ; then :
else ${echo} "lock directory ${lock_dir} exists"
exit 1
fi
if mkdir ${lock_dir}
then ${echo} "x - created lock directory ${lock_dir}."
else ${echo} "x - failed to create lock directory ${lock_dir}."
exit 1
fi
# ============= jtag2wb.tcl ==============
if test -n "${keep_file}" && test -f 'jtag2wb.tcl'
then
${echo} "x - SKIPPING jtag2wb.tcl (file already exists)"

else
${echo} "x - extracting jtag2wb.tcl (text)"
sed 's/^X//' << 'SHAR_EOF' > 'jtag2wb.tcl' &&
# Script prepared by Wojciech M. Zabolotny (wzab<at>ise.pw.edu.pl or
# wzab01<at>gmail.com) to drive the jtag2wb.vhd Wishbone controller.
# This code is published as PUBLIC DOMAIN or under Creative Commons
# CC0 license.
X
# Set two constants below according to the address and data width
# in the instantiated controller
set d_width 32
set a_width 32
X
set ad_width [ expr max($d_width, $a_width)]
set s_width [ expr $ad_width + 2 ]
set a_mask [ expr ( 1 << $a_width) - 1 ]
set d_mask [ expr ( 1 << $d_width) - 1 ]
X
proc bus_init { } {
X close_hw
X catch open_hw
X catch {connect_hw_server -url localhost:3121}
X # You may need to adjust the lines below!
X #set JTAG */xilinx_tcf/Xilinx/*
X set JTAG */Digilent/*
X current_hw_target [get_hw_targets $JTAG]
X open_hw_target -jtag_mode 1
X get_hw_devices
X run_state_hw_jtag reset
X run_state_hw_jtag idle
X # The length of the shift should correspond to the length of the instruction register of your FPGA
X # The shifted value should correspond to the USER command, that selects
X # your BSCANE2 as the data register
X scan_ir_hw_jtag 6 -tdi 02
}
X
proc bus_write { address value } {
X global ad_width
X global s_width
X global a_mask
X global d_mask
X set shval [ expr ( 3 << $ad_width ) | ( $address & $a_mask ) ]
X scan_dr_hw_jtag $s_width -tdi [format %x $shval]
X set shval [ expr ( 1 << $ad_width ) | ( $value & $d_mask ) ]
X scan_dr_hw_jtag $s_width -tdi [format %x $shval]
X #wait for operation to be completed
X while { true } {
X set shval [ scan_dr_hw_jtag $s_width -tdi 0x0 ]
X set shval [expr 0x$shval ]
X if { $shval & 0x200000000 } break
X }
X if { $shval & 0x100000000 } {
X puts OK
X } else {
X puts ERROR
X }
X #puts [format %x $shval]
}
X
proc bus_read { address } {
X global ad_width
X global s_width
X global a_mask
X global d_mask
X set shval [ expr ( 2 << $ad_width ) | ( $address & $a_mask ) ]
X scan_dr_hw_jtag $s_width -tdi [format %x $shval]
X while { true } {
X set shval [ scan_dr_hw_jtag $s_width -tdi 0x0 ]
X set shval [expr 0x$shval ]
X #puts [format %x $shval]
X if { $shval & 0x200000000 } break
X }
X if { $shval & 0x100000000 } {
X puts OK
X } else {
X puts ERROR
X }
X return [expr $shval & $d_mask]
}
X
SHAR_EOF
(set 20 18 12 30 17 18 18 'jtag2wb.tcl'
eval "${shar_touch}") && \
chmod 0644 'jtag2wb.tcl'
if test $? -ne 0
then ${echo} "restore of jtag2wb.tcl failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'jtag2wb.tcl': 'MD5 check failed'
) << \SHAR_EOF
0e5e298ed107bf50deffbdc18dbf1bbe jtag2wb.tcl
SHAR_EOF

else
test `LC_ALL=C wc -c < 'jtag2wb.tcl'` -ne 2329 && \
${echo} "restoration warning: size of 'jtag2wb.tcl' is not 2329"
fi
fi
# ============= jtag2wb.vhd ==============
if test -n "${keep_file}" && test -f 'jtag2wb.vhd'
then
${echo} "x - SKIPPING jtag2wb.vhd (file already exists)"

else
${echo} "x - extracting jtag2wb.vhd (text)"
sed 's/^X//' << 'SHAR_EOF' > 'jtag2wb.vhd' &&
-------------------------------------------------------------------------------
-- Title : jtag2wb - simple bridge providing control of Wishbone bus
-- via JTAG interface. You may use it e.g. to control WB
-- from Vivado Tcl console.
-- Project :
-------------------------------------------------------------------------------
-- File : jtag2wb.vhd
-- Author : Wojciech M. Zabolotny
-- License : PUBLIC DOMAIN or Creative Commons CC0
-- Company :
-- Created : 2018-12-20
-- Last update: 2018-12-30
-- Platform :
-- Standard : VHDL'93
-------------------------------------------------------------------------------
-- Description:
--
--
-- That code is significantly based on my JTAG bus controller
-- published in https://groups.google.com/d/msg/alt.sources/Rh5yEuF2YGE/p6UB0RdRS-AJ
-- thread on alt.sources Usenet group.
--
-- The two MSB bits encode the operation.
-- 1,0 - Sending address for READ operation (immediately triggers READ on WB)
-- 1,1 - Sending address for WRITE operation (next DATA transfer triggers the
-- WRITE operation)
-- 0,1 - Sending data for WRITE operation
-- 0,0 - Reading status and data after READ operation, reading status after
-- WRITE operation
--
-- The WB controller operates in the WB clock domain.
-- Disappearance of the JTAG clock should not block its operation
-- and the whole WB bus (that maybe also controlled by other hosts!).
-- Therefore, implementation of the whole WB controller with JTAG clock
-- and using my WB-CDC may be not the best idea!
-- We know, that there will be at least two jt_TCK clock pulses before
-- the capture (see Ug835, decription of scan_dr_hw_jtag).
-- IDLE->DRSELECT->DRCAPTURE
-- Therefore we may use CDC requiring two jt_TCK pulses.
-- It is done with two pairs of signals: s_start:s_start_sync and
-- s_done:s_done_async
-- The "ping-pong" approach is used so the new command is triggered when
-- s_start /= s_done. The command is completed (successfully or not)
-- when s_start = s_done.
-- Please note, that you need to specify the appropriate timing constraints
-- for signals passed between JTAG and WB clock domains:
-- s_din, wb_status, s_address, s_data, s_mode.
-- You may also need to increase the number of synchronization stages.
--
-- The address remains unchanged after the operation. That makes implementation
-- of RMW operations easy. You may do READ (which sets the address),
-- then calculate the new value and issue WRITE (address was already set).
X
-------------------------------------------------------------------------------
-- Copyright (c) 2018 Wojciech M. Zabolotny (wzab<at>ise.pw.edu.pl or
-- wzab01<at>gmail.com )
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2018-12-20 1.0 wzab Created
-------------------------------------------------------------------------------
--
-- This program is PUBLIC DOMAIN or Creative Commons CC0 code
-- You can do with it whatever you want. However, NO WARRANTY of ANY KIND
-- is provided
--
--
X
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_unsigned.all;
library work;
use work.wishbone_pkg.all;
X
entity jtag2wb is
X generic (
X addr_width : integer := 32;
X data_width : integer := 32);
X port (
X -- Wishbone bus connection
X master_clk_i : in std_logic;
X master_rst_n_i : in std_logic;
X master_i : in t_wishbone_master_in;
X master_o : out t_wishbone_master_out
X );
end jtag2wb;
X
architecture syn of jtag2wb is
X
X component BSCANE2
X generic (
X JTAG_CHAIN : integer);
X port (
X CAPTURE : out std_ulogic;
X DRCK : out std_ulogic;
X RESET : out std_ulogic;
X RUNTEST : out std_ulogic;
X SEL : out std_ulogic;
X SHIFT : out std_ulogic;
X TCK : out std_ulogic;
X TDI : out std_ulogic;
X TMS : out std_ulogic;
X UPDATE : out std_ulogic;
X TDO : in std_ulogic);
X end component;
X
X signal jt_shift, jt_update, jt_tdi, jt_tdo, jt_tck, jt_tms, jt_drck,
X jt_capture, jt_sel, jt_reset : std_ulogic; -- := '0';
X
X
X signal s_done, s_done_async : std_logic := '0';
X signal s_start, s_start_sync : std_logic := '0';
X signal s_address : std_logic_vector(addr_width-1 downto 0);
X signal s_din : std_logic_vector(data_width-1 downto 0);
X signal s_data : std_logic_vector(data_width-1 downto 0);
X
X function maximum(L, R : integer) return integer is
X begin
X if L > R then
X return L;
X else
X return R;
X end if;
X end;
X
X type T_MODE is (SM_READ,SM_WRITE);
X signal s_mode : T_MODE := SM_READ;
X
X type TWB_STATE is (SWB_IDLE, SWB_WAIT_ACK);
X signal wb_state : TWB_STATE := SWB_IDLE;
X
X signal wb_status : std_logic := '0';
X
X constant DR_SHIFT_LEN : integer := maximum(addr_width+2, data_width+2);
X -- Register storing the access address and mode (read/write)
X signal dr_shift : std_logic_vector(DR_SHIFT_LEN-1 downto 0) := (others => '0');
X
begin
X
X
X BSCANE2_1 : BSCANE2
X generic map (
X JTAG_CHAIN => 1)
X port map (
X CAPTURE => jt_CAPTURE,
X DRCK => jt_DRCK,
X RESET => jt_RESET,
X SEL => jt_SEL,
X SHIFT => jt_SHIFT,
X TCK => jt_TCK,
X TDI => jt_TDI,
X TMS => jt_TMS,
X UPDATE => jt_UPDATE,
X TDO => jt_TDO);
X
X -- Generate the read and write strobes
X --out_fifo_rd <= '1' when jt_capture = '1' and jt_sel = '1' and out_fifo_empty='0' else '0';
X -- Generate the write strobe for the external bus - when write_cmd, and this
X -- is the data word
X --in_fifo_wr <= '1' when jt_update = '1' and jt_sel = '1' and
X -- in_fifo_full = '0' and dr_shift(DR_SHIFT_LEN-1) = '1' else '0';
X
X -- Load and shift data to dr_addr_and_mode register
X -- The first process handles the JTAG access. Therefore we can't put here
X -- any waitstates.
X pjtag1 : process (jt_tck, jt_reset)
X variable oper : std_logic_vector(1 downto 0);
X begin -- process
X if jt_reset = '1' then
X dr_shift <= (others => '0');
X s_done <= '0';
X s_start <= '0';
X elsif jt_tck'event and jt_tck = '1' then -- falling clock edge - state
X -- Synchronization of the s_done signal
X s_done <= s_done_async;
X -- defaults
X oper := dr_shift(DR_SHIFT_LEN-1 downto DR_SHIFT_LEN-2);
X --
X if jt_sel = '1' then
X if jt_update = '1' then
X -- We received the JTAG command
X case oper is
X when "11" =>
X -- Write, we have to wait for data to be written
X -- So here we store the address, and set the mode to "WRITE"
X s_address <= dr_shift(addr_width-1 downto 0);
X s_mode <= SM_WRITE;
X when "10" =>
X -- Read, we have received the address to read from
X s_address <= dr_shift(addr_width-1 downto 0);
X -- Start immediately the read operation
X s_start <= not s_start;
X s_mode <= SM_READ;
X when "01" =>
X -- Data for "WRITE"
X s_mode <= SM_WRITE; -- Added for RMW!
X s_data <= dr_shift(data_width-1 downto 0);
X s_start <= not s_start;
X when "00" =>
X -- Read the status and received data - no action needed
X null ;
X when others => null;
X end case;
X end if;
X if jt_capture = '1' then
X if s_start = s_done then
X -- Read the data
X dr_shift <= (others => '0');
X dr_shift(DR_SHIFT_LEN-1) <= '1';
X dr_shift(DR_SHIFT_LEN-2) <= wb_status;
X -- Read the dout from the WB controller
X dr_shift(data_width-1 downto 0) <= s_din;
X else
X -- Operation in progress
X dr_shift <= (others => '0');
X end if;
X end if;
X if jt_shift = '1' then
X -- Shift the register
X dr_shift(DR_SHIFT_LEN-1) <= jt_tdi;
X for i in 0 to DR_SHIFT_LEN-2 loop
X dr_shift(i) <= dr_shift(i+1);
X end loop; -- i
X end if;
X end if;
X end if;
X end process pjtag1;
X jt_TDO <= dr_shift(0);
X
X -- Here is the implementation of the WB controller with CDC
X wpm: process (master_clk_i) is
X begin -- process wpm
X if master_clk_i'event and master_clk_i = '1' then -- rising clock edge
X if master_rst_n_i = '0' then -- synchronous reset (active low)
X master_o.cyc <= '0';
X master_o.stb <= '0';
X master_o.adr <= (others => '0');
X master_o.dat <= (others => '0');
X wb_state <= SWB_IDLE;
X s_done_async <= '0';
X s_start_sync <= '0';
X else
X -- Synchronize the start signal
X s_start_sync <= s_start;
X -- Main state machine
X case wb_state is
X when SWB_IDLE =>
X if s_start_sync /= s_done_async then
X -- New operation is scheduled
X -- Check if it is read or write
X master_o.adr <= s_address;
X master_o.dat <= s_data;
X master_o.stb <= '1';
X master_o.cyc <= '1';
X master_o.sel <= (others => '1');
X if s_mode = SM_WRITE then
X master_o.we <= '1';
X else
X master_o.we <= '0';
X end if;
X wb_state <= SWB_WAIT_ACK;
X end if;
X when SWB_WAIT_ACK =>
X if master_i.ack = '1' then
X s_din <= master_i.dat;
X wb_status <= '1';
X s_done_async <= s_start_sync;
X master_o.stb <= '0';
X master_o.cyc <= '0';
X wb_state <= SWB_IDLE;
X end if;
X if master_i.err = '1' then
X s_din <= master_i.dat;
X wb_status <= '0';
X s_done_async <= s_start_sync;
X master_o.stb <= '0';
X master_o.cyc <= '0';
X wb_state <= SWB_IDLE;
X end if;
X when others => null;
X end case;
X
X end if;
X end if;
X end process wpm;
X
X
end syn;
SHAR_EOF
(set 20 18 12 30 17 18 42 'jtag2wb.vhd'
eval "${shar_touch}") && \
chmod 0644 'jtag2wb.vhd'
if test $? -ne 0
then ${echo} "restore of jtag2wb.vhd failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'jtag2wb.vhd': 'MD5 check failed'
) << \SHAR_EOF
b9b1009f871973d8082e2e3ee97e7082 jtag2wb.vhd
SHAR_EOF

else
test `LC_ALL=C wc -c < 'jtag2wb.vhd'` -ne 10251 && \
${echo} "restoration warning: size of 'jtag2wb.vhd' is not 10251"
fi
fi
if rm -fr ${lock_dir}
then ${echo} "x - removed lock directory ${lock_dir}."
else ${echo} "x - failed to remove lock directory ${lock_dir}."
exit 1
fi
exit 0
Wojciech M. Zabołotny
2019-03-05 19:54:28 UTC
Permalink
Below are the sources of the JTAG to Wishbone bridge allowing
to control the Wishbone bridge in Intel/Altera FPGA
from quartus_stp in Tcl language.

Pleade note, that you may need to modify the jtag2wb.tcl file
for your hardware.

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.15.2).
# To extract the files from this archive, save it to some FILE, remove
# everything before the '#!/bin/sh' line above, then type 'sh FILE'.
#
lock_dir=_sh09613
# Made on 2019-03-05 20:48 CET by <***@wzdell>.
# Source directory was '/tmp/ooo'.
#
# Existing files will *not* be overwritten, unless '-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 3790 -rw-r--r-- jtag2wb.tcl
# 11666 -rw-r--r-- jtag2wb.vhd
#
MD5SUM=${MD5SUM-md5sum}
f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'`
test -n "${f}" && md5check=true || md5check=false
${md5check} || \
echo 'Note: not verifying md5sums. Consider installing GNU coreutils.'
if test "X$1" = "X-c"
then keep_file=''
else keep_file=true
fi
echo=echo
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=
locale_dir=
set_echo=false

for dir in $PATH
do
if test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
case `$dir/gettext --version 2>&1 | sed 1q` in
*GNU*) gettext_dir=$dir
set_echo=true
break ;;
esac
fi
done

if ${set_echo}
then
set_echo=false
for dir in $PATH
do
if test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
set_echo=true
break
fi
done

if ${set_echo}
then
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
fi
IFS="$save_IFS"
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null
then if (echo -n test; echo 1,2,3) | grep n >/dev/null
then shar_n= shar_c='
'
else shar_n=-n shar_c= ; fi
else shar_n= shar_c='\c' ; fi
f=shar-touch.$$
st1=200112312359.59
st2=123123592001.59
st2tr=123123592001.5 # old SysV 14-char limit
st3=1231235901

if touch -am -t ${st1} ${f} >/dev/null 2>&1 && \
test ! -f ${st1} && test -f ${f}; then
shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'

elif touch -am ${st2} ${f} >/dev/null 2>&1 && \
test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'

elif touch -am ${st3} ${f} >/dev/null 2>&1 && \
test ! -f ${st3} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$2 "$8"'

else
shar_touch=:
echo
${echo} 'WARNING: not restoring timestamps. Consider getting and
installing GNU '\''touch'\'', distributed in GNU coreutils...'
echo
fi
rm -f ${st1} ${st2} ${st2tr} ${st3} ${f}
#
if test ! -d ${lock_dir} ; then :
else ${echo} "lock directory ${lock_dir} exists"
exit 1
fi
if mkdir ${lock_dir}
then ${echo} "x - created lock directory ${lock_dir}."
else ${echo} "x - failed to create lock directory ${lock_dir}."
exit 1
fi
# ============= jtag2wb.tcl ==============
if test -n "${keep_file}" && test -f 'jtag2wb.tcl'
then
${echo} "x - SKIPPING jtag2wb.tcl (file already exists)"

else
${echo} "x - extracting jtag2wb.tcl (text)"
sed 's/^X//' << 'SHAR_EOF' > 'jtag2wb.tcl' &&
# This is the Tcl file providing the read and write
# procedures for my JTAG to Wishbone bridge
# The implementation is inspired by the help
# for "help_device_dr_shift" from Intel Quartus suite,
# also by my previous post:
# https://groups.google.com/d/msg/alt.sources/Rh5yEuF2YGE/p6UB0RdRS-AJ
# and finally by:
# http://idlelogiclabs.com/2012/04/15/talking-to-the-de0-nano-using-the-virtual-jtag-interface/
#
global programmer_name
global device_name
puts "Programming Hardware:"
foreach hardware_name [get_hardware_names] {
X puts $hardware_name
X #You may need to change the line below to match your hardware
X if { [string match "DE-SoC*" $hardware_name] } {
X set programmer_name $hardware_name
X }
}
puts "\nSelect JTAG chain connected to $programmer_name.\n";
# List all devices on the chain, and select the second device on the chain.
puts "\nDevices on the JTAG chain:"
foreach device_name [get_device_names -hardware_name $programmer_name] {
X puts $device_name
X #You may need to change the line below to select the right FPGA
X #in your hardware
X if { [string match "@2*" $device_name] } {
X set test_device $device_name
X }
}
puts "\nSelect device: $test_device.\n";
X
# Open device
open_device -hardware_name $programmer_name -device_name $test_device
X
set d_width 32
set a_width 32
X
set ad_width [ expr max($d_width, $a_width)]
set s_width [ expr $ad_width + 2 ]
set a_mask [ expr ( 1 << $a_width) - 1 ]
set d_mask [ expr ( 1 << $d_width) - 1 ]
X
proc bus_write { address value } {
X global ad_width
X global s_width
X global a_mask
X global d_mask
X set shval [ expr ( 3 << $ad_width ) | ( $address & $a_mask ) ]
X device_virtual_dr_shift -instance_index 0 -length $s_width -dr_value [format %x $shval] -value_in_hex
X set shval [ expr ( 1 << $ad_width ) | ( $value & $d_mask ) ]
X device_virtual_dr_shift -instance_index 0 -length $s_width -dr_value [format %x $shval] -value_in_hex
X #wait for operation to be completed
X while { true } {
X # Length of the constants in lines below should match the "s_width" value.
X set shval [ device_virtual_dr_shift -instance_index 0 -length $s_width -dr_value 000000000 -value_in_hex ]
X set shval [expr 0x$shval ]
X if { $shval & 0x200000000 } break
X }
X if { $shval & 0x100000000 } {
X puts OK
X } else {
X puts ERROR
X }
X #puts [format %x $shval]
}
X
proc bus_read { address } {
X global ad_width
X global s_width
X global a_mask
X global d_mask
X set shval [ expr ( 2 << $ad_width ) | ( $address & $a_mask ) ]
X device_virtual_dr_shift -instance_index 0 -length $s_width -dr_value [format %x $shval] -value_in_hex
X while { true } {
X set shval [ device_virtual_dr_shift -instance_index 0 -length $s_width -dr_value 000000000 -value_in_hex ]
X #puts $shval
X set shval [expr 0x$shval ]
X #puts [format %x $shval]
X if { $shval & 0x200000000 } break
X }
X if { $shval & 0x100000000 } {
X puts OK
X } else {
X puts ERROR
X }
X return [expr $shval & $d_mask]
}
X
proc bus_open {} {
X device_lock -timeout 10000
X device_virtual_ir_shift -instance_index 0 -ir_value 1 -no_captured_ir_value
}
proc bus_close {} {
X device_virtual_ir_shift -instance_index 0 -ir_value 0 -no_captured_ir_value
X device_unlock
}
# Below we perform a few accesses.
# Of course you may also source that file and then work interactively
bus_open
puts [format %x [bus_read 0x01080]]
puts [format %x [bus_read 0x01081]]
puts [format %x [bus_read 0x00]]
puts [format %x [bus_read 0x01]]
bus_write 0x1004 0x123
bus_write 0x1005 0x64
bus_write 0x1006 0x755
bus_write 0x1007 0x897
puts [format %x [bus_read 0x1005]]
puts [format %x [bus_read 0x1007]]
puts [format %x [bus_read 0x1006]]
puts [format %x [bus_read 0x1004]]
bus_close
X
SHAR_EOF
(set 20 19 03 05 20 46 31 'jtag2wb.tcl'
eval "${shar_touch}") && \
chmod 0644 'jtag2wb.tcl'
if test $? -ne 0
then ${echo} "restore of jtag2wb.tcl failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'jtag2wb.tcl': 'MD5 check failed'
) << \SHAR_EOF
85d5e52a0232c12855c9e8ffa9574651 jtag2wb.tcl
SHAR_EOF

else
test `LC_ALL=C wc -c < 'jtag2wb.tcl'` -ne 3790 && \
${echo} "restoration warning: size of 'jtag2wb.tcl' is not 3790"
fi
fi
# ============= jtag2wb.vhd ==============
if test -n "${keep_file}" && test -f 'jtag2wb.vhd'
then
${echo} "x - SKIPPING jtag2wb.vhd (file already exists)"

else
${echo} "x - extracting jtag2wb.vhd (text)"
sed 's/^X//' << 'SHAR_EOF' > 'jtag2wb.vhd' &&
-------------------------------------------------------------------------------
-- Title : jtag2wb - simple bridge providing control of Wishbone bus
-- via JTAG interface.
-- This version is modified for operation with Altera/Intel
-- FPGAs.
-- The code is based on similar bridge developed for Xilinx
-- FPGAs, published in
-- https://groups.google.com/d/msg/alt.sources/npW-y9S7qE0/M7vBcFyGCgAJ
-- You may use it e.g. to control WB
-- from quartus_stp utility.
-- Project :
-------------------------------------------------------------------------------
-- File : jtag2wb.vhd
-- Author : Wojciech M. Zabolotny
-- License : PUBLIC DOMAIN or Creative Commons CC0
-- Company :
-- Created : 2018-12-20
-- Last update: 2019-03-05
-- Platform :
-- Standard : VHDL'93
-------------------------------------------------------------------------------
-- Description:
--
--
-- That code is significantly based on my JTAG bus controller
-- published in https://groups.google.com/d/msg/alt.sources/Rh5yEuF2YGE/p6UB0RdRS-AJ
-- thread on alt.sources Usenet group.
--
-- The two MSB bits encode the operation.
-- 1,0 - Sending address for READ operation (immediately triggers READ on WB)
-- 1,1 - Sending address for WRITE operation (next DATA transfer triggers the
-- WRITE operation)
-- 0,1 - Sending data for WRITE operation
-- 0,0 - Reading status and data after READ operation, reading status after
-- WRITE operation
--
-- The WB controller operates in the WB clock domain.
-- Disappearance of the JTAG clock should not block its operation
-- and the whole WB bus (that maybe also controlled by other hosts!).
-- Therefore, implementation of the whole WB controller with JTAG clock
-- and using my WB-CDC may be not the best idea!
-- We know, that there will be at least two jt_TCK clock pulses before
-- the capture (see Ug835, decription of scan_dr_hw_jtag).
-- IDLE->DRSELECT->DRCAPTURE
-- Therefore we may use CDC requiring two jt_TCK pulses.
-- It is done with two pairs of signals: s_start:s_start_sync and
-- s_done_sync:s_done_async
-- The "ping-pong" approach is used so the new command is triggered when
-- s_start /= s_done_sync. The command is completed (successfully or not)
-- when s_start = s_done.
-- Please note, that you need to specify the appropriate timing constraints
-- for signals passed between JTAG and WB clock domains:
-- s_din, wb_status, s_address, s_data, s_mode.
-- You may also need to increase the number of synchronization stages.
--
-- The address remains unchanged after the operation. That makes implementation
-- of RMW operations easy. You may do READ (which sets the address),
-- then calculate the new value and issue WRITE (address was already set).
X
-------------------------------------------------------------------------------
-- Copyright (c) 2018 Wojciech M. Zabolotny (wzab<at>ise.pw.edu.pl or
-- wzab01<at>gmail.com )
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2018-12-20 1.0 wzab Created
-------------------------------------------------------------------------------
--
-- This program is PUBLIC DOMAIN or Creative Commons CC0 code
-- You can do with it whatever you want. However, NO WARRANTY of ANY KIND
-- is provided
--
--
X
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
--use ieee.std_logic_unsigned.all;
library work;
use work.wishbone_pkg.all;
X
entity jtag2wb is
X generic (
X addr_width : integer := 32;
X data_width : integer := 32);
X port (
X leds : out std_logic_vector(7 downto 0);
X -- Wishbone bus connection
X master_clk_i : in std_logic;
X master_rst_n_i : in std_logic;
X master_i : in t_wishbone_master_in;
X master_o : out t_wishbone_master_out
X );
end jtag2wb;
X
architecture syn of jtag2wb is
X
X component sld_virtual_jtag is
X generic (
X sld_auto_instance_index : string := "YES";
X sld_instance_index : integer := 0;
X sld_ir_width : integer := 2
X );
X port (
X tdi : out std_logic; -- tdi
X tdo : in std_logic := 'X'; -- tdo
X ir_in : out std_logic_vector(1 downto 0); -- ir_in
X ir_out : in std_logic_vector(1 downto 0) := (others => 'X'); -- ir_out
X virtual_state_cdr : out std_logic; -- virtual_state_cdr
X virtual_state_sdr : out std_logic; -- virtual_state_sdr
X virtual_state_e1dr : out std_logic; -- virtual_state_e1dr
X virtual_state_pdr : out std_logic; -- virtual_state_pdr
X virtual_state_e2dr : out std_logic; -- virtual_state_e2dr
X virtual_state_udr : out std_logic; -- virtual_state_udr
X virtual_state_cir : out std_logic; -- virtual_state_cir
X virtual_state_uir : out std_logic; -- virtual_state_uir
X tck : out std_logic -- clk
X );
X end component sld_virtual_jtag;
X
X attribute ASYNC_REG : string;
X signal jt_shift, jt_update, jt_tdi, jt_tdo, jt_tck, jt_tms,
X jt_capture, jt_sel, jt_reset : std_logic := '0';
X signal dr_bypass : std_logic := '0';
X -- jt_drck,
X
X
X signal s_done_sync, s_done_async : std_logic := '0';
X attribute ASYNC_REG of s_done_sync : signal is "TRUE";
X
X signal s_start, s_start_sync : std_logic := '0';
X attribute ASYNC_REG of s_start_sync : signal is "TRUE";
X
X signal s_address : std_logic_vector(addr_width-1 downto 0);
X signal s_din : std_logic_vector(data_width-1 downto 0);
X signal s_data : std_logic_vector(data_width-1 downto 0);
X
X signal rst_cnt : integer := 1000;
X
X function maximum(L, R : integer) return integer is
X begin
X if L > R then
X return L;
X else
X return R;
X end if;
X end;
X
X type T_MODE is (SM_READ, SM_WRITE);
X signal s_mode : T_MODE := SM_READ;
X
X type TWB_STATE is (SWB_IDLE, SWB_WAIT_ACK);
X signal wb_state : TWB_STATE := SWB_IDLE;
X
X signal wb_status : std_logic := '0';
X
X constant DR_SHIFT_LEN : integer := maximum(addr_width+2, data_width+2);
X -- Register storing the access address and mode (read/write)
X signal dr_shift : std_logic_vector(DR_SHIFT_LEN-1 downto 0) := (others => '0');
X signal ir_in : std_logic_vector(1 downto 0) := (others => '0');
X
begin
X
X jt_sel <= '0' when to_integer(unsigned(ir_in)) = 0 else '1';
X jt_reset <= '0' when rst_cnt = 0 else '1';
X
X process(jt_tck)
X begin
X if jt_tck'event and jt_tck = '1' then
X if rst_cnt > 0 then
X rst_cnt <= rst_cnt - 1;
X end if;
X end if;
X end process;
X
X virtual_jtag_0 : component sld_virtual_jtag
X generic map (
X sld_auto_instance_index => "YES",
X sld_instance_index => 0,
X sld_ir_width => 2
X )
X port map (
X tdi => jt_TDI, -- jtag.tdi
X tdo => jt_TDO, -- .tdo
X ir_in => ir_in, -- .ir_in
X ir_out => "00", -- .ir_out
X virtual_state_cdr => jt_CAPTURE, -- .virtual_state_cdr
X virtual_state_sdr => jt_SHIFT, -- .virtual_state_sdr
X virtual_state_e1dr => open, -- .virtual_state_e1dr
X virtual_state_pdr => open, -- .virtual_state_pdr
X virtual_state_e2dr => open, -- .virtual_state_e2dr
X virtual_state_udr => jt_UPDATE, -- .virtual_state_udr
X virtual_state_cir => open, -- .virtual_state_cir
X virtual_state_uir => open, -- .virtual_state_uir
X tck => jt_TCK -- tck.clk
X );
X
X -- Generate the read and write strobes
X --out_fifo_rd <= '1' when jt_capture = '1' and jt_sel = '1' and out_fifo_empty='0' else '0';
X -- Generate the write strobe for the external bus - when write_cmd, and this
X -- is the data word
X --in_fifo_wr <= '1' when jt_update = '1' and jt_sel = '1' and
X -- in_fifo_full = '0' and dr_shift(DR_SHIFT_LEN-1) = '1' else '0';
X
X -- Load and shift data to dr_addr_and_mode register
X -- The first process handles the JTAG access. Therefore we can't put here
X -- any waitstates.
X pjtag1 : process (jt_reset, jt_tck)
X variable oper : std_logic_vector(1 downto 0);
X begin -- process
X if jt_reset = '1' then
X dr_shift <= (others => '0');
X leds <= (others => '0');
X s_done_sync <= '0';
X s_start <= '0';
X elsif jt_tck'event and jt_tck = '1' then -- falling clock edge - state
X dr_bypass <= jt_tdi;
X -- Synchronization of the s_done_sync signal
X s_done_sync <= s_done_async;
X -- defaults
X oper := dr_shift(DR_SHIFT_LEN-1 downto DR_SHIFT_LEN-2);
X --
X if jt_sel = '1' then
X if jt_update = '1' then
X -- We received the JTAG command
X leds <= dr_shift(7 downto 0);
X case oper is
X when "11" =>
X -- Write, we have to wait for data to be written
X -- So here we store the address, and set the mode to "WRITE"
X s_address <= dr_shift(addr_width-1 downto 0);
X s_mode <= SM_WRITE;
X when "10" =>
X -- Read, we have received the address to read from
X s_address <= dr_shift(addr_width-1 downto 0);
X -- Start immediately the read operation
X s_start <= not s_start;
X s_mode <= SM_READ;
X when "01" =>
X -- Data for "WRITE"
X s_mode <= SM_WRITE; -- Added for RMW!
X s_data <= dr_shift(data_width-1 downto 0);
X s_start <= not s_start;
X when "00" =>
X -- Read the status and received data - no action needed
X null;
X when others => null;
X end case;
X end if;
X if jt_capture = '1' then
X if s_start = s_done_sync then
X -- Read the data
X dr_shift <= (others => '0');
X dr_shift(DR_SHIFT_LEN-1) <= '1';
X dr_shift(DR_SHIFT_LEN-2) <= wb_status;
X -- Read the dout from the WB controller
X dr_shift(data_width-1 downto 0) <= s_din;
X else
X -- Operation in progress
X dr_shift <= (others => '0');
X end if;
X end if;
X if jt_shift = '1' then
X -- Shift the register
X dr_shift(DR_SHIFT_LEN-1) <= jt_tdi;
X for i in 0 to DR_SHIFT_LEN-2 loop
X dr_shift(i) <= dr_shift(i+1);
X end loop; -- i
X end if;
X end if;
X end if;
X end process pjtag1;
X
X jt_TDO <= dr_shift(0) when jt_sel = '1' else dr_bypass;
X
X -- Here is the implementation of the WB controller with CDC
X wpm : process (master_clk_i) is
X begin -- process wpm
X if master_clk_i'event and master_clk_i = '1' then -- rising clock edge
X if master_rst_n_i = '0' then -- synchronous reset (active low)
X master_o.cyc <= '0';
X master_o.stb <= '0';
X master_o.adr <= (others => '0');
X master_o.dat <= (others => '0');
X wb_state <= SWB_IDLE;
X s_done_async <= '0';
X s_start_sync <= '0';
X else
X -- Synchronize the start signal
X s_start_sync <= s_start;
X -- Main state machine
X case wb_state is
X when SWB_IDLE =>
X if s_start_sync /= s_done_async then
X -- New operation is scheduled
X -- Check if it is read or write
X master_o.adr <= s_address;
X master_o.dat <= s_data;
X master_o.stb <= '1';
X master_o.cyc <= '1';
X master_o.sel <= (others => '1');
X if s_mode = SM_WRITE then
X master_o.we <= '1';
X else
X master_o.we <= '0';
X end if;
X wb_state <= SWB_WAIT_ACK;
X end if;
X when SWB_WAIT_ACK =>
X if master_i.ack = '1' then
X s_din <= master_i.dat;
X wb_status <= '1';
X s_done_async <= s_start_sync;
X master_o.stb <= '0';
X master_o.cyc <= '0';
X wb_state <= SWB_IDLE;
X end if;
X if master_i.err = '1' then
X s_din <= master_i.dat;
X wb_status <= '0';
X s_done_async <= s_start_sync;
X master_o.stb <= '0';
X master_o.cyc <= '0';
X wb_state <= SWB_IDLE;
X end if;
X end case;
X
X end if;
X end if;
X end process wpm;
X
X
end syn;
SHAR_EOF
(set 20 19 03 05 20 17 23 'jtag2wb.vhd'
eval "${shar_touch}") && \
chmod 0644 'jtag2wb.vhd'
if test $? -ne 0
then ${echo} "restore of jtag2wb.vhd failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'jtag2wb.vhd': 'MD5 check failed'
) << \SHAR_EOF
d3be88cd6a701194105cf0b4f1330cf5 jtag2wb.vhd
SHAR_EOF

else
test `LC_ALL=C wc -c < 'jtag2wb.vhd'` -ne 11666 && \
${echo} "restoration warning: size of 'jtag2wb.vhd' is not 11666"
fi
fi
if rm -fr ${lock_dir}
then ${echo} "x - removed lock directory ${lock_dir}."
else ${echo} "x - failed to remove lock directory ${lock_dir}."
exit 1
fi
exit 0
Wojciech M. Zabołotny
2019-03-05 20:16:31 UTC
Permalink
This archive implements the JTAG to Wishbone bridge for Intel/Altera FPGAs
equipped additionally with the TCP server.
You may run it in quartus_stp:
$ quartus_stp -t jtag2wb_srv.tcl

The server listens on port 9900 and understands simple commands:
wr address value
rd address
quit

To quickly check it, you may connect to it from netcat (you can see the format of responses):
$ nc localhost 9900

and enter commands:

$ nc localhost 9900
JTAG Bus Server 1.0
rd 1
value 35
rd 2
value 43
wr 2 73
OK
rd 1
value 35
rd 2
value 73
quit

This is a free, PUBLIC DOMAIN code, so you use it on your own risk!
Good luck!
Wojtek

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.15.2).
# To extract the files from this archive, save it to some FILE, remove
# everything before the '#!/bin/sh' line above, then type 'sh FILE'.
#
lock_dir=_sh09922
# Made on 2019-03-05 21:10 CET by <***@wzdell>.
# Source directory was '/tmp/ooo'.
#
# Existing files will *not* be overwritten, unless '-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 4146 -rw-r--r-- jtag2wb_srv.tcl
# 3790 -rw-r--r-- jtag2wb.tcl
# 11666 -rw-r--r-- jtag2wb.vhd
#
MD5SUM=${MD5SUM-md5sum}
f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'`
test -n "${f}" && md5check=true || md5check=false
${md5check} || \
echo 'Note: not verifying md5sums. Consider installing GNU coreutils.'
if test "X$1" = "X-c"
then keep_file=''
else keep_file=true
fi
echo=echo
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=
locale_dir=
set_echo=false

for dir in $PATH
do
if test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
case `$dir/gettext --version 2>&1 | sed 1q` in
*GNU*) gettext_dir=$dir
set_echo=true
break ;;
esac
fi
done

if ${set_echo}
then
set_echo=false
for dir in $PATH
do
if test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
set_echo=true
break
fi
done

if ${set_echo}
then
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
fi
IFS="$save_IFS"
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null
then if (echo -n test; echo 1,2,3) | grep n >/dev/null
then shar_n= shar_c='
'
else shar_n=-n shar_c= ; fi
else shar_n= shar_c='\c' ; fi
f=shar-touch.$$
st1=200112312359.59
st2=123123592001.59
st2tr=123123592001.5 # old SysV 14-char limit
st3=1231235901

if touch -am -t ${st1} ${f} >/dev/null 2>&1 && \
test ! -f ${st1} && test -f ${f}; then
shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'

elif touch -am ${st2} ${f} >/dev/null 2>&1 && \
test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'

elif touch -am ${st3} ${f} >/dev/null 2>&1 && \
test ! -f ${st3} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$2 "$8"'

else
shar_touch=:
echo
${echo} 'WARNING: not restoring timestamps. Consider getting and
installing GNU '\''touch'\'', distributed in GNU coreutils...'
echo
fi
rm -f ${st1} ${st2} ${st2tr} ${st3} ${f}
#
if test ! -d ${lock_dir} ; then :
else ${echo} "lock directory ${lock_dir} exists"
exit 1
fi
if mkdir ${lock_dir}
then ${echo} "x - created lock directory ${lock_dir}."
else ${echo} "x - failed to create lock directory ${lock_dir}."
exit 1
fi
# ============= jtag2wb_srv.tcl ==============
if test -n "${keep_file}" && test -f 'jtag2wb_srv.tcl'
then
${echo} "x - SKIPPING jtag2wb_srv.tcl (file already exists)"

else
${echo} "x - extracting jtag2wb_srv.tcl (text)"
sed 's/^X//' << 'SHAR_EOF' > 'jtag2wb_srv.tcl' &&
# This is the Tcl file providing the read and write
# procedures for my JTAG to Wishbone bridge
# The implementation is inspired by the help
# for "help_device_dr_shift" from Intel Quartus suite,
# also by my previous post:
# https://groups.google.com/d/msg/alt.sources/Rh5yEuF2YGE/p6UB0RdRS-AJ
# and finally by:
# http://idlelogiclabs.com/2012/04/15/talking-to-the-de0-nano-using-the-virtual-jtag-interface/
# This version is additionally extended with simple TCP server
# based on the one described in:
# https://groups.google.com/d/msg/alt.sources/Rh5yEuF2YGE/0qmZx2qMBwAJ
#
global programmer_name
global device_name
puts "Programming Hardware:"
foreach hardware_name [get_hardware_names] {
X puts $hardware_name
X #You may need to change the line below to match your hardware
X if { [string match "DE-SoC*" $hardware_name] } {
X set programmer_name $hardware_name
X }
}
puts "\nSelect JTAG chain connected to $programmer_name.\n";
# List all devices on the chain, and select the second device on the chain.
puts "\nDevices on the JTAG chain:"
foreach device_name [get_device_names -hardware_name $programmer_name] {
X puts $device_name
X #You may need to change the line below to select the right FPGA
X #in your hardware
X if { [string match "@2*" $device_name] } {
X set test_device $device_name
X }
}
puts "\nSelect device: $test_device.\n";
X
# Open device
open_device -hardware_name $programmer_name -device_name $test_device
X
set d_width 32
set a_width 32
X
set ad_width [ expr max($d_width, $a_width)]
set s_width [ expr $ad_width + 2 ]
set a_mask [ expr ( 1 << $a_width) - 1 ]
set d_mask [ expr ( 1 << $d_width) - 1 ]
X
proc bus_write { address value } {
X global ad_width
X global s_width
X global a_mask
X global d_mask
X set shval [ expr ( 3 << $ad_width ) | ( $address & $a_mask ) ]
X device_virtual_dr_shift -instance_index 0 -length $s_width -dr_value [format %x $shval] -value_in_hex
X set shval [ expr ( 1 << $ad_width ) | ( $value & $d_mask ) ]
X device_virtual_dr_shift -instance_index 0 -length $s_width -dr_value [format %x $shval] -value_in_hex
X #wait for operation to be completed
X while { true } {
X # Length of the constants in lines below should match the "s_width" value.
X set shval [ device_virtual_dr_shift -instance_index 0 -length $s_width -dr_value 000000000 -value_in_hex ]
X set shval [expr 0x$shval ]
X if { $shval & 0x200000000 } break
X }
X if { $shval & 0x100000000 } {
X puts OK
X } else {
X puts ERROR
X }
X #puts [format %x $shval]
}
X
proc bus_read { address } {
X global ad_width
X global s_width
X global a_mask
X global d_mask
X set shval [ expr ( 2 << $ad_width ) | ( $address & $a_mask ) ]
X device_virtual_dr_shift -instance_index 0 -length $s_width -dr_value [format %x $shval] -value_in_hex
X while { true } {
X set shval [ device_virtual_dr_shift -instance_index 0 -length $s_width -dr_value 000000000 -value_in_hex ]
X #puts $shval
X set shval [expr 0x$shval ]
X #puts [format %x $shval]
X if { $shval & 0x200000000 } break
X }
X if { $shval & 0x100000000 } {
X puts OK
X } else {
X puts ERROR
X }
X return [expr $shval & $d_mask]
}
X
proc bus_open {} {
X device_lock -timeout 10000
X device_virtual_ir_shift -instance_index 0 -ir_value 1 -no_captured_ir_value
}
proc bus_close {} {
X device_virtual_ir_shift -instance_index 0 -ir_value 0 -no_captured_ir_value
X device_unlock
}
proc srv_proc {channel clientaddr clientport} {
X puts $channel "JTAG Bus Server 1.0"
X flush $channel
X puts "Connection from $clientaddr registered"
X bus_open
X while { true } {
X set cmdline [gets $channel]
X lassign $cmdline cmd addr val
X switch $cmd {
X quit {
X break
X }
X rd {
X #puts "Reading from $addr"
X set val [ bus_read $addr ]
X puts $channel "value $val"
X flush $channel
X }
X wr {
X #puts "writing $val to $addr"
X bus_write $addr $val
X puts $channel "OK"
X flush $channel
X }
X }
X }
X close $channel
X bus_close
}
X
socket -server srv_proc 9900
vwait forever
SHAR_EOF
(set 20 19 03 05 21 08 32 'jtag2wb_srv.tcl'
eval "${shar_touch}") && \
chmod 0644 'jtag2wb_srv.tcl'
if test $? -ne 0
then ${echo} "restore of jtag2wb_srv.tcl failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'jtag2wb_srv.tcl': 'MD5 check failed'
) << \SHAR_EOF
e6f19aff992f7b8ac4c64c93d49e992e jtag2wb_srv.tcl
SHAR_EOF

else
test `LC_ALL=C wc -c < 'jtag2wb_srv.tcl'` -ne 4146 && \
${echo} "restoration warning: size of 'jtag2wb_srv.tcl' is not 4146"
fi
fi
# ============= jtag2wb.tcl ==============
if test -n "${keep_file}" && test -f 'jtag2wb.tcl'
then
${echo} "x - SKIPPING jtag2wb.tcl (file already exists)"

else
${echo} "x - extracting jtag2wb.tcl (text)"
sed 's/^X//' << 'SHAR_EOF' > 'jtag2wb.tcl' &&
# This is the Tcl file providing the read and write
# procedures for my JTAG to Wishbone bridge
# The implementation is inspired by the help
# for "help_device_dr_shift" from Intel Quartus suite,
# also by my previous post:
# https://groups.google.com/d/msg/alt.sources/Rh5yEuF2YGE/p6UB0RdRS-AJ
# and finally by:
# http://idlelogiclabs.com/2012/04/15/talking-to-the-de0-nano-using-the-virtual-jtag-interface/
#
global programmer_name
global device_name
puts "Programming Hardware:"
foreach hardware_name [get_hardware_names] {
X puts $hardware_name
X #You may need to change the line below to match your hardware
X if { [string match "DE-SoC*" $hardware_name] } {
X set programmer_name $hardware_name
X }
}
puts "\nSelect JTAG chain connected to $programmer_name.\n";
# List all devices on the chain, and select the second device on the chain.
puts "\nDevices on the JTAG chain:"
foreach device_name [get_device_names -hardware_name $programmer_name] {
X puts $device_name
X #You may need to change the line below to select the right FPGA
X #in your hardware
X if { [string match "@2*" $device_name] } {
X set test_device $device_name
X }
}
puts "\nSelect device: $test_device.\n";
X
# Open device
open_device -hardware_name $programmer_name -device_name $test_device
X
set d_width 32
set a_width 32
X
set ad_width [ expr max($d_width, $a_width)]
set s_width [ expr $ad_width + 2 ]
set a_mask [ expr ( 1 << $a_width) - 1 ]
set d_mask [ expr ( 1 << $d_width) - 1 ]
X
proc bus_write { address value } {
X global ad_width
X global s_width
X global a_mask
X global d_mask
X set shval [ expr ( 3 << $ad_width ) | ( $address & $a_mask ) ]
X device_virtual_dr_shift -instance_index 0 -length $s_width -dr_value [format %x $shval] -value_in_hex
X set shval [ expr ( 1 << $ad_width ) | ( $value & $d_mask ) ]
X device_virtual_dr_shift -instance_index 0 -length $s_width -dr_value [format %x $shval] -value_in_hex
X #wait for operation to be completed
X while { true } {
X # Length of the constants in lines below should match the "s_width" value.
X set shval [ device_virtual_dr_shift -instance_index 0 -length $s_width -dr_value 000000000 -value_in_hex ]
X set shval [expr 0x$shval ]
X if { $shval & 0x200000000 } break
X }
X if { $shval & 0x100000000 } {
X puts OK
X } else {
X puts ERROR
X }
X #puts [format %x $shval]
}
X
proc bus_read { address } {
X global ad_width
X global s_width
X global a_mask
X global d_mask
X set shval [ expr ( 2 << $ad_width ) | ( $address & $a_mask ) ]
X device_virtual_dr_shift -instance_index 0 -length $s_width -dr_value [format %x $shval] -value_in_hex
X while { true } {
X set shval [ device_virtual_dr_shift -instance_index 0 -length $s_width -dr_value 000000000 -value_in_hex ]
X #puts $shval
X set shval [expr 0x$shval ]
X #puts [format %x $shval]
X if { $shval & 0x200000000 } break
X }
X if { $shval & 0x100000000 } {
X puts OK
X } else {
X puts ERROR
X }
X return [expr $shval & $d_mask]
}
X
proc bus_open {} {
X device_lock -timeout 10000
X device_virtual_ir_shift -instance_index 0 -ir_value 1 -no_captured_ir_value
}
proc bus_close {} {
X device_virtual_ir_shift -instance_index 0 -ir_value 0 -no_captured_ir_value
X device_unlock
}
# Below we perform a few accesses.
# Of course you may also source that file and then work interactively
bus_open
puts [format %x [bus_read 0x01080]]
puts [format %x [bus_read 0x01081]]
puts [format %x [bus_read 0x00]]
puts [format %x [bus_read 0x01]]
bus_write 0x1004 0x123
bus_write 0x1005 0x64
bus_write 0x1006 0x755
bus_write 0x1007 0x897
puts [format %x [bus_read 0x1005]]
puts [format %x [bus_read 0x1007]]
puts [format %x [bus_read 0x1006]]
puts [format %x [bus_read 0x1004]]
bus_close
X
SHAR_EOF
(set 20 19 03 05 20 46 31 'jtag2wb.tcl'
eval "${shar_touch}") && \
chmod 0644 'jtag2wb.tcl'
if test $? -ne 0
then ${echo} "restore of jtag2wb.tcl failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'jtag2wb.tcl': 'MD5 check failed'
) << \SHAR_EOF
85d5e52a0232c12855c9e8ffa9574651 jtag2wb.tcl
SHAR_EOF

else
test `LC_ALL=C wc -c < 'jtag2wb.tcl'` -ne 3790 && \
${echo} "restoration warning: size of 'jtag2wb.tcl' is not 3790"
fi
fi
# ============= jtag2wb.vhd ==============
if test -n "${keep_file}" && test -f 'jtag2wb.vhd'
then
${echo} "x - SKIPPING jtag2wb.vhd (file already exists)"

else
${echo} "x - extracting jtag2wb.vhd (text)"
sed 's/^X//' << 'SHAR_EOF' > 'jtag2wb.vhd' &&
-------------------------------------------------------------------------------
-- Title : jtag2wb - simple bridge providing control of Wishbone bus
-- via JTAG interface.
-- This version is modified for operation with Altera/Intel
-- FPGAs.
-- The code is based on similar bridge developed for Xilinx
-- FPGAs, published in
-- https://groups.google.com/d/msg/alt.sources/npW-y9S7qE0/M7vBcFyGCgAJ
-- You may use it e.g. to control WB
-- from quartus_stp utility.
-- Project :
-------------------------------------------------------------------------------
-- File : jtag2wb.vhd
-- Author : Wojciech M. Zabolotny
-- License : PUBLIC DOMAIN or Creative Commons CC0
-- Company :
-- Created : 2018-12-20
-- Last update: 2019-03-05
-- Platform :
-- Standard : VHDL'93
-------------------------------------------------------------------------------
-- Description:
--
--
-- That code is significantly based on my JTAG bus controller
-- published in https://groups.google.com/d/msg/alt.sources/Rh5yEuF2YGE/p6UB0RdRS-AJ
-- thread on alt.sources Usenet group.
--
-- The two MSB bits encode the operation.
-- 1,0 - Sending address for READ operation (immediately triggers READ on WB)
-- 1,1 - Sending address for WRITE operation (next DATA transfer triggers the
-- WRITE operation)
-- 0,1 - Sending data for WRITE operation
-- 0,0 - Reading status and data after READ operation, reading status after
-- WRITE operation
--
-- The WB controller operates in the WB clock domain.
-- Disappearance of the JTAG clock should not block its operation
-- and the whole WB bus (that maybe also controlled by other hosts!).
-- Therefore, implementation of the whole WB controller with JTAG clock
-- and using my WB-CDC may be not the best idea!
-- We know, that there will be at least two jt_TCK clock pulses before
-- the capture (see Ug835, decription of scan_dr_hw_jtag).
-- IDLE->DRSELECT->DRCAPTURE
-- Therefore we may use CDC requiring two jt_TCK pulses.
-- It is done with two pairs of signals: s_start:s_start_sync and
-- s_done_sync:s_done_async
-- The "ping-pong" approach is used so the new command is triggered when
-- s_start /= s_done_sync. The command is completed (successfully or not)
-- when s_start = s_done.
-- Please note, that you need to specify the appropriate timing constraints
-- for signals passed between JTAG and WB clock domains:
-- s_din, wb_status, s_address, s_data, s_mode.
-- You may also need to increase the number of synchronization stages.
--
-- The address remains unchanged after the operation. That makes implementation
-- of RMW operations easy. You may do READ (which sets the address),
-- then calculate the new value and issue WRITE (address was already set).
X
-------------------------------------------------------------------------------
-- Copyright (c) 2018 Wojciech M. Zabolotny (wzab<at>ise.pw.edu.pl or
-- wzab01<at>gmail.com )
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2018-12-20 1.0 wzab Created
-------------------------------------------------------------------------------
--
-- This program is PUBLIC DOMAIN or Creative Commons CC0 code
-- You can do with it whatever you want. However, NO WARRANTY of ANY KIND
-- is provided
--
--
X
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
--use ieee.std_logic_unsigned.all;
library work;
use work.wishbone_pkg.all;
X
entity jtag2wb is
X generic (
X addr_width : integer := 32;
X data_width : integer := 32);
X port (
X leds : out std_logic_vector(7 downto 0);
X -- Wishbone bus connection
X master_clk_i : in std_logic;
X master_rst_n_i : in std_logic;
X master_i : in t_wishbone_master_in;
X master_o : out t_wishbone_master_out
X );
end jtag2wb;
X
architecture syn of jtag2wb is
X
X component sld_virtual_jtag is
X generic (
X sld_auto_instance_index : string := "YES";
X sld_instance_index : integer := 0;
X sld_ir_width : integer := 2
X );
X port (
X tdi : out std_logic; -- tdi
X tdo : in std_logic := 'X'; -- tdo
X ir_in : out std_logic_vector(1 downto 0); -- ir_in
X ir_out : in std_logic_vector(1 downto 0) := (others => 'X'); -- ir_out
X virtual_state_cdr : out std_logic; -- virtual_state_cdr
X virtual_state_sdr : out std_logic; -- virtual_state_sdr
X virtual_state_e1dr : out std_logic; -- virtual_state_e1dr
X virtual_state_pdr : out std_logic; -- virtual_state_pdr
X virtual_state_e2dr : out std_logic; -- virtual_state_e2dr
X virtual_state_udr : out std_logic; -- virtual_state_udr
X virtual_state_cir : out std_logic; -- virtual_state_cir
X virtual_state_uir : out std_logic; -- virtual_state_uir
X tck : out std_logic -- clk
X );
X end component sld_virtual_jtag;
X
X attribute ASYNC_REG : string;
X signal jt_shift, jt_update, jt_tdi, jt_tdo, jt_tck, jt_tms,
X jt_capture, jt_sel, jt_reset : std_logic := '0';
X signal dr_bypass : std_logic := '0';
X -- jt_drck,
X
X
X signal s_done_sync, s_done_async : std_logic := '0';
X attribute ASYNC_REG of s_done_sync : signal is "TRUE";
X
X signal s_start, s_start_sync : std_logic := '0';
X attribute ASYNC_REG of s_start_sync : signal is "TRUE";
X
X signal s_address : std_logic_vector(addr_width-1 downto 0);
X signal s_din : std_logic_vector(data_width-1 downto 0);
X signal s_data : std_logic_vector(data_width-1 downto 0);
X
X signal rst_cnt : integer := 1000;
X
X function maximum(L, R : integer) return integer is
X begin
X if L > R then
X return L;
X else
X return R;
X end if;
X end;
X
X type T_MODE is (SM_READ, SM_WRITE);
X signal s_mode : T_MODE := SM_READ;
X
X type TWB_STATE is (SWB_IDLE, SWB_WAIT_ACK);
X signal wb_state : TWB_STATE := SWB_IDLE;
X
X signal wb_status : std_logic := '0';
X
X constant DR_SHIFT_LEN : integer := maximum(addr_width+2, data_width+2);
X -- Register storing the access address and mode (read/write)
X signal dr_shift : std_logic_vector(DR_SHIFT_LEN-1 downto 0) := (others => '0');
X signal ir_in : std_logic_vector(1 downto 0) := (others => '0');
X
begin
X
X jt_sel <= '0' when to_integer(unsigned(ir_in)) = 0 else '1';
X jt_reset <= '0' when rst_cnt = 0 else '1';
X
X process(jt_tck)
X begin
X if jt_tck'event and jt_tck = '1' then
X if rst_cnt > 0 then
X rst_cnt <= rst_cnt - 1;
X end if;
X end if;
X end process;
X
X virtual_jtag_0 : component sld_virtual_jtag
X generic map (
X sld_auto_instance_index => "YES",
X sld_instance_index => 0,
X sld_ir_width => 2
X )
X port map (
X tdi => jt_TDI, -- jtag.tdi
X tdo => jt_TDO, -- .tdo
X ir_in => ir_in, -- .ir_in
X ir_out => "00", -- .ir_out
X virtual_state_cdr => jt_CAPTURE, -- .virtual_state_cdr
X virtual_state_sdr => jt_SHIFT, -- .virtual_state_sdr
X virtual_state_e1dr => open, -- .virtual_state_e1dr
X virtual_state_pdr => open, -- .virtual_state_pdr
X virtual_state_e2dr => open, -- .virtual_state_e2dr
X virtual_state_udr => jt_UPDATE, -- .virtual_state_udr
X virtual_state_cir => open, -- .virtual_state_cir
X virtual_state_uir => open, -- .virtual_state_uir
X tck => jt_TCK -- tck.clk
X );
X
X -- Generate the read and write strobes
X --out_fifo_rd <= '1' when jt_capture = '1' and jt_sel = '1' and out_fifo_empty='0' else '0';
X -- Generate the write strobe for the external bus - when write_cmd, and this
X -- is the data word
X --in_fifo_wr <= '1' when jt_update = '1' and jt_sel = '1' and
X -- in_fifo_full = '0' and dr_shift(DR_SHIFT_LEN-1) = '1' else '0';
X
X -- Load and shift data to dr_addr_and_mode register
X -- The first process handles the JTAG access. Therefore we can't put here
X -- any waitstates.
X pjtag1 : process (jt_reset, jt_tck)
X variable oper : std_logic_vector(1 downto 0);
X begin -- process
X if jt_reset = '1' then
X dr_shift <= (others => '0');
X leds <= (others => '0');
X s_done_sync <= '0';
X s_start <= '0';
X elsif jt_tck'event and jt_tck = '1' then -- falling clock edge - state
X dr_bypass <= jt_tdi;
X -- Synchronization of the s_done_sync signal
X s_done_sync <= s_done_async;
X -- defaults
X oper := dr_shift(DR_SHIFT_LEN-1 downto DR_SHIFT_LEN-2);
X --
X if jt_sel = '1' then
X if jt_update = '1' then
X -- We received the JTAG command
X leds <= dr_shift(7 downto 0);
X case oper is
X when "11" =>
X -- Write, we have to wait for data to be written
X -- So here we store the address, and set the mode to "WRITE"
X s_address <= dr_shift(addr_width-1 downto 0);
X s_mode <= SM_WRITE;
X when "10" =>
X -- Read, we have received the address to read from
X s_address <= dr_shift(addr_width-1 downto 0);
X -- Start immediately the read operation
X s_start <= not s_start;
X s_mode <= SM_READ;
X when "01" =>
X -- Data for "WRITE"
X s_mode <= SM_WRITE; -- Added for RMW!
X s_data <= dr_shift(data_width-1 downto 0);
X s_start <= not s_start;
X when "00" =>
X -- Read the status and received data - no action needed
X null;
X when others => null;
X end case;
X end if;
X if jt_capture = '1' then
X if s_start = s_done_sync then
X -- Read the data
X dr_shift <= (others => '0');
X dr_shift(DR_SHIFT_LEN-1) <= '1';
X dr_shift(DR_SHIFT_LEN-2) <= wb_status;
X -- Read the dout from the WB controller
X dr_shift(data_width-1 downto 0) <= s_din;
X else
X -- Operation in progress
X dr_shift <= (others => '0');
X end if;
X end if;
X if jt_shift = '1' then
X -- Shift the register
X dr_shift(DR_SHIFT_LEN-1) <= jt_tdi;
X for i in 0 to DR_SHIFT_LEN-2 loop
X dr_shift(i) <= dr_shift(i+1);
X end loop; -- i
X end if;
X end if;
X end if;
X end process pjtag1;
X
X jt_TDO <= dr_shift(0) when jt_sel = '1' else dr_bypass;
X
X -- Here is the implementation of the WB controller with CDC
X wpm : process (master_clk_i) is
X begin -- process wpm
X if master_clk_i'event and master_clk_i = '1' then -- rising clock edge
X if master_rst_n_i = '0' then -- synchronous reset (active low)
X master_o.cyc <= '0';
X master_o.stb <= '0';
X master_o.adr <= (others => '0');
X master_o.dat <= (others => '0');
X wb_state <= SWB_IDLE;
X s_done_async <= '0';
X s_start_sync <= '0';
X else
X -- Synchronize the start signal
X s_start_sync <= s_start;
X -- Main state machine
X case wb_state is
X when SWB_IDLE =>
X if s_start_sync /= s_done_async then
X -- New operation is scheduled
X -- Check if it is read or write
X master_o.adr <= s_address;
X master_o.dat <= s_data;
X master_o.stb <= '1';
X master_o.cyc <= '1';
X master_o.sel <= (others => '1');
X if s_mode = SM_WRITE then
X master_o.we <= '1';
X else
X master_o.we <= '0';
X end if;
X wb_state <= SWB_WAIT_ACK;
X end if;
X when SWB_WAIT_ACK =>
X if master_i.ack = '1' then
X s_din <= master_i.dat;
X wb_status <= '1';
X s_done_async <= s_start_sync;
X master_o.stb <= '0';
X master_o.cyc <= '0';
X wb_state <= SWB_IDLE;
X end if;
X if master_i.err = '1' then
X s_din <= master_i.dat;
X wb_status <= '0';
X s_done_async <= s_start_sync;
X master_o.stb <= '0';
X master_o.cyc <= '0';
X wb_state <= SWB_IDLE;
X end if;
X end case;
X
X end if;
X end if;
X end process wpm;
X
X
end syn;
SHAR_EOF
(set 20 19 03 05 20 17 23 'jtag2wb.vhd'
eval "${shar_touch}") && \
chmod 0644 'jtag2wb.vhd'
if test $? -ne 0
then ${echo} "restore of jtag2wb.vhd failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'jtag2wb.vhd': 'MD5 check failed'
) << \SHAR_EOF
d3be88cd6a701194105cf0b4f1330cf5 jtag2wb.vhd
SHAR_EOF

else
test `LC_ALL=C wc -c < 'jtag2wb.vhd'` -ne 11666 && \
${echo} "restoration warning: size of 'jtag2wb.vhd' is not 11666"
fi
fi
if rm -fr ${lock_dir}
then ${echo} "x - removed lock directory ${lock_dir}."
else ${echo} "x - failed to remove lock directory ${lock_dir}."
exit 1
fi
exit 0
Loading...