问题
I wonder if there is a shorter way to trigger on signal edges that are not the clock.
Consider the following example:
signal clock : std_logic;
signal ready : std_logic; -- comes from some slow component
signal last_ready : std_logic;
signal some_rare_condition : std_logic;
----------------------------------
process (clock) is
begin
if rising_edge (clock) then
if (some_rare_condition = '1') then
if (ready = '1') and (last_ready = '0') then
-- do something here, writing data to UART for example.
end if;
last_ready <= ready;
end if;
end if;
end process;
Here I want to do something if the signal 'ready' got a raising edge. The raising edge should only be evaluated if some_rare_condition is true.
I currently just remember the last state of the ready signal in a latch and build the edge detection logic myself.
Question: Is there a shorter, more elegant way to do this?
The way I do it right works just fine, but I litter up my code with all these last_ready signals. This seem to be such a common paradigm that I think I miss some language or library construct that helps me to keep my code clean and lean.
回答1:
You can write a rising or falling edge detection in two lines:
- a simple D-FF to register the old signal
- a comparison for the rising edge
Example code:
signal MMCM_Locked : STD_LOGIC;
signal MMCM_Locked_d : STD_LOGIC := '0';
signal MMCM_Locked_re : STD_LOGIC;
-- detect rising edge on CMB locked signals
MMCM_Locked_d <= MMCM_Locked when rising_edge(Control_Clock);
MMCM_Locked_re <= not MMCM_Locked_d and MMCM_Locked;
Edit 1
Of cause, you can also add an enable to this one-liner D-FF by defining some FF functions (this is still synthesizeable !).
-- d-flipflop with reset and enable
function ffdre(q : STD_LOGIC; d : STD_LOGIC; rst : STD_LOGIC := '0'; en : STD_LOGIC := '1') return STD_LOGIC is
begin
return ((d and en) or (q and not en)) and not rst;
end function;
function ffdre(q : STD_LOGIC_VECTOR; d : STD_LOGIC_VECTOR; rst : STD_LOGIC := '0'; en : STD_LOGIC := '1') return STD_LOGIC_VECTOR is
begin
return ((d and (q'range => en)) or (q and not (q'range => en))) and not (q'range => rst);
end function;
-- d-flipflop with set and enable
function ffdse(q : STD_LOGIC; d : STD_LOGIC; set : STD_LOGIC := '0'; en : STD_LOGIC := '1') return STD_LOGIC is
begin
return ((d and en) or (q and not en)) or set;
end function;
-- t-flipflop with reset and enable
function fftre(q : STD_LOGIC; rst : STD_LOGIC := '0'; en : STD_LOGIC := '1') return STD_LOGIC is
begin
return ((not q and en) or (q and not en)) and not rst;
end function;
-- rs-flipflop with dominant rst
function ffrs(q : STD_LOGIC; rst : STD_LOGIC := '0'; set : STD_LOGIC := '0') return STD_LOGIC is
begin
return (q or set) and not rst;
end function;
-- rs-flipflop with dominant set
function ffsr(q : STD_LOGIC; rst : STD_LOGIC := '0'; set : STD_LOGIC := '0') return STD_LOGIC is
begin
return (q and not rst) or set;
end function;
Example:
mySignal_d <= ffdre(q => mySignal_d, d => mySignal, en => myEnable) when rising_edge(Clock);
回答2:
I'm not aware of any synthesizable language construct that accomplishes what you're looking for that doesn't involve explicitly declaring a register and doing the edge check yourself in one way or another.
Among things one might think to try:
if rising_edge(clk) then
if some_rare_condition = '1' then
if rising_edge(ready) then
...
Not only will this (probably?) not synthesize, but to make it all the way through the rising_edge(ready) check, the events would have to be exactly simultaneous (in sim, down to the delta cycle, which would probably be extremely inconvenient).
Or maybe:
if ready = '1' and ready'last_value = '0' and ready'last_event < CLK_PERIOD then
Kind of unwieldy, not synthesizable (more or less works in sim, though), and not very portable because of the dependence on CLK_PERIOD (though there may be a more elegant way to use this sort of construct that I'm not thinking of).
You're only using one extra signal declaration and assignment, which is not that much extra code, but if you really want to reduce that, you could use a small reusable component:
ready_edge : edge_detect
port map (
clk => clock,
ena => some_rare_condition,
sig => ready,
edge => ready_edge
);
process (clock)
begin
if rising_edge(clock) then
if ready_edge = '1' then
...
Add some generics to cover slight variations. Slightly more code, but fewer signals, if that's what you're going for. (edit: well, not actually fewer signals I suppose, but at least it's all wrapped up neatly)
回答3:
If VHDL functions allowed inout parameters, you could ease the pain a little:
impure function first_time(ready : in std_logic; last : inout std_logic)
return boolean is
begin
last <= ready;
return ready = '1' and last = '0';
end first_time;
Unfortunately they don't.
If you only need edge detection on one signal, an impure function can modify state (signals as here, or a variable declared in the same process as this function) the following should work :
impure function first_ready return boolean is
begin
last_ready <= ready;
return ready = '1' and last_ready = '0';
end first_ready;
...
if first_ready then
do_something ...
end if;
although some synthesis tools may not accept it.
In any case it's not very scalable, you need a separate function and last_xxx signal for each signal you need to edge_detect on, though you can reuse each edge detector as many times as you need it within the process.
来源:https://stackoverflow.com/questions/27229600/vhdl-short-form-to-trigger-actions-on-raising-edges