Please consider the following (simplified) testcase:
library ieee;
use ieee.std_logic_1164.all;
entity testcase3 is
generic (
edge : std_logic := '1'
);
port (
clk : in std_logic;
D : in std_logic;
Q : out std_logic
);
end testcase3;
architecture behavior of testcase3 is
begin
tc3: process(clk)
begin
if (clk'event and clk=edge) then
Q <= D;
end if;
end process;
end behavior;
During simulation GHDL does not complain about this construct, but when running:
ghdl --synth testcase3.vhd -e testcase3
the folllowing error is reported:
testcase3.vhd:20:30:error: ill-formed clock-level, '0' or '1' expected
Naive question: is such a construct portable ? Is it accepted by synthesizers ?
I will test it at work with Synplify.
EDIT: Synthesis with Synplify works. You get a DFF with positive or negated clock input depending on the generic default value.
Both expressions (clk'event and clk='1') and (clk'event and clk='0') are similar to rising_edge(clk) and falling_edge(clk). It's correct for synthesis.
Most generic solutions I've seen are using an XOR on the clock like, which translates after optimization with a constant/generic to a NOT gate. Then the synthesizer translates a 180掳 phase shifted clock or a falling_edge to a FF with enabled clock inverter.
The testcase also synthesizes fine with Xilinx ISE and something very similar (with a std_logic generic determining the sensitive edge) was also synthesized with Cadence Genus. Unfortunately I do not have access to that tool myself otherwise I could verify the testcase as well.
For what it's worth, clk'event and clk='1' is not the same as rising_edge(clk).
The use of former is greatly discouraged, as it can lead to undesired behavior. It is too simple, in that all it cares about is that the new value on clk (after the triggering event) is '1'. Consider the following (possibly pathological) cases:
clk was 'H' and a driver turns on and now it's '1'. clk'event and clk='1' evaluates to true in this case, and that's (probably) not was expected.
clk was '0' and the driver stops driving and a pull-up pulls the signal to 'H'. Our comparison evaluates to false, and that's not what was expected. OK, it's what you expected because you wrote clk='1' but you see what I mean.
rising_edge() solves these issues by calling To_X01(clk) as well as checking to make sure the previous value was '0' with To_X01(clk'last_value) = '0'. In other words, it ensures that a rising edge really occurred on the signal.
Synthesizers still recognize the old idiom and use it to infer an edge-triggered flip-flop, so from that perspective it's the same as the function. But for modeling they aren't the same. (It would be nice if vendor-provided example code and templates recognized that we live in 2020 and not 1987 and used the function.)
Most helpful comment
For what it's worth,
clk'event and clk='1'is not the same asrising_edge(clk).The use of former is greatly discouraged, as it can lead to undesired behavior. It is too simple, in that all it cares about is that the new value on
clk(after the triggering event) is'1'. Consider the following (possibly pathological) cases:clkwas'H'and a driver turns on and now it's'1'.clk'event and clk='1'evaluates to true in this case, and that's (probably) not was expected.clkwas'0'and the driver stops driving and a pull-up pulls the signal to'H'. Our comparison evaluates to false, and that's not what was expected. OK, it's what you expected because you wroteclk='1'but you see what I mean.rising_edge()solves these issues by callingTo_X01(clk)as well as checking to make sure the previous value was'0'withTo_X01(clk'last_value) = '0'. In other words, it ensures that a rising edge really occurred on the signal.Synthesizers still recognize the old idiom and use it to infer an edge-triggered flip-flop, so from that perspective it's the same as the function. But for modeling they aren't the same. (It would be nice if vendor-provided example code and templates recognized that we live in 2020 and not 1987 and used the function.)