├── doc ├── main.pdf ├── dia │ ├── arch │ │ ├── main.dia │ │ └── main.png │ └── master_pipeline │ │ ├── main.dia │ │ └── main.png ├── clean.sh ├── build.sh ├── main.tex └── texpp.py ├── sim ├── hssl │ ├── main.tcl │ ├── build.sh │ ├── clean.sh │ ├── main.prj │ ├── run.sh │ ├── common.tcl │ ├── hssl.tcl │ └── main.vhd ├── isim │ ├── build.sh │ ├── ssi.tcl │ ├── biss.tcl │ ├── clean.sh │ ├── endat.tcl │ ├── main.tcl │ ├── run.sh │ ├── common.tcl │ ├── main.prj │ └── user.tcl └── common │ └── main.vhd ├── README ├── com └── com.sh └── src ├── absenc_slave_ssi.vhd ├── absenc_master_biss.vhd ├── absenc_master_ssi.vhd ├── absenc_slave_biss.vhd ├── absenc_utils.vhd ├── absenc_slave_endat.vhd ├── absenc_master_endat.vhd ├── absenc_reader_hssl.vhd ├── absenc_slave.vhd ├── absenc_master.vhd └── absenc_pkg.vhd /doc/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/texane/absenc/HEAD/doc/main.pdf -------------------------------------------------------------------------------- /sim/hssl/main.tcl: -------------------------------------------------------------------------------- 1 | source common.tcl 2 | source hssl.tcl 3 | run 200 us 4 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Absolute encoder VHDL core 2 | Refer to doc/main.pdf for more information 3 | -------------------------------------------------------------------------------- /doc/dia/arch/main.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/texane/absenc/HEAD/doc/dia/arch/main.dia -------------------------------------------------------------------------------- /doc/dia/arch/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/texane/absenc/HEAD/doc/dia/arch/main.png -------------------------------------------------------------------------------- /sim/hssl/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | fuse -intstyle ise -incremental -o main -prj main.prj main 3 | -------------------------------------------------------------------------------- /sim/isim/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | fuse -intstyle ise -incremental -o main -prj main.prj main 3 | -------------------------------------------------------------------------------- /doc/dia/master_pipeline/main.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/texane/absenc/HEAD/doc/dia/master_pipeline/main.dia -------------------------------------------------------------------------------- /doc/dia/master_pipeline/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/texane/absenc/HEAD/doc/dia/master_pipeline/main.png -------------------------------------------------------------------------------- /sim/isim/ssi.tcl: -------------------------------------------------------------------------------- 1 | isim force add {/main/enc_type} 2 2 | wave add /main/master/gen_ssi/master_ssi/curr_state 3 | wave add /main/slave/gen_ssi/slave_ssi/curr_state 4 | -------------------------------------------------------------------------------- /sim/hssl/clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | rm main 4 | rm -rf isim 5 | rm isim.wdb 6 | rm isim.log 7 | rm fuse.log 8 | rm fuseRelaunch.cmd 9 | rm fuse.xmsgs 10 | -------------------------------------------------------------------------------- /sim/hssl/main.prj: -------------------------------------------------------------------------------- 1 | vhdl work ../../src/absenc_pkg.vhd 2 | vhdl work ../../src/absenc_utils.vhd 3 | vhdl work ../../src/absenc_reader_hssl.vhd 4 | vhdl work ./main.vhd 5 | -------------------------------------------------------------------------------- /sim/isim/biss.tcl: -------------------------------------------------------------------------------- 1 | isim force add {/main/enc_type} 1 2 | wave add /main/master/gen_biss/master_biss/curr_state 3 | wave add /main/slave/gen_biss/slave_biss/curr_state 4 | -------------------------------------------------------------------------------- /sim/isim/clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | rm main 4 | rm -rf isim 5 | rm isim.wdb 6 | rm isim.log 7 | rm fuse.log 8 | rm fuseRelaunch.cmd 9 | rm fuse.xmsgs 10 | -------------------------------------------------------------------------------- /sim/isim/endat.tcl: -------------------------------------------------------------------------------- 1 | isim force add {/main/enc_type} 0 2 | wave add /main/master/gen_endat/master_endat/curr_state 3 | wave add /main/slave/gen_endat/slave_endat/curr_state 4 | -------------------------------------------------------------------------------- /sim/hssl/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -z "$1" ]; then 4 | main_tcl='main.tcl' 5 | else 6 | main_tcl="$1" 7 | fi ; 8 | 9 | ./main -gui -tclbatch $main_tcl 10 | -------------------------------------------------------------------------------- /sim/isim/main.tcl: -------------------------------------------------------------------------------- 1 | source common.tcl 2 | 3 | if { [ file exists user.tcl ] == 1 } { 4 | source user.tcl 5 | } else { 6 | source endat.tcl 7 | } 8 | 9 | run 200 us 10 | -------------------------------------------------------------------------------- /sim/isim/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -z "$1" ]; then 4 | main_tcl='main.tcl' 5 | else 6 | main_tcl="$1" 7 | fi ; 8 | 9 | ./main -gui -tclbatch $main_tcl 10 | -------------------------------------------------------------------------------- /sim/hssl/common.tcl: -------------------------------------------------------------------------------- 1 | # system clock 2 | 3 | isim force add {/main/clk} \ 4 | 1 -value 0 -radix bin -time 10 ns -repeat 20 ns 5 | 6 | isim force add {/main/rst} \ 7 | 1 -value 0 -time 2 us 8 | 9 | 10 | # main 11 | wave add /main/rst 12 | wave add /main/reader_data 13 | -------------------------------------------------------------------------------- /doc/clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | for f in main.{aux,bbl,blg,log,toc}; do 4 | [ -e $f ] && rm $f; 5 | done 6 | 7 | for f in main.pp.{tex,aux,bbl,blg,log,toc}; do 8 | [ -e $f ] && rm $f; 9 | done 10 | 11 | for f in version.{tex,tex.aux}; do 12 | [ -e $f ] && rm $f; 13 | done 14 | -------------------------------------------------------------------------------- /sim/isim/common.tcl: -------------------------------------------------------------------------------- 1 | # system clock 2 | 3 | isim force add {/main/clk} \ 4 | 1 -value 0 -radix bin -time 10 ns -repeat 20 ns 5 | 6 | isim force add {/main/rst} \ 7 | 1 -value 0 -time 2 us 8 | 9 | 10 | # main 11 | wave add /main/mosi 12 | wave add /main/miso 13 | wave add /main/master_data 14 | 15 | # master clock 16 | wave add /main/master/ma_clk 17 | -------------------------------------------------------------------------------- /sim/isim/main.prj: -------------------------------------------------------------------------------- 1 | vhdl work ../../src/absenc_pkg.vhd 2 | vhdl work ../../src/absenc_utils.vhd 3 | vhdl work ../../src/absenc_master.vhd 4 | vhdl work ../../src/absenc_master_endat.vhd 5 | vhdl work ../../src/absenc_master_biss.vhd 6 | vhdl work ../../src/absenc_master_ssi.vhd 7 | vhdl work ../../src/absenc_slave.vhd 8 | vhdl work ../../src/absenc_slave_endat.vhd 9 | vhdl work ../../src/absenc_slave_biss.vhd 10 | vhdl work ../../src/absenc_slave_ssi.vhd 11 | vhdl work ../common/main.vhd 12 | -------------------------------------------------------------------------------- /com/com.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | vlib work 4 | 5 | vcom -quiet ../src/absenc_pkg.vhd 6 | vcom -quiet ../src/absenc_master_biss.vhd 7 | vcom -quiet ../src/absenc_master_ssi.vhd 8 | vcom -quiet ../src/absenc_master_endat.vhd 9 | vcom -quiet ../src/absenc_master.vhd 10 | vcom -quiet ../src/absenc_slave_biss.vhd 11 | vcom -quiet ../src/absenc_slave_ssi.vhd 12 | vcom -quiet ../src/absenc_slave_endat.vhd 13 | vcom -quiet ../src/absenc_slave.vhd 14 | vcom -quiet ../src/absenc_reader_hssl.vhd 15 | -------------------------------------------------------------------------------- /sim/isim/user.tcl: -------------------------------------------------------------------------------- 1 | # source endat.tcl 2 | source biss.tcl 3 | # source ssi.tcl 4 | 5 | # wave add /main/master/latched_data 6 | # wave add /main/master/msb_data 7 | # wave add /main/master/signed_data 8 | # wave add /main/master/conv_data 9 | 10 | # wave add /main/master/sipo_val 11 | # wave add /main/master/sipo_latch 12 | # wave add /main/slave/piso_lval 13 | 14 | # wave add /main/slave/gen_endat/slave_endat/sipo_val 15 | 16 | wave add /main/clk 17 | wave add /main/ma_clk 18 | wave add /main/ma_delayed_clk 19 | wave add /main/master/ma_half_match -------------------------------------------------------------------------------- /doc/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # preprocess using texpp 4 | TEXPP_DIRS="." 5 | TEXPP_DIRS="$HOME/segfs/repo/texpp $TEXPP_DIRS" 6 | TEXPP_DIRS="/segfs/linux/dance_sdk/tools $TEXPP_DIRS" 7 | for d in $TEXPP_DIRS; do 8 | p="$d/texpp.py" 9 | if [ -e $p ]; then 10 | TEXPP_PATH=$p 11 | break 12 | fi 13 | done 14 | 15 | if [ -z "$TEXPP_PATH" ]; then 16 | echo 'texpp not found' 17 | exit -1 18 | fi 19 | 20 | $TEXPP_PATH main.tex main.pp.tex > main.pp.tex 21 | 22 | # create version.tex 23 | echo -n '\newcommand{\version}{SVN revision ' > version.tex 24 | (svnversion -c -n || echo none) >> version.tex 25 | echo -n '}' >> version.tex 26 | 27 | texi2pdf main.pp.tex 28 | mv main.pp.pdf main.pdf 29 | -------------------------------------------------------------------------------- /sim/hssl/hssl.tcl: -------------------------------------------------------------------------------- 1 | wave add /main/hssl_reader/sclk 2 | wave add /main/hssl_reader/sori 3 | 4 | wave add /main/hssl_reader/count_match_edge 5 | wave add /main/hssl_reader/count_val -radix unsigned 6 | wave add /main/hssl_reader/count_top -radix unsigned 7 | wave add /main/hssl_reader/count_top_latched -radix unsigned 8 | # wave add /main/hssl_reader/count_match -radix unsigned 9 | 10 | wave add /main/hssl_reader/tm_match 11 | wave add /main/hssl_reader/tm_val -radix unsigned 12 | wave add /main/hssl_reader/tm_gap -radix unsigned 13 | 14 | # wave add /main/count_match 15 | # wave add /main/count_top -radix unsigned 16 | # wave add /main/count_val -radix unsigned 17 | 18 | # wave add /main/tm_match 19 | # wave add /main/tm_top -radix unsigned 20 | # wave add /main/tm_val -radix unsigned -------------------------------------------------------------------------------- /src/absenc_slave_ssi.vhd: -------------------------------------------------------------------------------- 1 | -- 2 | -- https://en.wikipedia.org/wiki/Synchronous_Serial_Interface 3 | 4 | 5 | library ieee; 6 | use ieee.std_logic_1164.all; 7 | use ieee.std_logic_unsigned.all; 8 | use ieee.numeric_std.all; 9 | use ieee.math_real.all; 10 | 11 | library work; 12 | 13 | 14 | entity slave_ssi is 15 | generic 16 | ( 17 | CLK_FREQ: integer 18 | ); 19 | port 20 | ( 21 | -- local clock 22 | clk: in std_logic; 23 | rst: in std_logic; 24 | 25 | -- master clock edges 26 | ma_clk_redge: in std_logic; 27 | ma_clk_fedge: in std_logic; 28 | 29 | -- the edge we are interested in 30 | ma_clk_edge: out std_logic; 31 | 32 | -- master data input, output 33 | miso: out std_logic; 34 | mosi: in std_logic; 35 | 36 | -- gate to drive output (1 to drive it) 37 | gate: out std_logic; 38 | 39 | -- actual data to send and length 40 | data: in std_logic_vector; 41 | len: in unsigned; 42 | 43 | -- timeout counter 44 | tm_match: in std_logic; 45 | tm_top: out unsigned; 46 | 47 | -- general purpose counter 48 | count_top: out unsigned; 49 | count_match: in std_logic; 50 | count_rst: out std_logic; 51 | 52 | -- piso register 53 | piso_rval: in std_logic_vector; 54 | piso_lval: in std_logic_vector; 55 | piso_ini: out std_logic_vector; 56 | piso_load: out std_logic; 57 | 58 | -- refer to package for comments 59 | ssi_flags: in std_logic_vector 60 | ); 61 | 62 | end slave_ssi; 63 | 64 | 65 | architecture slave_ssi_rtl of slave_ssi is 66 | 67 | constant DATA_WITH: integer := data'length; 68 | 69 | -- 70 | -- default timeout value. standard says: 71 | -- https://upload.wikimedia.org/wikipedia/commons/8/8d/Ssisingletransmission.jpg 72 | 73 | constant TM_VAL: integer := work.absenc_pkg.us_to_count 74 | (work.absenc_pkg.SLAVE_DEFAULT_TM_US, CLK_FREQ, tm_top'length); 75 | 76 | 77 | -- 78 | -- state machine 79 | 80 | type ssi_state_t is 81 | ( 82 | SSI_IDLE, 83 | SSI_DATA, 84 | SSI_DOT, 85 | SSI_TP 86 | ); 87 | 88 | constant SSI_TMOUT: ssi_state_t := SSI_IDLE; 89 | constant SSI_ERR: ssi_state_t := SSI_IDLE; 90 | 91 | signal curr_state: ssi_state_t; 92 | signal next_state: ssi_state_t; 93 | 94 | 95 | -- 96 | -- terminating pattern bits 97 | 98 | signal is_dot_bit: std_logic; 99 | 100 | 101 | begin 102 | 103 | 104 | -- 105 | -- state automaton 106 | 107 | process 108 | begin 109 | wait until rising_edge(clk); 110 | 111 | if (rst = '1') then 112 | curr_state <= SSI_IDLE; 113 | elsif (tm_match = '1') then 114 | curr_state <= SSI_TMOUT; 115 | elsif (ma_clk_redge = '1') then 116 | curr_state <= next_state; 117 | end if; 118 | 119 | end process; 120 | 121 | 122 | process(curr_state, count_match, tm_match, is_dot_bit) 123 | begin 124 | 125 | next_state <= curr_state; 126 | 127 | case curr_state is 128 | 129 | when SSI_IDLE => 130 | next_state <= SSI_DATA; 131 | 132 | when SSI_DATA => 133 | if count_match = '1' then 134 | if is_dot_bit = '1' then 135 | next_state <= SSI_DOT; 136 | else 137 | next_state <= SSI_TP; 138 | end if; 139 | else 140 | next_state <= SSI_DATA; 141 | end if; 142 | 143 | when SSI_DOT => 144 | next_state <= SSI_TP; 145 | 146 | when SSI_TP => 147 | if (tm_match = '1') then 148 | next_state <= SSI_IDLE; 149 | end if; 150 | 151 | when others => 152 | next_state <= SSI_ERR; 153 | 154 | end case; 155 | 156 | end process; 157 | 158 | 159 | process 160 | begin 161 | wait until rising_edge(clk); 162 | 163 | miso <= '0'; 164 | count_top <= (count_top'range => '0'); 165 | count_rst <= '0'; 166 | piso_load <= '0'; 167 | piso_ini <= (piso_ini'range => '0'); 168 | is_dot_bit <= is_dot_bit; 169 | 170 | case curr_state is 171 | 172 | when SSI_IDLE => 173 | miso <= '1'; 174 | piso_load <= '1'; 175 | piso_ini <= data; 176 | count_top <= len(count_top'range); 177 | count_rst <= '1'; 178 | is_dot_bit <= ssi_flags(2) or ssi_flags(3) or ssi_flags(4); 179 | 180 | when SSI_DATA => 181 | -- dynamic shift registers, ug901-vivado-synthesis.pdf, p.84 182 | miso <= piso_lval(to_integer(len) - 1); 183 | 184 | when SSI_DOT => 185 | miso <= '0'; 186 | 187 | when SSI_TP => 188 | miso <= '0'; 189 | 190 | when others => 191 | 192 | end case; 193 | 194 | end process; 195 | 196 | 197 | -- 198 | -- timeout 199 | 200 | tm_top <= to_unsigned(TM_VAL, tm_top'length); 201 | 202 | 203 | -- 204 | -- master clock edge we are looking 205 | 206 | ma_clk_edge <= ma_clk_redge; 207 | 208 | 209 | -- 210 | -- gate always enabled 211 | 212 | gate <= '1'; 213 | 214 | 215 | end slave_ssi_rtl; 216 | -------------------------------------------------------------------------------- /src/absenc_master_biss.vhd: -------------------------------------------------------------------------------- 1 | library ieee; 2 | use ieee.std_logic_1164.all; 3 | use ieee.std_logic_unsigned.all; 4 | use ieee.numeric_std.all; 5 | use ieee.math_real.all; 6 | 7 | library work; 8 | 9 | 10 | entity master_biss is 11 | 12 | generic 13 | ( 14 | CLK_FREQ: integer 15 | ); 16 | port 17 | ( 18 | -- local clock 19 | clk: in std_logic; 20 | rst: in std_logic; 21 | 22 | -- master clock edges 23 | ma_clk_fedge: in std_logic; 24 | ma_clk_redge: in std_logic; 25 | 26 | -- the edges we are interested in 27 | ma_clk_edge: out std_logic; 28 | 29 | -- master clock reset 30 | -- if ma_clk_rst_en, use ma_clk_rst_level 31 | ma_clk_rst_en: out std_logic; 32 | ma_clk_rst_val: out std_logic; 33 | 34 | -- master out, slave in 35 | mosi: out std_logic; 36 | miso: in std_logic; 37 | 38 | -- gate to drive output (1 to drive it) 39 | gate: out std_logic; 40 | 41 | -- desired data length 42 | len: in unsigned; 43 | 44 | -- timeout counter 45 | tm_match: in std_logic; 46 | tm_top: out unsigned; 47 | 48 | -- general purpose counter 49 | count_top: out unsigned; 50 | count_match: in std_logic; 51 | count_rst: out std_logic; 52 | 53 | -- sipo register 54 | sipo_val: in std_logic_vector; 55 | sipo_latch: out std_logic; 56 | 57 | -- enable data conversion stages 58 | gray_to_bin_en: out std_logic; 59 | lsb_to_msb_en: out std_logic 60 | ); 61 | 62 | end entity; 63 | 64 | 65 | architecture absenc_master_biss_rtl of master_biss is 66 | 67 | 68 | -- 69 | -- default timeout value. standard says: 70 | -- http://biss-interface.com/files/Bissinterface_c5es.pdf, page 18 71 | 72 | constant TM_VAL: integer := work.absenc_pkg.us_to_count 73 | (15, CLK_FREQ, tm_top'length); 74 | 75 | 76 | -- 77 | -- state machine 78 | 79 | type biss_state_t is 80 | ( 81 | BISS_SYNC0, 82 | BISS_SYNC1, 83 | BISS_ACK, 84 | BISS_START, 85 | BISS_CDS, 86 | BISS_DATA, 87 | BISS_STOP 88 | ); 89 | 90 | constant BISS_TMOUT: biss_state_t := BISS_SYNC0; 91 | constant BISS_ERR: biss_state_t := BISS_SYNC0; 92 | 93 | signal curr_state: biss_state_t; 94 | signal next_state: biss_state_t; 95 | 96 | 97 | begin 98 | 99 | 100 | -- 101 | -- state automaton 102 | 103 | process 104 | begin 105 | wait until rising_edge(clk); 106 | 107 | if (rst = '1') then 108 | curr_state <= BISS_SYNC0; 109 | elsif ((ma_clk_redge or tm_match) = '1') then 110 | curr_state <= next_state; 111 | end if; 112 | 113 | end process; 114 | 115 | 116 | process(curr_state, count_match, tm_match, miso) 117 | begin 118 | 119 | next_state <= curr_state; 120 | 121 | case curr_state is 122 | 123 | when BISS_SYNC0 => 124 | next_state <= BISS_SYNC1; 125 | 126 | when BISS_SYNC1 => 127 | next_state <= BISS_ACK; 128 | 129 | when BISS_ACK => 130 | -- TODO: check miso == 0 131 | next_state <= BISS_START; 132 | 133 | when BISS_START => 134 | if miso = '1' then 135 | next_state <= BISS_CDS; 136 | end if; 137 | 138 | when BISS_CDS => 139 | next_state <= BISS_DATA; 140 | 141 | when BISS_DATA => 142 | if count_match = '1' then 143 | next_state <= BISS_STOP; 144 | else 145 | next_state <= BISS_DATA; 146 | end if; 147 | 148 | when BISS_STOP => 149 | if (tm_match = '1') then 150 | next_state <= BISS_SYNC0; 151 | end if; 152 | 153 | when others => 154 | next_state <= BISS_ERR; 155 | 156 | end case; 157 | 158 | end process; 159 | 160 | 161 | process 162 | begin 163 | wait until rising_edge(clk); 164 | 165 | ma_clk_rst_en <= '0'; 166 | count_top <= (count_top'range => '0'); 167 | count_rst <= '0'; 168 | sipo_latch <= '0'; 169 | 170 | case curr_state is 171 | 172 | when BISS_SYNC0 => 173 | 174 | when BISS_SYNC1 => 175 | 176 | when BISS_ACK => 177 | 178 | when BISS_START => 179 | 180 | when BISS_CDS => 181 | -- TODO: capture CDS 182 | count_top <= len(count_top'range); 183 | count_rst <= '1'; 184 | 185 | when BISS_DATA => 186 | 187 | when BISS_STOP => 188 | sipo_latch <= '1'; 189 | ma_clk_rst_en <= '1'; 190 | 191 | when others => 192 | 193 | end case; 194 | 195 | end process; 196 | 197 | 198 | -- 199 | -- clock reset or idle value 200 | 201 | ma_clk_rst_val <= '1'; 202 | 203 | 204 | -- 205 | -- timeout 206 | 207 | tm_top <= to_unsigned(TM_VAL, tm_top'length); 208 | 209 | 210 | -- 211 | -- gray to binary disabled 212 | 213 | gray_to_bin_en <= '0'; 214 | 215 | 216 | -- 217 | -- lsb to msb disabled 218 | 219 | lsb_to_msb_en <= '0'; 220 | 221 | 222 | -- 223 | -- gate always disabled 224 | 225 | gate <= '0'; 226 | mosi <= '0'; 227 | 228 | 229 | -- 230 | -- use rising edge 231 | 232 | ma_clk_edge <= ma_clk_redge; 233 | 234 | 235 | end architecture; 236 | -------------------------------------------------------------------------------- /src/absenc_master_ssi.vhd: -------------------------------------------------------------------------------- 1 | -- 2 | -- ssi master implementation 3 | 4 | 5 | library ieee; 6 | use ieee.std_logic_1164.all; 7 | use ieee.std_logic_unsigned.all; 8 | use ieee.numeric_std.all; 9 | use ieee.math_real.all; 10 | 11 | library work; 12 | 13 | 14 | entity master_ssi is 15 | 16 | generic 17 | ( 18 | CLK_FREQ: integer 19 | ); 20 | port 21 | ( 22 | -- local clock 23 | clk: in std_logic; 24 | rst: in std_logic; 25 | 26 | -- master clock edges 27 | ma_clk_fedge: in std_logic; 28 | ma_clk_redge: in std_logic; 29 | 30 | -- the edge we are interested in 31 | ma_clk_edge: out std_logic; 32 | 33 | -- master clock reset 34 | -- if ma_clk_rst_en, use ma_clk_rst_level 35 | ma_clk_rst_en: out std_logic; 36 | ma_clk_rst_val: out std_logic; 37 | 38 | -- master out, slave in 39 | mosi: out std_logic; 40 | miso: in std_logic; 41 | 42 | -- gate to drive output (1 to drive it) 43 | gate: out std_logic; 44 | 45 | -- desired data length 46 | len: in unsigned; 47 | 48 | -- timeout counter 49 | tm_match: in std_logic; 50 | tm_top: out unsigned; 51 | 52 | -- general purpose counter 53 | count_top: out unsigned; 54 | count_match: in std_logic; 55 | count_rst: out std_logic; 56 | 57 | -- sipo register 58 | sipo_val: in std_logic_vector; 59 | sipo_latch: out std_logic; 60 | 61 | -- enable data conversion stages 62 | gray_to_bin_en: out std_logic; 63 | lsb_to_msb_en: out std_logic; 64 | 65 | -- refer to package for comments 66 | ssi_flags: in std_logic_vector; 67 | ssi_delay_fdiv: in unsigned 68 | ); 69 | 70 | end entity; 71 | 72 | 73 | architecture absenc_master_ssi_rtl of master_ssi is 74 | 75 | 76 | -- 77 | -- default timeout value. standard says: 78 | -- https://upload.wikimedia.org/wikipedia/commons/8/8d/Ssisingletransmission.jpg 79 | 80 | constant TM_VAL: integer := work.absenc_pkg.us_to_count 81 | (work.absenc_pkg.MASTER_DEFAULT_TM_US, CLK_FREQ, tm_top'length); 82 | 83 | 84 | -- 85 | -- state machine 86 | 87 | type ssi_state_t is 88 | ( 89 | SSI_START, 90 | SSI_DATA, 91 | SSI_DOT, 92 | SSI_TP 93 | ); 94 | 95 | constant SSI_ERR: ssi_state_t := SSI_START; 96 | 97 | signal curr_state: ssi_state_t; 98 | signal next_state: ssi_state_t; 99 | 100 | 101 | -- 102 | -- gray to bin internal register 103 | 104 | signal is_gray: std_logic; 105 | 106 | 107 | -- 108 | -- terminating pattern bits 109 | 110 | signal is_dot_bit: std_logic; 111 | 112 | 113 | begin 114 | 115 | 116 | -- 117 | -- state automaton 118 | 119 | process 120 | begin 121 | wait until rising_edge(clk); 122 | 123 | if (rst = '1') then 124 | curr_state <= SSI_START; 125 | elsif ((ma_clk_redge or tm_match) = '1') then 126 | curr_state <= next_state; 127 | end if; 128 | 129 | end process; 130 | 131 | 132 | process(curr_state, count_match, is_dot_bit) 133 | begin 134 | 135 | next_state <= curr_state; 136 | 137 | case curr_state is 138 | 139 | when SSI_START => 140 | next_state <= SSI_DATA; 141 | 142 | when SSI_DATA => 143 | if count_match = '1' then 144 | if is_dot_bit = '1' then 145 | next_state <= SSI_DOT; 146 | else 147 | next_state <= SSI_TP; 148 | end if; 149 | end if; 150 | 151 | when SSI_DOT => 152 | next_state <= SSI_TP; 153 | 154 | when SSI_TP => 155 | next_state <= SSI_START; 156 | 157 | when others => 158 | next_state <= SSI_ERR; 159 | 160 | end case; 161 | 162 | end process; 163 | 164 | 165 | process 166 | begin 167 | wait until rising_edge(clk); 168 | 169 | ma_clk_rst_en <= '0'; 170 | count_top <= (count_top'range => '0'); 171 | count_rst <= '0'; 172 | sipo_latch <= '0'; 173 | is_gray <= is_gray; 174 | is_dot_bit <= is_dot_bit; 175 | 176 | case curr_state is 177 | 178 | when SSI_START => 179 | count_top <= len(count_top'range); 180 | count_rst <= '1'; 181 | is_gray <= ssi_flags(1); 182 | is_dot_bit <= ssi_flags(2) or ssi_flags(3) or ssi_flags(4); 183 | 184 | when SSI_DATA => 185 | 186 | when SSI_DOT => 187 | sipo_latch <= '1'; 188 | 189 | when SSI_TP => 190 | -- do not latch twice 191 | sipo_latch <= not is_dot_bit; 192 | ma_clk_rst_en <= '1'; 193 | 194 | when others => 195 | 196 | end case; 197 | 198 | end process; 199 | 200 | 201 | -- 202 | -- clock reset or idle value 203 | 204 | ma_clk_rst_val <= '1'; 205 | 206 | 207 | -- 208 | -- timeout 209 | 210 | tm_top <= to_unsigned(TM_VAL, tm_top'length); 211 | 212 | 213 | -- 214 | -- gray to binary 215 | 216 | gray_to_bin_en <= is_gray; 217 | 218 | 219 | -- 220 | -- lsb to msb disabled 221 | 222 | lsb_to_msb_en <= '0'; 223 | 224 | 225 | -- 226 | -- gate always disabled 227 | 228 | gate <= '0'; 229 | mosi <= '0'; 230 | 231 | 232 | -- 233 | -- use rising edge 234 | 235 | ma_clk_edge <= ma_clk_redge; 236 | 237 | 238 | end architecture; 239 | -------------------------------------------------------------------------------- /src/absenc_slave_biss.vhd: -------------------------------------------------------------------------------- 1 | -- BISS automaton notes 2 | -- point to point configuration 3 | -- http://biss-interface.com/files/Bissinterface_c5es.pdf, figure 1 4 | -- slave idle state (SLO = 1) 5 | -- first rising edge of MA is for slave sync 6 | -- second rising edge of MA is for slave ack (SLO = 0) 7 | -- start bit (SLO = 1) 8 | -- CDS bit 9 | -- data bits 10 | -- stop bit (SLO = 0) and CDM bit (MA) 11 | 12 | 13 | library ieee; 14 | use ieee.std_logic_1164.all; 15 | use ieee.std_logic_unsigned.all; 16 | use ieee.numeric_std.all; 17 | use ieee.math_real.all; 18 | 19 | library work; 20 | 21 | 22 | entity slave_biss is 23 | generic 24 | ( 25 | CLK_FREQ: integer 26 | ); 27 | port 28 | ( 29 | -- local clock 30 | clk: in std_logic; 31 | rst: in std_logic; 32 | 33 | -- master clock edges 34 | ma_clk_redge: in std_logic; 35 | ma_clk_fedge: in std_logic; 36 | 37 | -- the edge we are interested in 38 | ma_clk_edge: out std_logic; 39 | 40 | -- master data input, output 41 | miso: out std_logic; 42 | mosi: in std_logic; 43 | 44 | -- gate to drive output (1 to drive it) 45 | gate: out std_logic; 46 | 47 | -- actual data to send and length 48 | data: in std_logic_vector; 49 | len: in unsigned; 50 | 51 | -- timeout counter 52 | tm_match: in std_logic; 53 | tm_top: out unsigned; 54 | 55 | -- general purpose counter 56 | count_top: out unsigned; 57 | count_match: in std_logic; 58 | count_rst: out std_logic; 59 | 60 | -- piso register 61 | piso_rval: in std_logic_vector; 62 | piso_lval: in std_logic_vector; 63 | piso_ini: out std_logic_vector; 64 | piso_load: out std_logic 65 | ); 66 | 67 | end slave_biss; 68 | 69 | 70 | architecture slave_biss_rtl of slave_biss is 71 | 72 | -- 73 | -- default timeout value. standard says: 74 | -- http://biss-interface.com/files/Bissinterface_c5es.pdf, page 18 75 | 76 | constant TM_VAL: integer := work.absenc_pkg.us_to_count 77 | (work.absenc_pkg.SLAVE_DEFAULT_TM_US, CLK_FREQ, tm_top'length); 78 | 79 | -- 80 | -- state machine 81 | 82 | type biss_state_t is 83 | ( 84 | BISS_SYNC0, 85 | BISS_SYNC1, 86 | BISS_ACK, 87 | BISS_START, 88 | BISS_CDS, 89 | BISS_DATA, 90 | BISS_STOP 91 | ); 92 | 93 | constant BISS_TMOUT: biss_state_t := BISS_SYNC0; 94 | constant BISS_ERR: biss_state_t := BISS_SYNC0; 95 | 96 | signal curr_state: biss_state_t; 97 | signal next_state: biss_state_t; 98 | 99 | 100 | begin 101 | 102 | 103 | -- 104 | -- state automaton 105 | 106 | process 107 | begin 108 | wait until rising_edge(clk); 109 | 110 | if (rst = '1') then 111 | curr_state <= BISS_SYNC0; 112 | elsif (tm_match = '1') then 113 | curr_state <= BISS_TMOUT; 114 | elsif (ma_clk_redge = '1') then 115 | curr_state <= next_state; 116 | end if; 117 | 118 | end process; 119 | 120 | 121 | process(curr_state, count_match, tm_match) 122 | begin 123 | 124 | next_state <= curr_state; 125 | 126 | case curr_state is 127 | 128 | when BISS_SYNC0 => 129 | next_state <= BISS_SYNC1; 130 | 131 | when BISS_SYNC1 => 132 | next_state <= BISS_ACK; 133 | 134 | when BISS_ACK => 135 | next_state <= BISS_START; 136 | 137 | when BISS_START => 138 | next_state <= BISS_CDS; 139 | 140 | when BISS_CDS => 141 | next_state <= BISS_DATA; 142 | 143 | when BISS_DATA => 144 | if count_match = '1' then 145 | next_state <= BISS_STOP; 146 | else 147 | next_state <= BISS_DATA; 148 | end if; 149 | 150 | when BISS_STOP => 151 | if (tm_match = '1') then 152 | next_state <= BISS_SYNC0; 153 | end if; 154 | 155 | when others => 156 | next_state <= BISS_ERR; 157 | 158 | end case; 159 | 160 | end process; 161 | 162 | 163 | process 164 | begin 165 | wait until rising_edge(clk); 166 | 167 | miso <= '0'; 168 | count_top <= (count_top'range => '0'); 169 | count_rst <= '0'; 170 | piso_load <= '0'; 171 | 172 | case curr_state is 173 | 174 | when BISS_SYNC0 => 175 | miso <= '1'; 176 | count_rst <= '1'; 177 | 178 | when BISS_SYNC1 => 179 | miso <= '1'; 180 | count_rst <= '1'; 181 | 182 | when BISS_ACK => 183 | miso <= '0'; 184 | 185 | when BISS_START => 186 | miso <= '1'; 187 | 188 | when BISS_CDS => 189 | -- TODO: output CDS bit 190 | miso <= '0'; 191 | 192 | count_top <= len(count_top'range); 193 | count_rst <= '1'; 194 | 195 | piso_ini <= data; 196 | piso_load <= '1'; 197 | 198 | when BISS_DATA => 199 | -- dynamic shift registers, ug901-vivado-synthesis.pdf, p.84 200 | miso <= piso_lval(to_integer(len) - 1); 201 | 202 | when BISS_STOP => 203 | -- TODO: capture CDM 204 | miso <= '0'; 205 | 206 | when others => 207 | 208 | end case; 209 | 210 | end process; 211 | 212 | 213 | -- 214 | -- timeout 215 | 216 | tm_top <= to_unsigned(TM_VAL, tm_top'length); 217 | 218 | 219 | -- 220 | -- master clock edge we are looking 221 | 222 | ma_clk_edge <= ma_clk_redge; 223 | 224 | 225 | -- 226 | -- gate always enabled 227 | 228 | gate <= '1'; 229 | 230 | 231 | end slave_biss_rtl; 232 | -------------------------------------------------------------------------------- /src/absenc_utils.vhd: -------------------------------------------------------------------------------- 1 | -- 2 | -- create a mask with all length bit set 3 | 4 | 5 | library ieee; 6 | use ieee.std_logic_1164.all; 7 | use ieee.std_logic_unsigned.all; 8 | use ieee.numeric_std.all; 9 | 10 | 11 | entity len_to_mask is 12 | port 13 | ( 14 | len: in unsigned; 15 | mask: out std_logic_vector 16 | ); 17 | end entity; 18 | 19 | architecture len_to_mask_rtl of len_to_mask is 20 | 21 | constant size: integer := mask'length; 22 | 23 | begin 24 | 25 | -- 26 | -- result in a mask such as: 27 | -- mask(mask'length - 1 downto len) = '0'; 28 | -- mask(len - 1 downto 0) = '1'; 29 | 30 | process(len) 31 | begin 32 | for i in 2 to size loop 33 | if i = to_integer(len) then 34 | mask(size - 1 downto i) <= (others => '0'); 35 | mask(i - 1 downto 0) <= (others => '1'); 36 | end if; 37 | end loop; 38 | end process; 39 | 40 | end len_to_mask_rtl; 41 | 42 | 43 | -- 44 | -- lsb to msb conversion 45 | 46 | 47 | library ieee; 48 | use ieee.std_logic_1164.all; 49 | use ieee.std_logic_unsigned.all; 50 | use ieee.numeric_std.all; 51 | 52 | 53 | entity lsb_to_msb is 54 | port 55 | ( 56 | en: in std_logic; 57 | data_len: in unsigned; 58 | lsb_data: in std_logic_vector; 59 | msb_data: out std_logic_vector 60 | ); 61 | end entity; 62 | 63 | architecture lsb_to_msb_rtl of lsb_to_msb is 64 | 65 | constant size: integer := lsb_data'length; 66 | 67 | begin 68 | 69 | -- 70 | -- lsb_data: uuuuLxxxxM 71 | -- msb_data: uuuuMxxxxL 72 | -- where u undefined, x 0 or 1 73 | 74 | process(en, lsb_data) 75 | begin 76 | 77 | if (en = '1') then 78 | 79 | for i in 1 to size loop 80 | if i = to_integer(data_len) then 81 | msb_data(msb_data'length - 1 downto i) <= (others => '0'); 82 | for j in 0 to (i - 1) loop 83 | msb_data(j) <= lsb_data(i - 1 - j); 84 | end loop; 85 | exit ; 86 | end if; 87 | end loop; 88 | 89 | else 90 | 91 | msb_data <= lsb_data; 92 | 93 | end if; 94 | 95 | end process; 96 | 97 | end lsb_to_msb_rtl; 98 | 99 | 100 | -- 101 | -- binary to gray conversion 102 | 103 | 104 | library ieee; 105 | use ieee.std_logic_1164.all; 106 | 107 | 108 | entity bin_to_gray is 109 | port 110 | ( 111 | -- enable conversion 112 | en: in std_logic; 113 | bin_data: in std_logic_vector; 114 | gray_data: out std_logic_vector 115 | ); 116 | end entity; 117 | 118 | architecture bin_to_gray_rtl of bin_to_gray is 119 | 120 | constant size: integer := bin_data'length; 121 | 122 | begin 123 | 124 | assert (bin_data'length = gray_data'length) 125 | report "size differs" severity failure; 126 | 127 | process(en, bin_data) is 128 | begin 129 | if en = '1' then 130 | for j in 0 to (size - 2) loop 131 | gray_data(j) <= bin_data(j) xor bin_data(j + 1); 132 | end loop; 133 | gray_data(size - 1) <= bin_data(size - 1); 134 | else 135 | gray_data <= bin_data; 136 | end if; 137 | end process; 138 | 139 | end bin_to_gray_rtl; 140 | 141 | 142 | -- 143 | -- gray to binary conversion 144 | 145 | 146 | library ieee; 147 | use ieee.std_logic_1164.all; 148 | 149 | 150 | entity gray_to_bin is 151 | port 152 | ( 153 | -- enable conversion 154 | en: in std_logic; 155 | gray_data: in std_logic_vector; 156 | bin_data: out std_logic_vector 157 | ); 158 | end entity; 159 | 160 | architecture gray_to_bin_rtl of gray_to_bin is 161 | 162 | constant size: integer := bin_data'length; 163 | 164 | signal tmp_data: std_logic_vector(bin_data'range); 165 | 166 | begin 167 | 168 | assert (bin_data'length = gray_data'length) 169 | report "size differs" severity failure; 170 | 171 | process(en, gray_data, tmp_data) is 172 | begin 173 | if (en = '1') then 174 | for j in 0 to (size - 2) loop 175 | tmp_data(j) <= gray_data(j) xor tmp_data(j + 1); 176 | end loop; 177 | tmp_data(size - 1) <= gray_data(size - 1); 178 | bin_data <= tmp_data; 179 | else 180 | bin_data <= gray_data; 181 | end if; 182 | end process; 183 | 184 | end gray_to_bin_rtl; 185 | 186 | 187 | -- 188 | -- extend sign 189 | 190 | 191 | library ieee; 192 | use ieee.std_logic_1164.all; 193 | use ieee.std_logic_unsigned.all; 194 | use ieee.numeric_std.all; 195 | 196 | 197 | entity extend_sign is 198 | port 199 | ( 200 | data_len: in unsigned; 201 | data_in: in std_logic_vector; 202 | data_out: out std_logic_vector; 203 | len_mask: in std_logic_vector 204 | ); 205 | end entity; 206 | 207 | architecture extend_sign_rtl of extend_sign is 208 | 209 | constant size: integer := data_in'length; 210 | signal is_signed: std_logic; 211 | 212 | begin 213 | 214 | process(data_in, data_len) 215 | begin 216 | -- fixme: modelsim fails without this check 217 | -- synthesis translate_off 218 | is_signed <= '0'; 219 | if data_len > 0 then 220 | -- synthesis translate_on 221 | 222 | is_signed <= data_in(to_integer(data_len) - 1); 223 | 224 | -- synthesis translate_off 225 | end if; 226 | -- synthesis translate_on 227 | 228 | end process; 229 | 230 | process(is_signed, data_in) 231 | begin 232 | if (is_signed = '1') then 233 | data_out <= data_in or not len_mask; 234 | else 235 | data_out <= data_in; 236 | end if; 237 | end process; 238 | 239 | end extend_sign_rtl; 240 | -------------------------------------------------------------------------------- /sim/common/main.vhd: -------------------------------------------------------------------------------- 1 | -- 2 | -- delayed signal 3 | -- delay time resolution is 1 / CLK_FREQ 4 | 5 | 6 | library ieee; 7 | use ieee.std_logic_1164.all; 8 | use ieee.std_logic_unsigned.all; 9 | use ieee.numeric_std.all; 10 | use ieee.math_real.all; 11 | 12 | 13 | library work; 14 | 15 | 16 | entity delay is 17 | generic 18 | ( 19 | CLK_FREQ: integer; 20 | DELAY_NS: real := 0.0 21 | ); 22 | port 23 | ( 24 | -- local clock 25 | clk: in std_logic; 26 | 27 | -- signal to be delayed 28 | sin: in std_logic; 29 | 30 | -- delayed signal 31 | sout: out std_logic 32 | ); 33 | 34 | end delay; 35 | 36 | 37 | architecture delay_rtl of delay is 38 | 39 | 40 | -- 41 | -- convert nanoseconds to count at freq 42 | 43 | function ns_to_count 44 | ( 45 | freq: real; 46 | ns: real 47 | ) 48 | return integer is 49 | begin 50 | -- ns = count * (1 / freq) * 1000000000 51 | -- count = (ns * freq) / 1000000000 52 | return integer(ceil((ns * freq) / 1000000000.0)); 53 | end ns_to_count; 54 | 55 | 56 | -- 57 | -- delay buffer 58 | -- delay_count the buffer depth 59 | -- add a small constant to allow DELAY_NS to be 0 60 | 61 | constant DELAY_COUNT: integer := ns_to_count(DELAY_NS + 0.01, real(CLK_FREQ)); 62 | signal delay_buf: std_logic_vector(DELAY_COUNT - 1 downto 0); 63 | 64 | 65 | begin 66 | 67 | 68 | gen_zero: if DELAY_NS = 0.0 generate 69 | delay_buf(0) <= sin; 70 | end generate gen_zero; 71 | 72 | 73 | gen_one: if ((DELAY_NS /= 0.0) and (DELAY_COUNT = 1)) generate 74 | process 75 | begin 76 | wait until rising_edge(clk); 77 | delay_buf(0) <= sin; 78 | end process; 79 | end generate gen_one; 80 | 81 | 82 | gen_not_one: if DELAY_COUNT /= 1 generate 83 | process 84 | begin 85 | wait until rising_edge(clk); 86 | delay_buf <= sin & delay_buf(delay_buf'length - 1 downto 1); 87 | end process; 88 | end generate gen_not_one; 89 | 90 | 91 | sout <= delay_buf(0); 92 | 93 | 94 | end delay_rtl; 95 | 96 | 97 | -- 98 | -- main 99 | 100 | library ieee; 101 | use ieee.std_logic_1164.all; 102 | use ieee.std_logic_unsigned.all; 103 | use ieee.numeric_std.all; 104 | 105 | 106 | library work; 107 | 108 | 109 | entity main is end main; 110 | 111 | 112 | architecture rtl of main is 113 | 114 | 115 | -- 116 | -- cable length to nanoseconds 117 | 118 | function cable_len_to_ns 119 | ( 120 | -- cable length in meters 121 | len: real 122 | ) 123 | return real is 124 | -- light speed in m/s 125 | constant C: real := 299792458.0; 126 | -- propagation delay in ns/m (around 5ns/m) 127 | constant PROP_DELAY_NS: real := 1000000000.0 / (0.64 * C); 128 | begin 129 | return len * PROP_DELAY_NS; 130 | end cable_len_to_ns; 131 | 132 | 133 | -- 134 | -- configuration constants 135 | 136 | constant CLK_FREQ: integer := 50000000; 137 | 138 | constant DATA_LEN: integer := 16; 139 | constant LEN_WIDTH: integer := work.absenc_pkg.integer_length(DATA_LEN); 140 | 141 | -- 142 | -- local clock and reset 143 | 144 | signal rst: std_ulogic; 145 | signal clk: std_ulogic; 146 | 147 | -- 148 | -- data sent by slave to master 149 | 150 | constant partial_data: std_logic_vector := "1001010001"; 151 | 152 | constant partial_zeros: 153 | std_logic_vector(DATA_LEN - 1 downto partial_data'length) := 154 | (others => '0'); 155 | 156 | constant slave_data: std_logic_vector(DATA_LEN - 1 downto 0) := 157 | partial_zeros & partial_data; 158 | 159 | constant len: unsigned := to_unsigned(partial_data'length, LEN_WIDTH); 160 | 161 | -- 162 | -- data read by master from slave 163 | 164 | signal master_data: std_logic_vector(DATA_LEN - 1 downto 0); 165 | 166 | -- 167 | -- master clock frequency divider 168 | -- 1MHz clock 169 | 170 | constant ma_fdiv: unsigned := to_unsigned(integer(50), 8); 171 | 172 | -- 173 | -- selected encoder type 174 | 175 | signal enc_type: integer; 176 | 177 | -- 178 | -- master slave outputs 179 | 180 | signal mosi: std_logic; 181 | signal miso: std_logic; 182 | 183 | signal ma_clk: std_logic; 184 | 185 | 186 | -- 187 | -- delayed master clock 188 | 189 | signal ma_delayed_clk: std_logic; 190 | 191 | 192 | begin 193 | 194 | 195 | delay: entity work.delay 196 | generic map 197 | ( 198 | CLK_FREQ => CLK_FREQ, 199 | DELAY_NS => cable_len_to_ns(30.0) 200 | ) 201 | port map 202 | ( 203 | clk => clk, 204 | sin => ma_clk, 205 | sout => ma_delayed_clk 206 | ); 207 | 208 | 209 | slave: work.absenc_pkg.slave 210 | generic map 211 | ( 212 | CLK_FREQ => CLK_FREQ 213 | ) 214 | port map 215 | ( 216 | clk => clk, 217 | rst => rst, 218 | ma_clk => ma_delayed_clk, 219 | miso => miso, 220 | mosi => mosi, 221 | gate => open, 222 | data => slave_data, 223 | len => len, 224 | enc_type => enc_type, 225 | ssi_flags => work.absenc_pkg.SSI_DEFAULT_FLAGS 226 | ); 227 | 228 | 229 | master: work.absenc_pkg.master 230 | generic map 231 | ( 232 | CLK_FREQ => CLK_FREQ 233 | ) 234 | port map 235 | ( 236 | clk => clk, 237 | rst => rst, 238 | ma_fdiv => ma_fdiv, 239 | ma_clk => ma_clk, 240 | mosi => mosi, 241 | miso => miso, 242 | gate => open, 243 | data => master_data, 244 | len => len, 245 | enc_type => enc_type, 246 | ssi_flags => work.absenc_pkg.SSI_DEFAULT_FLAGS, 247 | ssi_delay_fdiv => work.absenc_pkg.SSI_DEFAULT_DELAY_FDIV 248 | ); 249 | 250 | 251 | end rtl; 252 | -------------------------------------------------------------------------------- /doc/main.tex: -------------------------------------------------------------------------------- 1 | \documentclass[12pt]{article} 2 | \usepackage{listings} 3 | \usepackage{url} 4 | \usepackage{comment} 5 | \usepackage{graphicx} 6 | 7 | 8 | \begin{document} 9 | 10 | 11 | %% command definition 12 | 13 | \newcommand{\todo}[1] 14 | {\paragraph{}\textbf{TODO}: #1} 15 | 16 | \lstnewenvironment{vhdl} 17 | {\lstset{language=VHDL, basicstyle=\tiny, frame=single}}{} 18 | 19 | \lstnewenvironment{sh} 20 | {\lstset{frame=single}}{} 21 | 22 | \newcommand{\longurl}[2] 23 | {\url{#1#2}} 24 | 25 | \newcommand{\longlongurl}[3] 26 | {\url{#1#2#3}} 27 | 28 | \IfFileExists{version.tex} 29 | {\input{version.tex}}{\newcommand{\version}{none}} 30 | 31 | 32 | %% block contents passed to tex preprocessor 33 | %% ignored when processed directly by latex 34 | %% use with \begin{texpp} \end{texpp} 35 | \excludecomment{texpp} 36 | 37 | 38 | %% 39 | %% document start here 40 | 41 | \title{Absolute encoder package} 42 | \author{Fabien Le Mentec \\ lementec@esrf.fr} 43 | \date{\small{version: \version}} 44 | \maketitle 45 | 46 | 47 | \newpage 48 | \setcounter{tocdepth}{2} 49 | \tableofcontents 50 | 51 | 52 | %% 53 | \newpage 54 | \section{Description} 55 | 56 | \subsection{Overview} 57 | \paragraph{} 58 | The absenc\_pkg implements components for absolute encoder masters and 59 | slaves. 60 | 61 | \paragraph{} 62 | The term \textit{master} refers to the component driving the clock, or at 63 | least initiating the data transfer. It is often referred to as the 64 | \textit{controller}. The term \textit{slave} refers to the actual encoder 65 | device. 66 | 67 | \paragraph{} 68 | This package is optimized for applications that can be dynamically configured 69 | to use one amongst different types of interfaces at a particular time. As much 70 | as possible, resources that can be shared across interfaces are factorized 71 | (counters, comparators, shift registers ...) and accessed through a multiplexer. 72 | However, and in order to avoid penalizing simpler applications, static 73 | configuration allows to exclude resources associated with an unused interface. 74 | \begin{center} 75 | \includegraphics[width=0.8\textwidth]{./dia/arch/main.png} 76 | \end{center} 77 | 78 | \paragraph{} 79 | The master implements the following data conversion pipeline: 80 | \begin{center} 81 | \includegraphics[width=0.5\textwidth]{./dia/master_pipeline/main.png} 82 | \end{center} 83 | 84 | \subsection{Features and limitations} 85 | \begin{itemize} 86 | \item Applicable to all 87 | \begin{itemize} 88 | \item master and slave modes, 89 | \item configurable master clock frequency, 90 | \item configurable data length (up to 48 bits), 91 | \item static timeout. 92 | \end{itemize} 93 | \item ENDAT 94 | \begin{itemize} 95 | \item send position mode version 2.1 only, 96 | \item no CRC check. 97 | \end{itemize} 98 | \item BISS 99 | \begin{itemize} 100 | \item point to point configuration only, 101 | \item no interleaved bit support (ie. CDS, CDM). 102 | \end{itemize} 103 | \item SSI 104 | \begin{itemize} 105 | \item optional gray data coding (master only), 106 | \item optional parity bit (not checked). 107 | \end{itemize} 108 | \item HSSL 109 | \begin{itemize} 110 | \item reader only, 111 | \item no \textit{master} or \textit{slave} interface. 112 | \end{itemize} 113 | \end{itemize} 114 | 115 | 116 | \subsection{Tested hardware} 117 | \paragraph{} 118 | The package has been tested with the following masters: 119 | \begin{itemize} 120 | \item ICEPAP ESRF motor controller (SSI), 121 | \item PEPU ESRF encoder processor (SSI, BISS, ENDAT), 122 | \item MUSST ESRF sequencing platform (SSI), 123 | \item Aerotech Soloist CP controller (BISS, ENDAT). 124 | \end{itemize} 125 | 126 | \paragraph{} 127 | The package has been tested with the following slaves: 128 | \begin{itemize} 129 | \item PEPU ESRF encoder processor (SSI, BISS, ENDAT), 130 | \item Kubler sendix 5863 SIL BISS encoders, 131 | \item IVO industries GA241 SSI encoders, 132 | \item Heidenhain ROQ 425 512 ENDAT encoders, 133 | \item Heidenhain ROQ 437 2048 ENDAT encoders. 134 | \end{itemize} 135 | 136 | 137 | \subsection{Performances} 138 | 139 | \subsubsection{EnDAT} 140 | \paragraph{} 141 | The following tests were done using a ROQ425 EnDAT 25 bits encoder. The master 142 | clock frequency was made variable, along with the cable length thus the signal 143 | propagation time. 144 | 145 | \begin{center} 146 | \begin{tabular}{ | c | c | c |} 147 | \hline 148 | cable length & propagation delay & validated frequency \\ \hline 149 | 3.5m & 141ns & 2.77MHz \\ \hline 150 | 20m & 392ns & 961KHz \\ \hline 151 | 30m & 478ns & 961KHz \\ \hline 152 | 50m & 709ns & 595KHz \\ \hline 153 | \end{tabular} 154 | \end{center} 155 | 156 | 157 | %% 158 | \newpage 159 | \section{Interfaces} 160 | 161 | \begin{texpp} 162 | Texpp.include( 163 | kind = 'interface', 164 | path = '../src/absenc_pkg.vhd', 165 | name = 'master' 166 | ) 167 | \end{texpp} 168 | 169 | \begin{texpp} 170 | Texpp.include( 171 | kind = 'interface', 172 | path = '../src/absenc_pkg.vhd', 173 | name = 'slave' 174 | ) 175 | \end{texpp} 176 | 177 | 178 | %% 179 | \newpage 180 | \section{Examples} 181 | 182 | \begin{texpp} 183 | Texpp.include( 184 | kind = 'example', 185 | path = '../sim/common/main.vhd', 186 | name = 'master' 187 | ) 188 | \end{texpp} 189 | 190 | \begin{texpp} 191 | Texpp.include( 192 | kind = 'example', 193 | path = '../sim/common/main.vhd', 194 | name = 'slave' 195 | ) 196 | \end{texpp} 197 | 198 | 199 | \end{document} 200 | -------------------------------------------------------------------------------- /src/absenc_slave_endat.vhd: -------------------------------------------------------------------------------- 1 | -- 2 | -- endat encoder implementation 3 | -- 4 | -- notes from endat2.2 technical information documentation 5 | -- esp. refer to page 6, Position Values 6 | -- 7 | -- command format: 8 | -- send data without additional parameter 9 | -- 2T(x2) & mode(x6) & 2T(x2) & S(x1) & F1(x1) & DATA(xlen) & CRC(x5) 10 | -- 11 | -- recovery time: 12 | -- 10 to 30 us for endat2.1 13 | -- 1.25 to 3.75 us for endat2.2 14 | -- 15 | -- samples at rising edge 16 | -- 17 | -- data sent LSbit first 18 | 19 | 20 | library ieee; 21 | use ieee.std_logic_1164.all; 22 | use ieee.std_logic_unsigned.all; 23 | use ieee.numeric_std.all; 24 | use ieee.math_real.all; 25 | 26 | library work; 27 | 28 | 29 | entity slave_endat is 30 | generic 31 | ( 32 | CLK_FREQ: integer 33 | ); 34 | port 35 | ( 36 | -- local clock 37 | clk: in std_logic; 38 | rst: in std_logic; 39 | 40 | -- master clock edges 41 | ma_clk_redge: in std_logic; 42 | ma_clk_fedge: in std_logic; 43 | 44 | -- the edge we are interested in 45 | ma_clk_edge: out std_logic; 46 | 47 | -- master data input, output 48 | miso: out std_logic; 49 | mosi: in std_logic; 50 | 51 | -- gate to drive output (1 to drive it) 52 | gate: out std_logic; 53 | 54 | -- actual data to send and length 55 | data: in std_logic_vector; 56 | len: in unsigned; 57 | 58 | -- timeout counter 59 | tm_match: in std_logic; 60 | tm_top: out unsigned; 61 | 62 | -- general purpose counter 63 | count_top: out unsigned; 64 | count_match: in std_logic; 65 | count_rst: out std_logic; 66 | 67 | -- piso register 68 | piso_rval: in std_logic_vector; 69 | piso_lval: in std_logic_vector; 70 | piso_ini: out std_logic_vector; 71 | piso_load: out std_logic 72 | ); 73 | 74 | end slave_endat; 75 | 76 | 77 | architecture slave_endat_rtl of slave_endat is 78 | 79 | 80 | -- 81 | -- default timeout value. standard says: 10 to 30 us 82 | 83 | constant TM_VAL: integer := work.absenc_pkg.us_to_count 84 | (work.absenc_pkg.SLAVE_DEFAULT_TM_US, CLK_FREQ, tm_top'length); 85 | 86 | 87 | -- 88 | -- main state machine 89 | 90 | type endat_state_t is 91 | ( 92 | ENDAT_IDLE, 93 | ENDAT_T0, 94 | ENDAT_T1, 95 | ENDAT_MODE, 96 | ENDAT_T3, 97 | ENDAT_T4, 98 | 99 | ENDAT_T5, 100 | 101 | ENDAT_START, 102 | ENDAT_F1, 103 | ENDAT_DATA, 104 | 105 | ENDAT_CRC5_FIRST, 106 | ENDAT_CRC5_CONT 107 | ); 108 | 109 | constant ENDAT_TMOUT: endat_state_t := ENDAT_IDLE; 110 | constant ENDAT_ERR: endat_state_t := ENDAT_IDLE; 111 | 112 | signal curr_state: endat_state_t; 113 | signal next_state: endat_state_t; 114 | 115 | 116 | -- 117 | -- serial in, parallel out (sipo) register 118 | 119 | signal sipo_val: std_logic_vector(6 - 1 downto 0); 120 | 121 | 122 | begin 123 | 124 | 125 | -- 126 | -- sipo register 127 | -- sampled at falling edge by slave 128 | 129 | process 130 | begin 131 | 132 | wait until rising_edge(clk); 133 | 134 | if (ma_clk_redge = '1') then 135 | sipo_val <= sipo_val(sipo_val'length - 2 downto 0) & mosi; 136 | end if; 137 | 138 | end process; 139 | 140 | 141 | -- 142 | -- state automaton 143 | 144 | process 145 | begin 146 | 147 | wait until rising_edge(clk); 148 | 149 | if (rst = '1') then 150 | curr_state <= ENDAT_IDLE; 151 | elsif (tm_match = '1') then 152 | curr_state <= ENDAT_TMOUT; 153 | elsif (ma_clk_fedge = '1') then 154 | curr_state <= next_state; 155 | end if; 156 | 157 | end process; 158 | 159 | 160 | process(curr_state, count_match, sipo_val, tm_match) 161 | begin 162 | 163 | next_state <= curr_state; 164 | 165 | case curr_state is 166 | 167 | when ENDAT_IDLE => 168 | next_state <= ENDAT_T0; 169 | 170 | when ENDAT_T0 => 171 | next_state <= ENDAT_T1; 172 | 173 | when ENDAT_T1 => 174 | next_state <= ENDAT_MODE; 175 | 176 | when ENDAT_MODE => 177 | -- serial register value is sampled at rising edge 178 | -- but next_state computed at falling edge. thus 179 | -- we compare here when count_match 180 | if count_match = '1' then 181 | if sipo_val(5 downto 0) = "000111" then 182 | next_state <= ENDAT_T3; 183 | else 184 | next_state <= ENDAT_ERR; 185 | end if; 186 | end if; 187 | 188 | when ENDAT_T3 => 189 | next_state <= ENDAT_T4; 190 | 191 | when ENDAT_T4 => 192 | next_state <= ENDAT_T5; 193 | 194 | when ENDAT_T5 => 195 | next_state <= ENDAT_START; 196 | 197 | when ENDAT_START => 198 | next_state <= ENDAT_F1; 199 | 200 | when ENDAT_F1 => 201 | next_state <= ENDAT_DATA; 202 | 203 | when ENDAT_DATA => 204 | if count_match = '1' then 205 | next_state <= ENDAT_CRC5_FIRST; 206 | end if; 207 | 208 | when ENDAT_CRC5_FIRST => 209 | next_state <= ENDAT_CRC5_CONT; 210 | 211 | when ENDAT_CRC5_CONT => 212 | if count_match = '1' then 213 | next_state <= ENDAT_IDLE; 214 | end if; 215 | 216 | when others => 217 | next_state <= ENDAT_ERR; 218 | 219 | end case; 220 | 221 | end process; 222 | 223 | 224 | process 225 | begin 226 | 227 | wait until rising_edge(clk); 228 | 229 | gate <= '0'; 230 | miso <= '0'; 231 | count_top <= (count_top'range => '0'); 232 | count_rst <= '0'; 233 | piso_load <= '0'; 234 | 235 | case curr_state is 236 | 237 | when ENDAT_IDLE => 238 | 239 | when ENDAT_T0 => 240 | 241 | when ENDAT_T1 => 242 | count_top <= to_unsigned(integer(6), count_top'length); 243 | count_rst <= '1'; 244 | 245 | when ENDAT_MODE => 246 | 247 | when ENDAT_T3 => 248 | 249 | when ENDAT_T4 => 250 | 251 | when ENDAT_T5 => 252 | -- data line stabilization state 253 | gate <= '1'; 254 | miso <= '0'; 255 | 256 | when ENDAT_START => 257 | gate <= '1'; 258 | miso <= '1'; 259 | 260 | when ENDAT_F1 => 261 | -- error bit (F1) 262 | gate <= '1'; 263 | miso <= '0'; 264 | 265 | count_top <= len(count_top'range); 266 | count_rst <= '1'; 267 | 268 | piso_ini <= data; 269 | piso_load <= '1'; 270 | 271 | when ENDAT_DATA => 272 | gate <= '1'; 273 | miso <= piso_rval(0); 274 | 275 | when ENDAT_CRC5_FIRST => 276 | count_top <= to_unsigned(integer(5 - 1), count_top'length); 277 | count_rst <= '1'; 278 | gate <= '1'; 279 | miso <= '0'; 280 | 281 | when ENDAT_CRC5_CONT => 282 | gate <= '1'; 283 | miso <= '0'; 284 | 285 | when others => 286 | 287 | end case; 288 | 289 | end process; 290 | 291 | 292 | -- 293 | -- timeout 294 | 295 | tm_top <= to_unsigned(TM_VAL, tm_top'length); 296 | 297 | 298 | -- 299 | -- master clock edge we are looking 300 | 301 | ma_clk_edge <= ma_clk_fedge; 302 | 303 | 304 | end slave_endat_rtl; 305 | -------------------------------------------------------------------------------- /src/absenc_master_endat.vhd: -------------------------------------------------------------------------------- 1 | library ieee; 2 | use ieee.std_logic_1164.all; 3 | use ieee.std_logic_unsigned.all; 4 | use ieee.numeric_std.all; 5 | use ieee.math_real.all; 6 | 7 | library work; 8 | 9 | 10 | entity master_endat is 11 | 12 | generic 13 | ( 14 | CLK_FREQ: integer 15 | ); 16 | port 17 | ( 18 | -- local clock 19 | clk: in std_logic; 20 | rst: in std_logic; 21 | 22 | -- master clock edges 23 | ma_clk_fedge: in std_logic; 24 | ma_clk_redge: in std_logic; 25 | 26 | -- the edge we are interested in 27 | ma_clk_edge: out std_logic; 28 | 29 | -- master clock reset 30 | -- if ma_clk_rst_en, use ma_clk_rst_level 31 | ma_clk_rst_en: out std_logic; 32 | ma_clk_rst_val: out std_logic; 33 | 34 | -- master out, slave in 35 | mosi: out std_logic; 36 | miso: in std_logic; 37 | 38 | -- gate to drive output (1 to drive it) 39 | gate: out std_logic; 40 | 41 | -- desired data length 42 | len: in unsigned; 43 | 44 | -- timeout counter 45 | tm_match: in std_logic; 46 | tm_top: out unsigned; 47 | 48 | -- general purpose counter 49 | count_top: out unsigned; 50 | count_match: in std_logic; 51 | count_rst: out std_logic; 52 | 53 | -- sipo register 54 | sipo_val: in std_logic_vector; 55 | sipo_latch: out std_logic; 56 | 57 | -- enable data conversion stages 58 | gray_to_bin_en: out std_logic; 59 | lsb_to_msb_en: out std_logic 60 | ); 61 | 62 | end entity; 63 | 64 | 65 | architecture absenc_master_endat_rtl of master_endat is 66 | 67 | 68 | -- 69 | -- default timeout value. standard says: 10 to 30 us 70 | 71 | constant TM_VAL: integer := work.absenc_pkg.us_to_count 72 | (work.absenc_pkg.MASTER_DEFAULT_TM_US, CLK_FREQ, tm_top'length); 73 | 74 | 75 | -- 76 | -- main state machine 77 | 78 | type endat_state_t is 79 | ( 80 | ENDAT_INIT, 81 | 82 | ENDAT_T0, 83 | ENDAT_T1, 84 | ENDAT_MODE, 85 | ENDAT_T3, 86 | ENDAT_T4, 87 | 88 | ENDAT_T5, 89 | 90 | ENDAT_START, 91 | ENDAT_F1, 92 | ENDAT_DATA, 93 | 94 | ENDAT_CRC5_FIRST, 95 | ENDAT_CRC5_CONT, 96 | 97 | ENDAT_DONE 98 | ); 99 | 100 | constant ENDAT_TMOUT: endat_state_t := ENDAT_DONE; 101 | constant ENDAT_ERR: endat_state_t := ENDAT_DONE; 102 | 103 | signal curr_state: endat_state_t; 104 | signal next_state: endat_state_t; 105 | 106 | 107 | -- 108 | -- right shifted parallel in, serial out register 109 | -- loaded values are in reverse order 110 | 111 | constant piso_ini: std_logic_vector := "111000"; 112 | signal piso_val: std_logic_vector(piso_ini'length - 1 downto 0); 113 | signal piso_load: std_logic; 114 | 115 | 116 | begin 117 | 118 | 119 | -- 120 | -- state automaton 121 | 122 | process 123 | begin 124 | wait until rising_edge(clk); 125 | 126 | if (rst = '1') then 127 | curr_state <= ENDAT_TMOUT; 128 | elsif ((tm_match or ma_clk_fedge) = '1') then 129 | curr_state <= next_state; 130 | end if; 131 | 132 | end process; 133 | 134 | 135 | process(curr_state, count_match, tm_match, miso) 136 | begin 137 | 138 | next_state <= curr_state; 139 | 140 | case curr_state is 141 | 142 | when ENDAT_INIT => 143 | next_state <= ENDAT_T0; 144 | 145 | when ENDAT_T0 => 146 | next_state <= ENDAT_T1; 147 | 148 | when ENDAT_T1 => 149 | next_state <= ENDAT_MODE; 150 | 151 | when ENDAT_MODE => 152 | if (count_match = '1') then 153 | next_state <= ENDAT_T3; 154 | end if; 155 | 156 | when ENDAT_T3 => 157 | next_state <= ENDAT_T4; 158 | 159 | when ENDAT_T4 => 160 | next_state <= ENDAT_T5; 161 | 162 | when ENDAT_T5 => 163 | next_state <= ENDAT_START; 164 | 165 | when ENDAT_START => 166 | if (miso = '1') then 167 | next_state <= ENDAT_F1; 168 | elsif (tm_match = '1') then 169 | next_state <= ENDAT_TMOUT; 170 | end if; 171 | 172 | when ENDAT_F1 => 173 | next_state <= ENDAT_DATA; 174 | 175 | when ENDAT_DATA => 176 | if (count_match = '1') then 177 | next_state <= ENDAT_CRC5_FIRST; 178 | end if; 179 | 180 | when ENDAT_CRC5_FIRST => 181 | next_state <= ENDAT_CRC5_CONT; 182 | 183 | when ENDAT_CRC5_CONT => 184 | if (count_match = '1') then 185 | next_state <= ENDAT_DONE; 186 | end if; 187 | 188 | when ENDAT_DONE => 189 | -- wait for at least one timeout 190 | next_state <= ENDAT_INIT; 191 | 192 | when others => 193 | next_state <= ENDAT_ERR; 194 | 195 | end case; 196 | 197 | end process; 198 | 199 | 200 | process 201 | begin 202 | 203 | wait until rising_edge(clk); 204 | 205 | ma_clk_rst_en <= '0'; 206 | count_top <= (count_top'range => '0'); 207 | count_rst <= '0'; 208 | sipo_latch <= '0'; 209 | gate <= '0'; 210 | mosi <= '0'; 211 | piso_load <= '0'; 212 | 213 | case curr_state is 214 | 215 | when ENDAT_INIT => 216 | gate <= '1'; 217 | 218 | when ENDAT_T0 => 219 | gate <= '1'; 220 | 221 | when ENDAT_T1 => 222 | piso_load <= '1'; 223 | gate <= '1'; 224 | count_top <= to_unsigned(6, count_top'length); 225 | count_rst <= '1'; 226 | 227 | when ENDAT_MODE => 228 | mosi <= piso_val(0); 229 | gate <= '1'; 230 | 231 | when ENDAT_T3 => 232 | mosi <= '0'; 233 | gate <= '1'; 234 | 235 | when ENDAT_T4 => 236 | mosi <= '0'; 237 | gate <= '1'; 238 | 239 | when ENDAT_T5 => 240 | 241 | when ENDAT_START => 242 | 243 | when ENDAT_F1 => 244 | count_top <= len(count_top'range); 245 | count_rst <= '1'; 246 | 247 | when ENDAT_DATA => 248 | -- sent LSb first 249 | 250 | when ENDAT_CRC5_FIRST => 251 | count_top <= to_unsigned(integer(5 - 1), count_top'length); 252 | count_rst <= '1'; 253 | sipo_latch <= '1'; 254 | 255 | when ENDAT_CRC5_CONT => 256 | 257 | when ENDAT_DONE => 258 | ma_clk_rst_en <= '1'; 259 | 260 | when others => 261 | 262 | end case; 263 | 264 | end process; 265 | 266 | 267 | -- 268 | -- right shifted piso register 269 | -- set at falling edge, sample by slave at redge 270 | 271 | process 272 | begin 273 | wait until rising_edge(clk); 274 | 275 | if (piso_load = '1') then 276 | piso_val <= piso_ini; 277 | elsif (ma_clk_fedge = '1') then 278 | piso_val <= '0' & piso_val(piso_val'length - 1 downto 1); 279 | end if; 280 | 281 | end process; 282 | 283 | 284 | -- 285 | -- clock reset or idle value 286 | 287 | ma_clk_rst_val <= '1'; 288 | 289 | 290 | -- 291 | -- timeout 292 | 293 | tm_top <= to_unsigned(TM_VAL, tm_top'length); 294 | 295 | 296 | -- 297 | -- gray to binary disabled 298 | 299 | gray_to_bin_en <= '0'; 300 | 301 | 302 | -- 303 | -- lsb to msb enabled 304 | 305 | lsb_to_msb_en <= '1'; 306 | 307 | 308 | -- 309 | -- use falling edge 310 | 311 | ma_clk_edge <= ma_clk_fedge; 312 | 313 | 314 | end architecture; 315 | -------------------------------------------------------------------------------- /sim/hssl/main.vhd: -------------------------------------------------------------------------------- 1 | -- 2 | -- main 3 | 4 | library ieee; 5 | use ieee.std_logic_1164.all; 6 | use ieee.std_logic_unsigned.all; 7 | use ieee.numeric_std.all; 8 | 9 | 10 | library work; 11 | 12 | 13 | entity main is end main; 14 | 15 | 16 | architecture rtl of main is 17 | 18 | -- 19 | -- configuration constants 20 | 21 | constant CLK_FREQ: integer := 50000000; 22 | 23 | constant DATA_LEN: integer := 8; 24 | constant LEN_WIDTH: integer := work.absenc_pkg.integer_length(1 + DATA_LEN); 25 | 26 | -- 27 | -- local clock and reset 28 | 29 | signal rst: std_ulogic; 30 | signal clk: std_ulogic; 31 | 32 | -- 33 | -- sender data 34 | 35 | constant partial_data: std_logic_vector := "01100101"; 36 | 37 | -- constant partial_zeros: 38 | -- std_logic_vector(DATA_LEN - 1 downto partial_data'length) := 39 | -- (others => '0'); 40 | 41 | -- constant sender_data: std_logic_vector(DATA_LEN - 1 downto 0) := 42 | -- partial_zeros & partial_data; 43 | 44 | constant sender_data: std_logic_vector(DATA_LEN - 1 downto 0) := 45 | partial_data; 46 | 47 | constant len: unsigned := to_unsigned(partial_data'length, LEN_WIDTH); 48 | 49 | -- 50 | -- reader writer data 51 | 52 | signal reader_data: std_logic_vector(DATA_LEN - 1 downto 0); 53 | 54 | -- 55 | -- master clock frequency divider 56 | -- 1MHz clock 57 | 58 | constant sclk_fdiv: unsigned := to_unsigned(integer(50), 8); 59 | 60 | -- 61 | -- clock data signals 62 | 63 | signal sori: std_logic; 64 | 65 | 66 | -- 67 | -- timeout counter 68 | 69 | constant TM_MAX: integer := work.absenc_pkg.us_to_count 70 | (100, CLK_FREQ, integer'high); 71 | 72 | constant TM_LEN: integer := 73 | work.absenc_pkg.integer_length(TM_MAX); 74 | 75 | constant DEFAULT_TM_GAP: integer := work.absenc_pkg.us_to_count 76 | (5, CLK_FREQ, TM_LEN); 77 | 78 | signal tm_val: unsigned(TM_LEN - 1 downto 0); 79 | signal tm_top: unsigned(tm_val'range); 80 | signal tm_match: std_logic; 81 | signal tm_en: std_logic; 82 | 83 | signal tm_gap: unsigned(TM_LEN - 1 downto 0); 84 | 85 | 86 | -- 87 | -- general counter 88 | 89 | signal count_val: unsigned(LEN_WIDTH - 1 downto 0); 90 | signal count_top: unsigned(count_val'range); 91 | signal count_top_latched: unsigned(count_val'range); 92 | signal count_rst: std_logic; 93 | signal count_match: std_logic; 94 | 95 | 96 | -- 97 | -- parallel in, serial out (PISO) register 98 | 99 | signal piso_val: std_logic_vector(sender_data'range); 100 | signal piso_ini: std_logic_vector(sender_data'range); 101 | signal piso_load: std_logic; 102 | 103 | 104 | -- 105 | -- sender clock 106 | 107 | signal sclk_rst_en: std_logic; 108 | 109 | signal sclk_val: std_logic; 110 | signal sclk_en: std_logic; 111 | 112 | signal sclk_half_fdiv: unsigned(sclk_fdiv'length - 1 downto 0); 113 | signal sclk_half_count: unsigned(sclk_half_fdiv'length - 1 downto 0); 114 | signal sclk_half_match: std_logic; 115 | 116 | signal sclk_redge: std_logic; 117 | signal sclk_fedge: std_logic; 118 | 119 | 120 | -- 121 | -- hssl sender state machine 122 | 123 | type hssl_state_t is 124 | ( 125 | HSSL_IDLE, 126 | HSSL_DATA, 127 | HSSL_TP 128 | ); 129 | 130 | constant HSSL_TMOUT: hssl_state_t := HSSL_IDLE; 131 | constant HSSL_ERR: hssl_state_t := HSSL_IDLE; 132 | 133 | signal curr_state: hssl_state_t; 134 | signal next_state: hssl_state_t; 135 | 136 | 137 | begin 138 | 139 | 140 | -- 141 | -- sender clock generation 142 | -- sender clock frequency is clk divided by sclk_fdiv 143 | -- generate edges using a counter from sclk_fdiv/2 to 0 144 | 145 | sclk_half_fdiv <= sclk_fdiv srl 1; 146 | sclk_half_match <= '1' when sclk_half_count = 1 else '0'; 147 | 148 | process 149 | begin 150 | wait until rising_edge(clk); 151 | 152 | if ((rst or sclk_rst_en or sclk_half_match) = '1') then 153 | sclk_half_count <= sclk_half_fdiv; 154 | else 155 | sclk_half_count <= sclk_half_count - 1; 156 | end if; 157 | 158 | end process; 159 | 160 | process 161 | begin 162 | wait until rising_edge(clk); 163 | 164 | if ((rst or sclk_rst_en) = '1') then 165 | sclk_val <= '0'; 166 | else 167 | sclk_val <= sclk_val xor sclk_half_match; 168 | end if; 169 | 170 | end process; 171 | 172 | process 173 | begin 174 | wait until rising_edge(clk); 175 | sclk_redge <= sclk_half_match and (not sclk_val); 176 | end process; 177 | 178 | process 179 | begin 180 | wait until rising_edge(clk); 181 | sclk_fedge <= sclk_half_match and sclk_val; 182 | end process; 183 | 184 | 185 | -- 186 | -- general purpose counter 187 | -- monotically incrementing 188 | -- increment every master edge 189 | -- starts from 1 190 | 191 | process 192 | begin 193 | 194 | wait until rising_edge(clk); 195 | 196 | if (count_rst = '1') then 197 | count_val <= to_unsigned(integer(1), count_val'length); 198 | elsif (sclk_fedge = '1') then 199 | count_val <= count_val + 1; 200 | end if; 201 | 202 | end process; 203 | 204 | count_match <= '1' when (count_val = count_top) else '0'; 205 | 206 | 207 | -- 208 | -- timeout counter 209 | -- tm_val decrement from tm_top to 0 210 | -- tm_val reloaded at sclk_edge 211 | 212 | tm_top <= to_unsigned(DEFAULT_TM_GAP, tm_top'length); 213 | 214 | process 215 | begin 216 | wait until rising_edge(clk); 217 | 218 | tm_match <= '0'; 219 | 220 | if (rst = '1') then 221 | tm_val <= tm_top; 222 | elsif (tm_val = 0) then 223 | tm_val <= tm_top; 224 | tm_match <= '1'; 225 | elsif (tm_en = '1') then 226 | tm_val <= tm_val - 1; 227 | end if; 228 | 229 | end process; 230 | 231 | 232 | -- 233 | -- hssl reader 234 | 235 | tm_gap <= to_unsigned(DEFAULT_TM_GAP, tm_top'length); 236 | 237 | hssl_reader: work.absenc_pkg.reader_hssl 238 | generic map 239 | ( 240 | CLK_FREQ => CLK_FREQ 241 | ) 242 | port map 243 | ( 244 | clk => clk, 245 | rst => rst, 246 | sclk => sclk_en, 247 | sori => sori, 248 | data => reader_data, 249 | len => len, 250 | tm_gap => tm_gap 251 | ); 252 | 253 | 254 | -- 255 | -- piso right shifted register 256 | 257 | process 258 | begin 259 | 260 | wait until rising_edge(clk); 261 | 262 | if (piso_load = '1') then 263 | piso_val <= piso_ini; 264 | elsif (sclk_redge = '1') then 265 | piso_val <= '0' & piso_val(piso_val'length - 1 downto 1); 266 | end if; 267 | 268 | end process; 269 | 270 | 271 | -- 272 | -- HSSL sender 273 | 274 | process 275 | begin 276 | wait until rising_edge(clk); 277 | 278 | if (rst = '1') then 279 | curr_state <= HSSL_IDLE; 280 | elsif (tm_match = '1') then 281 | curr_state <= HSSL_TMOUT; 282 | elsif (sclk_redge = '1') then 283 | curr_state <= next_state; 284 | end if; 285 | 286 | end process; 287 | 288 | process(curr_state, count_match) 289 | begin 290 | 291 | next_state <= curr_state; 292 | 293 | case curr_state is 294 | 295 | when HSSL_IDLE => 296 | next_state <= HSSL_DATA; 297 | 298 | when HSSL_DATA => 299 | if count_match = '1' then 300 | next_state <= HSSL_TP; 301 | else 302 | next_state <= HSSL_DATA; 303 | end if; 304 | 305 | when HSSL_TP => 306 | 307 | when others => 308 | next_state <= HSSL_ERR; 309 | 310 | end case; 311 | 312 | end process; 313 | 314 | 315 | count_top <= len; 316 | 317 | process 318 | begin 319 | wait until rising_edge(clk); 320 | 321 | count_rst <= '0'; 322 | piso_load <= '0'; 323 | piso_ini <= (piso_ini'range => '0'); 324 | sori <= '0'; 325 | tm_en <= '0'; 326 | sclk_en <= sclk_val; 327 | 328 | case curr_state is 329 | 330 | when HSSL_IDLE => 331 | count_rst <= '1'; 332 | piso_load <= '1'; 333 | piso_ini <= sender_data(piso_ini'range); 334 | 335 | when HSSL_DATA => 336 | sori <= piso_val(0); 337 | 338 | when HSSL_TP => 339 | sori <= '0'; 340 | tm_en <= '1'; 341 | sclk_en <= '0'; 342 | 343 | when others => 344 | 345 | end case; 346 | 347 | end process; 348 | 349 | 350 | end rtl; 351 | -------------------------------------------------------------------------------- /src/absenc_reader_hssl.vhd: -------------------------------------------------------------------------------- 1 | -- 2 | -- HSSL reader implementation (Attocube ID3010 interferometer) 3 | -- IDS_Manual_v2.0.0.pdf, page 20 4 | -- sampling is done on the falling edge 5 | -- state is updated on the rising edge 6 | -- data sent MSb first, encoded using 2's complement 7 | 8 | 9 | library ieee; 10 | use ieee.std_logic_1164.all; 11 | use ieee.std_logic_unsigned.all; 12 | use ieee.numeric_std.all; 13 | use ieee.math_real.all; 14 | 15 | library work; 16 | 17 | 18 | entity reader_hssl is 19 | generic 20 | ( 21 | CLK_FREQ: integer 22 | ); 23 | port 24 | ( 25 | -- local clock 26 | clk: in std_logic; 27 | rst: in std_logic; 28 | 29 | -- master clock 30 | sclk: in std_logic; 31 | 32 | -- sender out, reader in 33 | sori: in std_logic; 34 | 35 | -- actual data to send and length 36 | data: out std_logic_vector; 37 | 38 | -- configuration 39 | len: in unsigned; 40 | tm_gap: in unsigned 41 | ); 42 | 43 | end entity; 44 | 45 | 46 | architecture reader_hssl_rtl of reader_hssl is 47 | 48 | constant DATA_LEN: integer := data'length; 49 | 50 | 51 | -- 52 | -- conversion pipeline 53 | 54 | type conv_state_t is 55 | ( 56 | CONV_WAIT_LATCH, 57 | CONV_EXTEND_SIGN, 58 | CONV_DONE 59 | ); 60 | 61 | constant CONV_ERR: conv_state_t := CONV_WAIT_LATCH; 62 | 63 | signal conv_curr_state: conv_state_t; 64 | signal conv_next_state: conv_state_t; 65 | 66 | signal latched_data: std_logic_vector(data'range); 67 | signal signed_data: std_logic_vector(data'range); 68 | signal conv_data: std_logic_vector(data'range); 69 | signal len_mask: std_logic_vector(data'range); 70 | 71 | 72 | -- 73 | -- timeout counter 74 | 75 | signal tm_val: unsigned(tm_gap'length - 1 downto 0); 76 | alias tm_top: unsigned(tm_val'range) is tm_gap(tm_gap'range); 77 | signal tm_match: std_logic; 78 | 79 | 80 | -- 81 | -- general counter 82 | 83 | signal count_val: unsigned(len'range); 84 | signal count_top: unsigned(count_val'range); 85 | signal count_top_latched: unsigned(count_val'range); 86 | signal count_rst: std_logic; 87 | signal count_match: std_logic; 88 | signal count_match_pre: std_logic; 89 | signal count_match_edge: std_logic; 90 | 91 | 92 | -- 93 | -- sender clock edges 94 | -- redge is for rising edge 95 | -- fedge is for falling edge 96 | -- edge is the one selected by encoder 97 | 98 | signal sclk_pre: std_logic; 99 | signal sclk_redge: std_logic; 100 | signal sclk_fedge: std_logic; 101 | 102 | 103 | -- 104 | -- master clock filter 105 | 106 | signal sclk_buf: std_logic_vector(2 downto 0); 107 | signal sclk_filt: std_logic; 108 | 109 | 110 | -- 111 | -- serial in, parallel out (SIPO) register 112 | 113 | signal sipo_val: std_logic_vector(DATA_LEN - 1 downto 0); 114 | signal sipo_latch: std_logic; 115 | 116 | 117 | begin 118 | 119 | 120 | -- 121 | -- master clock filter 122 | -- sometimes, slave detects fantom master clock edges 123 | -- filtering the master clock fixes this issue 124 | 125 | process 126 | begin 127 | wait until rising_edge(clk); 128 | sclk_buf <= sclk & sclk_buf(sclk_buf'length - 1 downto 1); 129 | end process; 130 | 131 | process(sclk_buf, sclk_filt) 132 | begin 133 | sclk_filt <= sclk_filt; 134 | 135 | if sclk_buf = (sclk_buf'range => '0') then 136 | sclk_filt <= '0'; 137 | elsif sclk_buf = (sclk_buf'range => '1') then 138 | sclk_filt <= '1'; 139 | end if; 140 | end process; 141 | 142 | 143 | -- 144 | -- master clock edge detection 145 | 146 | process 147 | begin 148 | wait until rising_edge(clk); 149 | 150 | if (rst = '1') then 151 | sclk_redge <= '0'; 152 | sclk_fedge <= '0'; 153 | sclk_pre <= sclk_filt; 154 | else 155 | sclk_redge <= (not sclk_pre) and sclk_filt; 156 | sclk_fedge <= sclk_pre and (not sclk_filt); 157 | sclk_pre <= sclk_filt; 158 | end if; 159 | 160 | end process; 161 | 162 | 163 | -- 164 | -- general purpose counter 165 | -- monotically incrementing 166 | -- increment every master edge 167 | -- starts from 1 168 | 169 | process 170 | begin 171 | 172 | wait until rising_edge(clk); 173 | 174 | if (count_rst = '1') then 175 | count_val <= to_unsigned(integer(0), count_val'length); 176 | elsif (sclk_fedge = '1') then 177 | count_val <= count_val + 1; 178 | end if; 179 | 180 | end process; 181 | 182 | 183 | -- 184 | -- general purpose counter comparator 185 | -- count_match set to one when count_val = count_top 186 | 187 | process 188 | begin 189 | 190 | wait until rising_edge(clk); 191 | 192 | if (count_rst = '1') then 193 | count_top_latched <= count_top; 194 | end if; 195 | 196 | end process; 197 | 198 | count_match <= '1' when (count_val = count_top_latched) else '0'; 199 | 200 | process 201 | begin 202 | wait until rising_edge(clk); 203 | count_match_edge <= (not count_match_pre) and count_match; 204 | count_match_pre <= count_match; 205 | end process; 206 | 207 | 208 | -- 209 | -- timeout counter 210 | -- tm_val decrement from tm_top to 0 211 | -- tm_val reloaded at sclk_edge 212 | 213 | process 214 | begin 215 | wait until rising_edge(clk); 216 | 217 | tm_match <= '0'; 218 | 219 | if ((rst or sclk_redge or sclk_fedge) = '1') then 220 | tm_val <= tm_top; 221 | elsif (tm_val = 0) then 222 | tm_val <= tm_top; 223 | tm_match <= '1'; 224 | else 225 | tm_val <= tm_val - 1; 226 | end if; 227 | 228 | end process; 229 | 230 | 231 | -- 232 | -- sipo register 233 | 234 | process 235 | begin 236 | 237 | wait until rising_edge(clk); 238 | 239 | if (sclk_fedge = '1') then 240 | sipo_val <= sipo_val(sipo_val'length - 2 downto 0) & sori; 241 | end if; 242 | 243 | end process; 244 | 245 | 246 | -- 247 | -- data conversion pipeline. ordering: 248 | -- data read (latched at sipo_latch redge from sipo_val) 249 | -- extend_sign 250 | -- latched to data 251 | -- bin_to_sfixed (implemented in top) 252 | 253 | sipo_latch <= count_match_edge; 254 | 255 | process 256 | begin 257 | wait until rising_edge(clk); 258 | 259 | if (rst = '1') then 260 | conv_curr_state <= CONV_WAIT_LATCH; 261 | else 262 | conv_curr_state <= conv_next_state; 263 | end if; 264 | 265 | end process; 266 | 267 | 268 | process(conv_curr_state, sipo_latch) 269 | begin 270 | 271 | conv_next_state <= conv_curr_state; 272 | 273 | case conv_curr_state is 274 | 275 | when CONV_WAIT_LATCH => 276 | if (sipo_latch = '1') then 277 | conv_next_state <= CONV_EXTEND_SIGN; 278 | end if; 279 | 280 | when CONV_EXTEND_SIGN => 281 | conv_next_state <= CONV_DONE; 282 | 283 | when CONV_DONE => 284 | conv_next_state <= CONV_WAIT_LATCH; 285 | 286 | when others => 287 | conv_next_state <= CONV_ERR; 288 | 289 | end case; 290 | 291 | end process; 292 | 293 | 294 | process 295 | begin 296 | wait until rising_edge(clk); 297 | 298 | latched_data <= latched_data; 299 | conv_data <= conv_data; 300 | 301 | case conv_curr_state is 302 | 303 | when CONV_WAIT_LATCH => 304 | latched_data <= sipo_val and len_mask; 305 | 306 | when CONV_EXTEND_SIGN => 307 | 308 | when CONV_DONE => 309 | conv_data <= signed_data; 310 | 311 | when others => 312 | 313 | end case; 314 | 315 | end process; 316 | 317 | data <= conv_data; 318 | 319 | 320 | len_to_mask: work.absenc_pkg.len_to_mask 321 | port map 322 | ( 323 | len => len, 324 | mask => len_mask 325 | ); 326 | 327 | 328 | extend_sign: work.absenc_pkg.extend_sign 329 | port map 330 | ( 331 | data_len => len, 332 | data_in => latched_data, 333 | data_out => signed_data, 334 | len_mask => len_mask 335 | ); 336 | 337 | 338 | -- 339 | -- state automaton 340 | 341 | process 342 | begin 343 | wait until rising_edge(clk); 344 | 345 | count_top <= (count_top'range => '0'); 346 | count_rst <= '0'; 347 | 348 | if (tm_match = '1') then 349 | count_top <= len; 350 | count_rst <= '1'; 351 | end if; 352 | 353 | end process; 354 | 355 | 356 | end reader_hssl_rtl; 357 | -------------------------------------------------------------------------------- /src/absenc_slave.vhd: -------------------------------------------------------------------------------- 1 | library ieee; 2 | use ieee.std_logic_1164.all; 3 | use ieee.std_logic_unsigned.all; 4 | use ieee.numeric_std.all; 5 | use ieee.math_real.all; 6 | 7 | library work; 8 | 9 | 10 | entity slave is 11 | 12 | generic 13 | ( 14 | CLK_FREQ: integer; 15 | ENABLE_ENDAT: boolean; 16 | ENABLE_BISS: boolean; 17 | ENABLE_SSI: boolean 18 | ); 19 | port 20 | ( 21 | -- local clock 22 | clk: in std_logic; 23 | rst: in std_logic; 24 | 25 | -- master clock and data 26 | ma_clk: in std_logic; 27 | miso: out std_logic; 28 | mosi: in std_logic; 29 | gate: out std_logic; 30 | 31 | -- data and length 32 | data: in std_logic_vector; 33 | len: in unsigned; 34 | 35 | -- encoder type 36 | enc_type: in integer; 37 | 38 | -- ssi specific control registers 39 | ssi_flags: in std_logic_vector 40 | ); 41 | 42 | end slave; 43 | 44 | 45 | architecture absenc_slave_rtl of slave is 46 | 47 | constant DATA_LEN: integer := data'length; 48 | 49 | -- 50 | -- timeout counter 51 | 52 | constant TM_MAX: integer := work.absenc_pkg.us_to_count 53 | (work.absenc_pkg.MAX_TM_US, CLK_FREQ, integer'high); 54 | 55 | constant TM_LEN: integer := 56 | work.absenc_pkg.integer_length(TM_MAX); 57 | 58 | constant DEFAULT_TM_TOP: integer := work.absenc_pkg.us_to_count 59 | (work.absenc_pkg.SLAVE_DEFAULT_TM_US, CLK_FREQ, TM_LEN); 60 | 61 | signal tm_val: unsigned(TM_LEN - 1 downto 0); 62 | signal tm_top: unsigned(tm_val'range); 63 | signal tm_match: std_logic; 64 | 65 | -- 66 | -- general counter 67 | 68 | signal count_val: 69 | unsigned(work.absenc_pkg.integer_length(DATA_LEN) - 1 downto 0); 70 | signal count_top: unsigned(count_val'range); 71 | signal count_top_latched: unsigned(count_val'range); 72 | signal count_rst: std_logic; 73 | signal count_match: std_logic; 74 | 75 | -- 76 | -- parallel in, serial out (PISO) register 77 | -- rval is the right shifted value (for lsb) 78 | -- lval is the left shifted value (for msb) 79 | 80 | signal piso_rval: std_logic_vector(data'range); 81 | signal piso_lval: std_logic_vector(data'range); 82 | signal piso_ini: std_logic_vector(data'range); 83 | signal piso_load: std_logic; 84 | 85 | -- 86 | -- master clock edges 87 | -- redge is for rising edge 88 | -- fedge is for falling edge 89 | -- edge is the one selected by encoder 90 | 91 | signal ma_clk_pre: std_logic; 92 | signal ma_clk_redge: std_logic; 93 | signal ma_clk_fedge: std_logic; 94 | signal ma_clk_edge: std_logic; 95 | 96 | -- 97 | -- master clock filter 98 | 99 | signal ma_clk_buf: std_logic_vector(2 downto 0); 100 | signal ma_clk_filt: std_logic; 101 | 102 | -- 103 | -- encoder multiplexed signal 104 | -- 105 | -- the mux size depends on ENABLE_xxx generics. the idea is to eliminate 106 | -- unneeded FPGA logic by instantiating only modules enabled by the generics. 107 | -- to do so, we have functions that translate from ENC_TYPE_xxx to ENC_MUX_xxx 108 | -- and vice versa. ENC_TYPE_xxx represent all the possible encoders, while 109 | -- ENC_MUX_xxx represent an encoder index in the mux, if enabled by generics. 110 | 111 | subtype tm_val_t is unsigned(tm_val'range); 112 | type tm_val_array_t is array(integer range<>) of tm_val_t; 113 | 114 | subtype count_val_t is unsigned(count_val'range); 115 | type count_val_array_t is array(integer range<>) of count_val_t; 116 | 117 | subtype piso_val_t is std_logic_vector(data'range); 118 | type piso_val_array_t is array(integer range<>) of piso_val_t; 119 | 120 | constant enc_mux_to_type: work.absenc_pkg.enc_type_array_t := 121 | work.absenc_pkg.gen_enc_mux_to_type(ENABLE_ENDAT, ENABLE_BISS, ENABLE_SSI); 122 | 123 | constant ENC_MUX_COUNT: integer := 124 | work.absenc_pkg.get_enc_mux_count(ENABLE_ENDAT, ENABLE_BISS, ENABLE_SSI); 125 | 126 | constant ENC_MUX_ENDAT: integer := 127 | work.absenc_pkg.get_enc_mux_endat(ENABLE_ENDAT, ENABLE_BISS, ENABLE_SSI); 128 | 129 | constant ENC_MUX_BISS: integer := 130 | work.absenc_pkg.get_enc_mux_biss(ENABLE_ENDAT, ENABLE_BISS, ENABLE_SSI); 131 | 132 | constant ENC_MUX_SSI: integer := 133 | work.absenc_pkg.get_enc_mux_ssi(ENABLE_ENDAT, ENABLE_BISS, ENABLE_SSI); 134 | 135 | signal ma_clk_edge_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0); 136 | signal miso_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0); 137 | signal gate_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0); 138 | signal tm_top_mux: tm_val_array_t(ENC_MUX_COUNT - 1 downto 0); 139 | signal count_rst_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0); 140 | signal count_top_mux: count_val_array_t(ENC_MUX_COUNT - 1 downto 0); 141 | signal piso_ini_mux: piso_val_array_t(ENC_MUX_COUNT - 1 downto 0); 142 | signal piso_load_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0); 143 | 144 | 145 | begin 146 | 147 | 148 | -- 149 | -- assertions 150 | 151 | assert (ENC_MUX_COUNT /= 0) 152 | report "ENC_MUX_COUNT == 0" severity failure; 153 | 154 | 155 | -- 156 | -- master clock filter 157 | -- sometimes, slave detects fantom master clock edges 158 | -- filtering the master clock fixes this issue 159 | 160 | process 161 | begin 162 | wait until rising_edge(clk); 163 | ma_clk_buf <= ma_clk & ma_clk_buf(ma_clk_buf'length - 1 downto 1); 164 | end process; 165 | 166 | process(ma_clk_buf, ma_clk_filt) 167 | begin 168 | ma_clk_filt <= ma_clk_filt; 169 | 170 | if ma_clk_buf = (ma_clk_buf'range => '0') then 171 | ma_clk_filt <= '0'; 172 | elsif ma_clk_buf = (ma_clk_buf'range => '1') then 173 | ma_clk_filt <= '1'; 174 | end if; 175 | end process; 176 | 177 | 178 | -- 179 | -- master clock edge detection 180 | 181 | process 182 | begin 183 | wait until rising_edge(clk); 184 | 185 | if (rst = '1') then 186 | ma_clk_redge <= '0'; 187 | ma_clk_fedge <= '0'; 188 | ma_clk_pre <= ma_clk_filt; 189 | else 190 | ma_clk_redge <= (not ma_clk_pre) and ma_clk_filt; 191 | ma_clk_fedge <= ma_clk_pre and (not ma_clk_filt); 192 | ma_clk_pre <= ma_clk_filt; 193 | end if; 194 | 195 | end process; 196 | 197 | 198 | -- 199 | -- piso right shifted register 200 | 201 | process 202 | begin 203 | 204 | wait until rising_edge(clk); 205 | 206 | if (piso_load = '1') then 207 | piso_rval <= piso_ini; 208 | elsif (ma_clk_edge = '1') then 209 | piso_rval <= '0' & piso_rval(piso_rval'length - 1 downto 1); 210 | end if; 211 | 212 | end process; 213 | 214 | 215 | -- 216 | -- piso left shifted register 217 | 218 | process 219 | begin 220 | 221 | wait until rising_edge(clk); 222 | 223 | if (piso_load = '1') then 224 | piso_lval <= piso_ini; 225 | elsif (ma_clk_edge = '1') then 226 | piso_lval <= piso_lval(piso_lval'length - 2 downto 0) & '0'; 227 | end if; 228 | 229 | end process; 230 | 231 | 232 | -- 233 | -- general purpose counter 234 | -- monotically incrementing 235 | -- increment every master edge 236 | -- starts from 1 237 | 238 | process 239 | begin 240 | 241 | wait until rising_edge(clk); 242 | 243 | if (count_rst = '1') then 244 | count_val <= to_unsigned(integer(1), count_val'length); 245 | elsif (ma_clk_edge = '1') then 246 | count_val <= count_val + 1; 247 | end if; 248 | 249 | end process; 250 | 251 | 252 | -- 253 | -- general purpose counter comparator 254 | -- count_match set to one when count_val = count_top 255 | 256 | process 257 | begin 258 | 259 | wait until rising_edge(clk); 260 | 261 | if (count_rst = '1') then 262 | count_top_latched <= count_top; 263 | end if; 264 | 265 | end process; 266 | 267 | count_match <= '1' when (count_val = count_top_latched) else '0'; 268 | 269 | 270 | -- 271 | -- timeout counter 272 | -- tm_val decrement from tm_top to 0 273 | -- tm_val reloaded at ma_clk_edge 274 | 275 | process 276 | begin 277 | wait until rising_edge(clk); 278 | 279 | tm_match <= '0'; 280 | 281 | if ((rst or ma_clk_edge) = '1') then 282 | tm_val <= tm_top; 283 | elsif (tm_val = 0) then 284 | tm_val <= tm_top; 285 | tm_match <= '1'; 286 | else 287 | tm_val <= tm_val - 1; 288 | end if; 289 | 290 | end process; 291 | 292 | 293 | -- 294 | -- encoder slave implementations 295 | 296 | gen_endat: if ENABLE_ENDAT = TRUE generate 297 | slave_endat: work.absenc_pkg.slave_endat 298 | generic map 299 | ( 300 | CLK_FREQ => CLK_FREQ 301 | ) 302 | port map 303 | ( 304 | clk => clk, 305 | rst => rst, 306 | ma_clk_redge => ma_clk_redge, 307 | ma_clk_fedge => ma_clk_fedge, 308 | ma_clk_edge => ma_clk_edge_mux(ENC_MUX_ENDAT), 309 | miso => miso_mux(ENC_MUX_ENDAT), 310 | mosi => mosi, 311 | gate => gate_mux(ENC_MUX_ENDAT), 312 | data => data, 313 | len => len, 314 | tm_match => tm_match, 315 | tm_top => tm_top_mux(ENC_MUX_ENDAT), 316 | count_top => count_top_mux(ENC_MUX_ENDAT), 317 | count_match => count_match, 318 | count_rst => count_rst_mux(ENC_MUX_ENDAT), 319 | piso_rval => piso_rval, 320 | piso_lval => piso_lval, 321 | piso_ini => piso_ini_mux(ENC_MUX_ENDAT), 322 | piso_load => piso_load_mux(ENC_MUX_ENDAT) 323 | ); 324 | end generate gen_endat; 325 | 326 | gen_biss: if ENABLE_BISS = TRUE generate 327 | slave_biss: work.absenc_pkg.slave_biss 328 | generic map 329 | ( 330 | CLK_FREQ => CLK_FREQ 331 | ) 332 | port map 333 | ( 334 | clk => clk, 335 | rst => rst, 336 | ma_clk_redge => ma_clk_redge, 337 | ma_clk_fedge => ma_clk_fedge, 338 | ma_clk_edge => ma_clk_edge_mux(ENC_MUX_BISS), 339 | miso => miso_mux(ENC_MUX_BISS), 340 | mosi => mosi, 341 | gate => gate_mux(ENC_MUX_BISS), 342 | data => data, 343 | len => len, 344 | tm_match => tm_match, 345 | tm_top => tm_top_mux(ENC_MUX_BISS), 346 | count_top => count_top_mux(ENC_MUX_BISS), 347 | count_match => count_match, 348 | count_rst => count_rst_mux(ENC_MUX_BISS), 349 | piso_rval => piso_rval, 350 | piso_lval => piso_lval, 351 | piso_ini => piso_ini_mux(ENC_MUX_BISS), 352 | piso_load => piso_load_mux(ENC_MUX_BISS) 353 | ); 354 | end generate gen_biss; 355 | 356 | gen_ssi: if ENABLE_SSI = TRUE generate 357 | slave_ssi: work.absenc_pkg.slave_ssi 358 | generic map 359 | ( 360 | CLK_FREQ => CLK_FREQ 361 | ) 362 | port map 363 | ( 364 | clk => clk, 365 | rst => rst, 366 | ma_clk_redge => ma_clk_redge, 367 | ma_clk_fedge => ma_clk_fedge, 368 | ma_clk_edge => ma_clk_edge_mux(ENC_MUX_SSI), 369 | miso => miso_mux(ENC_MUX_SSI), 370 | mosi => mosi, 371 | gate => gate_mux(ENC_MUX_SSI), 372 | data => data, 373 | len => len, 374 | tm_match => tm_match, 375 | tm_top => tm_top_mux(ENC_MUX_SSI), 376 | count_top => count_top_mux(ENC_MUX_SSI), 377 | count_match => count_match, 378 | count_rst => count_rst_mux(ENC_MUX_SSI), 379 | piso_rval => piso_rval, 380 | piso_lval => piso_lval, 381 | piso_ini => piso_ini_mux(ENC_MUX_SSI), 382 | piso_load => piso_load_mux(ENC_MUX_SSI), 383 | ssi_flags => ssi_flags 384 | ); 385 | end generate gen_ssi; 386 | 387 | 388 | -- 389 | -- enc_type multiplexer 390 | 391 | process 392 | ( 393 | enc_type, 394 | ma_clk_edge_mux, 395 | miso_mux, 396 | gate_mux, 397 | tm_top_mux, 398 | count_rst_mux, 399 | count_top_mux, 400 | piso_ini_mux, 401 | piso_load_mux 402 | ) 403 | 404 | begin 405 | 406 | ma_clk_edge <= '0'; 407 | miso <= '0'; 408 | gate <= '0'; 409 | tm_top <= to_unsigned(DEFAULT_TM_TOP, tm_top'length); 410 | count_rst <= '0'; 411 | count_top <= (count_top'range => '0'); 412 | piso_ini <= (piso_ini'range => '0'); 413 | piso_load <= '0'; 414 | 415 | for i in 0 to ENC_MUX_COUNT - 1 loop 416 | if enc_mux_to_type(i) = enc_type then 417 | ma_clk_edge <= ma_clk_edge_mux(i); 418 | miso <= miso_mux(i); 419 | gate <= gate_mux(i); 420 | tm_top <= tm_top_mux(i); 421 | count_rst <= count_rst_mux(i); 422 | count_top <= count_top_mux(i); 423 | piso_ini <= piso_ini_mux(i); 424 | piso_load <= piso_load_mux(i); 425 | end if; 426 | end loop; 427 | 428 | end process; 429 | 430 | 431 | end absenc_slave_rtl; 432 | -------------------------------------------------------------------------------- /doc/texpp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | # repo: https://github.com/texane/texpp 5 | # auth: fabien.lementec@gmail.com 6 | 7 | 8 | import re 9 | import os 10 | 11 | 12 | class Texpp: 13 | 14 | @staticmethod 15 | def init_class(): 16 | Texpp.latex_ext_re = re.compile('\.tex$') 17 | Texpp.vhdl_ext_re = re.compile('\.vhdl?$') 18 | Texpp.latex_start_re = re.compile('^\s*\\\\begin{texpp}\s*$') 19 | Texpp.latex_end_re = re.compile('^\s*\\\\end{texpp}\s*$') 20 | Texpp.vhdl_pkg_start_re = re.compile('^\s*package\s+(\w+)\s+is\s*$') 21 | Texpp.vhdl_pkg_end_re = re.compile('^\s*end\s*package\s+(\w+)\s*;\s*$') 22 | Texpp.vhdl_comp_start_re = re.compile('^\s*component\s+(\w+)\s*$') 23 | Texpp.vhdl_comp_end_re = re.compile('^\s*end\s*component\s*;\s*$') 24 | Texpp.vhdl_inst_start_re = re.compile('^\s*(\w+)\s*:.*$') 25 | Texpp.vhdl_inst_end_re = re.compile('^\s*\w*\);\s*$') 26 | Texpp.vhdl_comment_re = re.compile('^\s*--(.*)$') 27 | Texpp.EOB = 0 28 | Texpp.EOL = 1 29 | Texpp.EOF = 2 30 | return 31 | 32 | 33 | def __init__(self): 34 | Texpp.init_class() 35 | 36 | Texpp.self_ = self 37 | 38 | self.start_re = None 39 | self.end_re = None 40 | self.is_error = False 41 | self.block_str = '' 42 | self.in_block = False 43 | self.out_str = '' 44 | self.parsed_file_dir = None 45 | 46 | return 47 | 48 | 49 | @staticmethod 50 | def open_file(file_, flags = 'r'): 51 | try: f = open(file_, flags) 52 | except: f = None 53 | return f 54 | 55 | 56 | def is_block_start(self, line): 57 | if self.start_re.match(line): return True 58 | return False 59 | 60 | 61 | def is_block_end(self, line): 62 | if self.end_re.match(line): return True 63 | return False 64 | 65 | 66 | def process_block(self): 67 | exec(self.block_str) 68 | self.block_str = '' 69 | return 70 | 71 | 72 | def parse_file(self, filename): 73 | lang = self.ext_to_lang(filename) 74 | if lang == None: return False 75 | if lang != 'latex': return False 76 | 77 | self.start_re = Texpp.latex_start_re 78 | self.end_re = Texpp.latex_end_re 79 | 80 | self.parsed_file_dir = os.path.dirname(filename) 81 | if len(self.parsed_file_dir) == 0: self.parsed_file_dir = '.' 82 | 83 | f = Texpp.open_file(filename) 84 | if f == None: return -1 85 | while True: 86 | l = f.readline() 87 | if len(l) == 0: break 88 | 89 | if self.in_block == True: 90 | if self.is_block_end(l) == True: 91 | self.process_block() 92 | self.in_block = False 93 | else: 94 | self.block_str += l 95 | elif self.is_block_start(l) == True: 96 | self.in_block = True 97 | else: 98 | self.out_str += l 99 | 100 | return 0 101 | 102 | 103 | def output_file(self, filename): 104 | print(self.out_str) 105 | return True 106 | 107 | 108 | @staticmethod 109 | def ext_to_lang(x): 110 | if Texpp.latex_ext_re.search(x) != None: return 'latex' 111 | elif Texpp.vhdl_ext_re.search(x) != None: return 'vhdl' 112 | return None 113 | 114 | 115 | @staticmethod 116 | def latex_escape(s): 117 | return ( 118 | s.replace('_', '\\_'). 119 | replace("\t", "\\t"). 120 | replace("\b", "\\b"). 121 | replace("\n", "\\\\\n") 122 | ) 123 | 124 | 125 | @staticmethod 126 | def latex_format_error(e): 127 | s = Texpp.latex_escape('\textbf{error}: ' + e + '\n') 128 | return s 129 | 130 | 131 | @staticmethod 132 | def latex_format_code(s): 133 | return ( 134 | Texpp.latex_escape('\begin{vhdl}') + '\n' + 135 | s + 136 | Texpp.latex_escape('\end{vhdl}') + '\n' 137 | ) 138 | 139 | 140 | @staticmethod 141 | def latex_format_note(x): 142 | if x['err'] != None: return Texpp.latex_format_error(x['err']) 143 | s = Texpp.latex_escape('Note from file ' + x['file']) 144 | s += Texpp.latex_escape(' at line ' + str(x['lindex'])) 145 | s += '\n' 146 | s += Texpp.latex_format_code(x['lines']) 147 | s += '\n' 148 | return s 149 | 150 | 151 | @staticmethod 152 | def latex_format_interface(x): 153 | if x['err'] != None: return Texpp.latex_format_error(x['err']) 154 | s = Texpp.latex_escape( 155 | '\subsection{' + x['ns'] + '.' + x['name'] + '}' 156 | ) 157 | s += '\n' 158 | s += Texpp.latex_escape('Interface from file ' + x['file']) 159 | s += Texpp.latex_escape(' at line ' + str(x['lindex'])) 160 | s += '\n' 161 | s += Texpp.latex_format_code(x['lines']) 162 | s += '\n' 163 | return s 164 | 165 | 166 | @staticmethod 167 | def latex_format_example(x): 168 | if x['err'] != None: return Texpp.latex_format_error(x['err']) 169 | s = Texpp.latex_escape( 170 | '\subsection{' + x['name'] + '}' 171 | ) 172 | s += Texpp.latex_escape('Example from file ' + x['file']) 173 | s += Texpp.latex_escape(' at line ' + str(x['lindex'])) 174 | s += '\n' 175 | s += Texpp.latex_format_code(x['lines']) 176 | s += '\n' 177 | return s 178 | 179 | 180 | @staticmethod 181 | def vhdl_extract_interface(file_, name): 182 | x = {} 183 | x['err'] = 'not found (invalid syntax ...)' 184 | x['lang'] = 'vhdl' 185 | x['ns'] = '' 186 | x['name'] = name 187 | x['file'] = file_.name 188 | x['generics'] = [] 189 | x['ports'] = [] 190 | x['lines'] = '' 191 | x['lindex'] = 0 192 | 193 | in_pkg = False 194 | in_comp = False 195 | lindex = -1 196 | 197 | while True: 198 | l = file_.readline() 199 | if len(l) == 0: break 200 | 201 | lindex += 1 202 | 203 | if in_pkg == False: 204 | m = Texpp.vhdl_pkg_start_re.search(l) 205 | if (m == None) or (m.groups == 1): continue 206 | in_pkg = True 207 | x['ns'] = m.group(1) 208 | elif in_comp == False: 209 | m = Texpp.vhdl_comp_start_re.search(l) 210 | if (m == None) or (m.groups == 1): continue 211 | if m.group(1) != name: continue 212 | in_comp = True 213 | x['name'] = m.group(1) 214 | x['lines'] = l 215 | elif (in_pkg == True) and (in_comp == True): 216 | x['lines'] += l 217 | m = Texpp.vhdl_comp_end_re.search(l) 218 | if (m == None): continue 219 | x['err'] = None 220 | x['lindex'] = lindex 221 | break 222 | 223 | return x 224 | 225 | 226 | @staticmethod 227 | def vhdl_extract_example(file_, name): 228 | x = {} 229 | x['err'] = 'not found (invalid syntax ...)' 230 | x['name'] = name 231 | x['file'] = file_.name 232 | x['lines'] = '' 233 | x['lindex'] = 0 234 | 235 | in_example = False 236 | lindex = -1 237 | 238 | while True: 239 | l = file_.readline() 240 | if len(l) == 0: break 241 | 242 | lindex += 1 243 | 244 | if in_example == False: 245 | m = Texpp.vhdl_inst_start_re.search(l) 246 | if (m == None) or (m.groups == 1): continue 247 | if m.group(1) != name: continue 248 | in_example = True 249 | x['lindex'] = lindex 250 | x['lines'] = l 251 | else: 252 | x['lines'] += l 253 | m = Texpp.vhdl_inst_end_re.search(l) 254 | if m == None: continue 255 | x['err'] = None 256 | break 257 | 258 | return x 259 | 260 | 261 | @staticmethod 262 | def vhdl_extract_tags(file_, tags): 263 | x = {} 264 | x['err'] = 'not found (invalid syntax ...)' 265 | x['file'] = file_.name 266 | x['lines'] = '' 267 | x['lindex'] = 0 268 | 269 | start_re = re.compile('^\s*--\s*' + tags[0] + '.*$') 270 | if type(tags[1]) == str: 271 | end_re = re.compile('^\s*--\s*' + tags[1] + '.*$') 272 | elif tags[1] not in [ Texpp.EOB ]: 273 | x['err'] = 'invalid tag' 274 | return x 275 | else: 276 | end_re = None 277 | 278 | in_tag = False 279 | lindex = -1 280 | 281 | while True: 282 | l = file_.readline() 283 | if len(l) == 0: break 284 | 285 | lindex += 1 286 | 287 | if in_tag == False: 288 | m = start_re.search(l) 289 | if m == None: continue 290 | in_tag = True 291 | x['lindex'] = lindex 292 | x['lines'] = '' 293 | elif end_re != None: 294 | m = end_re.search(l) 295 | if m == None: 296 | x['lines'] += l 297 | continue 298 | x['err'] = None 299 | break 300 | elif tags[1] == Texpp.EOB: 301 | m = Texpp.vhdl_comment_re.search(l) 302 | if m != None: 303 | x['lines'] += l 304 | continue 305 | x['err'] = None 306 | break 307 | 308 | return x 309 | 310 | 311 | @staticmethod 312 | def make_error(s): 313 | x = {} 314 | x['err'] = s 315 | return x 316 | 317 | 318 | @staticmethod 319 | def make_kind_error(kind): 320 | return Texpp.make_error('invalid kind: ' + kind) 321 | 322 | 323 | def extract_(self, tags, kind, path, name): 324 | lang = self.ext_to_lang(path) 325 | if (lang == None) or (lang != 'vhdl'): 326 | x = Texpp.make_error('invalid file extension: ' + path) 327 | else: 328 | path = self.parsed_file_dir + '/' + path 329 | f = Texpp.open_file(path) 330 | if f == None: 331 | x = Texpp.make_error('file not found: ' + path) 332 | elif tags != None: 333 | x = Texpp.vhdl_extract_tags(f, tags) 334 | elif kind == 'interface': 335 | x = Texpp.vhdl_extract_interface(f, name) 336 | elif kind == 'example': 337 | x = Texpp.vhdl_extract_example(f, name) 338 | else: 339 | x = Texpp.make_kind_error(kind) 340 | return x 341 | 342 | 343 | @staticmethod 344 | def output(s): 345 | Texpp.self_.out_str += s 346 | return 347 | 348 | 349 | @staticmethod 350 | def format(x, kind): 351 | if x['err'] != None: s = Texpp.latex_format_error(x['err']) 352 | elif kind == 'interface': s = Texpp.latex_format_interface(x) 353 | elif kind == 'example': s = Texpp.latex_format_example(x) 354 | elif kind == 'note': s = Texpp.latex_format_note(x) 355 | else: s = Texpp.latex_format_error(Texpp.make_kind_error(kind)) 356 | return s 357 | 358 | 359 | @staticmethod 360 | def extract(tags = None, kind = None, path = None, name = None): 361 | return Texpp.self_.extract_(tags, kind, path, name) 362 | 363 | 364 | @staticmethod 365 | def include(tags = None, kind = None, path = None, name = None): 366 | x = Texpp.self_.extract_(tags, kind, path, name) 367 | s = Texpp.self_.format(x, kind) 368 | Texpp.self_.output(s) 369 | return 370 | 371 | 372 | if __name__ == '__main__': 373 | import sys 374 | in_name = sys.argv[1] 375 | out_name = sys.argv[2] 376 | pp = Texpp() 377 | pp.parse_file(in_name) 378 | pp.output_file(out_name) 379 | -------------------------------------------------------------------------------- /src/absenc_master.vhd: -------------------------------------------------------------------------------- 1 | library ieee; 2 | use ieee.std_logic_1164.all; 3 | use ieee.std_logic_unsigned.all; 4 | use ieee.numeric_std.all; 5 | use ieee.math_real.all; 6 | 7 | library work; 8 | 9 | 10 | entity master is 11 | 12 | generic 13 | ( 14 | CLK_FREQ: integer; 15 | ENABLE_ENDAT: boolean; 16 | ENABLE_BISS: boolean; 17 | ENABLE_SSI: boolean 18 | ); 19 | port 20 | ( 21 | -- local clock 22 | clk: in std_logic; 23 | rst: in std_logic; 24 | 25 | -- generated clock for slave 26 | -- fdiv divides CLK_FREQ 27 | ma_fdiv: in unsigned; 28 | ma_clk: out std_logic; 29 | 30 | -- master out, slave in 31 | mosi: out std_logic; 32 | miso: in std_logic; 33 | 34 | -- gate to drive output (1 to drive it) 35 | gate: out std_logic; 36 | 37 | -- data from slave, and data length 38 | data: out std_logic_vector; 39 | len: in unsigned; 40 | 41 | -- encoder type 42 | enc_type: in integer; 43 | 44 | -- ssi specific control registers 45 | ssi_flags: in std_logic_vector; 46 | ssi_delay_fdiv: in unsigned 47 | ); 48 | 49 | end entity; 50 | 51 | 52 | architecture absenc_master_rtl of master is 53 | 54 | constant DATA_LEN: integer := data'length; 55 | 56 | -- 57 | -- conversion pipeline 58 | 59 | type conv_state_t is 60 | ( 61 | CONV_WAIT_LATCH, 62 | CONV_LSB_TO_MSB, 63 | CONV_GRAY_TO_BIN, 64 | CONV_EXTEND_SIGN, 65 | CONV_DONE 66 | ); 67 | 68 | constant CONV_ERR: conv_state_t := CONV_WAIT_LATCH; 69 | 70 | signal curr_state: conv_state_t; 71 | signal next_state: conv_state_t; 72 | 73 | signal latched_data: std_logic_vector(data'range); 74 | signal msb_data: std_logic_vector(data'range); 75 | signal bin_data: std_logic_vector(data'range); 76 | signal signed_data: std_logic_vector(data'range); 77 | signal conv_data: std_logic_vector(data'range); 78 | signal len_mask: std_logic_vector(data'range); 79 | 80 | -- 81 | -- enable conversion stages 82 | 83 | signal gray_to_bin_en: std_logic; 84 | signal lsb_to_msb_en: std_logic; 85 | 86 | -- 87 | -- timeout counter 88 | 89 | constant TM_MAX: integer := work.absenc_pkg.us_to_count 90 | (work.absenc_pkg.MAX_TM_US, CLK_FREQ, integer'high); 91 | 92 | constant TM_LEN: integer := 93 | work.absenc_pkg.integer_length(TM_MAX); 94 | 95 | constant DEFAULT_TM_TOP: integer := work.absenc_pkg.us_to_count 96 | (work.absenc_pkg.MASTER_DEFAULT_TM_US, CLK_FREQ, TM_LEN); 97 | 98 | signal tm_val: unsigned(TM_LEN - 1 downto 0); 99 | signal tm_top: unsigned(tm_val'range); 100 | signal tm_match: std_logic; 101 | 102 | -- 103 | -- general counter 104 | 105 | signal count_val: 106 | unsigned(work.absenc_pkg.integer_length(DATA_LEN) - 1 downto 0); 107 | signal count_top: unsigned(count_val'range); 108 | signal count_top_latched: unsigned(count_val'range); 109 | signal count_rst: std_logic; 110 | signal count_match: std_logic; 111 | 112 | -- 113 | -- serial in, parallel out (SIPO) register 114 | 115 | signal sipo_val: std_logic_vector(DATA_LEN - 1 downto 0); 116 | signal sipo_latch_redge: std_logic; 117 | signal sipo_latch_pre: std_logic; 118 | signal sipo_latch: std_logic; 119 | 120 | -- 121 | -- master clock 122 | 123 | signal ma_clk_rst_en: std_logic; 124 | signal ma_clk_rst_val: std_logic; 125 | 126 | signal ma_clk_val: std_logic; 127 | 128 | signal ma_half_fdiv: unsigned(ma_fdiv'length - 1 downto 0); 129 | signal ma_half_count: unsigned(ma_half_fdiv'length - 1 downto 0); 130 | signal ma_half_match: std_logic; 131 | 132 | -- 133 | -- master clock rising falling edges 134 | 135 | signal ma_clk_redge: std_logic; 136 | signal ma_clk_fedge: std_logic; 137 | signal ma_clk_edge: std_logic; 138 | 139 | -- 140 | -- encoder multiplexed signal 141 | -- 142 | -- the mux size depends on ENABLE_xxx generics. the idea is to eliminate 143 | -- unneeded FPGA logic by instantiating only modules enabled by the generics. 144 | -- to do so, we have functions that translate from ENC_TYPE_xxx to ENC_MUX_xxx 145 | -- and vice versa. ENC_TYPE_xxx represent all the possible encoders, while 146 | -- ENC_MUX_xxx represent an encoder index in the mux, if enabled by generics. 147 | 148 | subtype tm_val_t is unsigned(tm_val'range); 149 | type tm_val_array_t is array(integer range<>) of tm_val_t; 150 | 151 | subtype count_val_t is unsigned(count_val'range); 152 | type count_val_array_t is array(integer range<>) of count_val_t; 153 | 154 | constant enc_mux_to_type: work.absenc_pkg.enc_type_array_t := 155 | work.absenc_pkg.gen_enc_mux_to_type(ENABLE_ENDAT, ENABLE_BISS, ENABLE_SSI); 156 | 157 | constant ENC_MUX_COUNT: integer := 158 | work.absenc_pkg.get_enc_mux_count(ENABLE_ENDAT, ENABLE_BISS, ENABLE_SSI); 159 | 160 | constant ENC_MUX_ENDAT: integer := 161 | work.absenc_pkg.get_enc_mux_endat(ENABLE_ENDAT, ENABLE_BISS, ENABLE_SSI); 162 | 163 | constant ENC_MUX_BISS: integer := 164 | work.absenc_pkg.get_enc_mux_biss(ENABLE_ENDAT, ENABLE_BISS, ENABLE_SSI); 165 | 166 | constant ENC_MUX_SSI: integer := 167 | work.absenc_pkg.get_enc_mux_ssi(ENABLE_ENDAT, ENABLE_BISS, ENABLE_SSI); 168 | 169 | signal ma_clk_edge_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0); 170 | signal ma_clk_rst_en_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0); 171 | signal ma_clk_rst_val_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0); 172 | signal mosi_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0); 173 | signal gate_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0); 174 | signal tm_top_mux: tm_val_array_t(ENC_MUX_COUNT - 1 downto 0); 175 | signal count_rst_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0); 176 | signal count_top_mux: count_val_array_t(ENC_MUX_COUNT - 1 downto 0); 177 | signal sipo_latch_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0); 178 | signal gray_to_bin_en_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0); 179 | signal lsb_to_msb_en_mux: std_logic_vector(ENC_MUX_COUNT - 1 downto 0); 180 | 181 | 182 | begin 183 | 184 | 185 | -- 186 | -- assert lengths to reduce generated logic 187 | -- ie. counter and comparator sizes 188 | 189 | assert (ma_fdiv'length <= 16) 190 | report "ma_fdiv'length too large" severity failure; 191 | 192 | 193 | -- 194 | -- master clock generation 195 | -- master clock frequency is clk divided by ma_fdiv 196 | -- generate edges using a counter from ma_fdiv/2 to 0 197 | 198 | ma_half_fdiv <= ma_fdiv srl 1; 199 | ma_half_match <= '1' when ma_half_count = 1 else '0'; 200 | 201 | process 202 | begin 203 | wait until rising_edge(clk); 204 | 205 | if ((rst or ma_clk_rst_en or ma_half_match) = '1') then 206 | ma_half_count <= ma_half_fdiv; 207 | else 208 | ma_half_count <= ma_half_count - 1; 209 | end if; 210 | 211 | end process; 212 | 213 | 214 | process 215 | begin 216 | wait until rising_edge(clk); 217 | 218 | if ((rst or ma_clk_rst_en) = '1') then 219 | ma_clk_val <= ma_clk_rst_val; 220 | else 221 | ma_clk_val <= ma_clk_val xor ma_half_match; 222 | end if; 223 | 224 | end process; 225 | 226 | ma_clk <= ma_clk_val; 227 | 228 | 229 | -- 230 | -- master clock edges 231 | 232 | process 233 | begin 234 | wait until rising_edge(clk); 235 | ma_clk_redge <= ma_half_match and (not ma_clk_val); 236 | end process; 237 | 238 | process 239 | begin 240 | wait until rising_edge(clk); 241 | ma_clk_fedge <= ma_half_match and ma_clk_val; 242 | end process; 243 | 244 | 245 | -- 246 | -- timeout counter 247 | -- tm_val decrement from tm_top to 0 248 | -- tm_val reloaded at ma_clk_edge 249 | 250 | process 251 | begin 252 | wait until rising_edge(clk); 253 | 254 | tm_match <= '0'; 255 | 256 | if ((rst or ma_clk_edge) = '1') then 257 | tm_val <= tm_top; 258 | elsif (tm_val = 0) then 259 | tm_val <= tm_top; 260 | tm_match <= '1'; 261 | else 262 | tm_val <= tm_val - 1; 263 | end if; 264 | 265 | end process; 266 | 267 | 268 | -- 269 | -- general purpose counter 270 | -- monotically incrementing 271 | -- increment every master edge 272 | -- starts from 1 273 | 274 | process 275 | begin 276 | 277 | wait until rising_edge(clk); 278 | 279 | if (count_rst = '1') then 280 | count_val <= to_unsigned(integer(1), count_val'length); 281 | elsif (ma_clk_edge = '1') then 282 | count_val <= count_val + 1; 283 | end if; 284 | 285 | end process; 286 | 287 | 288 | -- 289 | -- general purpose counter comparator 290 | -- count_match set to one when count_val = count_top 291 | 292 | process 293 | begin 294 | 295 | wait until rising_edge(clk); 296 | 297 | if (count_rst = '1') then 298 | count_top_latched <= count_top; 299 | end if; 300 | 301 | end process; 302 | 303 | count_match <= '1' when (count_val = count_top_latched) else '0'; 304 | 305 | 306 | -- 307 | -- sipo register 308 | 309 | process 310 | begin 311 | 312 | wait until rising_edge(clk); 313 | 314 | if (ma_clk_edge = '1') then 315 | sipo_val <= sipo_val(sipo_val'length - 2 downto 0) & miso; 316 | end if; 317 | 318 | end process; 319 | 320 | 321 | -- 322 | -- sipo_latch edge detector 323 | -- ma_clk_redge is not used as ma_clk may be disabled. 324 | -- instead, clk is used for detecting sipo_latch edges 325 | 326 | process 327 | begin 328 | wait until rising_edge(clk); 329 | sipo_latch_pre <= sipo_latch; 330 | end process; 331 | 332 | sipo_latch_redge <= (not sipo_latch_pre) and sipo_latch; 333 | 334 | 335 | -- 336 | -- data conversion pipeline. ordering: 337 | -- data from slave (latched at sipo_latch redge from sipo_val) 338 | -- lsb_to_msb 339 | -- gray_to_bin 340 | -- extend_sign 341 | -- latched to data 342 | -- bin_to_sfixed (implemented in top) 343 | 344 | process 345 | begin 346 | wait until rising_edge(clk); 347 | 348 | if (rst = '1') then 349 | curr_state <= CONV_WAIT_LATCH; 350 | else 351 | curr_state <= next_state; 352 | end if; 353 | 354 | end process; 355 | 356 | 357 | process(curr_state, sipo_latch_redge) 358 | begin 359 | 360 | next_state <= curr_state; 361 | 362 | case curr_state is 363 | 364 | when CONV_WAIT_LATCH => 365 | if (sipo_latch_redge = '1') then 366 | next_state <= CONV_LSB_TO_MSB; 367 | end if; 368 | 369 | when CONV_LSB_TO_MSB => 370 | next_state <= CONV_GRAY_TO_BIN; 371 | 372 | when CONV_GRAY_TO_BIN => 373 | next_state <= CONV_EXTEND_SIGN; 374 | 375 | when CONV_EXTEND_SIGN => 376 | next_state <= CONV_DONE; 377 | 378 | when CONV_DONE => 379 | next_state <= CONV_WAIT_LATCH; 380 | 381 | when others => 382 | next_state <= CONV_ERR; 383 | 384 | end case; 385 | 386 | end process; 387 | 388 | 389 | process 390 | begin 391 | wait until rising_edge(clk); 392 | 393 | latched_data <= latched_data; 394 | conv_data <= conv_data; 395 | 396 | case curr_state is 397 | 398 | when CONV_WAIT_LATCH => 399 | latched_data <= sipo_val and len_mask; 400 | 401 | when CONV_LSB_TO_MSB => 402 | 403 | when CONV_EXTEND_SIGN => 404 | 405 | when CONV_DONE => 406 | conv_data <= signed_data; 407 | 408 | when others => 409 | 410 | end case; 411 | 412 | end process; 413 | 414 | data <= conv_data; 415 | 416 | 417 | len_to_mask: work.absenc_pkg.len_to_mask 418 | port map 419 | ( 420 | len => len, 421 | mask => len_mask 422 | ); 423 | 424 | 425 | lsb_to_msb: work.absenc_pkg.lsb_to_msb 426 | port map 427 | ( 428 | en => lsb_to_msb_en, 429 | data_len => len, 430 | lsb_data => latched_data, 431 | msb_data => msb_data 432 | ); 433 | 434 | 435 | gray_to_bin: work.absenc_pkg.gray_to_bin 436 | port map 437 | ( 438 | en => gray_to_bin_en, 439 | gray_data => msb_data, 440 | bin_data => bin_data 441 | ); 442 | 443 | 444 | extend_sign: work.absenc_pkg.extend_sign 445 | port map 446 | ( 447 | data_len => len, 448 | data_in => bin_data, 449 | data_out => signed_data, 450 | len_mask => len_mask 451 | ); 452 | 453 | 454 | -- 455 | -- encoder master implementations 456 | 457 | gen_endat: if ENABLE_ENDAT = TRUE generate 458 | master_endat: work.absenc_pkg.master_endat 459 | generic map 460 | ( 461 | CLK_FREQ => CLK_FREQ 462 | ) 463 | port map 464 | ( 465 | clk => clk, 466 | rst => rst, 467 | ma_clk_fedge => ma_clk_fedge, 468 | ma_clk_redge => ma_clk_redge, 469 | ma_clk_edge => ma_clk_edge_mux(ENC_MUX_ENDAT), 470 | ma_clk_rst_en => ma_clk_rst_en_mux(ENC_MUX_ENDAT), 471 | ma_clk_rst_val => ma_clk_rst_val_mux(ENC_MUX_ENDAT), 472 | mosi => mosi_mux(ENC_MUX_ENDAT), 473 | miso => miso, 474 | gate => gate_mux(ENC_MUX_ENDAT), 475 | len => len, 476 | tm_match => tm_match, 477 | tm_top => tm_top_mux(ENC_MUX_ENDAT), 478 | count_top => count_top_mux(ENC_MUX_ENDAT), 479 | count_match => count_match, 480 | count_rst => count_rst_mux(ENC_MUX_ENDAT), 481 | sipo_val => sipo_val, 482 | sipo_latch => sipo_latch_mux(ENC_MUX_ENDAT), 483 | gray_to_bin_en => gray_to_bin_en_mux(ENC_MUX_ENDAT), 484 | lsb_to_msb_en => lsb_to_msb_en_mux(ENC_MUX_ENDAT) 485 | ); 486 | end generate gen_endat; 487 | 488 | 489 | gen_biss: if ENABLE_BISS = TRUE generate 490 | master_biss: work.absenc_pkg.master_biss 491 | generic map 492 | ( 493 | CLK_FREQ => CLK_FREQ 494 | ) 495 | port map 496 | ( 497 | clk => clk, 498 | rst => rst, 499 | ma_clk_fedge => ma_clk_fedge, 500 | ma_clk_redge => ma_clk_redge, 501 | ma_clk_edge => ma_clk_edge_mux(ENC_MUX_BISS), 502 | ma_clk_rst_en => ma_clk_rst_en_mux(ENC_MUX_BISS), 503 | ma_clk_rst_val => ma_clk_rst_val_mux(ENC_MUX_BISS), 504 | mosi => mosi_mux(ENC_MUX_BISS), 505 | miso => miso, 506 | gate => gate_mux(ENC_MUX_BISS), 507 | len => len, 508 | tm_match => tm_match, 509 | tm_top => tm_top_mux(ENC_MUX_BISS), 510 | count_top => count_top_mux(ENC_MUX_BISS), 511 | count_match => count_match, 512 | count_rst => count_rst_mux(ENC_MUX_BISS), 513 | sipo_val => sipo_val, 514 | sipo_latch => sipo_latch_mux(ENC_MUX_BISS), 515 | gray_to_bin_en => gray_to_bin_en_mux(ENC_MUX_BISS), 516 | lsb_to_msb_en => lsb_to_msb_en_mux(ENC_MUX_BISS) 517 | ); 518 | end generate gen_biss; 519 | 520 | 521 | gen_ssi: if ENABLE_SSI = TRUE generate 522 | master_ssi: work.absenc_pkg.master_ssi 523 | generic map 524 | ( 525 | CLK_FREQ => CLK_FREQ 526 | ) 527 | port map 528 | ( 529 | clk => clk, 530 | rst => rst, 531 | ma_clk_fedge => ma_clk_fedge, 532 | ma_clk_redge => ma_clk_redge, 533 | ma_clk_edge => ma_clk_edge_mux(ENC_MUX_SSI), 534 | ma_clk_rst_en => ma_clk_rst_en_mux(ENC_MUX_SSI), 535 | ma_clk_rst_val => ma_clk_rst_val_mux(ENC_MUX_SSI), 536 | mosi => mosi_mux(ENC_MUX_SSI), 537 | miso => miso, 538 | gate => gate_mux(ENC_MUX_SSI), 539 | len => len, 540 | tm_match => tm_match, 541 | tm_top => tm_top_mux(ENC_MUX_SSI), 542 | count_top => count_top_mux(ENC_MUX_SSI), 543 | count_match => count_match, 544 | count_rst => count_rst_mux(ENC_MUX_SSI), 545 | sipo_val => sipo_val, 546 | sipo_latch => sipo_latch_mux(ENC_MUX_SSI), 547 | gray_to_bin_en => gray_to_bin_en_mux(ENC_MUX_SSI), 548 | lsb_to_msb_en => lsb_to_msb_en_mux(ENC_MUX_SSI), 549 | ssi_flags => ssi_flags, 550 | ssi_delay_fdiv => ssi_delay_fdiv 551 | ); 552 | end generate gen_ssi; 553 | 554 | 555 | -- 556 | -- enc_type multiplexer 557 | 558 | process 559 | ( 560 | enc_type, 561 | ma_clk_edge_mux, 562 | ma_clk_rst_en_mux, 563 | ma_clk_rst_val_mux, 564 | mosi_mux, 565 | gate_mux, 566 | tm_top_mux, 567 | count_rst_mux, 568 | count_top_mux, 569 | sipo_latch_mux, 570 | gray_to_bin_en_mux, 571 | lsb_to_msb_en_mux 572 | ) 573 | 574 | begin 575 | 576 | ma_clk_edge <= ma_clk_redge; 577 | ma_clk_rst_en <= '0'; 578 | ma_clk_rst_val <= '1'; 579 | mosi <= '0'; 580 | gate <= '0'; 581 | tm_top <= to_unsigned(DEFAULT_TM_TOP, tm_top'length); 582 | count_rst <= '0'; 583 | count_top <= (others => '0'); 584 | sipo_latch <= '0'; 585 | gray_to_bin_en <= '0'; 586 | lsb_to_msb_en <= '0'; 587 | 588 | for i in 0 to ENC_MUX_COUNT - 1 loop 589 | if enc_mux_to_type(i) = enc_type then 590 | ma_clk_edge <= ma_clk_edge_mux(i); 591 | ma_clk_rst_en <= ma_clk_rst_en_mux(i); 592 | ma_clk_rst_val <= ma_clk_rst_val_mux(i); 593 | mosi <= mosi_mux(i); 594 | gate <= gate_mux(i); 595 | tm_top <= tm_top_mux(i); 596 | count_rst <= count_rst_mux(i); 597 | count_top <= count_top_mux(i); 598 | sipo_latch <= sipo_latch_mux(i); 599 | gray_to_bin_en <= gray_to_bin_en_mux(i); 600 | lsb_to_msb_en <= lsb_to_msb_en_mux(i); 601 | end if; 602 | end loop; 603 | 604 | end process; 605 | 606 | 607 | end absenc_master_rtl; 608 | -------------------------------------------------------------------------------- /src/absenc_pkg.vhd: -------------------------------------------------------------------------------- 1 | -- 2 | -- package for absolute encoder slave and master 3 | -- supported formats: ENDAT, BISS, SSI 4 | -- 5 | -- there is a bunch of code here. a typical user is 6 | -- interested in the following 2 components: 7 | -- absenc_pkg.slave 8 | -- absenc_pkg.master 9 | -- 10 | -- refer to sim/common/main.vhd for usage. 11 | 12 | 13 | library ieee; 14 | use ieee.std_logic_1164.all; 15 | use ieee.std_logic_unsigned.all; 16 | use ieee.numeric_std.all; 17 | use ieee.math_real.all; 18 | 19 | 20 | package absenc_pkg is 21 | 22 | 23 | -- 24 | -- encoder type identifiers 25 | 26 | constant ENC_TYPE_ENDAT: integer := 0; 27 | constant ENC_TYPE_BISS: integer := 1; 28 | constant ENC_TYPE_SSI: integer := 2; 29 | constant ENC_TYPE_COUNT: integer := 3; 30 | 31 | 32 | -- 33 | -- encoder positions in mux 34 | 35 | function get_enc_mux_endat 36 | ( 37 | enable_endat: boolean; 38 | enable_biss: boolean; 39 | enable_ssi: boolean 40 | ) 41 | return integer; 42 | 43 | 44 | function get_enc_mux_biss 45 | ( 46 | enable_endat: boolean; 47 | enable_biss: boolean; 48 | enable_ssi: boolean 49 | ) 50 | return integer; 51 | 52 | 53 | function get_enc_mux_ssi 54 | ( 55 | enable_endat: boolean; 56 | enable_biss: boolean; 57 | enable_ssi: boolean 58 | ) 59 | return integer; 60 | 61 | 62 | -- 63 | -- count enabled encoders 64 | 65 | function get_enc_mux_count 66 | ( 67 | enable_endat: boolean; 68 | enable_biss: boolean; 69 | enable_ssi: boolean 70 | ) 71 | return integer; 72 | 73 | 74 | -- 75 | -- mux position to encoder type 76 | 77 | type enc_type_array_t is array(ENC_TYPE_COUNT - 1 downto 0) of integer; 78 | 79 | function gen_enc_mux_to_type 80 | ( 81 | enable_endat: boolean; 82 | enable_biss: boolean; 83 | enable_ssi: boolean 84 | ) 85 | return enc_type_array_t; 86 | 87 | 88 | -- 89 | -- compute integer length 90 | 91 | function integer_length 92 | ( 93 | i: integer 94 | ) 95 | return integer; 96 | 97 | 98 | -- 99 | -- microsecond to counter clocked by freq 100 | 101 | function us_to_count 102 | ( 103 | us: integer; 104 | freq: integer; 105 | len: integer 106 | ) 107 | return integer; 108 | 109 | 110 | -- 111 | -- default timeout us 112 | -- conditions the lowset and highest ma_clk 113 | 114 | constant MAX_TM_US: integer := 100; 115 | constant MASTER_DEFAULT_TM_US: integer := 60; 116 | constant SLAVE_DEFAULT_TM_US: integer := 20; 117 | 118 | 119 | -- 120 | -- default ssi related values 121 | -- no spy mode 122 | -- binary coding 123 | -- no special terminating pattern 124 | -- no delay 125 | 126 | constant SSI_DEFAULT_FLAGS: std_logic_vector := "00000"; 127 | constant SSI_DEFAULT_DELAY_FDIV: unsigned(0 downto 0) := to_unsigned(0, 1); 128 | 129 | 130 | -- 131 | -- encoder slave interface 132 | 133 | component slave 134 | generic 135 | ( 136 | -- local clock frequency 137 | CLK_FREQ: integer; 138 | 139 | -- enable a specific implementation, or all by default 140 | -- disabling unneeded implementations optimizes resources 141 | ENABLE_ENDAT: boolean := TRUE; 142 | ENABLE_BISS: boolean := TRUE; 143 | ENABLE_SSI: boolean := TRUE 144 | ); 145 | port 146 | ( 147 | -- local clock and reset 148 | clk: in std_logic; 149 | rst: in std_logic; 150 | 151 | -- clock from master 152 | ma_clk: in std_logic; 153 | 154 | -- master in, slave out 155 | miso: out std_logic; 156 | mosi: in std_logic; 157 | 158 | -- gate to drive output (1 to drive it). used in PEPU 159 | -- first versions, set to open if not used. 160 | gate: out std_logic; 161 | 162 | -- data sent to master. typically the position 163 | -- important: use the smallest possible width, as 164 | -- this is used to derive other resource sizes 165 | data: in std_logic_vector; 166 | 167 | -- data length. typically the encoder resolution 168 | -- important: use the smallest possible width, as 169 | -- this is used to derive other resource sizes 170 | len: in unsigned; 171 | 172 | -- the selected encoder type 173 | enc_type: in integer; 174 | 175 | -- ssi specific flags 176 | -- set to work.absenc_pkg.SSI_DEFAULT_FLAGS if unused 177 | -- ssi_flags<0>: unused 178 | -- ssi_flags<1>: 0 or 1 for binary or gray data coding 179 | -- ssi_flags<2>: '.S' terminating pattern 180 | -- ssi_flags<3>: 'ES' terminating pattern 181 | -- ssi_flags<4>: 'OS' terminating pattern 182 | ssi_flags: in std_logic_vector 183 | ); 184 | end component; 185 | 186 | 187 | component slave_endat 188 | generic 189 | ( 190 | CLK_FREQ: integer 191 | ); 192 | port 193 | ( 194 | -- local clock 195 | clk: in std_logic; 196 | rst: in std_logic; 197 | 198 | -- master clock edges 199 | ma_clk_redge: in std_logic; 200 | ma_clk_fedge: in std_logic; 201 | 202 | -- the edge we are interested in 203 | ma_clk_edge: out std_logic; 204 | 205 | -- master in, slave out 206 | miso: out std_logic; 207 | mosi: in std_logic; 208 | 209 | -- gate to drive output (1 to drive it) 210 | gate: out std_logic; 211 | 212 | -- actual data to send and length 213 | data: in std_logic_vector; 214 | len: in unsigned; 215 | 216 | -- timeout counter 217 | tm_match: in std_logic; 218 | tm_top: out unsigned; 219 | 220 | -- general purpose counter 221 | count_top: out unsigned; 222 | count_match: in std_logic; 223 | count_rst: out std_logic; 224 | 225 | -- piso register 226 | piso_rval: in std_logic_vector; 227 | piso_lval: in std_logic_vector; 228 | piso_ini: out std_logic_vector; 229 | piso_load: out std_logic 230 | ); 231 | end component; 232 | 233 | 234 | component slave_biss 235 | generic 236 | ( 237 | CLK_FREQ: integer 238 | ); 239 | port 240 | ( 241 | -- local clock 242 | clk: in std_logic; 243 | rst: in std_logic; 244 | 245 | -- master clock edges 246 | ma_clk_redge: in std_logic; 247 | ma_clk_fedge: in std_logic; 248 | 249 | -- the edge we are interested in 250 | ma_clk_edge: out std_logic; 251 | 252 | -- master in, slave out 253 | miso: out std_logic; 254 | mosi: in std_logic; 255 | 256 | -- gate to drive output (1 to drive it) 257 | gate: out std_logic; 258 | 259 | -- actual data to send and length 260 | data: in std_logic_vector; 261 | len: in unsigned; 262 | 263 | -- timeout counter 264 | tm_match: in std_logic; 265 | tm_top: out unsigned; 266 | 267 | -- general purpose counter 268 | count_top: out unsigned; 269 | count_match: in std_logic; 270 | count_rst: out std_logic; 271 | 272 | -- piso register 273 | piso_rval: in std_logic_vector; 274 | piso_lval: in std_logic_vector; 275 | piso_ini: out std_logic_vector; 276 | piso_load: out std_logic 277 | ); 278 | end component; 279 | 280 | 281 | component slave_ssi 282 | generic 283 | ( 284 | CLK_FREQ: integer 285 | ); 286 | port 287 | ( 288 | -- local clock 289 | clk: in std_logic; 290 | rst: in std_logic; 291 | 292 | -- master clock edges 293 | ma_clk_redge: in std_logic; 294 | ma_clk_fedge: in std_logic; 295 | 296 | -- the edge we are interested in 297 | ma_clk_edge: out std_logic; 298 | 299 | -- master in, slave out 300 | miso: out std_logic; 301 | mosi: in std_logic; 302 | 303 | -- gate to drive output (1 to drive it) 304 | gate: out std_logic; 305 | 306 | -- actual data to send and length 307 | data: in std_logic_vector; 308 | len: in unsigned; 309 | 310 | -- timeout counter 311 | tm_match: in std_logic; 312 | tm_top: out unsigned; 313 | 314 | -- general purpose counter 315 | count_top: out unsigned; 316 | count_match: in std_logic; 317 | count_rst: out std_logic; 318 | 319 | -- piso register 320 | piso_rval: in std_logic_vector; 321 | piso_lval: in std_logic_vector; 322 | piso_ini: out std_logic_vector; 323 | piso_load: out std_logic; 324 | 325 | -- refer to absenc_pkg.slave for comments 326 | ssi_flags: in std_logic_vector 327 | ); 328 | end component; 329 | 330 | 331 | -- 332 | -- encoder master interface 333 | 334 | component master 335 | generic 336 | ( 337 | -- local clock frequency 338 | CLK_FREQ: integer; 339 | 340 | -- enable a specific implementation, or all by default 341 | -- disabling unneeded implementations optimizes resources 342 | ENABLE_ENDAT: boolean := TRUE; 343 | ENABLE_BISS: boolean := TRUE; 344 | ENABLE_SSI: boolean := TRUE 345 | ); 346 | port 347 | ( 348 | -- local clock and reset 349 | clk: in std_logic; 350 | rst: in std_logic; 351 | 352 | -- ma_clk is the clock signal generated by the master 353 | -- to the slave. its frequency is CLK_FREQ / ma_fdiv 354 | ma_fdiv: in unsigned; 355 | ma_clk: out std_logic; 356 | 357 | -- master out, slave in 358 | mosi: out std_logic; 359 | miso: in std_logic; 360 | 361 | -- gate to drive output (1 to drive it). used in PEPU 362 | -- first versions, set to open if not used. 363 | gate: out std_logic; 364 | 365 | -- data received from slave. typically the position 366 | -- important: use the smallest possible width, as 367 | -- this is used to derive other resource sizes 368 | data: out std_logic_vector; 369 | 370 | -- data length. typically the encoder resolution 371 | -- important: use the smallest possible width, as 372 | -- this is used to derive other resource sizes 373 | len: in unsigned; 374 | 375 | -- the selected encoder type 376 | enc_type: in integer; 377 | 378 | -- ssi specific flags 379 | -- set to work.absenc_pkg.SSI_DEFAULT_FLAGS if unused 380 | -- ssi_flags<0>: ssi spy mode (ie. master without clock) 381 | -- ssi_flags<1>: 0 or 1 for binary or gray data coding 382 | -- ssi_flags<2>: '.S' terminating pattern 383 | -- ssi_flags<3>: 'ES' terminating pattern 384 | -- ssi_flags<4>: 'OS' terminating pattern 385 | ssi_flags: in std_logic_vector; 386 | 387 | -- ssi frame delay. divides CLK_FREQ 388 | -- set to work.absenc_pkg.SSI_DEFAULT_DELAY_FDIV if unsued 389 | ssi_delay_fdiv: in unsigned 390 | ); 391 | end component; 392 | 393 | 394 | component master_endat 395 | generic 396 | ( 397 | CLK_FREQ: integer 398 | ); 399 | port 400 | ( 401 | -- local clock 402 | clk: in std_logic; 403 | rst: in std_logic; 404 | 405 | -- master clock edges 406 | ma_clk_fedge: in std_logic; 407 | ma_clk_redge: in std_logic; 408 | 409 | -- the edge we are interested in 410 | ma_clk_edge: out std_logic; 411 | 412 | -- master clock reset 413 | -- if ma_clk_rst_en, use ma_clk_rst_level 414 | ma_clk_rst_en: out std_logic; 415 | ma_clk_rst_val: out std_logic; 416 | 417 | -- master out, slave in 418 | mosi: out std_logic; 419 | miso: in std_logic; 420 | 421 | -- gate to drive output (1 to drive it) 422 | gate: out std_logic; 423 | 424 | -- desired data length 425 | len: in unsigned; 426 | 427 | -- timeout counter 428 | tm_match: in std_logic; 429 | tm_top: out unsigned; 430 | 431 | -- general purpose counter 432 | count_top: out unsigned; 433 | count_match: in std_logic; 434 | count_rst: out std_logic; 435 | 436 | -- sipo register 437 | sipo_val: in std_logic_vector; 438 | sipo_latch: out std_logic; 439 | 440 | -- enable data conversion stages 441 | gray_to_bin_en: out std_logic; 442 | lsb_to_msb_en: out std_logic 443 | ); 444 | end component; 445 | 446 | 447 | component master_biss 448 | generic 449 | ( 450 | CLK_FREQ: integer 451 | ); 452 | port 453 | ( 454 | -- local clock 455 | clk: in std_logic; 456 | rst: in std_logic; 457 | 458 | -- master clock edges 459 | ma_clk_fedge: in std_logic; 460 | ma_clk_redge: in std_logic; 461 | 462 | -- the edge we are interested in 463 | ma_clk_edge: out std_logic; 464 | 465 | -- master clock reset 466 | -- if ma_clk_rst_en, use ma_clk_rst_level 467 | ma_clk_rst_en: out std_logic; 468 | ma_clk_rst_val: out std_logic; 469 | 470 | -- master out, slave in 471 | mosi: out std_logic; 472 | miso: in std_logic; 473 | 474 | -- gate to drive output (1 to drive it) 475 | gate: out std_logic; 476 | 477 | -- desired data length 478 | len: in unsigned; 479 | 480 | -- timeout counter 481 | tm_match: in std_logic; 482 | tm_top: out unsigned; 483 | 484 | -- general purpose counter 485 | count_top: out unsigned; 486 | count_match: in std_logic; 487 | count_rst: out std_logic; 488 | 489 | -- sipo register 490 | sipo_val: in std_logic_vector; 491 | sipo_latch: out std_logic; 492 | 493 | -- enable data conversion stages 494 | gray_to_bin_en: out std_logic; 495 | lsb_to_msb_en: out std_logic 496 | ); 497 | end component; 498 | 499 | 500 | component master_ssi 501 | generic 502 | ( 503 | CLK_FREQ: integer 504 | ); 505 | port 506 | ( 507 | -- local clock 508 | clk: in std_logic; 509 | rst: in std_logic; 510 | 511 | -- master clock edges 512 | ma_clk_fedge: in std_logic; 513 | ma_clk_redge: in std_logic; 514 | 515 | -- the edge we are interested in 516 | ma_clk_edge: out std_logic; 517 | 518 | -- master clock reset 519 | -- if ma_clk_rst_en, use ma_clk_rst_level 520 | ma_clk_rst_en: out std_logic; 521 | ma_clk_rst_val: out std_logic; 522 | 523 | -- master out, slave in 524 | mosi: out std_logic; 525 | miso: in std_logic; 526 | 527 | -- gate to drive output (1 to drive it) 528 | gate: out std_logic; 529 | 530 | -- desired data length 531 | len: in unsigned; 532 | 533 | -- timeout counter 534 | tm_match: in std_logic; 535 | tm_top: out unsigned; 536 | 537 | -- general purpose counter 538 | count_top: out unsigned; 539 | count_match: in std_logic; 540 | count_rst: out std_logic; 541 | 542 | -- sipo register 543 | sipo_val: in std_logic_vector; 544 | sipo_latch: out std_logic; 545 | 546 | -- enable data conversion stages 547 | gray_to_bin_en: out std_logic; 548 | lsb_to_msb_en: out std_logic; 549 | 550 | -- refer to absenc_pkg.master for comments 551 | ssi_flags: in std_logic_vector; 552 | ssi_delay_fdiv: in unsigned 553 | ); 554 | end component; 555 | 556 | 557 | component reader_hssl is 558 | generic 559 | ( 560 | CLK_FREQ: integer 561 | ); 562 | port 563 | ( 564 | -- local clock 565 | clk: in std_logic; 566 | rst: in std_logic; 567 | 568 | -- sender clock 569 | sclk: in std_logic; 570 | 571 | -- sender out, reader in 572 | sori: in std_logic; 573 | 574 | -- actual data to send and length 575 | data: out std_logic_vector; 576 | 577 | -- configuration 578 | len: in unsigned; 579 | tm_gap: in unsigned 580 | ); 581 | 582 | end component; 583 | 584 | 585 | -- 586 | -- utilities 587 | 588 | component len_to_mask 589 | port 590 | ( 591 | len: in unsigned; 592 | mask: out std_logic_vector 593 | ); 594 | end component; 595 | 596 | 597 | component lsb_to_msb 598 | port 599 | ( 600 | en: in std_logic; 601 | data_len: in unsigned; 602 | lsb_data: in std_logic_vector; 603 | msb_data: out std_logic_vector 604 | ); 605 | end component; 606 | 607 | 608 | component bin_to_gray 609 | port 610 | ( 611 | en: in std_logic; 612 | bin_data: in std_logic_vector; 613 | gray_data: out std_logic_vector 614 | ); 615 | end component; 616 | 617 | 618 | component gray_to_bin 619 | port 620 | ( 621 | en: in std_logic; 622 | gray_data: in std_logic_vector; 623 | bin_data: out std_logic_vector 624 | ); 625 | end component; 626 | 627 | 628 | component extend_sign 629 | port 630 | ( 631 | data_len: in unsigned; 632 | data_in: in std_logic_vector; 633 | data_out: out std_logic_vector; 634 | len_mask: in std_logic_vector 635 | ); 636 | end component; 637 | 638 | 639 | end package absenc_pkg; 640 | 641 | 642 | package body absenc_pkg is 643 | 644 | -- 645 | -- encoder positions in mux 646 | 647 | function get_enc_mux_endat 648 | ( 649 | enable_endat: boolean; 650 | enable_biss: boolean; 651 | enable_ssi: boolean 652 | ) 653 | return integer is 654 | begin 655 | return 0; 656 | end get_enc_mux_endat; 657 | 658 | 659 | function get_enc_mux_biss 660 | ( 661 | enable_endat: boolean; 662 | enable_biss: boolean; 663 | enable_ssi: boolean 664 | ) 665 | return integer is 666 | variable i: integer; 667 | begin 668 | i := 0; 669 | if enable_endat = TRUE then i := i + 1; end if; 670 | return i; 671 | end get_enc_mux_biss; 672 | 673 | 674 | function get_enc_mux_ssi 675 | ( 676 | enable_endat: boolean; 677 | enable_biss: boolean; 678 | enable_ssi: boolean 679 | ) 680 | return integer is 681 | variable i: integer; 682 | begin 683 | i := 0; 684 | if enable_endat = TRUE then i := i + 1; end if; 685 | if enable_biss = TRUE then i := i + 1; end if; 686 | return i; 687 | end get_enc_mux_ssi; 688 | 689 | 690 | -- 691 | -- count enabled encoders 692 | 693 | function get_enc_mux_count 694 | ( 695 | enable_endat: boolean; 696 | enable_biss: boolean; 697 | enable_ssi: boolean 698 | ) 699 | return integer is 700 | variable n: integer; 701 | begin 702 | n := 0; 703 | if enable_endat = TRUE then n := n + 1; end if; 704 | if enable_biss = TRUE then n := n + 1; end if; 705 | if enable_ssi = TRUE then n := n + 1; end if; 706 | return n; 707 | end get_enc_mux_count; 708 | 709 | 710 | -- 711 | -- mux position to encoder type 712 | 713 | function gen_enc_mux_to_type 714 | ( 715 | enable_endat: boolean; 716 | enable_biss: boolean; 717 | enable_ssi: boolean 718 | ) 719 | return enc_type_array_t is 720 | variable a: enc_type_array_t; 721 | variable i: integer; 722 | begin 723 | 724 | i := 0; 725 | 726 | if enable_endat = TRUE then 727 | a(i) := ENC_TYPE_ENDAT; 728 | i := i + 1; 729 | end if; 730 | 731 | if enable_biss = TRUE then 732 | a(i) := ENC_TYPE_BISS; 733 | i := i + 1; 734 | end if; 735 | 736 | if enable_ssi = TRUE then 737 | a(i) := ENC_TYPE_SSI; 738 | i := i + 1; 739 | end if; 740 | 741 | return a; 742 | end gen_enc_mux_to_type; 743 | 744 | 745 | -- 746 | -- compute integer length 747 | 748 | function integer_length 749 | ( 750 | i: integer 751 | ) 752 | return integer is 753 | begin 754 | return integer(ceil(log2(real(i)))); 755 | end integer_length; 756 | 757 | 758 | -- 759 | -- microsecond to counter clocked by freq 760 | 761 | function us_to_count 762 | ( 763 | us: integer; 764 | freq: integer; 765 | len: integer 766 | ) 767 | return integer is 768 | variable count: integer; 769 | begin 770 | count := integer(ceil(real(us) * real(freq) / 1000000.0)); 771 | assert (integer_length(count) <= len) report "tm too high" severity failure; 772 | return count; 773 | end us_to_count; 774 | 775 | 776 | end package body absenc_pkg; 777 | --------------------------------------------------------------------------------