VHDL short form to trigger actions on raising edges

非 Y 不嫁゛ 提交于 2019-12-05 05:05:42

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!