Added test package and guide into documentation

This commit is contained in:
Thomas Fehmel
2017-06-28 07:42:58 -05:00
parent 8ee16f963b
commit 776cecb671
10 changed files with 483 additions and 134 deletions

View File

@@ -0,0 +1,4 @@
20
21
22
23

View File

@@ -0,0 +1,5 @@
10 10101010
11 11000010
12 10101010
13 00001111
1 00000001

View File

@@ -0,0 +1,6 @@
4 00000000
5 11111111
6 10000000
7 01001000
8 00000010
0 11111110

View File

@@ -32,3 +32,133 @@ lookuptable_ptr:
ld32 rZ,rX // load from pointer with offset
//...
\end{asm}
\clearpage
\section{How to test a design using the CAN test package}
%Designs that use a CAN controller as a peripheral device, are often complex and therefor hard to debug.
%This is especially true, if providing valid input data via the CAN bus is necessary in order to verify correct behavior.
The platform includes a foreign CAN controller IP component.
To ease integration and testing, a test package is provided with the platform source code.
With its help, input data and behavior of a CAN network can be modeled more easily.
The test package is located in \verb=can_tp.vhd= and a application demonstration is given as a testbench in \verb=can_demo_tb.vhd= in the testbench directory.
In this guide, two approaches on verifying a design are presented.
%In those situations, the need for tools occurs, that reliably provide input data and model the behavior of a CAN network,
%connected to the designs CAN interface.
%The CAN test package is a collection of VHDL functions and procedures that implement such tools.
%And this guide will explain their proper use.
%There are two basic approaches, recommended by this guide, on how to do so.
%They are described in the two following parts.
%The third part gives some hints on how to use these methods in a test bench.
\subsection{Simulating a CAN transmission}
The first approach focuses on generating CAN transmissions, that can be used as input for the design under test. This is achieved by unsing the procedure \verb=simulate_can_transmission= (Listing ~\ref{lst:interface_01}), which is configurable to any data and timing, your CAN interface uses. The user has not to worry about bit stuffing, crc and error detection.
The \verb=id= and \verb=data= parameters are bit vectors (\verb=std_logic_vector=) that specify the CAN ID and DATA bits of the message.
The actual length of the data part is configurable by the \verb=datasize= parameter. It is an integer specifying the amount of Bytes of the \verb=data= parameter, that will be included in the CAN transmission. The rest of \verb=data= will simply be ignored.
The timing is controlled by the \verb=t_bit= parameter, which is of VDHL type time.
It stands for the correct overall length of one CAN symbol (this value has to be calculated from the timing configuration of the tested CAN controller).
The \verb=rx= and \verb=tx= signals have to be connected to a simulated CAN bus. See Section ~\ref{subsec:int_tb} for more information. The signal \verb=tx= is the actual output of the procedure.
The \verb=test_result= parameter is a diagnostic output of an enumeration type that shows whether the transmission was successful, a CAN error occurs, the arbitration is lost or whether the tested CAN controller is not acknowledging the transmission.
\begin{vhdl}[Interface]{lst:interface_01}
procedure simulate_can_transmission(
constant id : in std_logic_vector(10 downto 0);
constant data : in std_logic_vector (0 to 63);
constant datasize : in integer;
constant t_bit : in time;
signal rx : in std_logic;
signal tx : inout std_logic;
signal test_result : out rx_check_result)
\end{vhdl}
\subsection{Creating a test network}
While the first approach is limited to transmitting can messages, this one aims at providing one or more fully functioning can nodes in a test network. Each node is implemented by an instantiation of \verb=can_vhdl_top=, but instead of steering them with a LT16soc design, functions of the can test package will do the job:
If a certain register in a CAN node should be written to, the procedure \verb=can_wb_write_reg= (Listing ~\ref{lst:interface_02}) comes in handy. Its parameter signals \verb=wbs_in= and \verb=wbs_out= must be connected to the CAN controller's Wishbone interface. The parameter \verb=addr= is an integer specifying the target register of the write and the parameter data is a bit vector containing the data. And finally a clock signal \verb=clk= is needed as input, in order to write to the CAN controller. (This must be the same clock used for operating the CAN node.)
\begin{vhdl}[Interface]{lst:interface_02}
procedure can_wb_write_reg(
signal wbs_in : out wb_slv_in_type;
signal wbs_out : in wb_slv_out_type;
constant addr : integer;
constant data : in std_logic_vector(7 downto 0);
signal clk : in std_logic)
\end{vhdl}
For extracting certain register contents, the procedure \verb=can_wb_read_reg= (Listing ~\ref{lst:interface_03}) is provided. It is used with the same parameters like \verb=can_wb_write_reg=, except a data input bit vector. Instead, a data output signal is provided, to access the read register contents.
\begin{vhdl}[Interface]{lst:interface_03}
procedure can_wb_read_reg(
signal wbs_in : out wb_slv_in_type;
signal wbs_out : in wb_slv_out_type;
constant addr : integer;
signal data : out std_logic_vector(7 downto 0);
signal clk : in std_logic)
\end{vhdl}
If successive writes are needed, the procedure \verb=write_regs_from_file= (Listing ~\ref{lst:interface_04}) is a convenient way to do this. The first Parameter is a path to a text file. Each line in the file stands for a CAN register, that should be written, and consits of a integer for the desired target register number and eight binary digits ('0' or '1') for the data. Both numbers are separated by one SPACE. See ~\ref{lst:default_setup} for an example.
\begin{vhdl}[Interface]{lst:interface_04}
procedure write_regs_from_file(
constant filename : in string;
signal wbs_in : out wb_slv_in_type;
signal wbs_out : in wb_slv_out_type;
signal clk : in std_logic)
\end{vhdl}
Successive reading can be done in a similar way with \verb=read_regs_with_fileaddr= (Listing ~\ref{lst:interface_05}). Here, the data part in each line of the file is ignored. If the registers written to should be read, the same file can be used. The parameter \verb=out_filename= determines an additional file, that is used to store the read register contents.
\begin{vhdl}[Interface]{lst:interface_05}
procedure read_regs_with_fileaddr(
constant filename : in string;
constant out_filename : in string;
signal wbs_in : out wb_slv_in_type;
signal wbs_out : in wb_slv_out_type;
signal clk : in std_logic)
\end{vhdl}
\subsection{Integrating the approaches in a test bench}
\label{subsec:int_tb}
In order to use the two presented approaches in a test bench, some details have to be considered:
\begin{itemize}
\item Connecting CAN nodes or transmitting custom messages requires the simulation of a CAN network. A VHDL design that does exactly this is provided: \verb=phys_can_sim= (Listing ~\ref{lst:interface_06}). The individual tx and rx signals of the connected can nodes are merged into two vectors. Thier size is determind by the generic parameter \verb=peer_num=, which should be equal to the number of clients connected to the CAN network.
\item When using the \verb=simulate_can_transmission= procedure, the \verb=tx= signal is only handled while the procedure is working. For all other times the signal has to be assigned manually.
\item When simulating the test bench with ISim, the relative file path in \verb=write_regs_from_file= and \verb=read_regs_with_fileaddr= has its root in the project folder. An example file, containing initialization data for a can node is shown in Listing ~\ref{lst:default_setup}.
\item When instantiating \verb=can_vhdl_top= (a CAN node without a lt16soc), do not forget to initialize its \verb=wbs_in= port signal porperly. This can be done by assigning the constant \verb=wbs_in_default= from the CAN test package to it.
\item A demo test bench using all the mechanics described in the two approaches is provided: \verb=can_demo_tb=.
\end{itemize}
\begin{vhdl}[Interface]{lst:interface_06}
entity phys_can_sim
generic(
peer_num : integer );
port(
rst : in std_logic;
rx_vector : out std_logic_vector(peer_num - 1 downto 0);
tx_vector : in std_logic_vector(peer_num - 1 downto 0) );
end entity phys_can_sim;
\end{vhdl}
\begin{vhdl}[default\_setup.tdf]{lst:default_setup}
4 00000000
5 11111111
6 10000000
7 01001000
8 00000010
0 11111110
\end{vhdl}

View File

@@ -9,6 +9,17 @@ package can_tp is
type rx_check_result is (success, can_error, arbitration_lost, no_ack);
constant wbs_in_default : wb_slv_in_type := (
(others=>'-'), -- adr
(others=>'-'), -- dat
'-', -- we
(others => '-'), -- sel
'0', -- stb
'0', -- cyc
"000", -- cti
"00" -- bte
);
procedure can_wb_write_reg(
signal wbs_in : out wb_slv_in_type;
signal wbs_out : in wb_slv_out_type;
@@ -22,14 +33,28 @@ package can_tp is
constant addr : in integer;
signal clk : in std_logic);
procedure can_wb_read_reg(
signal wbs_in : out wb_slv_in_type;
signal wbs_out : in wb_slv_out_type;
constant addr : in integer;
signal data : out std_logic_vector(7 downto 0);
signal clk : in std_logic);
procedure write_regs_from_file(
constant filename : in string;
signal wbs_in : out wb_slv_in_type;
signal wbs_out : in wb_slv_out_type;
signal clk : in std_logic);
-- procedure read_regs_with_fileaddr(
-- constant filename : in string;
-- signal wbs_in : out wb_slv_in_type;
-- signal wbs_out : in wb_slv_out_type;
-- signal clk : in std_logic);
procedure read_regs_with_fileaddr(
constant filename : in string;
constant out_filename : in string;
signal wbs_in : out wb_slv_in_type;
signal wbs_out : in wb_slv_out_type;
signal clk : in std_logic);
@@ -46,6 +71,11 @@ package can_tp is
constant data : std_logic_vector(7 downto 0)
) return std_logic_vector;
function canwb2data(
constant data_in : std_logic_vector(63 downto 0);
constant sel : std_logic_vector(3 downto 0)
) return std_logic_vector;
function can_crc(
constant data: in std_logic_vector(0 to 63);
constant datasize: in integer)return std_logic_vector;
@@ -119,7 +149,23 @@ package body can_tp is
return wbcan_data;
end data2canwb;
--does a asynchronous wb-single-write-handshake and writes to an register of the can controller
function canwb2data(
constant data_in : std_logic_vector(31 downto 0);
constant sel : std_logic_vector(3 downto 0)
) return std_logic_vector is
variable data_out : std_logic_vector(7 downto 0);
begin
case sel is
when "1000" => data_out := data_in (31 downto 24);
when "0100" => data_out := data_in(23 downto 16);
when "0010" => data_out := data_in(15 downto 8);
when "0001" => data_out := data_in(7 downto 0);
when others => data_out := "0000"; -- should not occour
end case;
return data_out;
end canwb2data;
--does a synchronous wb-single-write-handshake with waitstates and writes to an register of the can controller
procedure can_wb_write_reg(
signal wbs_in : out wb_slv_in_type;
signal wbs_out : in wb_slv_out_type;
@@ -172,6 +218,18 @@ package body can_tp is
end can_wb_read_reg;
procedure can_wb_read_reg(
signal wbs_in : out wb_slv_in_type;
signal wbs_out : in wb_slv_out_type;
constant addr : integer;
signal data : out std_logic_vector(7 downto 0);
signal clk : in std_logic) is
begin
can_wb_read_reg(wbs_in, wbs_out, addr, clk);
data <= canwb2data(wbs_out.dat, canint2sel(addr));
end can_wb_read_reg;
procedure write_regs_from_file(
constant filename : in string;
signal wbs_in : out wb_slv_in_type;
@@ -190,25 +248,57 @@ package body can_tp is
can_wb_write_reg(wbs_in, wbs_out, addr, data, clk);
wait for 50 ns;
end loop;
file_close(sourcefile);
end procedure write_regs_from_file;
-- procedure read_regs_with_fileaddr(
-- constant filename : in string;
-- signal wbs_in : out wb_slv_in_type;
-- signal wbs_out : in wb_slv_out_type;
-- signal clk : in std_logic) is
--
-- file sourcefile : text open read_mode is filename;
-- variable input_line : line;
-- variable output_line : line;
-- variable addr : integer;
-- begin
-- while not endfile(sourcefile) loop
-- readline(sourcefile, input_line); --read line
-- read(input_line, addr); --read addr of register
-- can_wb_read_reg(wbs_in, wbs_out, addr, clk);
-- wait for 50 ns;
-- end loop;
-- file_close(sourcefile);
-- end procedure read_regs_with_fileaddr;
procedure read_regs_with_fileaddr(
constant filename : in string;
constant out_filename : in string;
signal wbs_in : out wb_slv_in_type;
signal wbs_out : in wb_slv_out_type;
signal clk : in std_logic) is
file sourcefile : text open read_mode is filename;
file targetfile : text open write_mode is out_filename;
variable input_line : line;
variable output_line : line;
variable addr : integer;
begin
while not endfile(sourcefile) loop
readline(sourcefile, input_line); --read line
read(input_line, addr); --read addr of register
can_wb_read_reg(wbs_in, wbs_out, addr, clk);
--wait for 1 ns;
write(output_line,addr);
write(output_line, ' ' );
write(output_line,canwb2data(wbs_out.dat, canint2sel(addr)));
writeline(targetfile,output_line);
wait for 50 ns;
end loop;
file_close(sourcefile);
file_close(targetfile);
end procedure read_regs_with_fileaddr;
function can_crc(

View File

@@ -1086,8 +1086,8 @@ package body wb_tp is
msto.dat <= enc_wb_dat(adr(1 downto 0),size,writedata);
wait until rising_edge(clk);
wait until slvo.ack = '1' for 1 ps;
assert slvo.ack = '1' report "Slave did not ACK the write properly";
wait until slvo.ack = '1' for 100 ns;
assert slvo.ack = '1' report "Slave did not ACK the write properly within 10 wait states";
wait until rising_edge(clk);
@@ -1180,8 +1180,8 @@ package body wb_tp is
msto.adr <= adr;
wait until rising_edge(clk);
wait until slvo.ack='1' for 1 ps;
assert slvo.ack='1' report "Slave did not ACK the read properly";
wait until slvo.ack='1' for 100 ns;
assert slvo.ack='1' report "Slave did not ACK the read properly within 10 wait states";
if slvo.ack='1' then
readdata <= dec_wb_dat(sel,slvo.dat);
else

View File

@@ -0,0 +1,195 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.wishbone.all;
use work.can_tp.all;
use work.config.all;
entity can_demo_tb is
end entity can_demo_tb;
architecture RTL of can_demo_tb is
component can_vhdl_top is
generic(
memaddr : generic_addr_type;
addrmask : generic_mask_type
);
port(
clk : in std_logic;
rstn : in std_logic;
wbs_i : in wb_slv_in_type;
wbs_o : out wb_slv_out_type;
rx_i : in std_logic;
tx_o : out std_logic;
irq_on : out std_logic
);
end component can_vhdl_top;
component phys_can_sim
generic(
peer_num : integer );
port(
rst : in std_logic;
rx_vector : out std_logic_vector(peer_num - 1 downto 0);
tx_vector : in std_logic_vector(peer_num - 1 downto 0) );
end component phys_can_sim;
-- management signal
signal clk : std_logic := '0';
signal rst : std_logic := '1';
signal test_result: rx_check_result;
--signal tx_frame: std_logic_vector(0 to 108);
-- signals to/from controller 1
signal wbs_i1 : wb_slv_in_type := wbs_in_default;
signal wbs_o1 : wb_slv_out_type;
signal irq_on1 : std_logic;
signal data_out : std_logic_vector(7 downto 0) := (others => '0');
-- signals to/from controller 2
signal wbs_i2 : wb_slv_in_type:= wbs_in_default;
signal wbs_o2 : wb_slv_out_type;
signal irq_on2 : std_logic;
--signals can interconnect
constant peer_num_inst : integer := 3;
signal rx_vector : std_logic_vector(peer_num_inst - 1 downto 0);
signal tx_vector : std_logic_vector(peer_num_inst - 1 downto 0);
begin
can_inst_1 : component can_vhdl_top
generic map(
memaddr=>CFG_BADR_MEM,
addrmask=>CFG_MADR_FULL
)
port map(
clk => clk,
rstn => rst,
wbs_i => wbs_i1,
wbs_o => wbs_o1,
rx_i => rx_vector(0),
tx_o => tx_vector(0),
irq_on => irq_on1);
can_inst_2 : component can_vhdl_top
generic map(
memaddr=>CFG_BADR_MEM,
addrmask=>CFG_MADR_FULL
)
port map(
clk => clk,
rstn => rst,
wbs_i => wbs_i2,
wbs_o => wbs_o2,
rx_i => rx_vector(1),
tx_o => tx_vector(1),
irq_on => irq_on2);
can_interconnect : component phys_can_sim
generic map( peer_num => peer_num_inst)
port map( rst => rst,
rx_vector => rx_vector,
tx_vector => tx_vector);
-- stimuli
stimuli: process is
begin
report "begin stimuli" severity warning;
--this tx line is used manually
tx_vector(2) <= '1';
wait for 40 ns;
rst <= '0';
--setup both can nodes
write_regs_from_file( "./testdata/default_setup.tdf", wbs_i1, wbs_o1, clk);
--wait for 1000 ns;
write_regs_from_file( "./testdata/default_setup.tdf", wbs_i2, wbs_o2, clk);
wait for 1000 ns;
--setup and execute a 2 byte transmission in controller 1
write_regs_from_file( "./testdata/data_send.tdf", wbs_i1, wbs_o1, clk);
tx_vector(2) <= tx_vector(1);
--manual ack by copying controler 2's ack
wait on tx_vector(1);
tx_vector(2) <= '0';
wait for 300 ns;
tx_vector(2) <= '1';
wait on irq_on2;
--read status register of controller 1
can_wb_read_reg(wbs_i1, wbs_o1, 2, data_out ,clk);
--read from controller 2's read buffer
--read_regs_with_fileaddr("./testdata/data_read.tdf", wbs_i2, wbs_o2, clk);
read_regs_with_fileaddr("./testdata/data_read.tdf", "./results/read_data0.tdf", wbs_i2, wbs_o2, clk);
wait for 1200 ns;
--release receive buffer of controller 2
can_wb_write_reg(wbs_i2, wbs_o2, 1, "00000100", clk);
wait for 1200 ns;
--manually transmit a 2 byte message on tx line 2 (tx_vector(2))
simulate_can_transmission("11100010111", x"770F000000000000", 2, 300 ns, rx_vector(2), tx_vector(2), test_result);
tx_vector(2) <= '1';
wait on irq_on2;
--read from both receive buffers
read_regs_with_fileaddr("./testdata/data_read.tdf", "./results/read_data1.tdf", wbs_i1, wbs_o1, clk);
read_regs_with_fileaddr("./testdata/data_read.tdf", "./results/read_data2.tdf", wbs_i2, wbs_o2, clk);
wait for 2400 ns;
--release both receive buffers
can_wb_write_reg(wbs_i1, wbs_o1, 1, "00000100", clk);
can_wb_write_reg(wbs_i2, wbs_o2, 1, "00000100", clk);
wait for 1200 ns;
report "end stimuli" severity failure;
wait;
end process stimuli;
-- clock generation
clock : process is
begin
clk <= not clk;
wait for 10 ns / 2;
end process clock;
-- input files used in this testbench:
--
-- default_setup.tdf:
-- 4 00000000
-- 5 11111111
-- 6 10000000
-- 7 01001000
-- 8 00000010
-- 0 11111110
--
-- data_send.tdf:
-- 10 10101010
-- 11 11000010
-- 12 10101010
-- 13 00001111
-- 1 00000001
--
-- data_read.tdf:
-- 20
-- 21
-- 22
-- 23
end architecture RTL;

View File

@@ -1,128 +0,0 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.wishbone.all;
use work.can_tp.all;
entity can_tx_sim_tb is
end entity can_tx_sim_tb;
architecture RTL of can_tx_sim_tb is
component can_vhdl_top is
generic(
memaddr : generic_addr_type;
addrmask : generic_mask_type;
);
port(
clk : in std_logic;
rstn : in std_logic;
wbs_i : in wb_slv_in_type;
wbs_o : out wb_slv_out_type;
rx_i : in std_logic;
tx_o : out std_logic;
irq_on : out std_logic
);
end component can_vhdl_top;
component phys_can_sim
generic(
peer_num : integer );
port(
rst : in std_logic;
rx_vector : out std_logic_vector(peer_num - 1 downto 0);
tx_vector : in std_logic_vector(peer_num - 1 downto 0) );
end component phys_can_sim;
-- management signal
signal clk : std_logic := '0';
signal rst : std_logic := '1';
signal test_result: rx_check_result;
signal tx_frame: std_logic_vector(0 to 108);
-- signals main controller
signal wbs_i : wb_slv_in_type;
signal wbs_o : wb_slv_out_type;
signal irq_on : std_logic;
--signals can interconnect
constant peer_num_inst : integer := 2;
signal rx_vector : std_logic_vector(peer_num_inst - 1 downto 0);
signal tx_vector : std_logic_vector(peer_num_inst - 1 downto 0);
begin
can_inst_main : component can_vhdl_top
generic map(
memaddr=>CFG_BADR_MEM,
addrmask=>CFG_MADR_FULL
)
port map(
clk => clk,
rstn => rst,
wbs_i => wbs_i,
wbs_o => wbs_o,
rx_i => rx_vector(0),
tx_o => tx_vector(0),
irq_on => irq_on);
can_interconnect : component phys_can_sim
generic map( peer_num => peer_num_inst)
port map( rst => rst,
rx_vector => rx_vector,
tx_vector => tx_vector);
-- stimuli
stimuli: process is
--variable data : std_logic_vector (7 downto 0);
--variable addr : integer;
begin
wait for 10 ns;
rst <= '1';
wait for 40 ns;
rst <= '0';
tx_vector(1) <= '1';
write_regs_from_file( "./testdata/default_setup.tdf", wbs_i, wbs_o, clk);
wait for 8400 ns;
simulate_can_transmission("10101010111", x"770F0F0F00000000", 1, 300 ns, rx_vector(1), tx_vector(1), test_result);
wait for 2400 ns;
simulate_can_transmission("11100010111", x"770F0F0F00000000", 3, 300 ns, rx_vector(1), tx_vector(1), test_result);
wait for 8400 ns;
simulate_can_transmission("00000000011", x"770F0F0F00000000", 5, 300 ns, rx_vector(1), tx_vector(1), test_result);
wait for 8400 ns;
simulate_can_transmission("10111110011", x"770F0F0F00000000", 8, 300 ns, rx_vector(1), tx_vector(1), test_result);
tx_vector(1) <= '1';
wait;
report "end stimuli" severity warning;
end process stimuli;
sub_programm_test: process is
begin
tx_frame <= buildframe("00000000000", x"770F0F0F00000000", 1);
wait;
end process;
-- clock generation
clock : process is
begin
clk <= not clk;
wait for 10 ns / 2;
end process clock;
end architecture RTL;

View File

@@ -0,0 +1,47 @@
library ieee;
use ieee.std_logic_1164.all;
entity phys_can_sim is
generic(
peer_num : integer --number of can participants connected to the bus
);
port(
rst : in std_logic;
rx_vector : out std_logic_vector(peer_num - 1 downto 0); --vector containing all rx_signals
tx_vector : in std_logic_vector(peer_num - 1 downto 0) --vector containing all tx_signals
);
end entity;
architecture behav of phys_can_sim is
begin
process(tx_vector, rst)
variable value : std_logic := '1';
variable i : integer;
begin
if rst = '1' then
rx_vector <= (others => '1');
else
value := '1';
for i in 0 to peer_num - 1 loop
value := value and tx_vector(i);
end loop;
if value = '1' then
rx_vector <= (others => '1');
else
rx_vector <= (others => '0');
end if;
end if;
end process;
end architecture;
--TODO: physical transmission delay simulation if needed (in a later simulation stage)

Binary file not shown.