Search code examples
vhdl

VHDL register multiplication


Question #1: are the following two variants equivalent with respect to the signal c?

Variant 1:

signal a : unsigned(31 downto 0);
signal b : unsigned(31 downto 0);
signal c : unsigned(63 downto 0);

process(slow_clk) is
begin
    if (rising_edge(slow_clk)) then
        c <= a * b;
    end if;
end process;

Variant 2:

signal a : unsigned(31 downto 0);
signal b : unsigned(31 downto 0);
signal c_async : unsigned(63 downto 0);
signal c : unsigned(63 downto 0);

c_async <= a * b;

process(slow_clk) is
begin
    if (rising_edge(slow_clk)) then
        c <= c_async;
    end if;
end process;

Question #2: Let's say a is 0, b is 0 and c is also 0. Then a becomes 0x55555555, and b becomes 0xAAAAAAAA. c is still 0 but it is about to become 0x38E38E3871C71C72.

Does c suddenly changes value from 0 to 0x38E38E3871C71C72? Or do some bits get set earlier and some later?

Suppose there is another process that working on a faster clock and it's sampling c. I need to know that this process can sample only definite multiplication results, not some junk.


Solution

  • Question #1: in order to answer we would need a definition of equivalent. If it means "will produce the same hardware after synthesis" the answer is very likely yes (unless your synthesizer is a strange one).

    But if it means "cannot be distinguished during a joint simulation" the answer is no. As the explanation is a bit complicated here is an executable proof (in order to fully understand it you'll need to read the chapter about the simulation algorithm of a good VHDL book).

    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std_unsigned.all;
    
    entity foo is
    end entity foo;
    
    architecture bar of foo is
    
      signal slow_clk: std_ulogic := '0';
      signal a : std_ulogic_vector(31 downto 0) := (others => '0');
      signal b : std_ulogic_vector(31 downto 0) := (others => '0');
      signal c_async : std_ulogic_vector(63 downto 0) := (others => '0');
      signal c1, c2 : std_ulogic_vector(63 downto 0) := (others => '0');
    
    begin
    
      c_async <= a * b;
    
      process(slow_clk) is
      begin
        if (rising_edge(slow_clk)) then
          c1 <= c_async;
          c2 <= a * b;
        end if;
      end process;
    
      assert c1 = c2
        report "mismatch: c1=" & to_hstring(c1) & ", c2=" & to_hstring(c2)
        severity note;
    
      process
      begin
        wait for 1 ns;
        a <= X"55555555"; -- simultaneous change of a, b and slow_clk
        b <= X"AAAAAAAA";
        slow_clk <= '1';
        wait;
      end process;
    
    end architecture bar;
    
    $ ghdl -a --std=08 foo.vhd
    $ ghdl -r --std=08 foo
    foo.vhd:28:3:@1ns:(assertion note): mismatch: c1=0000000000000000, c2=38E38E3871C71C72
    

    Question #2: again, we must distinguish the simulation semantics and the synthesis result. In simulation all bits of c change simultaneously, one "delta" after the rising edge of slow_clk (a "delta" is a symbolic delay with 0 physical duration that makes sense only in simulation and that you'll understand if you read your VHDL book). You can sample c with a faster clock without any problem.

    After synthesis and mapping on your hardware target technology (ASIC, FPGA...) the result will not be a pure software abstraction any more. It will be made of real transistors. And all bits of c will change at different times, even if they are very close. And the changes will not be instantaneous; if you could observe each electrical signal you'd see rising and falling slopes, not edges.

    If you have another faster clock that's supposed to sample c there are 2 different situations:

    1. The faster clock and slow_clk are perfectly synchronized, that is, slow_clk rising edges always happen at the same time as rising edges of your faster clock with almost no skew. You can safely sample c with the faster clock, you will always get a sound value.

    2. The two clocks are not synchronized. You cannot simply sample c with the faster clock. You would sometimes get a sound result. But sometimes, if c changes about at the same time as the rising edge of the faster clock, you would get a mixture of old and new values. Even worse, you would sometimes get bits that are not nice zero or ones because the corresponding electrical signals have an intermediate voltage between ground and power supply. These "meta-stabilities" would finally converge to a nice zero or one but before that they could also propagate in the downstream hardware and cause unexpected behaviours.

    In this second case you need a hardware re-synchronizer between the two clock domains. And what you also need now is not a VHDL book, it's a book about digital hardware design, especially its chapter about cross-clock-domains synchronization. Teaser: the re-synchronizer samples c with the faster clock but it also samples slow_clk, and it uses this to decide if the sampled c is reliable or not.