Wojciech M. Zabołotny
2018-12-30 16:32:22 UTC
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
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