Commit eab3620d authored by lementec's avatar lementec

add doc, sim, src

parents
#!/usr/bin/env bash
# preprocess using texpp
TEXPP_DIRS="."
TEXPP_DIRS="$HOME/segfs/repo/texpp $TEXPP_DIRS"
TEXPP_DIRS="/segfs/linux/dance_sdk/tools $TEXPP_DIRS"
for d in $TEXPP_DIRS; do
p="$d/texpp.py"
if [ -e $p ]; then
TEXPP_PATH=$p
break
fi
done
if [ -z "$TEXPP_PATH" ]; then
echo 'texpp not found'
exit -1
fi
$TEXPP_PATH main.tex main.pp.tex > main.pp.tex
# create version.tex
echo -n '\newcommand{\version}{SVN revision ' > version.tex
(svnversion -c -n || echo none) >> version.tex
echo -n '}' >> version.tex
texi2pdf main.pp.tex
mv main.pp.pdf main.pdf
#!/usr/bin/env bash
for f in main.{aux,bbl,blg,log,toc}; do
[ -e $f ] && rm $f;
done
for f in main.pp.{tex,aux,bbl,blg,log,toc}; do
[ -e $f ] && rm $f;
done
for f in version.{tex,tex.aux}; do
[ -e $f ] && rm $f;
done
File added
\documentclass[12pt]{article}
\usepackage{listings}
\usepackage{url}
\usepackage{comment}
\usepackage{graphicx}
\begin{document}
%% command definition
\newcommand{\todo}[1]
{\paragraph{}\textbf{TODO}: #1}
\lstnewenvironment{vhdl}
{\lstset{language=VHDL, basicstyle=\tiny, frame=single}}{}
\lstnewenvironment{sh}
{\lstset{frame=single}}{}
\newcommand{\longurl}[2]
{\url{#1#2}}
\newcommand{\longlongurl}[3]
{\url{#1#2#3}}
\IfFileExists{version.tex}
{\input{version.tex}}{\newcommand{\version}{none}}
%% block contents passed to tex preprocessor
%% ignored when processed directly by latex
%% use with \begin{texpp} \end{texpp}
\excludecomment{texpp}
%%
%% document start here
\title{Absolute encoder package}
\author{Fabien Le Mentec \\ lementec@esrf.fr}
\date{\small{version: \version}}
\maketitle
\newpage
\setcounter{tocdepth}{2}
\tableofcontents
%%
\newpage
\section{Description}
\subsection{Overview}
\paragraph{}
The absenc\_pkg implements components for absolute encoder masters and
slaves.
\paragraph{}
The term \textit{master} refers to the component driving the clock, or at
least initiating the data transfer. It is often referred to as the
\textit{controller}. The term \textit{slave} refers to the actual encoder
device.
\paragraph{}
This package is optimized for applications that can be dynamically configured
to use one amongst different types of interfaces at a particular time. As much
as possible, resources that can be shared across interfaces are factorized
(counters, comparators, shift registers ...) and accessed through a multiplexer.
However, and in order to avoid penalizing simpler applications, static
configuration allows to exclude resources associated with an unused interface.
\begin{center}
\includegraphics[width=0.8\textwidth]{./dia/arch/main.png}
\end{center}
\paragraph{}
The master implements the following data conversion pipeline:
\begin{center}
\includegraphics[width=0.5\textwidth]{./dia/master_pipeline/main.png}
\end{center}
\subsection{Features and limitations}
\begin{itemize}
\item Applicable to all
\begin{itemize}
\item master and slave modes,
\item configurable master clock frequency,
\item configurable data length (up to 48 bits),
\item static timeout.
\end{itemize}
\item ENDAT
\begin{itemize}
\item send position mode version 2.1 only,
\item no CRC check.
\end{itemize}
\item BISS
\begin{itemize}
\item point to point configuration only,
\item no interleaved bit support (ie. CDS, CDM).
\end{itemize}
\item SSI
\begin{itemize}
\item optional gray data coding (master only),
\item optional parity bit (not checked).
\end{itemize}
\end{itemize}
\subsection{Tested hardware}
\paragraph{}
The package has been tested with the following masters:
\begin{itemize}
\item ICEPAP ESRF motor controller (SSI),
\item PEPU ESRF encoder processor (SSI, BISS, ENDAT),
\item MUSST ESRF sequencing platform (SSI),
\item Aerotech Soloist CP controller (BISS, ENDAT).
\end{itemize}
\paragraph{}
The package has been tested with the following slaves:
\begin{itemize}
\item PEPU ESRF encoder processor (SSI, BISS, ENDAT),
\item Kubler sendix 5863 SIL BISS encoders,
\item IVO industries GA241 SSI encoders,
\item Heidenhain ROQ 425 512 ENDAT encoders,
\item Heidenhain ROQ 437 2048 ENDAT encoders.
\end{itemize}
\subsection{Performances}
\todo
%%
\newpage
\section{Interfaces}
\begin{texpp}
Texpp.include(
kind = 'interface',
path = '../src/absenc_pkg.vhd',
name = 'master'
)
\end{texpp}
\begin{texpp}
Texpp.include(
kind = 'interface',
path = '../src/absenc_pkg.vhd',
name = 'slave'
)
\end{texpp}
%%
\newpage
\section{Examples}
\begin{texpp}
Texpp.include(
kind = 'example',
path = '../sim/common/main.vhd',
name = 'master'
)
\end{texpp}
\begin{texpp}
Texpp.include(
kind = 'example',
path = '../sim/common/main.vhd',
name = 'slave'
)
\end{texpp}
\end{document}
This diff is collapsed.
--
-- delayed signal
-- delay time resolution is 1 / CLK_FREQ
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
library work;
entity delay is
generic
(
CLK_FREQ: integer;
DELAY_NS: real := 0.0
);
port
(
-- local clock
clk: in std_logic;
-- signal to be delayed
sin: in std_logic;
-- delayed signal
sout: out std_logic
);
end delay;
architecture delay_rtl of delay is
--
-- convert nanoseconds to count at freq
function ns_to_count
(
freq: real;
ns: real
)
return integer is
begin
-- ns = count * (1 / freq) * 1000000000
-- count = (ns * freq) / 1000000000
return integer(ceil((ns * freq) / 1000000000.0));
end ns_to_count;
--
-- delay buffer
-- delay_count the buffer depth
-- add a small constant to allow DELAY_NS to be 0
constant DELAY_COUNT: integer := ns_to_count(DELAY_NS + 0.01, real(CLK_FREQ));
signal delay_buf: std_logic_vector(DELAY_COUNT - 1 downto 0);
begin
gen_zero: if DELAY_NS = 0.0 generate
delay_buf(0) <= sin;
end generate gen_zero;
gen_one: if ((DELAY_NS /= 0.0) and (DELAY_COUNT = 1)) generate
process
begin
wait until rising_edge(clk);
delay_buf(0) <= sin;
end process;
end generate gen_one;
gen_not_one: if DELAY_COUNT /= 1 generate
process
begin
wait until rising_edge(clk);
delay_buf <= sin & delay_buf(delay_buf'length - 1 downto 1);
end process;
end generate gen_not_one;
sout <= delay_buf(0);
end delay_rtl;
--
-- main
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
library work;
entity main is end main;
architecture rtl of main is
--
-- cable length to nanoseconds
function cable_len_to_ns
(
-- cable length in meters
len: real
)
return real is
-- light speed in m/s
constant C: real := 299792458.0;
-- propagation delay in ns/m (around 5ns/m)
constant PROP_DELAY_NS: real := 1000000000.0 / (0.64 * C);
begin
return len * PROP_DELAY_NS;
end cable_len_to_ns;
--
-- configuration constants
constant CLK_FREQ: integer := 50000000;
constant DATA_LEN: integer := 16;
constant LEN_WIDTH: integer := work.absenc_pkg.integer_length(DATA_LEN);
--
-- local clock and reset
signal rst: std_ulogic;
signal clk: std_ulogic;
--
-- data sent by slave to master
constant partial_data: std_logic_vector := "1001010001";
constant partial_zeros:
std_logic_vector(DATA_LEN - 1 downto partial_data'length) :=
(others => '0');
constant slave_data: std_logic_vector(DATA_LEN - 1 downto 0) :=
partial_zeros & partial_data;
constant len: unsigned := to_unsigned(partial_data'length, LEN_WIDTH);
--
-- data read by master from slave
signal master_data: std_logic_vector(DATA_LEN - 1 downto 0);
--
-- master clock frequency divider
-- 1MHz clock
constant ma_fdiv: unsigned := to_unsigned(integer(50), 8);
--
-- selected encoder type
signal enc_type: integer;
--
-- master slave outputs
signal mosi: std_logic;
signal miso: std_logic;
signal ma_clk: std_logic;
--
-- delayed master clock
signal ma_delayed_clk: std_logic;
begin
delay: entity work.delay
generic map
(
CLK_FREQ => CLK_FREQ,
DELAY_NS => cable_len_to_ns(30.0)
)
port map
(
clk => clk,
sin => ma_clk,
sout => ma_delayed_clk
);
slave: work.absenc_pkg.slave
generic map
(
CLK_FREQ => CLK_FREQ
)
port map
(
clk => clk,
rst => rst,
ma_clk => ma_delayed_clk,
miso => miso,
mosi => mosi,
gate => open,
data => slave_data,
len => len,
enc_type => enc_type,
ssi_flags => work.absenc_pkg.SSI_DEFAULT_FLAGS
);
master: work.absenc_pkg.master
generic map
(
CLK_FREQ => CLK_FREQ
)
port map
(
clk => clk,
rst => rst,
ma_fdiv => ma_fdiv,
ma_clk => ma_clk,
mosi => mosi,
miso => miso,
gate => open,
data => master_data,
len => len,
enc_type => enc_type,
ssi_flags => work.absenc_pkg.SSI_DEFAULT_FLAGS,
ssi_delay_fdiv => work.absenc_pkg.SSI_DEFAULT_DELAY_FDIV
);
end rtl;
isim force add {/main/enc_type} 1
wave add /main/master/gen_biss/master_biss/curr_state
wave add /main/slave/gen_biss/slave_biss/curr_state
#!/usr/bin/env sh
fuse -intstyle ise -incremental -o main -prj main.prj main
#!/usr/bin/env sh
rm main
rm -rf isim
rm isim.wdb
rm isim.log
rm fuse.log
rm fuseRelaunch.cmd
rm fuse.xmsgs
# system clock
isim force add {/main/clk} \
1 -value 0 -radix bin -time 10 ns -repeat 20 ns
isim force add {/main/rst} \
1 -value 0 -time 2 us
# main
wave add /main/mosi
wave add /main/miso
wave add /main/master_data
# master clock
wave add /main/master/ma_clk
isim force add {/main/enc_type} 0
wave add /main/master/gen_endat/master_endat/curr_state
wave add /main/slave/gen_endat/slave_endat/curr_state
vhdl work ../../src/absenc_pkg.vhd
vhdl work ../../src/absenc_utils.vhd
vhdl work ../../src/absenc_master.vhd
vhdl work ../../src/absenc_master_endat.vhd
vhdl work ../../src/absenc_master_biss.vhd
vhdl work ../../src/absenc_master_ssi.vhd
vhdl work ../../src/absenc_slave.vhd
vhdl work ../../src/absenc_slave_endat.vhd
vhdl work ../../src/absenc_slave_biss.vhd
vhdl work ../../src/absenc_slave_ssi.vhd
vhdl work ../common/main.vhd
source common.tcl
if { [ file exists user.tcl ] == 1 } {
source user.tcl
} else {
source endat.tcl
}
run 200 us
#!/usr/bin/env bash
if [ -z "$1" ]; then
main_tcl='main.tcl'
else
main_tcl="$1"
fi ;
./main -gui -tclbatch $main_tcl
isim force add {/main/enc_type} 2
wave add /main/master/gen_ssi/master_ssi/curr_state
wave add /main/slave/gen_ssi/slave_ssi/curr_state
# source endat.tcl
# source biss.tcl
source ssi.tcl
# wave add /main/master/latched_data
# wave add /main/master/msb_data
# wave add /main/master/signed_data
# wave add /main/master/conv_data
# wave add /main/master/sipo_val
# wave add /main/master/sipo_latch
# wave add /main/slave/piso_lval
# wave add /main/slave/gen_endat/slave_endat/sipo_val
wave add /main/clk
wave add /main/ma_clk
wave add /main/ma_delayed_clk
This diff is collapsed.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
library work;
entity master_biss is
generic
(
CLK_FREQ: integer
);
port
(
-- local clock
clk: in std_logic;
rst: in std_logic;
-- master clock edges
ma_clk_fedge: in std_logic;
ma_clk_redge: in std_logic;
-- the edges we are interested in
ma_clk_edge: out std_logic;
-- master clock reset
-- if ma_clk_rst_en, use ma_clk_rst_level
ma_clk_rst_en: out std_logic;
ma_clk_rst_val: out std_logic;
-- master out, slave in
mosi: out std_logic;
miso: in std_logic;
-- gate to drive output (1 to drive it)
gate: out std_logic;
-- desired data length
len: in unsigned;
-- timeout counter
tm_match: in std_logic;
tm_top: out unsigned;
-- general purpose counter
count_top: out unsigned;
count_match: in std_logic;
count_rst: out std_logic;
-- sipo register
sipo_val: in std_logic_vector;
sipo_latch: out std_logic;
-- enable data conversion stages
gray_to_bin_en: out std_logic;
lsb_to_msb_en: out std_logic
);
end entity;
architecture absenc_master_biss_rtl of master_biss is
--
-- default timeout value. standard says:
-- http://biss-interface.com/files/Bissinterface_c5es.pdf, page 18
constant TM_VAL: integer := work.absenc_pkg.us_to_count
(work.absenc_pkg.MASTER_DEFAULT_TM_US, CLK_FREQ, tm_top'length);
--
-- state machine
type biss_state_t is
(
BISS_SYNC0,
BISS_SYNC1,
BISS_ACK,
BISS_START,
BISS_CDS,
BISS_DATA,
BISS_STOP
);
constant BISS_TMOUT: biss_state_t := BISS_SYNC0;
constant BISS_ERR: biss_state_t := BISS_SYNC0;
signal curr_state: biss_state_t;
signal next_state: biss_state_t;
begin
--
-- state automaton
process
begin
wait until rising_edge(clk);
if (rst = '1') then
curr_state <= BISS_SYNC0;
elsif ((ma_clk_redge or tm_match) = '1') then
curr_state <= next_state;
end if;
end process;
process(curr_state, count_match, tm_match)
begin
next_state <= curr_state;
case curr_state is
when BISS_SYNC0 =>
next_state <= BISS_SYNC1;
when BISS_SYNC1 =>
next_state <= BISS_ACK;
when BISS_ACK =>
-- TODO: check miso == 0
next_state <= BISS_START;
when BISS_START =>
-- TODO: check miso == 1
next_state <= BISS_CDS;
when BISS_CDS =>
next_state <= BISS_DATA;
when BISS_DATA =>
if count_match = '1' then
next_state <= BISS_STOP;
else
next_state <= BISS_DATA;
end if;
when BISS_STOP =>
if (tm_match = '1') then
next_state <= BISS_SYNC0;
end if;
when others =>
next_state <= BISS_ERR;
end case;
end process;
process
begin
wait until rising_edge(clk);
ma_clk_rst_en <= '0';
count_top <= (others => '0');
count_rst <= '0';
sipo_latch <= '0';
case curr_state is
when BISS_SYNC0 =>
when BISS_SYNC1 =>
when BISS_ACK =>
when BISS_START =>
when BISS_CDS =>
-- TODO: capture CDS
count_top <= len(count_top'range);
count_rst <= '1';
when BISS_DATA =>
when BISS_STOP =>
sipo_latch <= '1';
ma_clk_rst_en <= '1';
when others =>
end case;
end process;
--
-- clock reset or idle value
ma_clk_rst_val <= '1';
--
-- timeout
tm_top <= to_unsigned(TM_VAL, tm_top'length);
--
-- gray to binary disabled
gray_to_bin_en <= '0';
--
-- lsb to msb disabled
lsb_to_msb_en <= '0';
--
-- gate always disabled
gate <= '0';
mosi <= '0';
--
-- use rising edge
ma_clk_edge <= ma_clk_redge;
end architecture;
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
library work;
entity master_endat is
generic
(
CLK_FREQ: integer
);
port
(
-- local clock
clk: in std_logic;
rst: in std_logic;
-- master clock edges
ma_clk_fedge: in std_logic;
ma_clk_redge: in std_logic;
-- the edge we are interested in
ma_clk_edge: out std_logic;
-- master clock reset
-- if ma_clk_rst_en, use ma_clk_rst_level
ma_clk_rst_en: out std_logic;
ma_clk_rst_val: out std_logic;
-- master out, slave in
mosi: out std_logic;
miso: in std_logic;
-- gate to drive output (1 to drive it)
gate: out std_logic;
-- desired data length
len: in unsigned;
-- timeout counter
tm_match: in std_logic;
tm_top: out unsigned;
-- general purpose counter
count_top: out unsigned;
count_match: in std_logic;
count_rst: out std_logic;
-- sipo register
sipo_val: in std_logic_vector;
sipo_latch: out std_logic;
-- enable data conversion stages
gray_to_bin_en: out std_logic;
lsb_to_msb_en: out std_logic
);
end entity;
architecture absenc_master_endat_rtl of master_endat is
--
-- default timeout value. standard says: 10 to 30 us
constant TM_VAL: integer := work.absenc_pkg.us_to_count
(work.absenc_pkg.MASTER_DEFAULT_TM_US, CLK_FREQ, tm_top'length);
--
-- main state machine
type endat_state_t is
(
ENDAT_INIT,
ENDAT_T0,
ENDAT_T1,
ENDAT_MODE,
ENDAT_T3,
ENDAT_T4,
ENDAT_T5,
ENDAT_START,
ENDAT_F1,
ENDAT_DATA,
ENDAT_CRC5_FIRST,
ENDAT_CRC5_CONT,
ENDAT_DONE
);
constant ENDAT_TMOUT: endat_state_t := ENDAT_DONE;
constant ENDAT_ERR: endat_state_t := ENDAT_DONE;
signal curr_state: endat_state_t;
signal next_state: endat_state_t;
--
-- right shifted parallel in, serial out register
-- loaded values are in reverse order
constant piso_ini: std_logic_vector := "111000";
signal piso_val: std_logic_vector(piso_ini'length - 1 downto 0);
signal piso_load: std_logic;
begin
--
-- state automaton
process
begin
wait until rising_edge(clk);
if (rst = '1') then
curr_state <= ENDAT_TMOUT;
elsif ((tm_match or ma_clk_fedge) = '1') then
curr_state <= next_state;
end if;
end process;
process(curr_state, count_match, tm_match, miso)
begin
next_state <= curr_state;
case curr_state is
when ENDAT_INIT =>
next_state <= ENDAT_T0;
when ENDAT_T0 =>
next_state <= ENDAT_T1;
when ENDAT_T1 =>
next_state <= ENDAT_MODE;
when ENDAT_MODE =>
if (count_match = '1') then
next_state <= ENDAT_T3;
end if;
when ENDAT_T3 =>
next_state <= ENDAT_T4;
when ENDAT_T4 =>
next_state <= ENDAT_T5;
when ENDAT_T5 =>
next_state <= ENDAT_START;
when ENDAT_START =>
if (miso = '1') then
next_state <= ENDAT_F1;
elsif (tm_match = '1') then
next_state <= ENDAT_TMOUT;
end if;
when ENDAT_F1 =>
next_state <= ENDAT_DATA;
when ENDAT_DATA =>
if (count_match = '1') then
next_state <= ENDAT_CRC5_FIRST;
end if;
when ENDAT_CRC5_FIRST =>
next_state <= ENDAT_CRC5_CONT;
when ENDAT_CRC5_CONT =>
if (count_match = '1') then
next_state <= ENDAT_DONE;
end if;
when ENDAT_DONE =>
-- wait for at least one timeout
next_state <= ENDAT_INIT;
when others =>
next_state <= ENDAT_ERR;
end case;
end process;
process
begin
wait until rising_edge(clk);
ma_clk_rst_en <= '0';
count_top <= (others => '0');
count_rst <= '0';
sipo_latch <= '0';
gate <= '0';
mosi <= '0';
piso_load <= '0';
case curr_state is
when ENDAT_INIT =>
gate <= '1';
when ENDAT_T0 =>
gate <= '1';
when ENDAT_T1 =>
piso_load <= '1';
gate <= '1';
count_top <= to_unsigned(6, count_top'length);
count_rst <= '1';
when ENDAT_MODE =>
mosi <= piso_val(0);
gate <= '1';
when ENDAT_T3 =>
mosi <= '0';
gate <= '1';
when ENDAT_T4 =>
mosi <= '0';
gate <= '1';
when ENDAT_T5 =>
when ENDAT_START =>
when ENDAT_F1 =>
count_top <= len(count_top'range);
count_rst <= '1';
when ENDAT_DATA =>
-- sent LSb first
when ENDAT_CRC5_FIRST =>
count_top <= to_unsigned(integer(5 - 1), count_top'length);
count_rst <= '1';
sipo_latch <= '1';
when ENDAT_CRC5_CONT =>
when ENDAT_DONE =>
ma_clk_rst_en <= '1';
when others =>
end case;
end process;
--
-- right shifted piso register
-- set at falling edge, sample by slave at redge
process
begin
wait until rising_edge(clk);
if (piso_load = '1') then
piso_val <= piso_ini;
elsif (ma_clk_fedge = '1') then
piso_val <= '0' & piso_val(piso_val'length - 1 downto 1);
end if;
end process;
--
-- clock reset or idle value
ma_clk_rst_val <= '1';
--
-- timeout
tm_top <= to_unsigned(TM_VAL, tm_top'length);
--
-- gray to binary disabled
gray_to_bin_en <= '0';
--
-- lsb to msb enabled
lsb_to_msb_en <= '1';
--
-- use falling edge
ma_clk_edge <= ma_clk_fedge;
end architecture;
--
-- ssi master implementation
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
library work;
entity master_ssi is
generic
(
CLK_FREQ: integer
);
port
(
-- local clock
clk: in std_logic;
rst: in std_logic;
-- master clock edges
ma_clk_fedge: in std_logic;
ma_clk_redge: in std_logic;
-- the edge we are interested in
ma_clk_edge: out std_logic;
-- master clock reset
-- if ma_clk_rst_en, use ma_clk_rst_level
ma_clk_rst_en: out std_logic;
ma_clk_rst_val: out std_logic;
-- master out, slave in
mosi: out std_logic;
miso: in std_logic;
-- gate to drive output (1 to drive it)
gate: out std_logic;
-- desired data length
len: in unsigned;
-- timeout counter
tm_match: in std_logic;
tm_top: out unsigned;
-- general purpose counter
count_top: out unsigned;
count_match: in std_logic;
count_rst: out std_logic;
-- sipo register
sipo_val: in std_logic_vector;
sipo_latch: out std_logic;
-- enable data conversion stages
gray_to_bin_en: out std_logic;
lsb_to_msb_en: out std_logic;
-- refer to package for comments
ssi_flags: in std_logic_vector;
ssi_delay_fdiv: in unsigned
);
end entity;
architecture absenc_master_ssi_rtl of master_ssi is
--
-- default timeout value. standard says:
-- https://upload.wikimedia.org/wikipedia/commons/8/8d/Ssisingletransmission.jpg
constant TM_VAL: integer := work.absenc_pkg.us_to_count
(work.absenc_pkg.MASTER_DEFAULT_TM_US, CLK_FREQ, tm_top'length);
--
-- state machine
type ssi_state_t is
(
SSI_START,
SSI_DATA,
SSI_DOT,
SSI_TP
);
constant SSI_ERR: ssi_state_t := SSI_START;
signal curr_state: ssi_state_t;
signal next_state: ssi_state_t;
--
-- gray to bin internal register
signal is_gray: std_logic;
--
-- terminating pattern bits
signal is_dot_bit: std_logic;
begin
--
-- state automaton
process
begin
wait until rising_edge(clk);
if (rst = '1') then
curr_state <= SSI_START;
elsif ((ma_clk_redge or tm_match) = '1') then
curr_state <= next_state;
end if;
end process;
process(curr_state, count_match, is_dot_bit)
begin
next_state <= curr_state;
case curr_state is
when SSI_START =>
next_state <= SSI_DATA;
when SSI_DATA =>
if count_match = '1' then
if is_dot_bit = '1' then
next_state <= SSI_DOT;
else
next_state <= SSI_TP;
end if;
end if;
when SSI_DOT =>
next_state <= SSI_TP;
when SSI_TP =>
next_state <= SSI_START;
when others =>
next_state <= SSI_ERR;
end case;
end process;
process
begin
wait until rising_edge(clk);
ma_clk_rst_en <= '0';
count_top <= (others => '0');
count_rst <= '0';
sipo_latch <= '0';
is_gray <= is_gray;
is_dot_bit <= is_dot_bit;
case curr_state is
when SSI_START =>
count_top <= len(count_top'range);
count_rst <= '1';
is_gray <= ssi_flags(1);
is_dot_bit <= ssi_flags(2) or ssi_flags(3) or ssi_flags(4);
when SSI_DATA =>
when SSI_DOT =>
sipo_latch <= '1';
when SSI_TP =>
-- do not latch twice
sipo_latch <= not is_dot_bit;
ma_clk_rst_en <= '1';
when others =>
end case;
end process;
--
-- clock reset or idle value
ma_clk_rst_val <= '1';
--
-- timeout
tm_top <= to_unsigned(TM_VAL, tm_top'length);
--
-- gray to binary
gray_to_bin_en <= is_gray;
--
-- lsb to msb disabled
lsb_to_msb_en <= '0';
--
-- gate always disabled
gate <= '0';
mosi <= '0';
--
-- use rising edge
ma_clk_edge <= ma_clk_redge;
end architecture;
This diff is collapsed.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
library work;
entity slave is
generic
(
CLK_FREQ: integer;
ENABLE_ENDAT: boolean;
ENABLE_BISS: boolean;
ENABLE_SSI: boolean
);
port
(
-- local clock
clk: in std_logic;
rst: in std_logic;
-- master clock and data
ma_clk: in std_logic;
miso: out std_logic;
mosi: in std_logic;
gate: out std_logic;
-- data and length
data: in std_logic_vector;
len: in unsigned;
-- encoder type
enc_type: in integer;
-- ssi specific control registers
ssi_flags: in std_logic_vector
);
end slave;
architecture absenc_slave_rtl of slave is
constant DATA_LEN: integer := data'length;
--
-- timeout counter
constant TM_MAX: integer := work.absenc_pkg.us_to_count
(work.absenc_pkg.MAX_TM_US, CLK_FREQ, integer'high);
constant TM_LEN: integer :=
work.absenc_pkg.integer_length(TM_MAX);
constant DEFAULT_TM_TOP: integer := work.absenc_pkg.us_to_count
(work.absenc_pkg.SLAVE_DEFAULT_TM_US, CLK_FREQ, TM_LEN);
signal tm_val: unsigned(TM_LEN - 1 downto 0);
signal tm_top: unsigned(tm_val'range);
signal tm_match: std_logic;
--
-- general counter
signal count_val:
unsigned(work.absenc_pkg.integer_length(DATA_LEN) - 1 downto 0);
signal count_top: unsigned(count_val'range);
signal count_top_latched: unsigned(count_val'range);
signal count_rst: std_logic;
signal count_match: std_logic;
--
-- parallel in, serial out (PISO) register
-- rval is the right shifted value (for lsb)
-- lval is the left shifted value (for msb)
signal piso_rval: std_logic_vector(data'range);
signal piso_lval: std_logic_vector(data'range);
signal piso_ini: std_logic_vector(data'range);
signal piso_load: std_logic;
--
-- master clock edges
-- redge is for rising edge
-- fedge is for falling edge
-- edge is the one selected by encoder
signal ma_clk_pre: std_logic;
signal ma_clk_redge: std_logic;
signal ma_clk_fedge: std_logic;
signal ma_clk_edge: std_logic;
--
-- master clock filter
signal ma_clk_buf: std_logic_vector(2 downto 0);
signal ma_clk_filt: std_logic;
--
-- encoder multiplexed signal
--
-- the mux size depends on ENABLE_xxx generics. the idea is to eliminate
-- unneeded FPGA logic by instantiating only modules enabled by the generics.
-- to do so, we have functions that translate from ENC_TYPE_xxx to ENC_MUX_xxx
-- and vice versa. ENC_TYPE_xxx represent all the possible encoders, while
-- ENC_MUX_xxx represent an encoder index in the mux, if enabled by generics.
subtype tm_val_t is unsigned(tm_val'range);
type tm_val_array_t is array(integer range<>) of tm_val_t;
subtype count_val_t is unsigned(count_val'range);
type count_val_array_t is array(integer range<>) of count_val_t;
subtype piso_val_t is std_logic_vector(data'range);
type piso_val_array_t is array(integer range<>) of piso_val_t;
constant enc_mux_to_type: work.absenc_pkg.enc_type_array_t :=
work.absenc_pkg.gen_enc_mux_to_type(ENABLE_ENDAT, ENABLE_BISS, ENABLE_SSI);
constant ENC_MUX_COUNT: integer :=
work.absenc_pkg.get_enc_mux_count(ENABLE_ENDAT, ENABLE_BISS, ENABLE_SSI);
constant ENC_MUX_ENDAT: integer :=
work.absenc_pkg.get_enc_mux_endat(ENABLE_ENDAT, ENABLE_BISS, ENABLE_SSI);
constant ENC_MUX_BISS: integer :=
work.absenc_pkg.get_enc_mux_biss(ENABLE_ENDAT, ENABLE_BISS, ENABLE_SSI);
constant ENC_MUX_SSI: integer :=
work.absenc_pkg.get_enc_mux_ssi(ENABLE_ENDAT, ENABLE_BISS, ENABLE_SSI);
signal ma_clk_edge_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0);
signal miso_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0);
signal gate_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0);
signal tm_top_mux: tm_val_array_t(ENC_MUX_COUNT - 1 downto 0);
signal count_rst_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0);
signal count_top_mux: count_val_array_t(ENC_MUX_COUNT - 1 downto 0);
signal piso_ini_mux: piso_val_array_t(ENC_MUX_COUNT - 1 downto 0);
signal piso_load_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0);
begin
--
-- assertions
assert (ENC_MUX_COUNT /= 0)
report "ENC_MUX_COUNT == 0" severity failure;
--
-- master clock filter
-- sometimes, slave detects fantom master clock edges
-- filtering the master clock fixes this issue
process
begin
wait until rising_edge(clk);
ma_clk_buf <= ma_clk & ma_clk_buf(ma_clk_buf'length - 1 downto 1);
end process;
process(ma_clk_buf, ma_clk_filt)
begin
ma_clk_filt <= ma_clk_filt;
if ma_clk_buf = (ma_clk_buf'range => '0') then
ma_clk_filt <= '0';
elsif ma_clk_buf = (ma_clk_buf'range => '1') then
ma_clk_filt <= '1';
end if;
end process;
--
-- master clock edge detection
process
begin
wait until rising_edge(clk);
if (rst = '1') then
ma_clk_redge <= '0';
ma_clk_fedge <= '0';
ma_clk_pre <= ma_clk_filt;
else
ma_clk_redge <= (not ma_clk_pre) and ma_clk_filt;
ma_clk_fedge <= ma_clk_pre and (not ma_clk_filt);
ma_clk_pre <= ma_clk_filt;
end if;
end process;
--
-- piso right shifted register
process
begin
wait until rising_edge(clk);
if (piso_load = '1') then
piso_rval <= piso_ini;
elsif (ma_clk_edge = '1') then
piso_rval <= '0' & piso_rval(piso_rval'length - 1 downto 1);
end if;
end process;
--
-- piso left shifted register
process
begin
wait until rising_edge(clk);
if (piso_load = '1') then
piso_lval <= piso_ini;
elsif (ma_clk_edge = '1') then
piso_lval <= piso_lval(piso_lval'length - 2 downto 0) & '0';
end if;
end process;
--
-- general purpose counter
-- monotically incrementing
-- increment every master edge
-- starts from 1
process
begin
wait until rising_edge(clk);
if (count_rst = '1') then
count_val <= to_unsigned(integer(1), count_val'length);
elsif (ma_clk_edge = '1') then
count_val <= count_val + 1;
end if;
end process;
--
-- general purpose counter comparator
-- count_match set to one when count_val = count_top
process
begin
wait until rising_edge(clk);
if (count_rst = '1') then
count_top_latched <= count_top;
end if;
end process;
count_match <= '1' when (count_val = count_top_latched) else '0';
--
-- timeout counter
-- tm_val decrement from tm_top to 0
-- tm_val reloaded at ma_clk_edge
process
begin
wait until rising_edge(clk);
tm_match <= '0';
if ((rst or ma_clk_edge) = '1') then
tm_val <= tm_top;
elsif (tm_val = 0) then
tm_val <= tm_top;
tm_match <= '1';
else
tm_val <= tm_val - 1;
end if;
end process;
--
-- encoder slave implementations
gen_endat: if ENABLE_ENDAT = TRUE generate
slave_endat: work.absenc_pkg.slave_endat
generic map
(
CLK_FREQ => CLK_FREQ
)
port map
(
clk => clk,
rst => rst,
ma_clk_redge => ma_clk_redge,
ma_clk_fedge => ma_clk_fedge,
ma_clk_edge => ma_clk_edge_mux(ENC_MUX_ENDAT),
miso => miso_mux(ENC_MUX_ENDAT),
mosi => mosi,
gate => gate_mux(ENC_MUX_ENDAT),
data => data,
len => len,
tm_match => tm_match,
tm_top => tm_top_mux(ENC_MUX_ENDAT),
count_top => count_top_mux(ENC_MUX_ENDAT),
count_match => count_match,
count_rst => count_rst_mux(ENC_MUX_ENDAT),
piso_rval => piso_rval,
piso_lval => piso_lval,
piso_ini => piso_ini_mux(ENC_MUX_ENDAT),
piso_load => piso_load_mux(ENC_MUX_ENDAT)
);
end generate gen_endat;
gen_biss: if ENABLE_BISS = TRUE generate
slave_biss: work.absenc_pkg.slave_biss
generic map
(
CLK_FREQ => CLK_FREQ
)
port map
(
clk => clk,
rst => rst,
ma_clk_redge => ma_clk_redge,
ma_clk_fedge => ma_clk_fedge,
ma_clk_edge => ma_clk_edge_mux(ENC_MUX_BISS),
miso => miso_mux(ENC_MUX_BISS),
mosi => mosi,
gate => gate_mux(ENC_MUX_BISS),
data => data,
len => len,
tm_match => tm_match,
tm_top => tm_top_mux(ENC_MUX_BISS),
count_top => count_top_mux(ENC_MUX_BISS),
count_match => count_match,
count_rst => count_rst_mux(ENC_MUX_BISS),
piso_rval => piso_rval,
piso_lval => piso_lval,
piso_ini => piso_ini_mux(ENC_MUX_BISS),
piso_load => piso_load_mux(ENC_MUX_BISS)
);
end generate gen_biss;
gen_ssi: if ENABLE_SSI = TRUE generate
slave_ssi: work.absenc_pkg.slave_ssi
generic map
(
CLK_FREQ => CLK_FREQ
)
port map
(
clk => clk,
rst => rst,
ma_clk_redge => ma_clk_redge,
ma_clk_fedge => ma_clk_fedge,
ma_clk_edge => ma_clk_edge_mux(ENC_MUX_SSI),
miso => miso_mux(ENC_MUX_SSI),
mosi => mosi,
gate => gate_mux(ENC_MUX_SSI),
data => data,
len => len,
tm_match => tm_match,
tm_top => tm_top_mux(ENC_MUX_SSI),
count_top => count_top_mux(ENC_MUX_SSI),
count_match => count_match,
count_rst => count_rst_mux(ENC_MUX_SSI),
piso_rval => piso_rval,
piso_lval => piso_lval,
piso_ini => piso_ini_mux(ENC_MUX_SSI),
piso_load => piso_load_mux(ENC_MUX_SSI),
ssi_flags => ssi_flags
);
end generate gen_ssi;
--
-- enc_type multiplexer
process
(
enc_type,
ma_clk_edge_mux,
miso_mux,
gate_mux,
tm_top_mux,
count_rst_mux,
count_top_mux,
piso_ini_mux,
piso_load_mux
)
begin
ma_clk_edge <= '0';
miso <= '0';
gate <= '0';
tm_top <= to_unsigned(DEFAULT_TM_TOP, tm_top'length);
count_rst <= '0';
count_top <= (others => '0');
piso_ini <= (others => '0');
piso_load <= '0';
for i in 0 to ENC_MUX_COUNT - 1 loop
if enc_mux_to_type(i) = enc_type then
ma_clk_edge <= ma_clk_edge_mux(i);
miso <= miso_mux(i);
gate <= gate_mux(i);
tm_top <= tm_top_mux(i);
count_rst <= count_rst_mux(i);
count_top <= count_top_mux(i);
piso_ini <= piso_ini_mux(i);
piso_load <= piso_load_mux(i);
end if;
end loop;
end process;
end absenc_slave_rtl;
-- BISS automaton notes
-- point to point configuration
-- http://biss-interface.com/files/Bissinterface_c5es.pdf, figure 1
-- slave idle state (SLO = 1)
-- first rising edge of MA is for slave sync
-- second rising edge of MA is for slave ack (SLO = 0)
-- start bit (SLO = 1)
-- CDS bit
-- data bits
-- stop bit (SLO = 0) and CDM bit (MA)
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
library work;
entity slave_biss is
generic
(
CLK_FREQ: integer
);
port
(
-- local clock
clk: in std_logic;
rst: in std_logic;
-- master clock edges
ma_clk_redge: in std_logic;
ma_clk_fedge: in std_logic;
-- the edge we are interested in
ma_clk_edge: out std_logic;
-- master data input, output
miso: out std_logic;
mosi: in std_logic;
-- gate to drive output (1 to drive it)
gate: out std_logic;
-- actual data to send and length
data: in std_logic_vector;
len: in unsigned;
-- timeout counter
tm_match: in std_logic;
tm_top: out unsigned;
-- general purpose counter
count_top: out unsigned;
count_match: in std_logic;
count_rst: out std_logic;
-- piso register
piso_rval: in std_logic_vector;
piso_lval: in std_logic_vector;
piso_ini: out std_logic_vector;
piso_load: out std_logic
);
end slave_biss;
architecture slave_biss_rtl of slave_biss is
--
-- default timeout value. standard says:
-- http://biss-interface.com/files/Bissinterface_c5es.pdf, page 18
constant TM_VAL: integer := work.absenc_pkg.us_to_count
(work.absenc_pkg.SLAVE_DEFAULT_TM_US, CLK_FREQ, tm_top'length);
--
-- state machine
type biss_state_t is
(
BISS_SYNC0,
BISS_SYNC1,
BISS_ACK,
BISS_START,
BISS_CDS,
BISS_DATA,
BISS_STOP
);
constant BISS_TMOUT: biss_state_t := BISS_SYNC0;
constant BISS_ERR: biss_state_t := BISS_SYNC0;
signal curr_state: biss_state_t;
signal next_state: biss_state_t;
begin
--
-- state automaton
process
begin
wait until rising_edge(clk);
if (rst = '1') then
curr_state <= BISS_SYNC0;
elsif (tm_match = '1') then
curr_state <= BISS_TMOUT;
elsif (ma_clk_redge = '1') then
curr_state <= next_state;
end if;
end process;
process(curr_state, count_match, tm_match)
begin
next_state <= curr_state;
case curr_state is
when BISS_SYNC0 =>
next_state <= BISS_SYNC1;
when BISS_SYNC1 =>
next_state <= BISS_ACK;
when BISS_ACK =>
next_state <= BISS_START;
when BISS_START =>
next_state <= BISS_CDS;
when BISS_CDS =>
next_state <= BISS_DATA;
when BISS_DATA =>
if count_match = '1' then
next_state <= BISS_STOP;
else
next_state <= BISS_DATA;
end if;
when BISS_STOP =>
if (tm_match = '1') then
next_state <= BISS_SYNC0;
end if;
when others =>
next_state <= BISS_ERR;
end case;
end process;
process
begin
wait until rising_edge(clk);
miso <= '0';
count_top <= (others => '0');
count_rst <= '0';
piso_load <= '0';
case curr_state is
when BISS_SYNC0 =>
miso <= '1';
count_rst <= '1';
when BISS_SYNC1 =>
miso <= '1';
count_rst <= '1';
when BISS_ACK =>
miso <= '0';
when BISS_START =>
miso <= '1';
when BISS_CDS =>
-- TODO: output CDS bit
miso <= '0';
count_top <= len(count_top'range);
count_rst <= '1';
piso_ini <= data;
piso_load <= '1';
when BISS_DATA =>
-- dynamic shift registers, ug901-vivado-synthesis.pdf, p.84
miso <= piso_lval(to_integer(len) - 1);
when BISS_STOP =>
-- TODO: capture CDM
miso <= '0';
when others =>
end case;
end process;
--
-- timeout
tm_top <= to_unsigned(TM_VAL, tm_top'length);
--
-- master clock edge we are looking
ma_clk_edge <= ma_clk_redge;
--
-- gate always enabled
gate <= '1';
end slave_biss_rtl;
--
-- endat encoder implementation
--
-- notes from endat2.2 technical information documentation
-- esp. refer to page 6, Position Values
--
-- command format:
-- send data without additional parameter
-- 2T(x2) & mode(x6) & 2T(x2) & S(x1) & F1(x1) & DATA(xlen) & CRC(x5)
--
-- recovery time:
-- 10 to 30 us for endat2.1
-- 1.25 to 3.75 us for endat2.2
--
-- samples at rising edge
--
-- data sent LSbit first
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
library work;
entity slave_endat is
generic
(
CLK_FREQ: integer
);
port
(
-- local clock
clk: in std_logic;
rst: in std_logic;
-- master clock edges
ma_clk_redge: in std_logic;
ma_clk_fedge: in std_logic;
-- the edge we are interested in
ma_clk_edge: out std_logic;
-- master data input, output
miso: out std_logic;
mosi: in std_logic;
-- gate to drive output (1 to drive it)
gate: out std_logic;
-- actual data to send and length
data: in std_logic_vector;
len: in unsigned;
-- timeout counter
tm_match: in std_logic;
tm_top: out unsigned;
-- general purpose counter
count_top: out unsigned;
count_match: in std_logic;
count_rst: out std_logic;
-- piso register
piso_rval: in std_logic_vector;
piso_lval: in std_logic_vector;
piso_ini: out std_logic_vector;
piso_load: out std_logic
);
end slave_endat;
architecture slave_endat_rtl of slave_endat is
--
-- default timeout value. standard says: 10 to 30 us
constant TM_VAL: integer := work.absenc_pkg.us_to_count
(work.absenc_pkg.SLAVE_DEFAULT_TM_US, CLK_FREQ, tm_top'length);
--
-- main state machine
type endat_state_t is
(
ENDAT_IDLE,
ENDAT_T0,
ENDAT_T1,
ENDAT_MODE,
ENDAT_T3,
ENDAT_T4,
ENDAT_T5,
ENDAT_START,
ENDAT_F1,
ENDAT_DATA,
ENDAT_CRC5_FIRST,
ENDAT_CRC5_CONT
);
constant ENDAT_TMOUT: endat_state_t := ENDAT_IDLE;
constant ENDAT_ERR: endat_state_t := ENDAT_IDLE;
signal curr_state: endat_state_t;
signal next_state: endat_state_t;
--
-- serial in, parallel out (sipo) register
signal sipo_val: std_logic_vector(6 - 1 downto 0);
begin
--
-- sipo register
-- sampled at falling edge by slave
process
begin
wait until rising_edge(clk);
if (ma_clk_redge = '1') then
sipo_val <= sipo_val(sipo_val'length - 2 downto 0) & mosi;
end if;
end process;
--
-- state automaton
process
begin
wait until rising_edge(clk);
if (rst = '1') then
curr_state <= ENDAT_IDLE;
elsif (tm_match = '1') then
curr_state <= ENDAT_TMOUT;
elsif (ma_clk_fedge = '1') then
curr_state <= next_state;
end if;
end process;
process(curr_state, count_match, sipo_val, tm_match)
begin
next_state <= curr_state;
case curr_state is
when ENDAT_IDLE =>
next_state <= ENDAT_T0;
when ENDAT_T0 =>
next_state <= ENDAT_T1;
when ENDAT_T1 =>
next_state <= ENDAT_MODE;
when ENDAT_MODE =>
-- serial register value is sampled at rising edge
-- but next_state computed at falling edge. thus
-- we compare here when count_match
if count_match = '1' then
if sipo_val(5 downto 0) = "000111" then
next_state <= ENDAT_T3;
else
next_state <= ENDAT_ERR;
end if;
end if;
when ENDAT_T3 =>
next_state <= ENDAT_T4;
when ENDAT_T4 =>
next_state <= ENDAT_T5;
when ENDAT_T5 =>
next_state <= ENDAT_START;
when ENDAT_START =>
next_state <= ENDAT_F1;
when ENDAT_F1 =>
next_state <= ENDAT_DATA;
when ENDAT_DATA =>
if count_match = '1' then
next_state <= ENDAT_CRC5_FIRST;
end if;
when ENDAT_CRC5_FIRST =>
next_state <= ENDAT_CRC5_CONT;
when ENDAT_CRC5_CONT =>
if count_match = '1' then
next_state <= ENDAT_IDLE;
end if;
when others =>
next_state <= ENDAT_ERR;
end case;
end process;
process
begin
wait until rising_edge(clk);
gate <= '0';
miso <= '0';
count_top <= (others => '0');
count_rst <= '0';
piso_load <= '0';
case curr_state is
when ENDAT_IDLE =>
when ENDAT_T0 =>
when ENDAT_T1 =>
count_top <= to_unsigned(integer(6), count_top'length);
count_rst <= '1';
when ENDAT_MODE =>
when ENDAT_T3 =>
when ENDAT_T4 =>
when ENDAT_T5 =>
-- data line stabilization state
gate <= '1';
miso <= '0';
when ENDAT_START =>
gate <= '1';
miso <= '1';
when ENDAT_F1 =>
-- error bit (F1)
gate <= '1';
miso <= '0';
count_top <= len(count_top'range);
count_rst <= '1';
piso_ini <= data;
piso_load <= '1';
when ENDAT_DATA =>
gate <= '1';
miso <= piso_rval(0);
when ENDAT_CRC5_FIRST =>
count_top <= to_unsigned(integer(5 - 1), count_top'length);
count_rst <= '1';
gate <= '1';
miso <= '0';
when ENDAT_CRC5_CONT =>
gate <= '1';
miso <= '0';
when others =>
end case;
end process;
--
-- timeout
tm_top <= to_unsigned(TM_VAL, tm_top'length);
--
-- master clock edge we are looking
ma_clk_edge <= ma_clk_fedge;
end slave_endat_rtl;
--
-- https://en.wikipedia.org/wiki/Synchronous_Serial_Interface
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
library work;
entity slave_ssi is
generic
(
CLK_FREQ: integer
);
port
(
-- local clock
clk: in std_logic;
rst: in std_logic;
-- master clock edges
ma_clk_redge: in std_logic;
ma_clk_fedge: in std_logic;
-- the edge we are interested in
ma_clk_edge: out std_logic;
-- master data input, output
miso: out std_logic;
mosi: in std_logic;
-- gate to drive output (1 to drive it)
gate: out std_logic;
-- actual data to send and length
data: in std_logic_vector;
len: in unsigned;
-- timeout counter
tm_match: in std_logic;
tm_top: out unsigned;
-- general purpose counter
count_top: out unsigned;
count_match: in std_logic;
count_rst: out std_logic;
-- piso register
piso_rval: in std_logic_vector;
piso_lval: in std_logic_vector;
piso_ini: out std_logic_vector;
piso_load: out std_logic;
-- refer to package for comments
ssi_flags: in std_logic_vector
);
end slave_ssi;
architecture slave_ssi_rtl of slave_ssi is
constant DATA_WITH: integer := data'length;
--
-- default timeout value. standard says:
-- https://upload.wikimedia.org/wikipedia/commons/8/8d/Ssisingletransmission.jpg
constant TM_VAL: integer := work.absenc_pkg.us_to_count
(work.absenc_pkg.SLAVE_DEFAULT_TM_US, CLK_FREQ, tm_top'length);
--
-- state machine
type ssi_state_t is
(
SSI_IDLE,
SSI_DATA,
SSI_DOT,
SSI_TP
);
constant SSI_TMOUT: ssi_state_t := SSI_IDLE;
constant SSI_ERR: ssi_state_t := SSI_IDLE;
signal curr_state: ssi_state_t;
signal next_state: ssi_state_t;
--
-- terminating pattern bits
signal is_dot_bit: std_logic;
begin
--
-- state automaton
process
begin
wait until rising_edge(clk);
if (rst = '1') then
curr_state <= SSI_IDLE;
elsif (tm_match = '1') then
curr_state <= SSI_TMOUT;
elsif (ma_clk_redge = '1') then
curr_state <= next_state;
end if;
end process;
process(curr_state, count_match, tm_match, is_dot_bit)
begin
next_state <= curr_state;
case curr_state is
when SSI_IDLE =>
next_state <= SSI_DATA;
when SSI_DATA =>
if count_match = '1' then
if is_dot_bit = '1' then
next_state <= SSI_DOT;
else
next_state <= SSI_TP;
end if;
else
next_state <= SSI_DATA;
end if;
when SSI_DOT =>
next_state <= SSI_TP;
when SSI_TP =>
if (tm_match = '1') then
next_state <= SSI_IDLE;
end if;
when others =>
next_state <= SSI_ERR;
end case;
end process;
process
begin
wait until rising_edge(clk);
miso <= '0';
count_top <= (others => '0');
count_rst <= '0';
piso_load <= '0';
piso_ini <= (others => '0');
is_dot_bit <= is_dot_bit;
case curr_state is
when SSI_IDLE =>
miso <= '1';
piso_load <= '1';
piso_ini <= data;
count_top <= len(count_top'range);
count_rst <= '1';
is_dot_bit <= ssi_flags(2) or ssi_flags(3) or ssi_flags(4);
when SSI_DATA =>
-- dynamic shift registers, ug901-vivado-synthesis.pdf, p.84
miso <= piso_lval(to_integer(len) - 1);
when SSI_DOT =>
miso <= '0';
when SSI_TP =>
miso <= '0';
when others =>
end case;
end process;
--
-- timeout
tm_top <= to_unsigned(TM_VAL, tm_top'length);
--
-- master clock edge we are looking
ma_clk_edge <= ma_clk_redge;
--
-- gate always enabled
gate <= '1';
end slave_ssi_rtl;
--
-- create a mask with all length bit set
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
entity len_to_mask is
port
(
len: in unsigned;
mask: out std_logic_vector
);
end entity;
architecture len_to_mask_rtl of len_to_mask is
constant size: integer := mask'length;
begin
--
-- result in a mask such as:
-- mask(mask'length - 1 downto len) = '0';
-- mask(len - 1 downto 0) = '1';
process(len)
begin
for i in 2 to size loop
if i = to_integer(len) then
mask(size - 1 downto i) <= (others => '0');
mask(i - 1 downto 0) <= (others => '1');
end if;
end loop;
end process;
end len_to_mask_rtl;
--
-- lsb to msb conversion
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
entity lsb_to_msb is
port
(
en: in std_logic;
data_len: in unsigned;
lsb_data: in std_logic_vector;
msb_data: out std_logic_vector
);
end entity;
architecture lsb_to_msb_rtl of lsb_to_msb is
constant size: integer := lsb_data'length;
begin
--
-- lsb_data: uuuuLxxxxM
-- msb_data: uuuuMxxxxL
-- where u undefined, x 0 or 1
process(en, lsb_data)
begin
if (en = '1') then
for i in 1 to size loop
if i = to_integer(data_len) then
msb_data(msb_data'length - 1 downto i) <= (others => '0');
for j in 0 to (i - 1) loop
msb_data(j) <= lsb_data(i - 1 - j);
end loop;
exit ;
end if;
end loop;
else
msb_data <= lsb_data;
end if;
end process;
end lsb_to_msb_rtl;
--
-- binary to gray conversion
library ieee;
use ieee.std_logic_1164.all;
entity bin_to_gray is
port
(
-- enable conversion
en: in std_logic;
bin_data: in std_logic_vector;
gray_data: out std_logic_vector
);
end entity;
architecture bin_to_gray_rtl of bin_to_gray is
constant size: integer := bin_data'length;
begin
assert (bin_data'length = gray_data'length)
report "size differs" severity failure;
process(en, bin_data) is
begin
if en = '1' then
for j in 0 to (size - 2) loop
gray_data(j) <= bin_data(j) xor bin_data(j + 1);
end loop;
gray_data(size - 1) <= bin_data(size - 1);
else
gray_data <= bin_data;
end if;
end process;
end bin_to_gray_rtl;
--
-- gray to binary conversion
library ieee;
use ieee.std_logic_1164.all;
entity gray_to_bin is
port
(
-- enable conversion
en: in std_logic;
gray_data: in std_logic_vector;
bin_data: out std_logic_vector
);
end entity;
architecture gray_to_bin_rtl of gray_to_bin is
constant size: integer := bin_data'length;
signal tmp_data: std_logic_vector(bin_data'range);
begin
assert (bin_data'length = gray_data'length)
report "size differs" severity failure;
process(en, gray_data, tmp_data) is
begin
if (en = '1') then
for j in 0 to (size - 2) loop
tmp_data(j) <= gray_data(j) xor tmp_data(j + 1);
end loop;
tmp_data(size - 1) <= gray_data(size - 1);
bin_data <= tmp_data;
else
bin_data <= gray_data;
end if;
end process;
end gray_to_bin_rtl;
--
-- extend sign
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
entity extend_sign is
port
(
data_len: in unsigned;
data_in: in std_logic_vector;
data_out: out std_logic_vector;
len_mask: in std_logic_vector
);
end entity;
architecture extend_sign_rtl of extend_sign is
constant size: integer := data_in'length;
signal is_signed: std_logic;
begin
is_signed <= data_in(to_integer(data_len) - 1);
process(is_signed, data_in)
begin
if (is_signed = '1') then
data_out <= data_in or not len_mask;
else
data_out <= data_in;
end if;
end process;
end extend_sign_rtl;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment