-- Instantiation synthesis. -- Copyright (C) 2019 Tristan Gingold -- -- This file is part of GHDL. -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation; either version 2 of the License, or -- (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program; if not, write to the Free Software -- Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, -- MA 02110-1301, USA. with GNAT.SHA1; with Types; use Types; with Types_Utils; use Types_Utils; with Files_Map; with Name_Table; with Libraries; with Hash; use Hash; with Dyn_Tables; with Interning; with Synthesis; use Synthesis; with Grt.Algos; with Netlists; use Netlists; with Netlists.Builders; use Netlists.Builders; with Netlists.Cleanup; with Netlists.Memories; with Netlists.Expands; with Netlists.Concats; with Vhdl.Utils; use Vhdl.Utils; with Vhdl.Errors; with Vhdl.Ieee.Math_Real; with Vhdl.Std_Package; with Synth.Values; use Synth.Values; with Synth.Environment; use Synth.Environment; with Synth.Stmts; use Synth.Stmts; with Synth.Decls; use Synth.Decls; with Synth.Expr; use Synth.Expr; with Synth.Source; use Synth.Source; with Synth.Debugger; package body Synth.Insts is Root_Instance : Synth_Instance_Acc; function Mode_To_Port_Kind (Mode : Iir_Mode) return Port_Kind is begin case Mode is when Iir_In_Mode => return Port_In; when Iir_Buffer_Mode | Iir_Out_Mode => return Port_Out; when Iir_Inout_Mode => return Port_Inout; when Iir_Linkage_Mode | Iir_Unknown_Mode => raise Synth_Error; end case; end Mode_To_Port_Kind; -- Parameters that define an instance. type Inst_Params is record -- Declaration: either the entity or the component. Decl : Node; -- Implementation: the architecture or Null_Node for black boxes. Arch : Node; -- Configuration (Null_Node for black boxes). Config : Node; -- Values of generics. Syn_Inst : Synth_Instance_Acc; -- Encoding if the instance name. Encoding : Name_Encoding; end record; type Inst_Object is record Decl : Node; Arch : Node; Config : Node; Syn_Inst : Synth_Instance_Acc; M : Module; -- Encoding if the instance name. Encoding : Name_Encoding; end record; function Hash (Params : Inst_Params) return Hash_Value_Type is Res : Hash_Value_Type; begin Res := Hash_Value_Type (Params.Decl); Res := Res xor Hash_Value_Type (Params.Arch); Res := Res xor Hash_Value_Type (Params.Config); -- TODO: hash generics return Res; end Hash; function Equal (Obj : Inst_Object; Params : Inst_Params) return Boolean is Inter : Node; begin if Obj.Decl /= Params.Decl or else Obj.Arch /= Params.Arch or else Obj.Config /= Params.Config then return False; end if; Inter := Get_Generic_Chain (Params.Decl); while Inter /= Null_Node loop if not Is_Equal (Get_Value (Obj.Syn_Inst, Inter).Val, Get_Value (Params.Syn_Inst, Inter).Val) then return False; end if; Inter := Get_Chain (Inter); end loop; Inter := Get_Port_Chain (Params.Decl); while Inter /= Null_Node loop if not Is_Fully_Constrained_Type (Get_Type (Inter)) then if not Are_Types_Equal (Get_Value (Obj.Syn_Inst, Inter).Typ, Get_Value (Params.Syn_Inst, Inter).Typ) then return False; end if; end if; Inter := Get_Chain (Inter); end loop; return True; end Equal; procedure Hash_Uns64 (C : in out GNAT.SHA1.Context; Val : Uns64) is V : Uns64; S : String (1 .. 8); begin -- Store to S using little endianness. V := Val; for I in S'Range loop S (I) := Character'Val (V and 16#ff#); V := Shift_Right (V, 8); end loop; GNAT.SHA1.Update (C, S); end Hash_Uns64; procedure Hash_Bound (C : in out GNAT.SHA1.Context; B : Bound_Type) is begin Hash_Uns64 (C, Iir_Direction'Pos (B.Dir)); Hash_Uns64 (C, To_Uns64 (Int64 (B.Left))); Hash_Uns64 (C, To_Uns64 (Int64 (B.Right))); end Hash_Bound; procedure Hash_Bounds (C : in out GNAT.SHA1.Context; Typ : Type_Acc) is begin case Typ.Kind is when Type_Vector => Hash_Bound (C, Typ.Vbound); when Type_Array => for I in Typ.Abounds.D'Range loop Hash_Bound (C, Typ.Abounds.D (I)); end loop; when others => raise Internal_Error; end case; end Hash_Bounds; procedure Hash_Const (C : in out GNAT.SHA1.Context; Val : Value_Acc; Typ : Type_Acc) is begin case Val.Kind is when Value_Discrete => Hash_Uns64 (C, To_Uns64 (Val.Scal)); when Value_Float => Hash_Uns64 (C, To_Uns64 (Val.Fp)); when Value_Const_Array => declare El_Typ : constant Type_Acc := Get_Array_Element (Typ); begin -- Bounds. Hash_Bounds (C, Typ); -- Values. for I in Val.Arr.V'Range loop Hash_Const (C, Val.Arr.V (I), El_Typ); end loop; end; when Value_Const_Record => for I in Val.Rec.V'Range loop Hash_Const (C, Val.Rec.V (I), Typ.Rec.E (I).Typ); end loop; when Value_Const => Hash_Const (C, Val.C_Val, Typ); when Value_Alias => if Val.A_Off /= 0 then raise Internal_Error; end if; Hash_Const (C, Val.A_Obj, Typ); when Value_Net | Value_Wire | Value_Array | Value_Record | Value_Access | Value_File => raise Internal_Error; end case; end Hash_Const; function Get_Source_Identifier (Decl : Node) return Name_Id is use Files_Map; use Name_Table; Loc : constant Location_Type := Get_Location (Decl); Len : constant Natural := Get_Name_Length (Get_Identifier (Decl)); subtype Ident_Str is String (1 .. Len); File : Source_File_Entry; Pos : Source_Ptr; Buf : File_Buffer_Acc; begin Location_To_File_Pos (Loc, File, Pos); Buf := Get_File_Source (File); return Get_Identifier (Ident_Str (Buf (Pos .. Pos + Source_Ptr (Len - 1)))); end Get_Source_Identifier; function Create_Module_Name (Params : Inst_Params) return Sname is use GNAT.SHA1; Decl : constant Node := Params.Decl; Id : constant Name_Id := Get_Identifier (Decl); Generics : constant Node := Get_Generic_Chain (Decl); Ports : constant Node := Get_Port_Chain (Decl); Ctxt : GNAT.SHA1.Context; Has_Hash : Boolean; -- Create a buffer, store the entity name. -- For each generic: -- * write the value for integers. -- * write the identifier for enumerated type with only non-extended -- identifiers. -- * hash all other values -- Append the hash if any. use Name_Table; Id_Len : constant Natural := Get_Name_Length (Id); Str_Len : constant Natural := Id_Len + 512; pragma Assert (GNAT.SHA1.Hash_Length = 20); Str : String (1 .. Str_Len + 41); Len : Natural; Gen_Decl : Node; Gen : Valtyp; begin Len := Id_Len; Str (1 .. Len) := Get_Name_Ptr (Id) (1 .. Len); Has_Hash := False; case Params.Encoding is when Name_Hash => Ctxt := GNAT.SHA1.Initial_Context; Gen_Decl := Generics; while Gen_Decl /= Null_Node loop Gen := Get_Value (Params.Syn_Inst, Gen_Decl); case Gen.Val.Kind is when Value_Discrete => declare S : constant String := Uns64'Image (To_Uns64 (Gen.Val.Scal)); begin if Len + S'Length > Str_Len then Has_Hash := True; Hash_Const (Ctxt, Gen.Val, Gen.Typ); else Str (Len + 1 .. Len + S'Length) := S; pragma Assert (Str (Len + 1) = ' '); Str (Len + 1) := '_'; -- Overwrite the space. Len := Len + S'Length; end if; end; when others => Has_Hash := True; Hash_Const (Ctxt, Gen.Val, Gen.Typ); end case; Gen_Decl := Get_Chain (Gen_Decl); end loop; declare Port_Decl : Node; Port_Typ : Type_Acc; begin Port_Decl := Ports; while Port_Decl /= Null_Node loop if not Is_Fully_Constrained_Type (Get_Type (Port_Decl)) then Port_Typ := Get_Value (Params.Syn_Inst, Port_Decl).Typ; Has_Hash := True; Hash_Bounds (Ctxt, Port_Typ); end if; Port_Decl := Get_Chain (Port_Decl); end loop; end; if not Has_Hash and then Generics = Null_Node then -- Simple case: same name. -- TODO: what about two entities with the same identifier but -- declared in two different libraries ? -- TODO: what about extended identifiers ? return New_Sname_User (Id, No_Sname); end if; if Has_Hash then Str (Len + 1) := '_'; Len := Len + 1; Str (Len + 1 .. Len + 40) := GNAT.SHA1.Digest (Ctxt); Len := Len + 40; end if; when Name_Asis | Name_Parameters => return New_Sname_User (Get_Source_Identifier (Decl), No_Sname); when Name_Index => -- TODO. raise Internal_Error; end case; return New_Sname_User (Get_Identifier (Str (1 .. Len)), No_Sname); end Create_Module_Name; -- Create the name of an interface. function Create_Inter_Name (Decl : Node; Enc : Name_Encoding) return Sname is Id : Name_Id; begin case Enc is when Name_Asis | Name_Parameters => Id := Get_Source_Identifier (Decl); when others => Id := Get_Identifier (Decl); end case; return New_Sname_User (Id, No_Sname); end Create_Inter_Name; function Build (Params : Inst_Params) return Inst_Object is Decl : constant Node := Params.Decl; Arch : constant Node := Params.Arch; Imp : Node; Syn_Inst : Synth_Instance_Acc; Inter : Node; Inter_Type : Node; Inter_Typ : Type_Acc; Nbr_Inputs : Port_Nbr; Nbr_Outputs : Port_Nbr; Nbr_Params : Param_Nbr; Cur_Module : Module; Val : Valtyp; Id : Module_Id; begin if Get_Kind (Params.Decl) = Iir_Kind_Component_Declaration then pragma Assert (Params.Arch = Null_Node); pragma Assert (Params.Config = Null_Node); Imp := Params.Decl; else pragma Assert (Get_Kind (Params.Config) = Iir_Kind_Block_Configuration); Imp := Params.Arch; end if; -- Create the instance. Syn_Inst := Make_Instance (Root_Instance, Imp, No_Sname); -- Copy values for generics. Inter := Get_Generic_Chain (Decl); Nbr_Params := 0; while Inter /= Null_Node loop -- Bounds or range of the type. Inter_Type := Get_Subtype_Indication (Inter); if Inter_Type /= Null_Node then case Get_Kind (Inter_Type) is when Iir_Kind_Array_Subtype_Definition | Iir_Kind_Integer_Subtype_Definition => Create_Subtype_Object (Syn_Inst, Inter_Type, Get_Subtype_Object (Params.Syn_Inst, Inter_Type)); when others => null; end case; Nbr_Params := Nbr_Params + 1; end if; -- Object. Create_Object (Syn_Inst, Inter, Get_Value (Params.Syn_Inst, Inter)); Inter := Get_Chain (Inter); end loop