The obtained results seem to be interesting enough to share with you.
Maybe they will be useful for others as well.
I attach four files:
bus_ctrl.vhd - the bus controller described in VHDL
bus_ctrl_tb.vhd - the testbench (bus controller connected to the very
small RAM)
tester.py - the python program implementing the read, write and delay
routines and performing some simulated accesses
test.sh - the bash script demonstrating the above files (gtkwave is
needed to view the waveforms)
--
Best regards,
Wojtek Zabolotny
wz...@ise.pw.edu.pl
#!/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=_sh00431
# Made on 2007-06-20 21:45 CEST by <wzab@wzab>.
# Source directory was `/tmp/simul'.
#
# Existing files will *not* be overwritten, unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 3309 -rw-r--r-- bus_ctrl_tb.vhd
# 7147 -rw-r--r-- bus_ctrl.vhd
# 588 -rw-r--r-- tester.py
# 512 -rwxrw-r-- test.sh
#
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
# ============= bus_ctrl_tb.vhd ==============
if test -f 'bus_ctrl_tb.vhd' && test "$first_param" != -c; then
${echo} 'x -SKIPPING bus_ctrl_tb.vhd (file already exists)'
else
${echo} 'x - extracting bus_ctrl_tb.vhd (text)'
sed 's/^X//' << 'SHAR_EOF' > 'bus_ctrl_tb.vhd' &&
-------------------------------------------------------------------------------
-- Title : Testbench for design "bus_ctrl"
-- Project :
-------------------------------------------------------------------------------
-- File : bus_ctrl_tb.vhd
-- Author : wzab
-- Company :
-- Created : 2006-12-02
-- Last update: 2006-12-03
-- Platform :
-- Standard : VHDL'87
-------------------------------------------------------------------------------
-- Description:
-------------------------------------------------------------------------------
-- Copyright (c) 2006
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2006-12-02 1.0 wzab Created
-------------------------------------------------------------------------------
X
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use std.textio.all;
library work;
-------------------------------------------------------------------------------
X
entity bus_ctrl_tb is
X
end bus_ctrl_tb;
X
-------------------------------------------------------------------------------
X
architecture symul2 of bus_ctrl_tb is
X
X component bus_ctrl
X generic (
X addrwidth : integer;
X datawidth : integer;
X rdpipename : string;
X wrpipename : string);
X port (
X bus_address : out std_logic_vector((addrwidth-1) downto 0);
X bus_data : inout std_logic_vector((datawidth-1) downto 0);
X bus_read_nwrite : out std_logic;
X bus_cs : out std_logic);
X end component;
X
X type RAM_T is array (15 downto 0) of std_logic_vector(31 downto 0);
X signal ram : RAM_T;
X signal ram_addr : integer range 0 to 15;
X
X -- component generics
X constant addrwidth : integer := 32;
X constant datawidth : integer := 32;
X constant rdpipename : string := "rdpipe";
X constant wrpipename : string := "wrpipe";
X
X -- component ports
X signal bus_address : std_logic_vector((addrwidth-1) downto 0);
X signal bus_data : std_logic_vector((datawidth-1) downto 0);
X signal bus_read_nwrite : std_logic := '1';
X signal bus_cs : std_logic := '0';
X
X -- clock
X signal Clk : std_logic := '1';
X
begin -- symul2
X
X -- component instantiation
X DUT : bus_ctrl
X generic map (
X addrwidth => addrwidth,
X datawidth => datawidth,
X rdpipename => rdpipename,
X wrpipename => wrpipename)
X port map (
X bus_address => bus_address,
X bus_data => bus_data,
X bus_read_nwrite => bus_read_nwrite,
X bus_cs => bus_cs);
X
X -- simulation of RAM
X ram_addr <= to_integer(unsigned(bus_address(3 downto 0)));
X process (bus_cs, bus_data, bus_read_nwrite, ram, ram_addr)
X begin -- process
X if bus_cs = '1' then
X if bus_read_nwrite = '0' then
X ram(ram_addr) <= bus_data;
X else
X bus_data <= ram(ram_addr);
X end if;
X else
X bus_data <= (others => 'Z');
X end if;
X end process;
X
X Clk <= not Clk after 10 ns;
end symul2;
X
-------------------------------------------------------------------------------
X
configuration bus_ctrl_tb_symul2_cfg of bus_ctrl_tb is
X for symul2
X end for;
end bus_ctrl_tb_symul2_cfg;
X
-------------------------------------------------------------------------------
SHAR_EOF
(set 20 06 12 03 22 55 23 'bus_ctrl_tb.vhd'; eval "$shar_touch") &&
chmod 0644 'bus_ctrl_tb.vhd'
if test $? -ne 0
then ${echo} 'restore of bus_ctrl_tb.vhd failed'
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'bus_ctrl_tb.vhd: MD5
check failed'
) << SHAR_EOF
fe152099d8d40ee309480a0cecdff177 bus_ctrl_tb.vhd
SHAR_EOF
else
test `LC_ALL=C wc -c < 'bus_ctrl_tb.vhd'` -ne 3309 && \
${echo} 'restoration warning: size of bus_ctrl_tb.vhd is not 3309'
fi
fi
# ============= bus_ctrl.vhd ==============
if test -f 'bus_ctrl.vhd' && test "$first_param" != -c; then
${echo} 'x -SKIPPING bus_ctrl.vhd (file already exists)'
else
${echo} 'x - extracting bus_ctrl.vhd (text)'
sed 's/^X//' << 'SHAR_EOF' > 'bus_ctrl.vhd' &&
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use std.textio.all;
library work;
X
entity bus_ctrl is
X
X generic (
X addrwidth : integer := 32;
X datawidth : integer := 32;
X rdpipename : string := "rdpipe";
X wrpipename : string := "wrpipe"
X );
X
X port (
X bus_address : out std_logic_vector((addrwidth-1) downto 0);
X bus_data : inout std_logic_vector((datawidth-1) downto 0);
X bus_read_nwrite : out std_logic;
X bus_cs : out std_logic);
X
end bus_ctrl;
X
architecture simul of bus_ctrl is
X
begin -- simul
X
X
X
X process
X
X file write_pipe : text;
X file read_pipe : text;
X variable code : character;
X variable db_line : line;
X variable rd_line : line;
X variable wr_line : line;
X
X procedure read_hex_stlv (
X variable fline : inout line;
X constant nbits : integer;
X variable res : out std_logic_vector) is
X
X variable tmp : std_logic_vector((nbits+3) downto 0) :=
(others => '0');
X variable c : character;
X variable npos, nchars : integer;
X begin -- readhex
X nchars := (nbits+3)/4; -- number of hex chars to read
X for i in nchars-1 downto 0 loop
X npos := i*4+3;
X read (fline, c);
X case c is
X when '0' =>
X tmp(npos downto npos-3) := "0000";
X when '1' =>
X tmp(npos downto npos-3) := "0001";
X when '2' =>
X tmp(npos downto npos-3) := "0010";
X when '3' =>
X tmp(npos downto npos-3) := "0011";
X when '4' =>
X tmp(npos downto npos-3) := "0100";
X when '5' =>
X tmp(npos downto npos-3) := "0101";
X when '6' =>
X tmp(npos downto npos-3) := "0110";
X when '7' =>
X tmp(npos downto npos-3) := "0111";
X when '8' =>
X tmp(npos downto npos-3) := "1000";
X when '9' =>
X tmp(npos downto npos-3) := "1001";
X when 'a' =>
X tmp(npos downto npos-3) := "1010";
X when 'A' =>
X tmp(npos downto npos-3) := "1010";
X when 'b' =>
X tmp(npos downto npos-3) := "1011";
X when 'B' =>
X tmp(npos downto npos-3) := "1011";
X when 'c' =>
X tmp(npos downto npos-3) := "1100";
X when 'C' =>
X tmp(npos downto npos-3) := "1100";
X when 'd' =>
X tmp(npos downto npos-3) := "1101";
X when 'D' =>
X tmp(npos downto npos-3) := "1101";
X when 'e' =>
X tmp(npos downto npos-3) := "1110";
X when 'E' =>
X tmp(npos downto npos-3) := "1110";
X when 'f' =>
X tmp(npos downto npos-3) := "1111";
X when 'F' =>
X tmp(npos downto npos-3) := "1111";
X when others =>
X assert(false)
X report "Error: wrong separator in the write command"
severity error;
X end case;
X end loop; -- i
X res := tmp((nbits-1) downto 0);
X end read_hex_stlv;
X
X procedure write_stlv_hex2 (
X res : inout line;
X constant vec : std_logic_vector) is
X variable nibble : integer;
X constant hexdigs : string := "0123456789abcdef";
X begin -- stlv2hex
X nibble := 0;
X if vec'left <= vec'right then
X for i in vec'left to vec'right loop
X if vec(i) = '1' then
X nibble := nibble + 2**(i-vec'left);
X end if;
X end loop; -- i
X else
X for i in vec'right to vec'left loop
X if vec(i) = '1' then
X nibble := nibble + 2**(i-vec'right);
X end if;
X end loop; -- i
X end if;
X write(res, nibble);
X end write_stlv_hex2;
X
X procedure write_stlv_hex (
X res : inout line;
X constant vec : std_logic_vector) is
X variable nibble : integer;
X variable pos : integer;
X constant hexdigs : string := "0123456789abcdef";
X begin -- stlv2hex
X nibble := 1;
X if vec'right <= vec'left then
X for i in vec'left downto vec'right loop
X -- calculate the nibbles
X pos := i mod 4;
X if vec(i) = '1' then
X nibble := nibble + 2**(pos);
X end if;
X if pos=0 then
X write(res, hexdigs(nibble));
X nibble := 1;
X end if;
X end loop; -- i
X else
X for i in vec'right downto vec'left loop
X pos := i mod 4;
X if vec(i) = '1' then
X nibble := nibble + 2**(pos);
X end if;
X if pos=0 then
X write(res, hexdigs(nibble));
X nibble := 1;
X end if;
X end loop; -- i
X end if;
X end write_stlv_hex;
X
X procedure bus_read (
X variable address : in std_logic_vector((addrwidth-1) downto 0);
X variable data : out std_logic_vector((datawidth-1) downto 0)) is
X begin -- vme_read
X bus_address <= address;
X bus_read_nwrite <= '1';
X wait for 50 ns;
X bus_cs <= '1';
X wait for 50 ns;
X data := bus_data;
X bus_cs <= '0';
X wait for 10 ns;
X end bus_read;
X
X procedure bus_write (
X variable address : in std_logic_vector((addrwidth-1) downto 0);
X variable data : in std_logic_vector((datawidth-1) downto 0)) is
X begin
X bus_address <= address;
X bus_read_nwrite <= '0';
X wait for 10 ns;
X bus_data <= data;
X wait for 50 ns;
X bus_cs <= '1';
X wait for 50 ns;
X bus_cs <= '0';
X wait for 10 ns;
X bus_data <= (others => 'Z');
X wait for 10 ns;
X end bus_write;
X
X variable delay : integer;
X variable data : std_logic_vector(31 downto 0);
X variable address : std_logic_vector(31 downto 0);
X
X begin -- process
X file_open(write_pipe, wrpipename, read_mode);
X file_open(read_pipe, rdpipename, write_mode);
X bus_cs <= '0';
X bus_read_nwrite <= '1';
X while not endfile(write_pipe) loop
X -- We read the command from the wrpipe
X readline (write_pipe, rd_line);
X -- Analyze the line (Waddress,data)
X read (rd_line, code);
X case code is
X when 'W' =>
X read_hex_stlv(rd_line, addrwidth, address);
X read (rd_line, code);
X if code /= ',' then
X assert(false)
X report "Error: wrong separator in the write command"
severity error;
X end if;
X read_hex_stlv(rd_line, datawidth, data);
X bus_write(address, data);
X when 'R' =>
X read_hex_stlv(rd_line, addrwidth, address);
X bus_read(address, data);
X write_stlv_hex(wr_line, data);
X writeline(read_pipe, wr_line);
X when 'T' =>
X read_hex_stlv(rd_line,32,data);
X delay := to_integer(unsigned(data));
X for i in 1 to delay loop
X wait for 1 ns;
X end loop; -- i
X when others =>
X assert(false)
X report "Error: wrong character at the begining of the line"
severity error;
X end case;
X end loop;
X wait;
X end process;
X
X
end simul;
SHAR_EOF
(set 20 06 12 03 00 21 54 'bus_ctrl.vhd'; eval "$shar_touch") &&
chmod 0644 'bus_ctrl.vhd'
if test $? -ne 0
then ${echo} 'restore of bus_ctrl.vhd failed'
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'bus_ctrl.vhd: MD5 check
failed'
) << SHAR_EOF
6c891ba477196e61e8bc481ded642929 bus_ctrl.vhd
SHAR_EOF
else
test `LC_ALL=C wc -c < 'bus_ctrl.vhd'` -ne 7147 && \
${echo} 'restoration warning: size of bus_ctrl.vhd is not 7147'
fi
fi
# ============= tester.py ==============
if test -f 'tester.py' && test "$first_param" != -c; then
${echo} 'x -SKIPPING tester.py (file already exists)'
else
${echo} 'x - extracting tester.py (text)'
sed 's/^X//' << 'SHAR_EOF' > 'tester.py' &&
def bus_write(adr,dana):
X cmd="W"+("%8.8x" % adr)+","+("%8.8x" % dana)+"\n"
X wrpip.write(cmd)
X wrpip.flush()
X
def bus_read(adr):
X cmd="R"+("%8.8x" % adr)+"\n"
X wrpip.write(cmd)
X wrpip.flush()
X s=rdpip.readline()
X return eval("0x"+s)
X
def bus_delay(time_ns):
X cmd="T"+("%8.8x" % time_ns)+"\n"
X wrpip.write(cmd)
X wrpip.flush()
X
wrpip=open("wrpipe","w")
rdpip=open("rdpipe","r")
# Just two simple accesses to the bus, to show how it is working
bus_write(0x3,0x1234)
bus_write(0x6,0xcbd67874)
print hex(bus_read(0x6))
bus_delay(200)
print hex(bus_read(0x3))
X
SHAR_EOF
(set 20 06 12 03 00 24 03 'tester.py'; eval "$shar_touch") &&
chmod 0644 'tester.py'
if test $? -ne 0
then ${echo} 'restore of tester.py failed'
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'tester.py: MD5 check failed'
) << SHAR_EOF
b33c37d79e1797a4edc8fa488d11f881 tester.py
SHAR_EOF
else
test `LC_ALL=C wc -c < 'tester.py'` -ne 588 && \
${echo} 'restoration warning: size of tester.py is not 588'
fi
fi
# ============= test.sh ==============
if test -f 'test.sh' && test "$first_param" != -c; then
${echo} 'x -SKIPPING test.sh (file already exists)'
else
${echo} 'x - extracting test.sh (text)'
sed 's/^X//' << 'SHAR_EOF' > 'test.sh' &&
#!/bin/bash
set -e
rm -f rdpipe wrpipe
# Create the named pipes
mknod rdpipe p
mknod wrpipe p
# Compile the VHDL
export VHDLS="bus_ctrl.vhd bus_ctrl_tb.vhd"
STD=standard
#STD=synopsys
ghdl -a --std=02 --ieee=$STD $VHDLS
ghdl -e --std=02 --ieee=$STD bus_ctrl_tb
# Run the python script in the other xterm
xterm -e "python tester.py; echo 'press ENTER'; read" &
# Run the simulation
X./bus_ctrl_tb --wave=bus_ctrl_tb.ghw --stop-time=1000ns
# Let'us view the recorded waveforms
gtkwave bus_ctrl_tb.ghw wzrmb.sav
SHAR_EOF
(set 20 06 12 03 00 31 30 'test.sh'; eval "$shar_touch") &&
chmod 0764 'test.sh'
if test $? -ne 0
then ${echo} 'restore of test.sh failed'
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'test.sh: MD5 check failed'
) << SHAR_EOF
10dc8afa88ddb8f511a9f217513312b5 test.sh
SHAR_EOF
else
test `LC_ALL=C wc -c < 'test.sh'` -ne 512 && \
${echo} 'restoration warning: size of test.sh is not 512'
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