-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathgc_comparator.vhd
187 lines (159 loc) · 6.86 KB
/
gc_comparator.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
--------------------------------------------------------------------------------
-- CERN (BE-CO-HT)
-- General-Purpose Comparator
-- https://www.ohwr.org/projects/general-cores
--------------------------------------------------------------------------------
--
-- unit name: gc_comparator
--
-- description:
--
-- This unit implements a general-purpose comparator with optional hysteresis.
-- The unit offers two outputs, one is the "traditional" comparator output
-- (output is high as long as positive input is greater than negative input),
-- while the other one is a monostable (one clock tick long) output.
--
-- Inputs have configurable width and they will be treated as signed vectors
-- internally. The Hysteresis input, if used, will be treated as an unsigned
-- vector.
--
-- A "polarity inversion" input bit is also available to make the comparator
-- output active when the positive input is below the negative input. If not
-- used, this input defaults to "non-inverted".
--
-- A "comparator enable" bit is also available to disable the outputs of the
-- comparator. This is useful when changing settings (threshold, polarity) to
-- avoid glitches on the outputs.
--
-- Pleas note that after reset (or after re-enabling the input), the unit will
-- wait for the positive input to drop below (above if inverted) the negative
-- input before performing any comparisons. This is done to avoid output
-- glitches when switching on and/or when changing settings.
--
--------------------------------------------------------------------------------
-- Copyright (c) 2017 CERN / BE-CO-HT
--------------------------------------------------------------------------------
-- GNU LESSER GENERAL PUBLIC LICENSE
--------------------------------------------------------------------------------
-- This source file is free software; you can redistribute it and/or modify it
-- under the terms of the GNU Lesser General Public License as published by the
-- Free Software Foundation; either version 2.1 of the License, or (at your
-- option) any later version. This source is distributed in the hope that it
-- will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
-- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-- See the GNU Lesser General Public License for more details. You should have
-- received a copy of the GNU Lesser General Public License along with this
-- source; if not, download it from http://www.gnu.org/licenses/lgpl-2.1.html
--------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity gc_comparator is
generic (
-- Width of input vectors (in bits).
g_IN_WIDTH : natural := 32);
port (
clk_i : in std_logic;
rst_n_i : in std_logic := '1';
-- Polarity inversion
pol_inv_i : in std_logic := '0';
-- Comparator enable
enable_i : in std_logic := '1';
-- Positive input (treated as signed)
inp_i : in std_logic_vector(g_IN_WIDTH-1 downto 0);
-- Negative input (treated as signed)
inn_i : in std_logic_vector(g_IN_WIDTH-1 downto 0);
-- Hysteresis (treated as unsigned)
hys_i : in std_logic_vector(g_IN_WIDTH-1 downto 0) := (others => '0');
-- Comparator output
out_o : out std_logic;
out_p_o : out std_logic);
end entity gc_comparator;
architecture arch of gc_comparator is
type t_FSM_STATE is (S_IDLE, S_BELOW_THRES, S_ABOVE_THRES1, S_ABOVE_THRES2);
signal fsm_regin, fsm_regout : t_FSM_STATE := S_IDLE;
signal inp_signx : signed(g_IN_WIDTH downto 0) := (others => '0');
signal inn_signx : signed(g_IN_WIDTH downto 0) := (others => '0');
signal hys_signx : signed(g_IN_WIDTH downto 0) := (others => '0');
signal u_thres : signed(g_IN_WIDTH downto 0) := (others => '0');
signal l_thres : signed(g_IN_WIDTH downto 0) := (others => '0');
signal l_below : std_logic := '0';
signal u_above : std_logic := '0';
signal thr_cross : std_logic := '0';
signal hys_cross : std_logic := '0';
signal comp_out : std_logic := '0';
signal comp_out_p : std_logic := '0';
begin -- architecture arch
-- Convert inputs to signed and perform comparisons, taking into account
-- optional hysteresis and polarity inversion. See also:
-- https://en.wikipedia.org/wiki/Comparator#Hysteresis
inp_signx <= resize(signed(inp_i), g_IN_WIDTH+1);
inn_signx <= resize(signed(inn_i), g_IN_WIDTH+1);
hys_signx <= signed('0' & hys_i);
u_thres <= inn_signx when pol_inv_i = '0' else inn_signx + hys_signx;
l_thres <= inn_signx when pol_inv_i = '1' else inn_signx - hys_signx;
u_above <= '1' when inp_signx > u_thres else '0';
l_below <= '1' when inp_signx < l_thres else '0';
thr_cross <= u_above when pol_inv_i = '0' else l_below;
hys_cross <= l_below when pol_inv_i = '0' else u_above;
p_fsm_seq : process (clk_i) is
begin
if rising_edge(clk_i) then
if rst_n_i = '0' or enable_i = '0' then
fsm_regout <= S_IDLE;
else
fsm_regout <= fsm_regin;
end if;
end if;
end process p_fsm_seq;
p_fsm_comb : process (fsm_regout, hys_cross, thr_cross) is
-- Variable to hold changes to our FSM register
-- within the current clock cycle.
variable v_fsm_reg : t_FSM_STATE := S_IDLE;
begin
-- Default values for FSM combinatorial outputs. Overriden
-- when necessary by each state.
comp_out <= '0';
comp_out_p <= '0';
-- First load register values from previous cycle.
v_fsm_reg := fsm_regout;
case v_fsm_reg is
-- We can only end here after a reset or enable_i = '0'.
-- Wait for input to drop below lower threshold
-- before we start tracking it.
when S_IDLE =>
if hys_cross = '1' then
v_fsm_reg := S_BELOW_THRES;
end if;
-- Signal is below lower threshold.
when S_BELOW_THRES =>
if thr_cross = '1' then
v_fsm_reg := S_ABOVE_THRES1;
end if;
-- Signal is above upper threshold, pulse the
-- monostable output and change state.
when S_ABOVE_THRES1 =>
comp_out <= '1';
comp_out_p <= '1';
if hys_cross = '1' then
v_fsm_reg := S_BELOW_THRES;
else
v_fsm_reg := S_ABOVE_THRES2;
end if;
-- Signal is still below threshold, wait for it.
when S_ABOVE_THRES2 =>
comp_out <= '1';
if hys_cross = '1' then
v_fsm_reg := S_BELOW_THRES;
end if;
-- Re-init the FSM if something unexpected happens.
when others =>
v_fsm_reg := S_IDLE;
end case;
-- Last step, update register values for next cycle
fsm_regin <= v_fsm_reg;
end process p_fsm_comb;
-- Assign comparator outputs
out_o <= comp_out;
out_p_o <= comp_out_p;
end architecture arch;