-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathpsi_common_delay_cfg.vhd
151 lines (137 loc) · 6.02 KB
/
psi_common_delay_cfg.vhd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
------------------------------------------------------------------------------
-- Copyright (c) 2018 by Paul Scherrer Institute, Switzerland
-- All rights reserved.
-- Authors: Oliver Bruendler
-- Benoit Stef
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- Description
------------------------------------------------------------------------------
-- This is a delay element. It is either implemented in BRAM & SRL. The output
-- is always a fabric register for improved timing.
-- The delay is settable by a register and not fixed as the psi_common_delay
-- One can choose to hold last value when a delay increase is requested via
-- generic
-- NB: when a delay decrease is requested it takes 3 clock cycles to be valid
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.psi_common_math_pkg.all;
--@formatter:off
entity psi_common_delay_cfg is
generic(width_g : positive := 16; -- data vector width
max_delay_g : positive := 256; -- maximum delay wanted
rst_pol_g : std_logic := '1'; -- reset polarity
ram_behavior_g : string := "RBW"; -- "RBW" = read-before-write, "WBR" = write-before-read
hold_g : boolean := true); -- Holding value at output when delay increase is performed
port( clk_i : in std_logic; -- system clock
rst_i : in std_logic; -- system reset
dat_i : in std_logic_vector(width_g - 1 downto 0); -- data input
vld_i : in std_logic; -- valid/strobe signal input
del_i : in std_logic_vector(log2ceil(max_delay_g) - 1 downto 0); -- delay parameter input
dat_o : out std_logic_vector((width_g - 1) downto 0); -- data output
vld_o : out std_logic);
end entity;
--formatter:on
architecture rtl of psi_common_delay_cfg is
type srl_t is array (0 to 2) of std_logic_vector(width_g - 1 downto 0);
signal srl_s : srl_t := (others => (others => '0'));
signal mem_out_s : std_logic_vector(width_g - 1 downto 0);
signal rd_addr_s, wr_addr_s : std_logic_vector(log2ceil(max_delay_g) - 1 downto 0) := (others => '0');
signal mem_out2_s : std_logic_vector(width_g - 1 downto 0);
signal del_dff_s : std_logic_vector(del_i'range);
signal latch_count_s : unsigned(del_i'range) := (others => '0');
signal diff_s : unsigned(del_i'range) := (others => '0');
signal rs_s : std_logic;
constant ground_c : std_logic := '0';
begin
--*** address control process ***
p_bram : process(clk_i)
begin
if rising_edge(clk_i) then
if rst_i = rst_pol_g then
wr_addr_s <= (others => '0');
rd_addr_s <= (others => '0');
del_dff_s <= (others => '0');
latch_count_s <= (others => '0');
rs_s <= '0';
elsif vld_i = '1' then
del_dff_s <= del_i;
if from_uslv(del_i) <= 3 then
wr_addr_s <= (others => '0');
rd_addr_s <= (others => '0');
else
--*** write address mngt ***
wr_addr_s <= std_logic_vector(unsigned(wr_addr_s) + 1);
--*** read address mngt ***
if (rs_s = '1' or del_dff_s < del_i) and hold_g then
rd_addr_s <= rd_addr_s;
else
rd_addr_s <= std_logic_vector(unsigned(wr_addr_s) - unsigned(del_i) + 3);
end if;
--*** RS latch & counter for hold mode ***
if del_dff_s < del_i then
rs_s <= '1';
diff_s <= unsigned(del_i) - unsigned(del_dff_s);
latch_count_s <= (others => '0');
elsif latch_count_s = diff_s - 2 then
rs_s <= '0';
end if;
if rs_s = '1' then
latch_count_s <= latch_count_s + 1;
end if;
end if;
end if;
end if;
end process;
--*** memory instantiation ***
i_bram : entity work.psi_common_sdp_ram
generic map(
depth_g => 2**log2ceil(max_delay_g),
width_g => width_g,
is_async_g => false,
ram_style_g => "auto",
ram_behavior_g => ram_behavior_g)
port map(-- @suppress "Port map uses default values. Missing optional actuals: RdClk"
wr_clk_i => clk_i,
wr_addr_i => wr_addr_s,
wr_i => vld_i,
wr_dat_i => dat_i,
rd_clk_i => ground_c,
rd_addr_i => rd_addr_s,
rd_i => vld_i,
rd_dat_o => mem_out2_s);
--*** case where the delay change below 3 -> using SRL on the fly ***
p_srl : process(clk_i)
begin
if rising_edge(clk_i) then
if vld_i = '1' then
srl_s(0) <= dat_i;
srl_s(1 to srl_s'high) <= srl_s(0 to srl_s'high - 1);
end if;
end if;
end process;
-- take the output of a SRL instead --
mem_out_s <= dat_i when from_uslv(del_i) = 1
else srl_s(0) when from_uslv(del_i) = 2
else srl_s(1) when from_uslv(del_i) = 3
else mem_out2_s when from_uslv(del_i) > 3
else dat_i;
-- *** Single Stage ***
g_single : if max_delay_g = 1 generate
mem_out_s <= dat_i;
end generate;
-- *** Output register ***
p_outreg : process(clk_i)
begin
if rising_edge(clk_i) then
if rst_i = rst_pol_g then
dat_o <= (others => '0');
vld_o <= '0';
elsif vld_i = '1' then
dat_o <= mem_out_s;
vld_o <= '1';
end if;
end if;
end process;
end architecture;