Close

Generalised fault injection - part deux

A project log for VHDL library for gate-level verification

Collection of ASIC cells and ProASIC3 "tiles" in VHDL so I can design and verify optimised code without the proprietary libraries

yann-guidon-ygdesYann Guidon / YGDES 06/10/2019 at 19:170 Comments

The idea progresses while I gather the necessary ingredients for the new version of the library. At this moment I consider getting rid of all the C and VHPI code because VHDL already contains all the required features, I think.

Let's start with the fundamental feature, that evaluates the gate's function. It must be short, simple, efficient, fast. We work with SLV8 (std_logic_vector(7 downto 0)) as the boolean function table.

subtype FunctTableType is std_logic_vector( 7 downto 0);

The input signal A has the least significant effect on the index, so we have the linear relationship :

CBAIndex
0000
0011
0102
0113
1004
1015
1106
1117

OR3 is coded as "11111110" and AND3 as "10000000".

From there, it's very easy to look it up :

function TableLookup(
   A : std_logic ;
   B : std_logic ;
   C : std_logic ;
   Table : FunctTableType)
return std_logic is
  variable index : integer := 0;
begin
  if A = '1' then index := 1;         end if;
  if B = '1' then index := index + 2; end if;
  if C = '1' then index := index + 4; end if;
  return Table(index);
end function;

There. No C code required.

Then the body of the gate's entity applies this function to the corresponding table.

entity AND3 is
  port( A, B, C : in std_logic;
              Y : out std_logic);
end AND3;

architecture rtl of AND3 is
  constant Table : FuncTableType := "10000000";
begin
  Y <= TableLookup(A, B, C, Table) after gate_delay;
end rtl;

Nothing complex, here, either, though I have moved the constant for a separated declaration, instead of a mere inline mention. That's where the hinge of the system resides.


First, we must initialise the function table.

Initially I wanted it to be initialised in the gate file itself. It would make the system self-contained, reducing confusion and complexity. Then I realised that the initialisation (and table creation) would be computed, again and again, for every gate in the DUT. It's not an issue for the example INC8 unit but imagine a huge processor... So it's best to create all the tables once and for all, before everything. And since I don't want to do it by hand, some code is required (even though it will take more dev time, huh huh huh).

I want to reduce the duplication of code as much as possible and at that moment, it is VERY tempting to create only one entity/architecture, with a function table provided as a generic but this would break the compatibility with the historical ACTEL files. As a consequence, the collection of gate files will continue to exist...

However the constants can be precomputed in the package.

Library ieee;
    use ieee.std_logic_1164.all;

package proasic3 is
  subtype FunctTableType is std_logic_vector( 7 downto 0);
  type A3PTileName is (
    AND2, AND2A, AND3, AND3A, AND3B, AND3C, AO1, AO12, AO13, AO14,
    AO15, AO16, AO17, AO18, AO1A, AO1B, AO1C, AO1D, AO1E, AOI1, 
    AOI1A, AOI1B, AOI1C, AOI1D, AOI5, AX1, AX1A, AX1B, AX1C, AX1D, 
    AX1E, AXO1, AXO2, AXO3, AXO5, AXO6, AXO7, AXOI1, AXOI2, AXOI3, 
    AXOI4, AXOI5, AXOI7, CLKINT, DFN1, DFN1C0, DFN1C1, DFN1E0,
    DFN1E0C0, DFN1E0C1, DFN1E0P0, DFN1E0P1, DFN1E1, DFN1E1C0,
    DFN1E1C1, DFN1E1P0, DFN1E1P1, DFN1P0, DFN1P1, INV, MAJ3, MX2, 
    MX2A, MX2B, MX2C, NAND2, NAND3A, NAND3B, NAND3, NOR2A, NOR2, 
    NOR2B, NOR3, NOR3A, NOR3B, NOR3C, OA1A, OA1B, OA1C, OAI1, OR2, 
    OR2A, OR2B, OR3, OR3A, OR3B, OR3C, XA1, XA1A, XA1B, XA1C, XNOR2, 
    XNOR3, XO1, XO1A, XOR2, XOR3, ZOR3, ZOR3I);

  function Create_Table(f : A3PTileName) return FunctTableType;
  function TableLookup( A, B, C : std_logic; Table : FunctTableType) return std_logic;

  constant A3P_AND3 : FunctTableType := Create_Table(AND3);

  ...
  component AND3     port(A, B, C : in std_logic; Y : out std_logic); end component;
  ...

end proasic3;

package body ygrec8_def is
  function TableLookup(
     A, B, C : std_logic ;
     Table : FunctTableType)
  return std_logic is
    variable index : integer := 0;
  begin
    if A = '1' then index := 1;         end if;
    if B = '1' then index := index + 2; end if;
    if C = '1' then index := index + 4; end if;
    return Table(index);
  end function;

end ygrec8_def;

I must admit that at this point, I have tested nothing so I'm wildly speculating, but you might see where I'm going.

All the constants are created by a single function that contains all the boolean code in a large case().

function Create_Table(f : A3PTileName) return FunctTableType is
  variable i : integer := 0;
  variable A, B, C, Y : std_logic;
  variable t : FunctTableType;
begin
  C := '0';
  loop_C: loop
    B := '0';
    loop_B: loop
      A := '0';
      loop_A: loop

        -- nested loop's body:
        Y := ....
        -- huge case() here

        t[i] := Y;
        i := i+1;

        exit loop_A when A = '1';
        A := '1';
      end loop loop_A;
      exit loop_B when B = '1';
      B := '1';
    end loop loop_B;
    exit loop_C when C = '1';
    C := '1';
  end loop loop_C;

  return t;
end Create_Table;

 Then we simply have to copy-paste the previously tested and grep'ed code.

Discussions