Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

FPGA internal bus controller driven by JTAG interface

37 views
Skip to first unread message

wzab

unread,
Oct 16, 2009, 5:09:37 PM10/16/09
to
Archive-name: jtag_internel_bus_controller
Submitted-by: wz...@ise.pw.edu.pl (Wojciech M. Zabolotny)
Version: 1.0
Original-Author: wz...@ise.pw.edu.pl (Wojciech M. Zabolotny)

This is the PUBLIC domain code allowing you to drive the internal
parallel asynchronous bus, implemented in your FPGA, via JTA interface.
This code is written for Xilinx chips, featuring the BSCAN_SPARTAN3
component.

The controller is always defined as:

entity jtag_bus_ctl is
generic (
d_width : integer := 8;
a_width : integer := 8);
port (
din : in std_logic_vector((d_width-1) downto 0);
dout : out std_logic_vector((d_width-1) downto 0);
addr : out std_logic_vector((a_width-1) downto 0);
nwr : out std_logic;
nrd : out std_logic
);
end jtag_bus_ctl;

You can modify the width of the data and address buses.

Three version of bus controller are provided:
jtag_bus_ctl_1.vhd - uses two instructions USER1 (to send address and R/W mode)
and USER2 (to send/receive data)
jtag_bus_ctl_2.vhd - uses only one instruction USER1 and single shift register
to send both address+R/W mode and data.
Allows to optimize access by receiving the read data, when
the next command is being sent
jtag_bus_ctl_3.vhd - like version 2, but with "autoincrement" mode.
You can send address once, and then read/write multiple
data from consecutive addresses.
All controllers are described in more detailed way in the sources.

Files jtag_bus1.py, jtag_bus2.py and jtag_bus3.py are simple demos showing how
to communicate with those controllers using Python and urJTAG.
It is assumed, that FPGA implements simple asynchronous bus with 8-bit address
bus and 8-bit data bus.

Please note that I'm setting the "delaybeforesend" field in the pexpect.spawn
object to zero. Otherwise the communication is very slow.


#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.6.3).
# 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=_sh08651
# Made on 2009-10-16 23:02 CEST by <wzab@wzab>.
# Source directory was `/tmp/jtag_bus'.
#
# Existing files will *not* be overwritten, unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 2251 -rw-r--r-- jtag_bus1.py
# 2471 -rw-r--r-- jtag_bus2.py
# 3333 -rw-r--r-- jtag_bus3.py
# 6667 -rw-r--r-- jtag_bus_ctl_1.vhd
# 7388 -rw-r--r-- jtag_bus_ctl_2.vhd
# 9768 -rw-r--r-- jtag_bus_ctl_3.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.'
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=FAILED
locale_dir=FAILED
first_param="$1"
for dir in $PATH
do
if test "$gettext_dir" = FAILED && 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 ;;
esac
fi
if test "$locale_dir" = FAILED && test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
fi
done
IFS="$save_IFS"
if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
then
echo=echo
else
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
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'
${echo} '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
# ============= jtag_bus1.py ==============
if test -f 'jtag_bus1.py' && test "$first_param" != -c; then
${echo} 'x -SKIPPING jtag_bus1.py (file already exists)'
else
${echo} 'x - extracting jtag_bus1.py (text)'
sed 's/^X//' << 'SHAR_EOF' > 'jtag_bus1.py' &&
#!/usr/bin/python
import pexpect
import re
import time
class urjtag:
X def __init__(self):
X self.p=pexpect.spawn("urjtag")
X self.p.expect("jtag>")
X self.p.delaybeforesend=0
X def cmd(self,line):
X self.p.sendline(line)
X self.p.expect("jtag>")
X if self.p.before.find("Error:")>=0:
X #res=-1
X raise("error\n\n"+self.p.before)
X elif self.p.before.find("Unknown")>=0 or \
X self.p.before.find("unknown")>=0:
X #res=-2
X raise ("unknown command or syntax error\n\n"+self.p.before)
X return self.p.before
u=urjtag()
u.cmd("cable DLC5 ppdev /dev/parport0")
s=u.cmd("detect")
#Here we parse the output of the "detect" command,
#Building the list of the parts
parts=[]
finder=re.compile(r'.*Part\((?P<num>.*?)\):\s*(?P<name>\S*)', re.M)
start=0
while True:
X rs=finder.search(s,start)
X if not rs:
X break
X # We have found a new part, add it to the list of parts
X parts.append((rs.group('num'),rs.group('name')))
X start=rs.span()[1]
# OK, so we set all the parts except the xc3s500e_fg320 in the BYPASS
# mode
for p in parts:
X if p[1]=="xc3s200":
X part_select="part "+p[0]
X u.cmd(part_select)
X u.cmd("register UA 9")
X u.cmd("instruction ADDR 000010 UA")
X u.cmd("register UD 8")
X u.cmd("instruction DATA 000011 UD")
X u.cmd("instruction ADDR")
X u.cmd("shift ir")
X else:
X u.cmd("part "+p[0])
X u.cmd("instruction BYPASS")
X u.cmd("shift ir")
X
u.cmd(part_select)
def jt_write(address,data):
X u.cmd("instruction ADDR :: shift ir")
X u.cmd("dr 1"+address+" :: shift dr")
X u.cmd("instruction DATA :: shift ir")
X u.cmd("dr "+data+" :: shift dr")
def jt_read(address):
X u.cmd("instruction ADDR :: shift ir")
X u.cmd("dr 0"+address+" :: shift dr")
X u.cmd("instruction DATA :: shift ir")
X u.cmd("dr 00000000 :: shift dr")
X return u.cmd("dr")
jt_write("00000100","00000001")
jt_write("00000101","00000011")
jt_write("00000110","00000101")
jt_write("00000111","00001001")
X
# Now we send the shifting pattern to LEDs
s="00000000"
while True:
X jt_write("00000011",s) #write(3,s)
X if s[0]=='0': # shift s
X s=s[1:]+'1'
X else:
X s=s[1:]+'0'
X #print jt_read("00000101") #read(5)
X #print jt_read("00001101") #read(13)
X
SHAR_EOF
(set 20 09 10 16 22 59 57 'jtag_bus1.py'; eval "$shar_touch") &&
chmod 0644 'jtag_bus1.py'
if test $? -ne 0
then ${echo} 'restore of jtag_bus1.py failed'
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'jtag_bus1.py: MD5 check failed'
) << SHAR_EOF
4fd189b329ca7f4e72b57603b11abe1b jtag_bus1.py
SHAR_EOF
else
test `LC_ALL=C wc -c < 'jtag_bus1.py'` -ne 2251 && \
${echo} 'restoration warning: size of jtag_bus1.py is not 2251'
fi
fi
# ============= jtag_bus2.py ==============
if test -f 'jtag_bus2.py' && test "$first_param" != -c; then
${echo} 'x -SKIPPING jtag_bus2.py (file already exists)'
else
${echo} 'x - extracting jtag_bus2.py (text)'
sed 's/^X//' << 'SHAR_EOF' > 'jtag_bus2.py' &&
#!/usr/bin/python
import pexpect
import re
import time
class urjtag:
X def __init__(self):
X self.p=pexpect.spawn("urjtag")
X self.p.expect("jtag>")
X self.p.delaybeforesend=0
X def cmd(self,line):
X self.p.sendline(line)
X self.p.expect("jtag>")
X if self.p.before.find("Error:")>=0:
X #res=-1
X raise("error")
X elif self.p.before.find("Unknown")>=0 or \
X self.p.before.find("unknown")>=0:
X #res=-2
X raise ("unknown command or syntax error")
X return self.p.before
u=urjtag()
u.cmd("cable DLC5 ppdev /dev/parport0")
s=u.cmd("detect")
#Here we parse the output of the "detect" command,
#Building the list of the parts
parts=[]
finder=re.compile(r'.*Part\((?P<num>.*?)\):\s*(?P<name>\S*)', re.M)
start=0
while True:
X rs=finder.search(s,start)
X if not rs:
X break
X # We have found a new part, add it to the list of parts
X parts.append((rs.group('num'),rs.group('name')))
X start=rs.span()[1]
# OK, so we set all the parts except the xc3s500e_fg320 in the BYPASS
# mode
for p in parts:
X if p[1]=="xc3s200":
X part_select="part "+p[0]
X u.cmd(part_select)
X u.cmd("register BAR 10")
X u.cmd("instruction BUSACC 000010 BAR")
X u.cmd("instruction BUSACC")
X u.cmd("shift ir")
X else:
X u.cmd("part "+p[0])
X u.cmd("instruction BYPASS")
X u.cmd("shift ir")
X
# definition of simple read/write operations
# (do not use the possibility to receive data
# during transfer of the next command)
def jt_write(address,data):
X u.cmd("dr 11"+address+" :: shift dr")
X u.cmd("dr 00"+data+" :: shift dr")
def jt_read(address):
X u.cmd("dr 10"+address+" :: shift dr")
X u.cmd("dr 00"+"00000000"+" :: shift dr")
X return u.cmd("dr")
X
u.cmd(part_select)
# 0123456789
u.cmd("dr 1100000011 :: shift dr")
u.cmd("dr 0011010101 :: shift dr")
X
#Test simple commands
jt_write("00000100","00000110")
jt_write("00000101","00000111")
jt_write("00000110","00001000")
print jt_read("00000101")
# Now we send the shifting pattern to LEDs
s="00000000"
while True:
X u.cmd("dr 1100000011 :: shift dr") #write(3)
X u.cmd("dr 00"+s+" :: shift dr") # send the data to be written to address 3
X if s[0]=='0':
X s=s[1:]+'1'
X else:
X s=s[1:]+'0'
X u.cmd("dr 1000000101 :: shift dr") #read(5)
X u.cmd("dr 1000001101 :: shift dr") #read(13)
X print u.cmd("dr") # Result of read(5)
X u.cmd("dr 0000000101 :: shift dr") # receive last read data
X print u.cmd("dr") # Result of read(13)
X
X
SHAR_EOF
(set 20 09 10 16 22 59 33 'jtag_bus2.py'; eval "$shar_touch") &&
chmod 0644 'jtag_bus2.py'
if test $? -ne 0
then ${echo} 'restore of jtag_bus2.py failed'
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'jtag_bus2.py: MD5 check failed'
) << SHAR_EOF
5da51afbc7f3607b352e9e7a5a588fb1 jtag_bus2.py
SHAR_EOF
else
test `LC_ALL=C wc -c < 'jtag_bus2.py'` -ne 2471 && \
${echo} 'restoration warning: size of jtag_bus2.py is not 2471'
fi
fi
# ============= jtag_bus3.py ==============
if test -f 'jtag_bus3.py' && test "$first_param" != -c; then
${echo} 'x -SKIPPING jtag_bus3.py (file already exists)'
else
${echo} 'x - extracting jtag_bus3.py (text)'
sed 's/^X//' << 'SHAR_EOF' > 'jtag_bus3.py' &&
#!/usr/bin/python
import pexpect
import re
import time
class urjtag:
X def __init__(self):
X self.p=pexpect.spawn("urjtag")
X self.p.expect("jtag>")
X self.p.delaybeforesend=0
X def cmd(self,line):
X self.p.sendline(line)
X self.p.expect("jtag>")
X if self.p.before.find("Error:")>=0:
X #res=-1
X raise("error")
X elif self.p.before.find("Unknown")>=0 or \
X self.p.before.find("unknown")>=0:
X #res=-2
X raise ("unknown command or syntax error")
X return self.p.before
u=urjtag()
u.cmd("cable DLC5 ppdev /dev/parport0")
s=u.cmd("detect")
#Here we parse the output of the "detect" command,
#Building the list of the parts
parts=[]
finder=re.compile(r'.*Part\((?P<num>.*?)\):\s*(?P<name>\S*)', re.M)
start=0
while True:
X rs=finder.search(s,start)
X if not rs:
X break
X # We have found a new part, add it to the list of parts
X parts.append((rs.group('num'),rs.group('name')))
X start=rs.span()[1]
# OK, so we set all the parts except the xc3s500e_fg320 in the BYPASS
# mode
for p in parts:
X if p[1]=="xc3s200":
X part_select="part "+p[0]
X u.cmd(part_select)
X u.cmd("register BAR 10")
X u.cmd("instruction BUSACC 000010 BAR")
X u.cmd("instruction BUSACC")
X u.cmd("shift ir")
X else:
X u.cmd("part "+p[0])
X u.cmd("instruction BYPASS")
X u.cmd("shift ir")
# definition of simple read/write operations
# (do not use the possibility to receive data
# during transfer of the next command)
def jt_write(address,data):
X u.cmd("dr 11"+address+" :: shift dr")
X u.cmd("dr 00"+data+" :: shift dr")
def jt_read(address):
X u.cmd("dr 10"+address+" :: shift dr")
X u.cmd("dr 00"+"00000000"+" :: shift dr")
X return u.cmd("dr")
X
u.cmd(part_select)
# 0123456789
u.cmd("dr 1100000011 :: shift dr")
u.cmd("dr 0011010101 :: shift dr")
# Lets try the autoincrement write mode
u.cmd("dr 1100000100 :: shift dr") #write(4)
u.cmd("dr 0100000001 :: shift dr") #data->4, incr
u.cmd("dr 0100000011 :: shift dr") #data->5, incr
u.cmd("dr 0100000101 :: shift dr") #data->6, incr
u.cmd("dr 0100001001 :: shift dr") #data->7, incr
# Lets try the autoincrement read mode
u.cmd("dr 1000000000 :: shift dr") #read(0)
u.cmd("dr 0100000000 :: shift dr") #0->data, incr
print u.cmd("dr") # Result of read
u.cmd("dr 0100000000 :: shift dr") #1->data, incr
print u.cmd("dr") # Result of read
u.cmd("dr 0100000000 :: shift dr") #2->data, incr
print u.cmd("dr") # Result of read
u.cmd("dr 0100000000 :: shift dr") #3->data, incr
print u.cmd("dr") # Result of read
u.cmd("dr 0100000000 :: shift dr") #4->data, incr
print u.cmd("dr") # Result of read
u.cmd("dr 0100000000 :: shift dr") #5->data, incr
print u.cmd("dr") # Result of read
#Test simple commands
jt_write("00000100","00000110")
print jt_read("00000101")
# Now we send the shifting pattern to LEDs
s="00000000"
while True:
X u.cmd("dr 1100000011 :: shift dr") #write(3)
X u.cmd("dr 00"+s+" :: shift dr") # send the data to be written to address 3
X if s[0]=='0':
X s=s[1:]+'1'
X else:
X s=s[1:]+'0'
X u.cmd("dr 1000000101 :: shift dr") #read(5)
X u.cmd("dr 1000001101 :: shift dr") #read(13)
X #print u.cmd("dr") # Result of read(5) - you can uncomment it!
X u.cmd("dr 0000000101 :: shift dr") # receive last read data
X #print u.cmd("dr") # Result of read(13) - you can uncomment it!
X
X
SHAR_EOF
(set 20 09 10 16 22 54 45 'jtag_bus3.py'; eval "$shar_touch") &&
chmod 0644 'jtag_bus3.py'
if test $? -ne 0
then ${echo} 'restore of jtag_bus3.py failed'
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'jtag_bus3.py: MD5 check failed'
) << SHAR_EOF
03655ca4b65d809fea4df26877f9ad4b jtag_bus3.py
SHAR_EOF
else
test `LC_ALL=C wc -c < 'jtag_bus3.py'` -ne 3333 && \
${echo} 'restoration warning: size of jtag_bus3.py is not 3333'
fi
fi
# ============= jtag_bus_ctl_1.vhd ==============
if test -f 'jtag_bus_ctl_1.vhd' && test "$first_param" != -c; then
${echo} 'x -SKIPPING jtag_bus_ctl_1.vhd (file already exists)'
else
${echo} 'x - extracting jtag_bus_ctl_1.vhd (text)'
sed 's/^X//' << 'SHAR_EOF' > 'jtag_bus_ctl_1.vhd' &&
-------------------------------------------------------------------------------
-- Title : jtag_bus_ctl - interface between the JTAG and internal bus
-- Project :
-------------------------------------------------------------------------------
-- File : jtag_bus_ctl.vhd
-- Author : Wojciech M. Zabolotny
-- Company :
-- Created : 2009-10-13
-- Last update: 2009-10-16
-- Platform :
-- Standard : VHDL'93
-------------------------------------------------------------------------------
-- Description: This is implementation of the bus controller for Xilinx FPGAs
-- allowing you to access the internal bus via JTAG
-- The internal bus uses:
-- addr(a_width-1 downto 0) - address lines
-- dout(d_width-1 downto 0) - data to be written to register
-- on the internal bus
-- din(d_width-1 downto 0) - data read from the register on
-- on the internal bus
-- nrd - asynchronous read strobe
-- nwr - asynchronous write strobe
--
-- This implementation uses two JTAG instructions:
-- USER1 - dr length: 1+a_width
-- allows you to write the mode bit (1-write or 0-read)
-- and address of the accessed register
-- USER2 - dr_length d_width
-- allows you to read the data from the register or
-- write the data to the register
-- The strobes nrd and nwr are generated from the TAP controller states
-- so the JTAG frequency affects the length ofread/write pulses
-------------------------------------------------------------------------------
-- Copyright (c) 2009 Wojciech M. Zabolotny (wzab<at>ise.pw.edu.pl)
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2009-10-13 1.0 wzab Created
-------------------------------------------------------------------------------
--
-- This program is PUBLIC DOMAIN 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;
X
entity jtag_bus_ctl is
X generic (
X d_width : integer := 8;
X a_width : integer := 8);
X port (
X din : in std_logic_vector((d_width-1) downto 0);
X dout : out std_logic_vector((d_width-1) downto 0);
X addr : out std_logic_vector((a_width-1) downto 0);
X nwr : out std_logic;
X nrd : out std_logic
X );
end jtag_bus_ctl;
X
architecture syn of jtag_bus_ctl is
X
X component BSCAN_SPARTAN3
X port (CAPTURE : out std_ulogic;
X DRCK1 : out std_ulogic;
X DRCK2 : out std_ulogic;
X RESET : out std_ulogic;
X SEL1 : out std_ulogic;
X SEL2 : out std_ulogic;
X SHIFT : out std_ulogic;
X TDI : out std_ulogic;
X UPDATE : out std_ulogic;
X TDO1 : in std_ulogic;
X TDO2 : in std_ulogic);
X end component;
X signal jt_shift, jt_update, jt_tdi, jt_tdo1, jt_tdo2, jt_drck1, jt_drck2,
X jt_capture, jt_sel1, jt_sel2, jt_reset : std_ulogic; -- := '0';
X
X -- Register storing the access address and mode (read/write)
X signal dr_addr_and_mode : std_logic_vector(a_width downto 0);
X signal addr_and_mode : std_logic_vector(a_width downto 0);
X -- Register storing the data
X signal dr_data : std_logic_vector(d_width-1 downto 0);
X
X -- signals storing the access mode (read, write)
X signal write_access, read_access : std_logic := '0';
X
begin
X
X BSCAN_SPARTAN3_1 : BSCAN_SPARTAN3
X port map (
X CAPTURE => jt_CAPTURE,
X DRCK1 => jt_DRCK1,
X DRCK2 => jt_DRCK2,
X RESET => jt_RESET,
X SEL1 => jt_SEL1,
X SEL2 => jt_SEL2,
X SHIFT => jt_SHIFT,
X TDI => jt_TDI,
X UPDATE => jt_UPDATE,
X TDO1 => jt_TDO1,
X TDO2 => jt_TDO2);
X
X
X -- Generate the read strobe for external bus
X nrd <= '0' when jt_capture = '1' and jt_sel2 = '1' and read_access = '1' else '1';
X -- Generate the write strobe for the external bus
X nwr <= '0' when jt_update = '1' and jt_sel2 = '1' and write_access = '1' else '1';
X
X write_access <= addr_and_mode(a_width);
X read_access <= not write_access;
X
X addr <= addr_and_mode(a_width-1 downto 0);
X dout <= dr_data;
X
X -- Load and shift data to dr_addr_and_mode register
X pjtag1 : process (jt_drck1, jt_reset)
X begin -- process
X if jt_reset = '1' then
X dr_addr_and_mode <= (others => '0');
X elsif jt_drck1'event and jt_drck1 = '1' then -- falling clock edge - state
X if jt_shift = '0' then
X dr_addr_and_mode <= (others => '1');
X -- We do not need to capture anything... this is write only access
X null;
X else
X -- Shift the register
X dr_addr_and_mode(a_width) <= jt_tdi;
X for i in 0 to a_width-1 loop
X dr_addr_and_mode(i) <= dr_addr_and_mode(i+1);
X end loop; -- i
X end if;
X end if;
X end process pjtag1;
X
X pupd1 : process(jt_reset, jt_update)
X begin -- process
X if jt_reset = '1' then
X addr_and_mode <= (others => '0');
X elsif jt_update'event and jt_update = '1' then
X if jt_sel1 = '1' then
X addr_and_mode <= dr_addr_and_mode;
X end if;
X end if;
X end process pupd1;
X
X jt_TDO1 <= dr_addr_and_mode(0);
X
X -- Load and shift data to dr_data register
X pjtag2 : process (jt_drck2, jt_reset)
X begin -- process
X if jt_reset = '1' then
X dr_data <= (others => '0');
X elsif jt_drck2'event and jt_drck2 = '1' then -- falling clock edge - state
X if jt_shift = '0' then
X -- capture the data from din (however nrd is generated only for
X -- read accesses!
X dr_data <= din;
X null;
X else
X -- Shift the register
X dr_data(d_width-1) <= jt_tdi;
X for i in 0 to d_width-2 loop
X dr_data(i) <= dr_data(i+1);
X end loop; -- i
X end if;
X end if;
X end process pjtag2;
X
X -- The process servicing the update is not needed!
X -- We just generate the write strobe for the internal bus!
X -- Of course the internal bus must be fast enough!!!
X
-- pupd2 : process(jt_reset, jt_update)
-- begin -- process
-- if jt_reset = '1' then
-- data <= (others => '0');
-- elsif jt_update'event and jt_update = '1' then
-- if jt_sel2 = '1' then
-- data <= dr_data;
-- end if;
-- end if;
-- end process pupd2;
X
X jt_TDO2 <= dr_data(0);
X
end syn;
SHAR_EOF
(set 20 09 10 16 21 29 53 'jtag_bus_ctl_1.vhd'; eval "$shar_touch") &&
chmod 0644 'jtag_bus_ctl_1.vhd'
if test $? -ne 0
then ${echo} 'restore of jtag_bus_ctl_1.vhd failed'
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'jtag_bus_ctl_1.vhd: MD5 check failed'
) << SHAR_EOF
56fd27be9f1c2dbd6e3c5592cdc8121d jtag_bus_ctl_1.vhd
SHAR_EOF
else
test `LC_ALL=C wc -c < 'jtag_bus_ctl_1.vhd'` -ne 6667 && \
${echo} 'restoration warning: size of jtag_bus_ctl_1.vhd is not 6667'
fi
fi
# ============= jtag_bus_ctl_2.vhd ==============
if test -f 'jtag_bus_ctl_2.vhd' && test "$first_param" != -c; then
${echo} 'x -SKIPPING jtag_bus_ctl_2.vhd (file already exists)'
else
${echo} 'x - extracting jtag_bus_ctl_2.vhd (text)'
sed 's/^X//' << 'SHAR_EOF' > 'jtag_bus_ctl_2.vhd' &&
-------------------------------------------------------------------------------
-- Title : jtag_bus_ctl - interface between the JTAG and internal bus
-- Project :
-------------------------------------------------------------------------------
-- File : jtag_bus_ctl.vhd
-- Author : Wojciech M. Zabolotny
-- Company :
-- Created : 2009-10-13
-- Last update: 2009-10-16
-- Platform :
-- Standard : VHDL'93
-------------------------------------------------------------------------------
-- Description: This is implementation of the bus controller for Xilinx FPGAs
-- allowing you to access the internal bus via JTAG
-- The internal bus uses:
-- addr(a_width-1 downto 0) - address lines
-- dout(d_width-1 downto 0) - data to be written to register
-- on the internal bus
-- din(d_width-1 downto 0) - data read from the register on
-- on the internal bus
-- nrd - asynchronous read strobe
-- nwr - asynchronous write strobe
--
-- This implementation uses one JTAG instruction:
-- USER1 - dr length: max(2+a_width,1+d_width)
-- data transferred for this instruction may be:
-- (xxx: optional filler)
-- 10_xxx_address - READ command with address
-- 11_xxx_address - WRITE command with address
-- 0_xxx_data - DATA word to be written or dummy data in case
-- of read access
-- Due to the way the JTAG works, the read data are transferred
-- when next command or data word is shifted through the dr register.
--
-- It is perfectly OK to issue the READ command, and then WRITE command
-- The read data will be transferred when WRITE command is transmitted.
--
-- The write command must be followed by the data command (you may
-- issue a few data commands in sequence generating a few writes
-- to the same address - useful for bit-banging procedures).
--
-- If you want to read data without issuing the next command - send
-- the DATA word after the READ command. Please note, that multiple
-- DATA words after the READ commands do not allow you to perform
-- multiple reads! The first DATA word cancells the READ mode to
-- NOOP mode.
-- If you want to read the same address multiple times - you should
-- issue multiple READ commands with the same address (it does not
-- affects performance, as you always have to send new word to receive
-- the data)
--
-- The strobes nrd and nwr are generated from the TAP controller states
-- so the JTAG frequency affects the length ofread/write pulses
-------------------------------------------------------------------------------
-- Copyright (c) 2009 Wojciech M. Zabolotny (wzab<at>ise.pw.edu.pl)
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2009-10-13 1.0 wzab Created
-------------------------------------------------------------------------------
--
-- This program is PUBLIC DOMAIN code
-- You can do with it whatever you want, however NO WARRANTY of ANY KIND
-- is provided
--
X
X
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_unsigned.all;
library work;
X
entity jtag_bus_ctl is
X generic (
X d_width : integer := 8;
X a_width : integer := 8);
X port (
X din : in std_logic_vector((d_width-1) downto 0);
X dout : out std_logic_vector((d_width-1) downto 0);
X addr : out std_logic_vector((a_width-1) downto 0);
X nwr : out std_logic;
X nrd : out std_logic
X );
end jtag_bus_ctl;
X
architecture syn of jtag_bus_ctl is
X
X component BSCAN_SPARTAN3
X port (CAPTURE : out std_ulogic;
X DRCK1 : out std_ulogic;
X DRCK2 : out std_ulogic;
X RESET : out std_ulogic;
X SEL1 : out std_ulogic;
X SEL2 : out std_ulogic;
X SHIFT : out std_ulogic;
X TDI : out std_ulogic;
X UPDATE : out std_ulogic;
X TDO1 : in std_ulogic;
X TDO2 : in std_ulogic);
X end component;
X signal jt_shift, jt_update, jt_tdi, jt_tdo1, jt_tdo2, jt_drck1, jt_drck2,
X jt_capture, jt_sel1, jt_sel2, jt_reset : std_ulogic; -- := '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 constant DR_SHIFT_LEN : integer := maximum(a_width+2, d_width+1);
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 acc_addr : std_logic_vector(a_width-1 downto 0);
X -- Register storing the data
X signal write_cmd, read_cmd : std_logic := '0';
X
begin
X
X BSCAN_SPARTAN3_1 : BSCAN_SPARTAN3
X port map (
X CAPTURE => jt_CAPTURE,
X DRCK1 => jt_DRCK1,
X DRCK2 => jt_DRCK2,
X RESET => jt_RESET,
X SEL1 => jt_SEL1,
X SEL2 => jt_SEL2,
X SHIFT => jt_SHIFT,
X TDI => jt_TDI,
X UPDATE => jt_UPDATE,
X TDO1 => jt_TDO1,
X TDO2 => jt_TDO2);
X
X
X -- Generate the read strobe for external bus
X nrd <= '0' when jt_capture = '1' and jt_sel1 = '1' and read_cmd = '1' else '1';
X -- Generate the write strobe for the external bus - when write_cmd, and this
X -- is the data word
X nwr <= '0' when jt_update = '1' and jt_sel1 = '1' and
X write_cmd = '1' and dr_shift(DR_SHIFT_LEN-1) = '0' else '1';
X
X dout <= dr_shift(d_width-1 downto 0);
X addr <= acc_addr;
X
X -- Load and shift data to dr_addr_and_mode register
X pjtag1 : process (jt_drck1, jt_reset)
X begin -- process
X if jt_reset = '1' then
X dr_shift <= (others => '0');
X elsif jt_drck1'event and jt_drck1 = '1' then -- falling clock edge - state
X if jt_shift = '0' then
X if read_cmd = '1' then
X -- Read the data
X dr_shift <= din;
X end if;
X else
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 process pjtag1;
X
X pupd1a : process(jt_reset, jt_update)
X begin -- process
X if jt_reset = '1' then
X elsif jt_update'event and jt_update = '1' then
X if jt_sel1 = '1' then
X if dr_shift(DR_SHIFT_LEN-1 downto DR_SHIFT_LEN-2) = "10" then
X read_cmd <= '1';
X write_cmd <= '0';
X acc_addr <= dr_shift(a_width-1 downto 0);
X elsif dr_shift(DR_SHIFT_LEN-1 downto DR_SHIFT_LEN-2) = "11" then
X -- Write access
X read_cmd <= '0';
X write_cmd <= '1'; -- We PREPARE to write
X acc_addr <= dr_shift(a_width-1 downto 0);
X else
X -- Data
X read_cmd <= '0'; -- Block further READs
X end if;
X end if;
X end if;
X end process pupd1a;
X
X jt_TDO1 <= dr_shift(0);
X
end syn;
SHAR_EOF
(set 20 09 10 16 21 49 21 'jtag_bus_ctl_2.vhd'; eval "$shar_touch") &&
chmod 0644 'jtag_bus_ctl_2.vhd'
if test $? -ne 0
then ${echo} 'restore of jtag_bus_ctl_2.vhd failed'
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'jtag_bus_ctl_2.vhd: MD5 check failed'
) << SHAR_EOF
ab6395211d62b526e90bb1a6496877b8 jtag_bus_ctl_2.vhd
SHAR_EOF
else
test `LC_ALL=C wc -c < 'jtag_bus_ctl_2.vhd'` -ne 7388 && \
${echo} 'restoration warning: size of jtag_bus_ctl_2.vhd is not 7388'
fi
fi
# ============= jtag_bus_ctl_3.vhd ==============
if test -f 'jtag_bus_ctl_3.vhd' && test "$first_param" != -c; then
${echo} 'x -SKIPPING jtag_bus_ctl_3.vhd (file already exists)'
else
${echo} 'x - extracting jtag_bus_ctl_3.vhd (text)'
sed 's/^X//' << 'SHAR_EOF' > 'jtag_bus_ctl_3.vhd' &&
-------------------------------------------------------------------------------
-- Title : jtag_bus_ctl - interface between the JTAG and internal bus
-- Project :
-------------------------------------------------------------------------------
-- File : jtag_bus_ctl.vhd
-- Author : Wojciech M. Zabolotny
-- Company :
-- Created : 2009-10-13
-- Last update: 2009-10-16
-- Platform :
-- Standard : VHDL'93
-------------------------------------------------------------------------------
-- Description: This is implementation of the bus controller for Xilinx FPGAs
-- allowing you to access the internal bus via JTAG
-- The internal bus uses:
-- addr(a_width-1 downto 0) - address lines
-- dout(d_width-1 downto 0) - data to be written to register
-- on the internal bus
-- din(d_width-1 downto 0) - data read from the register on
-- on the internal bus
-- nrd - asynchronous read strobe
-- nwr - asynchronous write strobe
--
-- This implementation uses one JTAG instruction:
-- USER1 - dr length: max(2+a_width,1+d_width)
-- data transferred for this instruction may be:
-- (xxx: optional filler)
-- 10_xxx_address - READ command with address
-- 11_xxx_address - WRITE command with address
-- 00_xxx_data - DATA word to be written or dummy data in case
-- of read access - no change of address
-- THIS WORD MAY BE USED AFTER THE READ COMMAND
-- TO READ THE DATA WITHOUT ISSUING A NEW COMMAND
-- 01_xxx_data - DATA word to be written or dummy data in case
-- of read access - the access address gets
-- incremented afterwards!
--
-- Due to the way the JTAG works, the read data are transferred
-- when next command or data word is shifted through the dr register.
--
-- It is perfectly OK to issue the READ command, and then WRITE command
-- The read data will be transferred when WRITE command is transmitted.
--
-- The write command must be followed by the data command (you may
-- issue a few data commands in sequence generating a few writes
-- to the same address - useful for bit-banging procedures).
--
-- If you want to read data without issuing the next command - send
-- the DATA word 00_??? after the READ command. Please note, that
-- multiple DATA 00_??? words after the READ command do not allow
-- you to perform multiple reads! The first DATA word cancells
-- the READ mode to NOOP mode.
-- If you want to read the same address multiple times - you should
-- issue multiple READ commands with the same address (it does not
-- affects performance, as you always have to send new word to receive
-- the data)
--
-- If you want to read a few consecutive registers, then you
-- should issue the READ command with the address of the first
-- register, then multiple 01_???? DATA words to read data and
-- increase address, and finally 00_???? DATA word to receive
-- the value read from the last register without triggering a new
-- read operation.
--
-- If you want to write data to a few consecutive registers, then
-- you should issue the WRITE command with the address of the first
-- register and then multiple 01_???? DATA words. The last one
-- may be the 00_???? DATA word, but this is not necessary.
--
-- The strobes nrd and nwr are generated from the TAP controller states
-- so the JTAG frequency affects the length ofread/write pulses
-------------------------------------------------------------------------------
-- Copyright (c) 2009 Wojciech M. Zabolotny (wzab<at>ise.pw.edu.pl)
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2009-10-13 1.0 wzab Created
-------------------------------------------------------------------------------
--
-- This program is PUBLIC DOMAIN code
-- You can do with it whatever you want, however NO WARRANTY of ANY KIND
-- is provided
--
X
X
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_unsigned.all;
library work;
X
entity jtag_bus_ctl is
X generic (
X d_width : integer := 8;
X a_width : integer := 8);
X port (
X din : in std_logic_vector((d_width-1) downto 0);
X dout : out std_logic_vector((d_width-1) downto 0);
X addr : out std_logic_vector((a_width-1) downto 0);
X nwr : out std_logic;
X nrd : out std_logic
X );
end jtag_bus_ctl;
X
architecture syn of jtag_bus_ctl is
X
X component BSCAN_SPARTAN3
X port (CAPTURE : out std_ulogic;
X DRCK1 : out std_ulogic;
X DRCK2 : out std_ulogic;
X RESET : out std_ulogic;
X SEL1 : out std_ulogic;
X SEL2 : out std_ulogic;
X SHIFT : out std_ulogic;
X TDI : out std_ulogic;
X UPDATE : out std_ulogic;
X TDO1 : in std_ulogic;
X TDO2 : in std_ulogic);
X end component;
X signal jt_shift, jt_update, jt_tdi, jt_tdo1, jt_tdo2, jt_drck1, jt_drck2,
X jt_capture, jt_sel1, jt_sel2, jt_reset : std_ulogic; -- := '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 constant DR_SHIFT_LEN : integer := maximum(a_width+2, d_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 write_addr, read_addr : std_logic_vector(a_width-1 downto 0);
X -- Register storing the data
X signal write_cmd, read_cmd : std_logic := '0';
X
begin
X
X BSCAN_SPARTAN3_1 : BSCAN_SPARTAN3
X port map (
X CAPTURE => jt_CAPTURE,
X DRCK1 => jt_DRCK1,
X DRCK2 => jt_DRCK2,
X RESET => jt_RESET,
X SEL1 => jt_SEL1,
X SEL2 => jt_SEL2,
X SHIFT => jt_SHIFT,
X TDI => jt_TDI,
X UPDATE => jt_UPDATE,
X TDO1 => jt_TDO1,
X TDO2 => jt_TDO2);
X
X
X -- Generate the read strobe for external bus
X nrd <= '0' when jt_capture = '1' and jt_sel1 = '1' and read_cmd = '1' else '1';
X -- Generate the write strobe for the external bus - when write_cmd, and this
X -- is the data word
X nwr <= '0' when jt_update = '1' and jt_sel1 = '1' and
X write_cmd = '1' and dr_shift(DR_SHIFT_LEN-1) = '0' else '1';
X
X dout <= dr_shift(d_width-1 downto 0);
X addr <= write_addr when write_cmd = '1' else read_addr;
X
X -- Load and shift data to dr_addr_and_mode register
X pjtag1 : process (jt_drck1, jt_reset)
X begin -- process
X if jt_reset = '1' then
X dr_shift <= (others => '0');
X elsif jt_drck1'event and jt_drck1 = '1' then -- falling clock edge - state
X if jt_shift = '0' then
X if read_cmd = '1' then
X -- Read the data
X dr_shift <= din;
X end if;
X else
X -- Transfer the read_addr to the write_addr
X -- this is necessary to avoid updating the write_addr
X -- at the begining or end of the write cycle in the autoincrement mode
X write_addr <= read_addr;
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 process pjtag1;
X
X pupd1a : process(jt_reset, jt_update)
X begin -- process
X if jt_reset = '1' then
X elsif jt_update'event and jt_update = '1' then
X if jt_sel1 = '1' then
X if dr_shift(DR_SHIFT_LEN-1 downto DR_SHIFT_LEN-2) = "10" then
X read_cmd <= '1';
X write_cmd <= '0';
X read_addr <= dr_shift(a_width-1 downto 0);
X elsif dr_shift(DR_SHIFT_LEN-1 downto DR_SHIFT_LEN-2) = "11" then
X -- Write access
X read_cmd <= '0';
X write_cmd <= '1'; -- We PREPARE to write
X read_addr <= dr_shift(a_width-1 downto 0);
X -- we can not write the write_addr here
X -- It will be updated from the read_addr in another process
X -- this is needed to implement the autoincrement mode!
X elsif dr_shift(DR_SHIFT_LEN-1 downto DR_SHIFT_LEN-2) = "00" then
X -- Data
X read_cmd <= '0'; -- Block further READs
X else
X -- "01" - This is the autoincrement mode! we should increment
X -- the address, but the write cycle is still active!
X -- to avoid problems with the hold time we update now only the read address
X read_addr <= write_addr+1;
X -- the write address will be updated later, when shifting the next word
X end if;
X end if;
X end if;
X end process pupd1a;
X
X -- No update process is needed! Our controller just generates the nwr pulse
X -- for internal logic
X
-- pupd1b : process(jt_reset, jt_update)
-- begin -- process
-- if jt_reset = '1' then
-- null;
-- elsif jt_update'event and jt_update = '0' then
-- if jt_sel1 = '1' then
-- null;
-- end if;
-- end if;
-- end process pupd1b;
X
X jt_TDO1 <= dr_shift(0);
X
end syn;
SHAR_EOF
(set 20 09 10 16 22 23 21 'jtag_bus_ctl_3.vhd'; eval "$shar_touch") &&
chmod 0644 'jtag_bus_ctl_3.vhd'
if test $? -ne 0
then ${echo} 'restore of jtag_bus_ctl_3.vhd failed'
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'jtag_bus_ctl_3.vhd: MD5 check failed'
) << SHAR_EOF
4e91d379a6055babf9af0630ae151e59 jtag_bus_ctl_3.vhd
SHAR_EOF
else
test `LC_ALL=C wc -c < 'jtag_bus_ctl_3.vhd'` -ne 9768 && \
${echo} 'restoration warning: size of jtag_bus_ctl_3.vhd is not 9768'
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

wzab

unread,
Oct 17, 2009, 12:49:11 PM10/17/09
to
In files jtag_bus_ctl_2.vhd and jtag_bus_ctl_3.vhd one command should
be changed:

Instead of:


>        if read_cmd = '1' then

>          -- Read the data
>          dr_shift <= din;
>        end if;
>      else

there should be:


> if read_cmd = '1' then

> -- Read the data
> dr_shift(d_width-1 downto 0) <= din;
> end if;
> else

The original code compiles, but generates a warning regarding the data
width (e.g. for 8-bit address and 8-bit data:
" Width mismatch. <dr_shift> has a width of 10 bits but assigned
expression is 8-bit wide.")

0 new messages