- BlackBox:
    name: Clash.Explicit.Testbench.assert
    imports:
    - ~INCLUDENAME[0].all
    includes:
    - name: slv2string
      extension: vhdl
      template: |-
        -- helper function of Clash.Explicit.Testbench.assert
        library IEEE;
        use IEEE.STD_LOGIC_1164.ALL;

        package ~INCLUDENAME[0] is
          function slv2string (slv : std_logic_vector) return STRING;
        end;

        package body ~INCLUDENAME[0] is
          function slv2string (slv : std_logic_vector) return STRING is
             variable result : string (1 to slv'length);
             variable res_l : string (1 to 3);
             variable r : integer;
           begin
             r := 1;
             for i in slv'range loop
                res_l := std_logic'image(slv(i));
                result(r) := res_l(2);
                r := r + 1;
             end loop;
             return result;
          end slv2string;
        end;
    kind: Declaration
    type: |-
      assert
        :: (KnownDomain dom, Eq a, ShowX a)      -- (ARG[0],ARG[1],ARG[2])
        => Clock dom                             -- ARG[3]
        -> Reset dom                             -- ARG[4]
        -> String                                -- ARG[5]
        -> Signal dom a                          -- Checked value  (ARG[6])
        -> Signal dom a                          -- Expected value (ARG[7])
        -> Signal dom b                          -- Return valued  (ARG[8])
        -> Signal dom b
    template: |-
      -- assert begin
      ~GENSYM[assert][0] : block
        -- pragma translate_off
        signal ~GENSYM[actual][2] : ~TYP[6];
        signal ~GENSYM[expected][3] : ~TYP[7];
        -- pragma translate_on
      begin
        -- pragma translate_off
        ~SYM[2] <= ~ARG[6];
        ~SYM[3] <= ~ARG[7];
        process(~ARG[3]) is
        begin
          if (~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[3])) then
            assert (toSLV(~SYM[2]) = toSLV(~SYM[3])) report (~LIT[5] & ", expected: " & ~INCLUDENAME[0].slv2string(toSLV(~SYM[3])) & ", actual: " & ~INCLUDENAME[0].slv2string(toSLV(~SYM[2]))) severity error;
          end if;
        end process;
        -- pragma translate_on
        ~RESULT <= ~ARG[8];
      end block;
      -- assert end
- BlackBox:
    name: Clash.Explicit.Testbench.assertBitVector
    imports:
    - ~INCLUDENAME[0].all
    includes:
    - name: assertBitVector
      extension: vhdl
      template: |
        -- helper functions of Clash.Explicit.Testbench.assertBitVector
        library IEEE;
        use IEEE.STD_LOGIC_1164.ALL;

        package ~INCLUDENAME[0] is
          function non_std_match (l, r : std_logic_vector) return boolean;
          function slv2string (slv : std_logic_vector) return STRING;
        end;

        package body ~INCLUDENAME[0] is
          type match_table_type is array (std_ulogic, std_ulogic) of boolean;
          constant match_table: match_table_type :=
            ('0' | 'L' => ('0' | 'L' | '-' => true, others => false),
             '1' | 'H' => ('1' | 'H' | '-' => true, others => false),
             '-' => ('-' => true, others => false),
             others    =>             ('-' => true, others => false)
            );
          -- non_std_match is like std_match
          -- But only accepts '-' as don't care in its the second argument r.
          function non_std_match (l, r : std_logic_vector) return boolean is
            alias la : std_logic_vector (l'length downto 1) is l;
            alias ra : std_logic_vector (r'length downto 1) is r;
          begin
            for i in l'range loop
               if not match_table (l (i), r (i)) then
                 return false;
               end if;
            end loop;
            return true;
          end non_std_match;

          function slv2string (slv : std_logic_vector) return STRING is
             variable result : string (1 to slv'length);
             variable res_l : string (1 to 3);
             variable r : integer;
           begin
             r := 1;
             for i in slv'range loop
                res_l := std_logic'image(slv(i));
                result(r) := res_l(2);
                r := r + 1;
             end loop;
             return result;
          end slv2string;

        end;
    kind: Declaration
    type: |-
      assertBitVector
        :: ( KnownDomain dom        --                 ARG[0]
           , KnownNat n )           --                 ARG[1]
        => Clock dom                --                 ARG[2]
        -> Reset dom                --                 ARG[3]
        -> String                   --                 ARG[4]
        -> Signal dom (BitVector n) -- Checked value  (ARG[5])
        -> Signal dom (BitVector n) -- Expected value (ARG[6])
        -> Signal dom b             -- Return valued  (ARG[7])
        -> Signal dom b
    template: |-
      -- assertBitVector begin
      ~GENSYM[assert][0] : block
        -- pragma translate_off
        signal ~GENSYM[actual][2] : ~TYP[5];
        signal ~GENSYM[expected][3] : ~TYP[6];
        -- pragma translate_on
      begin
        -- pragma translate_off
        ~SYM[2] <= ~ARG[5];
        ~SYM[3] <= ~ARG[6];
        process(~ARG[2]) is
        begin
          if (~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[2])) then
            assert (~INCLUDENAME[0].non_std_match(toSLV(~SYM[2]),toSLV(~SYM[3]))) report (~LIT[4] & ", expected: " & ~INCLUDENAME[0].slv2string(toSLV(~SYM[3])) & ", actual: " & ~INCLUDENAME[0].slv2string(toSLV(~SYM[2]))) severity error;
          end if;
        end process;
        -- pragma translate_on
        ~RESULT <= ~ARG[7];
      end block;
      -- assertBitVector end
- BlackBox:
    name: Clash.Explicit.Testbench.tbClockGen
    comment: |-
      ModelSim and Vivado seem to round time values to an integer number of picoseconds.
              Use two half periods to prevent rounding errors from affecting the full period.
    kind: Declaration
    type: |-
      tbClockGen
        :: KnownDomain dom     -- ARG[0]
        => Signal dom Bool     -- ARG[1]
        -> Clock dom
    template: |-
      -- tbClockGen begin
      -- pragma translate_off
      ~GENSYM[clkGen][0] : process is
        constant ~GENSYM[half_periodH][1] : time := ~PERIOD[0]000 fs / 2;
        constant ~GENSYM[half_periodL][2] : time := ~PERIOD[0]000 fs - ~SYM[1];
      begin
        ~RESULT <= ~IF~ACTIVEEDGE[Rising][0]~THEN'0'~ELSE'1'~FI;
        wait for ~LONGESTPERIOD ps;
        while ~ARG[1] loop
          ~RESULT <= not ~RESULT;
          wait for ~SYM[1];
          ~RESULT <= not ~RESULT;
          wait for ~SYM[2];
        end loop;
        wait;
      end process;
      -- pragma translate_on
      -- tbClockGen end
    warning: Clash.Signal.Internal.tbClockGen is not synthesizable!
    workInfo: Always
- BlackBox:
    name: Clash.Explicit.Testbench.tbEnableGen
    kind: Declaration
    type: 'tbEnableGen ::
      Enable dom'
    template: ~RESULT <= true;
    workInfo: Always
