diff options
author | Tristan Gingold <tgingold@free.fr> | 2021-04-16 06:44:05 +0200 |
---|---|---|
committer | Tristan Gingold <tgingold@free.fr> | 2021-04-16 06:58:03 +0200 |
commit | 33b307ba8d64ae952334a7f79249178141adee2c (patch) | |
tree | ee312a4da3b39946d935f8ebf0cf8c722d8073bb /src/synth/synth-vhdl_aggr.adb | |
parent | a677e3cc38d1f5e3813bc1e5bd424ee0b59319ed (diff) | |
download | ghdl-33b307ba8d64ae952334a7f79249178141adee2c.tar.gz ghdl-33b307ba8d64ae952334a7f79249178141adee2c.tar.bz2 ghdl-33b307ba8d64ae952334a7f79249178141adee2c.zip |
synth: refactoring (synth.aggr -> synth.vhdl_aggr)
Diffstat (limited to 'src/synth/synth-vhdl_aggr.adb')
-rw-r--r-- | src/synth/synth-vhdl_aggr.adb | 537 |
1 files changed, 537 insertions, 0 deletions
diff --git a/src/synth/synth-vhdl_aggr.adb b/src/synth/synth-vhdl_aggr.adb new file mode 100644 index 000000000..6863924f0 --- /dev/null +++ b/src/synth/synth-vhdl_aggr.adb @@ -0,0 +1,537 @@ +-- Aggregates synthesis. +-- Copyright (C) 2020 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, see <gnu.org/licenses>. + +with Types; use Types; +with Str_Table; + +with Netlists; use Netlists; +with Netlists.Utils; use Netlists.Utils; +with Netlists.Builders; use Netlists.Builders; + +with Vhdl.Errors; use Vhdl.Errors; +with Vhdl.Utils; use Vhdl.Utils; + +with Synth.Errors; use Synth.Errors; +with Synth.Expr; use Synth.Expr; +with Synth.Stmts; use Synth.Stmts; +with Synth.Decls; use Synth.Decls; + +package body Synth.Vhdl_Aggr is + type Stride_Array is array (Dim_Type range <>) of Nat32; + + procedure Get_Index_Offset (Index : Int64; + Bounds : Bound_Type; + Expr : Iir; + Off : out Uns32; + Err_P : out Boolean) + is + Left : constant Int64 := Int64 (Bounds.Left); + Right : constant Int64 := Int64 (Bounds.Right); + begin + case Bounds.Dir is + when Dir_To => + if Index >= Left and then Index <= Right then + -- to + Off := Uns32 (Index - Left); + Err_P := False; + return; + end if; + when Dir_Downto => + if Index <= Left and then Index >= Right then + -- downto + Off := Uns32 (Left - Index); + Err_P := False; + return; + end if; + end case; + Error_Msg_Synth (+Expr, "index out of bounds"); + Off := 0; + Err_P := True; + end Get_Index_Offset; + + procedure Get_Index_Offset (Index : Valtyp; + Bounds : Bound_Type; + Expr : Iir; + Off : out Uns32; + Err_P : out Boolean) is + begin + Get_Index_Offset (Read_Discrete (Index), Bounds, Expr, Off, Err_P); + end Get_Index_Offset; + + function Fill_Stride (Typ : Type_Acc) return Stride_Array is + begin + case Typ.Kind is + when Type_Vector => + return (1 => 1); + when Type_Array => + declare + Bnds : constant Bound_Array_Acc := Typ.Abounds; + Res : Stride_Array (1 .. Bnds.Ndim); + Stride : Nat32; + begin + Stride := 1; + for I in reverse 2 .. Bnds.Ndim loop + Res (Dim_Type (I)) := Stride; + Stride := Stride * Nat32 (Bnds.D (I).Len); + end loop; + Res (1) := Stride; + return Res; + end; + when others => + raise Internal_Error; + end case; + end Fill_Stride; + + procedure Fill_Array_Aggregate (Syn_Inst : Synth_Instance_Acc; + Aggr : Node; + Res : Valtyp_Array_Acc; + Typ : Type_Acc; + First_Pos : Nat32; + Strides : Stride_Array; + Dim : Dim_Type; + Const_P : out Boolean; + Err_P : out boolean) + is + Ctxt : constant Context_Acc := Get_Build (Syn_Inst); + Bound : constant Bound_Type := Get_Array_Bound (Typ, Dim); + El_Typ : constant Type_Acc := Get_Array_Element (Typ); + Stride : constant Nat32 := Strides (Dim); + Value : Node; + Assoc : Node; + Nbr_Els : Nat32; + Sub_Err : Boolean; + + procedure Set_Elem (Pos : Nat32) + is + Sub_Const : Boolean; + Sub_Err : Boolean; + Val : Valtyp; + begin + Nbr_Els := Nbr_Els + 1; + + if Dim = Strides'Last then + Val := Synth_Expression_With_Type (Syn_Inst, Value, El_Typ); + Val := Synth_Subtype_Conversion (Ctxt, Val, El_Typ, False, Value); + pragma Assert (Res (Pos) = No_Valtyp); + Res (Pos) := Val; + if Val = No_Valtyp then + Err_P := True; + else + if Const_P and then not Is_Static (Val.Val) then + Const_P := False; + end if; + end if; + else + Fill_Array_Aggregate + (Syn_Inst, Value, Res, Typ, Pos, Strides, Dim + 1, + Sub_Const, Sub_Err); + Const_P := Const_P and Sub_Const; + Err_P := Err_P or Sub_Err; + end if; + end Set_Elem; + + procedure Set_Vector (Pos : Nat32; Len : Nat32; Val : Valtyp) is + begin + pragma Assert (Dim = Strides'Last); + if Len = 0 then + return; + end if; + pragma Assert (Res (Pos) = No_Valtyp); + Res (Pos) := Val; + + -- Mark following slots as busy so that 'others => x' won't fill + -- them. + for I in 2 .. Len loop + Res (Pos + I - 1).Typ := Val.Typ; + end loop; + + Nbr_Els := Nbr_Els + Len; + if Const_P and then not Is_Static (Val.Val) then + Const_P := False; + end if; + end Set_Vector; + + Pos : Nat32; + begin + Pos := First_Pos; + Nbr_Els := 0; + Const_P := True; + Err_P := False; + + if Get_Kind (Aggr) = Iir_Kind_String_Literal8 then + declare + Str_Id : constant String8_Id := Get_String8_Id (Aggr); + Str_Len : constant Int32 := Get_String_Length (Aggr); + E : Valtyp; + V : Nat8; + begin + pragma Assert (Stride = 1); + if Bound.Len /= Width (Str_Len) then + Error_Msg_Synth + (+Aggr, "string length doesn't match bound length"); + Err_P := True; + end if; + for I in 1 .. Pos32'Min (Pos32 (Str_Len), Pos32 (Bound.Len)) loop + E := Create_Value_Memory (El_Typ); + V := Str_Table.Element_String8 (Str_Id, I); + Write_U8 (E.Val.Mem, Nat8'Pos (V)); + Res (Pos) := E; + Pos := Pos + 1; + end loop; + return; + end; + end if; + + Assoc := Get_Association_Choices_Chain (Aggr); + while Is_Valid (Assoc) loop + Value := Get_Associated_Expr (Assoc); + loop + case Get_Kind (Assoc) is + when Iir_Kind_Choice_By_None => + if Get_Element_Type_Flag (Assoc) then + if Pos >= First_Pos + Stride * Nat32 (Bound.Len) then + Error_Msg_Synth (+Assoc, "element out of array bound"); + else + Set_Elem (Pos); + Pos := Pos + Stride; + end if; + else + declare + Val : Valtyp; + Val_Len : Uns32; + begin + Val := Synth_Expression_With_Basetype + (Syn_Inst, Value); + Val_Len := Get_Bound_Length (Val.Typ, 1); + pragma Assert (Stride = 1); + if Pos - First_Pos > Nat32 (Bound.Len - Val_Len) then + Error_Msg_Synth + (+Assoc, "element out of array bound"); + else + Set_Vector (Pos, Nat32 (Val_Len), Val); + Pos := Pos + Nat32 (Val_Len); + end if; + end; + end if; + when Iir_Kind_Choice_By_Others => + pragma Assert (Get_Element_Type_Flag (Assoc)); + declare + Last_Pos : constant Nat32 := + First_Pos + Nat32 (Bound.Len) * Stride; + begin + while Pos < Last_Pos loop + if Res (Pos) = No_Valtyp then + -- FIXME: the check is not correct if there is + -- an array. + Set_Elem (Pos); + end if; + Pos := Pos + Stride; + end loop; + end; + when Iir_Kind_Choice_By_Expression => + pragma Assert (Get_Element_Type_Flag (Assoc)); + declare + Ch : constant Node := Get_Choice_Expression (Assoc); + Idx : Valtyp; + Off : Uns32; + begin + Idx := Synth_Expression (Syn_Inst, Ch); + if not Is_Static (Idx.Val) then + Error_Msg_Synth (+Ch, "choice is not static"); + else + Get_Index_Offset (Idx, Bound, Ch, Off, Sub_Err); + Err_P := Err_P or Sub_Err; + exit when Err_P; + Set_Elem (First_Pos + Nat32 (Off) * Stride); + end if; + end; + when Iir_Kind_Choice_By_Range => + declare + Ch : constant Node := Get_Choice_Range (Assoc); + Rng : Discrete_Range_Type; + Val : Valtyp; + Rng_Len : Width; + Off : Uns32; + begin + Synth_Discrete_Range (Syn_Inst, Ch, Rng); + if Get_Element_Type_Flag (Assoc) then + Val := Create_Value_Discrete + (Rng.Left, + Get_Subtype_Object (Syn_Inst, + Get_Base_Type (Get_Type (Ch)))); + while In_Range (Rng, Read_Discrete (Val)) loop + Get_Index_Offset (Val, Bound, Ch, Off, Sub_Err); + Err_P := Err_P or Sub_Err; + exit when Err_P; + Set_Elem (First_Pos + Nat32 (Off) * Stride); + Update_Index (Rng, Val); + exit when Err_P; + end loop; + else + -- The direction must be the same. + if Rng.Dir /= Bound.Dir then + Error_Msg_Synth + (+Assoc, "direction of range does not match " + & "direction of array"); + end if; + -- FIXME: can the expression be unbounded ? + Val := Synth_Expression_With_Basetype + (Syn_Inst, Value); + -- The length must match the range. + Rng_Len := Get_Range_Length (Rng); + if Get_Bound_Length (Val.Typ, 1) /= Rng_Len then + Error_Msg_Synth + (+Value, "length doesn't match range"); + end if; + pragma Assert (Stride = 1); + Get_Index_Offset (Rng.Left, Bound, Ch, Off, Sub_Err); + Err_P := Err_P or Sub_Err; + exit when Err_P; + Set_Vector + (First_Pos + Nat32 (Off), Nat32 (Rng_Len), Val); + end if; + end; + when others => + Error_Msg_Synth + (+Assoc, "unhandled association form"); + end case; + Assoc := Get_Chain (Assoc); + exit when Is_Null (Assoc); + exit when not Get_Same_Alternative_Flag (Assoc); + exit when Err_P; + end loop; + end loop; + + if not Err_P and then Nbr_Els /= Nat32 (Bound.Len) then + Error_Msg_Synth (+Aggr, "aggregate length doesn't match its bound"); + Err_P := True; + end if; + end Fill_Array_Aggregate; + + procedure Fill_Record_Aggregate (Syn_Inst : Synth_Instance_Acc; + Aggr : Node; + Aggr_Typ : Type_Acc; + Rec : Valtyp_Array_Acc; + Err_P : out Boolean; + Const_P : out Boolean) + is + Ctxt : constant Context_Acc := Get_Build (Syn_Inst); + Value : Node; + Assoc : Node; + Pos : Nat32; + + -- POS is the element position, from 0 to nbr el - 1. + procedure Set_Elem (Pos : Nat32) + is + Val : Valtyp; + El_Type : Type_Acc; + begin + El_Type := Aggr_Typ.Rec.E (Iir_Index32 (Pos + 1)).Typ; + Val := Synth_Expression_With_Type (Syn_Inst, Value, El_Type); + if Const_P and not Is_Static (Val.Val) then + Const_P := False; + end if; + Val := Synth_Subtype_Conversion (Ctxt, Val, El_Type, False, Value); + if Val = No_Valtyp then + Err_P := True; + return; + end if; + -- Put in reverse order. The first record element (at position 0) + -- will be the LSB, so the last element of REC. + Rec (Nat32 (Rec'Last - Pos)) := Val; + end Set_Elem; + begin + Assoc := Get_Association_Choices_Chain (Aggr); + Pos := 0; + Const_P := True; + Err_P := False; + while Is_Valid (Assoc) loop + Value := Get_Associated_Expr (Assoc); + loop + case Get_Kind (Assoc) is + when Iir_Kind_Choice_By_None => + Set_Elem (Pos); + Pos := Pos + 1; + when Iir_Kind_Choice_By_Others => + for I in Rec'Range loop + if Rec (I) = No_Valtyp then + Set_Elem (Rec'Last - I); + end if; + end loop; + when Iir_Kind_Choice_By_Name => + Pos := Nat32 (Get_Element_Position + (Get_Named_Entity + (Get_Choice_Name (Assoc)))); + Set_Elem (Pos); + when others => + Error_Msg_Synth + (+Assoc, "unhandled association form"); + end case; + Assoc := Get_Chain (Assoc); + exit when Is_Null (Assoc); + exit when not Get_Same_Alternative_Flag (Assoc); + end loop; + end loop; + end Fill_Record_Aggregate; + + function Valtyp_Array_To_Net (Ctxt : Context_Acc; Tab : Valtyp_Array) + return Net + is + Res : Net; + Arr : Net_Array_Acc; + Idx : Nat32; + begin + Arr := new Net_Array (1 .. Tab'Length); + Idx := 0; + for I in Arr'Range loop + if Tab (I).Val /= null then + Idx := Idx + 1; + Arr (Idx) := Get_Net (Ctxt, Tab (I)); + end if; + end loop; + Concat_Array (Ctxt, Arr (1 .. Idx), Res); + Free_Net_Array (Arr); + return Res; + end Valtyp_Array_To_Net; + + function Synth_Aggregate_Array (Syn_Inst : Synth_Instance_Acc; + Aggr : Node; + Aggr_Type : Type_Acc) return Valtyp + is + Ctxt : constant Context_Acc := Get_Build (Syn_Inst); + Strides : constant Stride_Array := Fill_Stride (Aggr_Type); + Flen : constant Iir_Index32 := Get_Array_Flat_Length (Aggr_Type); + Tab_Res : Valtyp_Array_Acc; + Const_P : Boolean; + Err_P : Boolean; + Res : Valtyp; + begin + Tab_Res := new Valtyp_Array'(1 .. Nat32 (Flen) => No_Valtyp); + + Fill_Array_Aggregate (Syn_Inst, Aggr, Tab_Res, + Aggr_Type, 1, Strides, 1, Const_P, Err_P); + if Err_P then + return No_Valtyp; + end if; + + -- TODO: check all element types have the same bounds ? + + if Const_P then + declare + Off : Size_Type; + begin + Res := Create_Value_Memory (Aggr_Type); + Off := 0; + for I in Tab_Res'Range loop + if Tab_Res (I).Val /= null then + -- There can be holes due to sub-arrays. + Write_Value (Res.Val.Mem + Off, Tab_Res (I)); + Off := Off + Tab_Res (I).Typ.Sz; + end if; + end loop; + pragma Assert (Off = Aggr_Type.Sz); + end; + else + Res := Create_Value_Net + (Valtyp_Array_To_Net (Ctxt, Tab_Res.all), Aggr_Type); + end if; + + Free_Valtyp_Array (Tab_Res); + + return Res; + end Synth_Aggregate_Array; + + function Synth_Aggregate_Record (Syn_Inst : Synth_Instance_Acc; + Aggr : Node; + Aggr_Type : Type_Acc) return Valtyp + is + Ctxt : constant Context_Acc := Get_Build (Syn_Inst); + Tab_Res : Valtyp_Array_Acc; + Res_Typ : Type_Acc; + Res : Valtyp; + Err_P : Boolean; + Const_P : Boolean; + begin + -- Allocate the result. + Tab_Res := + new Valtyp_Array'(1 .. Nat32 (Aggr_Type.Rec.Len) => No_Valtyp); + + Fill_Record_Aggregate + (Syn_Inst, Aggr, Aggr_Type, Tab_Res, Err_P, Const_P); + + if Err_P then + Res := No_Valtyp; + else + case Type_Records (Aggr_Type.Kind) is + when Type_Unbounded_Record => + declare + Els_Typ : Rec_El_Array_Acc; + begin + Els_Typ := Create_Rec_El_Array (Aggr_Type.Rec.Len); + for I in Els_Typ.E'Range loop + Els_Typ.E (I).Typ := Tab_Res (Nat32 (I)).Typ; + end loop; + Res_Typ := Create_Record_Type (Els_Typ); + end; + when Type_Record => + Res_Typ := Aggr_Type; + end case; + + if Const_P then + Res := Create_Value_Memory (Res_Typ); + for I in Aggr_Type.Rec.E'Range loop + Write_Value (Res.Val.Mem + Res_Typ.Rec.E (I).Moff, + Tab_Res (Tab_Res'Last - Nat32 (I) + 1)); + end loop; + else + Res := Create_Value_Net + (Valtyp_Array_To_Net (Ctxt, Tab_Res.all), Res_Typ); + end if; + end if; + + Free_Valtyp_Array (Tab_Res); + + return Res; + end Synth_Aggregate_Record; + + -- Aggr_Type is the type from the context. + function Synth_Aggregate (Syn_Inst : Synth_Instance_Acc; + Aggr : Node; + Aggr_Type : Type_Acc) return Valtyp is + begin + case Aggr_Type.Kind is + when Type_Unbounded_Array | Type_Unbounded_Vector => + declare + Res_Type : Type_Acc; + begin + Res_Type := Synth_Array_Subtype_Indication + (Syn_Inst, Get_Type (Aggr)); + return Synth_Aggregate_Array (Syn_Inst, Aggr, Res_Type); + end; + when Type_Vector + | Type_Array => + return Synth_Aggregate_Array (Syn_Inst, Aggr, Aggr_Type); + when Type_Record + | Type_Unbounded_Record => + return Synth_Aggregate_Record (Syn_Inst, Aggr, Aggr_Type); + when others => + raise Internal_Error; + end case; + end Synth_Aggregate; + +end Synth.Vhdl_Aggr; |