diff options
| -rw-r--r-- | src/synth/netlists-utils.ads | 7 | ||||
| -rw-r--r-- | src/synth/synth-context.adb | 2 | ||||
| -rw-r--r-- | src/synth/synth-environment-debug.adb | 20 | ||||
| -rw-r--r-- | src/synth/synth-environment.adb | 618 | ||||
| -rw-r--r-- | src/synth/synth-environment.ads | 46 | ||||
| -rw-r--r-- | src/synth/synth-expr.adb | 1 | ||||
| -rw-r--r-- | src/synth/synth-expr.ads | 9 | ||||
| -rw-r--r-- | src/synth/synth-inference.adb | 37 | ||||
| -rw-r--r-- | src/synth/synth-inference.ads | 2 | ||||
| -rw-r--r-- | src/synth/synth-stmts.adb | 48 | 
10 files changed, 608 insertions, 182 deletions
| diff --git a/src/synth/netlists-utils.ads b/src/synth/netlists-utils.ads index bd8bd3e1c..d98eca7ac 100644 --- a/src/synth/netlists-utils.ads +++ b/src/synth/netlists-utils.ads @@ -17,8 +17,14 @@  --  along with this program; if not, write to the Free Software  --  Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,  --  MA 02110-1301, USA. +with Ada.Unchecked_Deallocation;  package Netlists.Utils is +   type Net_Array is array (Int32 range <>) of Net; +   type Net_Array_Acc is access Net_Array; +   procedure Free_Net_Array is new Ada.Unchecked_Deallocation +     (Net_Array, Net_Array_Acc); +     function Get_Nbr_Inputs (Inst : Instance) return Port_Nbr;     function Get_Nbr_Outputs (Inst : Instance) return Port_Nbr;     function Get_Nbr_Params (Inst : Instance) return Param_Nbr; @@ -55,4 +61,5 @@ package Netlists.Utils is     --  Unlink all unused instances of M.     procedure Remove_Unused_Instances (M : Module); +  end Netlists.Utils; diff --git a/src/synth/synth-context.adb b/src/synth/synth-context.adb index be229c4cd..49a5e54ef 100644 --- a/src/synth/synth-context.adb +++ b/src/synth/synth-context.adb @@ -338,7 +338,7 @@ package body Synth.Context is     begin        case Val.Kind is           when Value_Wire => -            return Get_Current_Value (Val.W); +            return Get_Current_Value (Build_Context, Val.W);           when Value_Net =>              return Val.N;           when Value_Mux2 => diff --git a/src/synth/synth-environment-debug.adb b/src/synth/synth-environment-debug.adb index fae810429..ca7c989b8 100644 --- a/src/synth/synth-environment-debug.adb +++ b/src/synth/synth-environment-debug.adb @@ -48,16 +48,28 @@ package body Synth.Environment.Debug is           end if;        end Dump_Value;        Rec : Seq_Assign_Record renames Assign_Table.Table (Asgn); +      P : Partial_Assign;     begin        Put ("Assign" & Seq_Assign'Image (Asgn)); -      Put (" Id:" & Wire_Id'Image (Rec.Id)); +      Put (" Wire Id:" & Wire_Id'Image (Rec.Id));        Put (", prev_assign:" & Seq_Assign'Image (Rec.Prev));        Put (", phi:" & Phi_Id'Image (Rec.Phi));        Put (", chain:" & Seq_Assign'Image (Rec.Chain));        New_Line; -      Put (" value: "); -      Dump_Value (Rec.Value); -      New_Line; +      Put_Line (" value:"); +      P := Rec.Asgns; +      while P /= No_Partial_Assign loop +         declare +            Pasgn : Partial_Assign_Record renames +              Partial_Assign_Table.Table (P); +         begin +            Put (" off:" & Uns32'Image (Pasgn.Offset)); +            Put (", "); +            Dump_Value (Pasgn.Value); +            New_Line; +            P := Pasgn.Next; +         end; +      end loop;     end Dump_Assign;     procedure Dump_Phi (Id : Phi_Id) diff --git a/src/synth/synth-environment.adb b/src/synth/synth-environment.adb index d6f64e21f..1ae10f951 100644 --- a/src/synth/synth-environment.adb +++ b/src/synth/synth-environment.adb @@ -20,7 +20,6 @@  with Netlists.Builders; use Netlists.Builders;  with Netlists.Utils; use Netlists.Utils; -with Netlists.Gates; use Netlists.Gates;  with Errorout; use Errorout;  with Synth.Inference;  with Synth.Errors; use Synth.Errors; @@ -28,6 +27,10 @@ with Vhdl.Nodes;  with Vhdl.Errors; use Vhdl.Errors;  package body Synth.Environment is +   function Get_Current_Assign_Value +     (Ctxt : Builders.Context_Acc; Wid : Wire_Id; Off : Uns32; Wd : Width) +     return Net; +     procedure Set_Wire_Mark (Wid : Wire_Id; Mark : Boolean := True) is     begin        Wire_Id_Table.Table (Wid).Mark_Flag := Mark; @@ -79,6 +82,26 @@ package body Synth.Environment is        Assign_Table.Table (Asgn).Chain := Chain;     end Set_Assign_Chain; +   function Get_Assign_Partial (Asgn : Seq_Assign) return Partial_Assign is +   begin +      return Assign_Table.Table (Asgn).Asgns; +   end Get_Assign_Partial; + +   function Get_Partial_Offset (Asgn : Partial_Assign) return Uns32 is +   begin +      return Partial_Assign_Table.Table (Asgn).Offset; +   end Get_Partial_Offset; + +   function Get_Partial_Value (Asgn : Partial_Assign) return Net is +   begin +      return Partial_Assign_Table.Table (Asgn).Value; +   end Get_Partial_Value; + +   function Get_Partial_Next (Asgn : Partial_Assign) return Partial_Assign is +   begin +      return Partial_Assign_Table.Table (Asgn).Next; +   end Get_Partial_Next; +     function Current_Phi return Phi_Id is     begin        return Phis_Table.Last; @@ -131,7 +154,7 @@ package body Synth.Environment is        Conc_Assign_Table.Table (Asgn).Next := Chain;     end Set_Conc_Chain; -   procedure Add_Conc_Assign_Partial +   procedure Add_Conc_Assign       (Wid : Wire_Id; Val : Net; Off : Uns32; Stmt : Source.Syn_Src)     is        Wire_Rec : Wire_Id_Record renames Wire_Id_Table.Table (Wid); @@ -142,97 +165,6 @@ package body Synth.Environment is                                   Stmt => Stmt));        Wire_Rec.Final_Assign := Conc_Assign_Table.Last;        Wire_Rec.Nbr_Final_Assign := Wire_Rec.Nbr_Final_Assign + 1; -   end Add_Conc_Assign_Partial; - -   function Is_Partial_Assignment (Val : Net; Prev_Val : Net) return Boolean -   is -      Inst : Instance; -      V : Net; -   begin -      if Val = Prev_Val then -         --  This particular case is a loop. -         return False; -      end if; - -      V := Val; -      loop -         Inst := Get_Parent (V); -         if Get_Id (Inst) = Id_Insert then -            V := Get_Input_Net (Inst, 0); -         else -            return V = Prev_Val; -         end if; -      end loop; -   end Is_Partial_Assignment; - -   procedure Add_Conc_Assign_Comb -     (Wid : Wire_Id; Val : Net; Stmt : Source.Syn_Src) -   is -      Wire_Rec : Wire_Id_Record renames Wire_Id_Table.Table (Wid); -   begin -      --  Check for partial assignment. -      if Is_Partial_Assignment (Val, Wire_Rec.Gate) then -         declare -            Wd : constant Width := Get_Width (Val); -            Idx : Uns32; -            Len : Width; -            Inst : Instance; -            V : Net; -            Ins_Idx : Uns32; -            Ins_Inp : Net; -            Ins_Wd : Width; -         begin -            --  Sweep all the bits. -            Idx := 0; -            while Idx < Wd loop -               --  We are interested in bits from Idx to the end. -               Len := Wd - Idx; -               V := Val; -               loop -                  Inst := Get_Parent (V); -                  if Get_Id (Inst) = Id_Insert then -                     Ins_Idx := Get_Param_Uns32 (Inst, 0); -                     Ins_Inp := Get_Input_Net (Inst, 1); -                     Ins_Wd := Get_Width (Ins_Inp); -                     if Idx < Ins_Idx then -                        --  Consider bits before this insert; continue. -                        Len := Ins_Idx - Idx; -                     elsif Idx >= Ins_Idx + Ins_Wd then -                        --  Already handled; continue. -                        null; -                     else -                        --  Partially handled. -                        Len := Ins_Idx + Ins_Wd - Idx; -                        if Len = Ins_Wd and then Idx = Ins_Idx then -                           --  Fully convered by this insert. -                           Add_Conc_Assign_Partial (Wid, Ins_Inp, Idx, Stmt); -                        else -                           --  TODO: extract bits from ins_inp. -                           raise Internal_Error; -                        end if; -                        Idx := Idx + Len; -                        exit; -                     end if; -                     --  Check with next insert gate. -                     V := Get_Input_Net (Inst, 0); -                  else -                     --  Not assigned. -                     pragma Assert (V = Wire_Rec.Gate); -                     Idx := Idx + Len; -                     exit; -                  end if; -               end loop; -            end loop; -         end; -      else -         Add_Conc_Assign_Partial (Wid, Val, 0, Stmt); -      end if; -   end Add_Conc_Assign_Comb; - -   procedure Add_Conc_Assign -     (Wid : Wire_Id; Val : Net; Stmt : Source.Syn_Src) is -   begin -      Add_Conc_Assign_Partial (Wid, Val, 0, Stmt);     end Add_Conc_Assign;     --  This procedure is called after each concurrent statement to assign @@ -254,28 +186,33 @@ package body Synth.Environment is              Outport : constant Net := Wire_Rec.Gate;              --  Must be connected to an Id_Output or Id_Signal              pragma Assert (Outport /= No_Net); -            Gate_Inst : Instance; -            Gate_In : Input; -            Drv : Net; +            P : Partial_Assign;           begin -            Gate_Inst := Get_Parent (Outport); -            Gate_In := Get_Input (Gate_Inst, 0); -            Drv := Get_Driver (Gate_In); -              case Wire_Rec.Kind is                 when Wire_Output                   | Wire_Signal                   | Wire_Variable => -                  if Drv /= No_Net then -                     --  Output already assigned -                     raise Internal_Error; -                  end if; +                  --  Check output is not already assigned. +                  pragma Assert +                    (Get_Input_Net (Get_Parent (Outport), 0) = No_Net); -                  Inference.Infere (Ctxt, Wid, Asgn_Rec.Value, Outport, Stmt);                 when others =>                    raise Internal_Error;              end case; +            P := Asgn_Rec.Asgns; +            pragma Assert (P /= No_Partial_Assign); +            while P /= No_Partial_Assign loop +               declare +                  Pa : Partial_Assign_Record renames +                    Partial_Assign_Table.Table (P); +               begin +                  Inference.Infere +                    (Ctxt, Wid, Pa.Value, Pa.Offset, Outport, Stmt); +                  P := Pa.Next; +               end; +            end loop; +              Asgn := Asgn_Rec.Chain;           end;        end loop; @@ -569,47 +506,284 @@ package body Synth.Environment is        return Res;     end Sort_Phi; -   function Get_Assign_Value (Asgn : Seq_Assign) return Net +   function Get_Assign_Value (Ctxt : Builders.Context_Acc; Asgn : Seq_Assign) +                             return Net     is        Asgn_Rec : Seq_Assign_Record renames Assign_Table.Table (Asgn); +      Wid_Rec : Wire_Id_Record renames Wire_Id_Table.Table (Asgn_Rec.Id); +      W : constant Width := Get_Width (Wid_Rec.Gate);     begin -      case Wire_Id_Table.Table (Asgn_Rec.Id).Kind is +      case Wid_Rec.Kind is           when Wire_Signal | Wire_Output | Wire_Inout | Wire_Variable => -            return Asgn_Rec.Value; +            null;           when Wire_Input | Wire_None =>              raise Internal_Error;        end case; + +      --  Cannot be empty. +      pragma Assert (Asgn_Rec.Asgns /= No_Partial_Assign); + +      --  Simple case: fully assigned. +      declare +         Pasgn : Partial_Assign_Record renames +           Partial_Assign_Table.Table (Asgn_Rec.Asgns); +      begin +         if Pasgn.Offset = 0 and then Get_Width (Pasgn.Value) = W then +            return Pasgn.Value; +         end if; +      end; + +      return Get_Current_Assign_Value (Ctxt, Asgn_Rec.Id, 0, W);     end Get_Assign_Value; -   function Get_Current_Value (Wid : Wire_Id) return Net +   function Get_Current_Value (Ctxt : Builders.Context_Acc; Wid : Wire_Id) +                              return Net     is        Wid_Rec : Wire_Id_Record renames Wire_Id_Table.Table (Wid);     begin        case Wid_Rec.Kind is           when Wire_Variable =>              if Wid_Rec.Cur_Assign = No_Seq_Assign then +               --  The variable was never assigned, so the variable value is +               --  the initial value. +               --  FIXME: use initial value directly ?                 return Wid_Rec.Gate;              else -               return Assign_Table.Table (Wid_Rec.Cur_Assign).Value; +               return Get_Assign_Value (Ctxt, Wid_Rec.Cur_Assign);              end if;           when Wire_Signal | Wire_Output | Wire_Inout | Wire_Input => +            --  For signals, always read the previous value.              return Wid_Rec.Gate;           when Wire_None =>              raise Internal_Error;        end case;     end Get_Current_Value; -   function Get_Last_Assigned_Value (Wid : Wire_Id) return Net +   function Get_Last_Assigned_Value +     (Ctxt : Builders.Context_Acc; Wid : Wire_Id) return Net     is        Wid_Rec : Wire_Id_Record renames Wire_Id_Table.Table (Wid);     begin        if Wid_Rec.Cur_Assign = No_Seq_Assign then           return Wid_Rec.Gate;        else -         return Get_Assign_Value (Wid_Rec.Cur_Assign); +         return Get_Assign_Value (Ctxt, Wid_Rec.Cur_Assign);        end if;     end Get_Last_Assigned_Value; +   --  Get the current value of W for WD bits at offset OFF. +   function Get_Current_Assign_Value +     (Ctxt : Builders.Context_Acc; Wid : Wire_Id; Off : Uns32; Wd : Width) +     return Net +   is +      Wire : Wire_Id_Record renames Wire_Id_Table.Table (Wid); +      First_Seq : Seq_Assign; +   begin +      --  Latest seq assign +      First_Seq := Wire.Cur_Assign; + +      --  If no seq assign, return current value. +      if First_Seq = No_Seq_Assign then +         if Off = 0 and then Wd = Get_Width (Wire.Gate) then +            return Wire.Gate; +         else +            return Build_Extract (Ctxt, Wire.Gate, Off, Wd); +         end if; +      end if; + +      --  If the range is the same as the seq assign, return the value. +      declare +         P : constant Partial_Assign := Get_Assign_Partial (First_Seq); +         V : Net; +      begin +         if Get_Partial_Offset (P) = Off then +            V := Get_Partial_Value (P); +            if Get_Width (V) = Wd then +               return V; +            end if; +         end if; +      end; + +      --  Build a vector +      declare +         Vec : Net_Tables.Instance; +         Seq : Seq_Assign; +         P : Partial_Assign; +         Cur_Off : Uns32; +         Cur_Wd : Width; + +         Last : Int32; +         Inst : Instance; +         Res : Net; +      begin +         Net_Tables.Init (Vec); +         Cur_Off := Off; +         Cur_Wd := Wd; +         pragma Assert (Wd > 0); +         loop +            --  Find value at CUR_OFF from assignment. +            Seq := First_Seq; +            P := Get_Assign_Partial (Seq); +            loop +               pragma Assert (P /= No_Partial_Assign); +               declare +                  Pr : Partial_Assign_Record renames +                    Partial_Assign_Table.Table (P); +                  Pw : constant Width := Get_Width (Pr.Value); +               begin +                  if Pr.Offset <= Cur_Off +                    and then Pr.Offset + Pw > Cur_Off +                  then +                     --  Found. +                     if Pr.Offset = Cur_Off and then Pw = Cur_Wd then +                        --  No need to extract. +                        Net_Tables.Append (Vec, Pr.Value); +                     else +                        Cur_Wd := Width'Min +                          (Cur_Wd, Pw - (Cur_Off - Pr.Offset)); +                        Net_Tables.Append +                          (Vec, Build_Extract (Ctxt, Pr.Value, +                                               Cur_Off - Pr.Offset, Cur_Wd)); +                     end if; +                     exit; +                  end if; +                  if Pr.Offset + Pw < Cur_Off then +                     --  Next partial; +                     P := Pr.Next; +                  elsif Pr.Offset > Cur_Off +                    and then Pr.Offset < Cur_Off + Cur_Wd +                  then +                     --  Reduce WD and continue to search in previous; +                     Cur_Wd := Pr.Offset - Cur_Off; +                     P := No_Partial_Assign; +                  else +                     --  Continue to search in previous. +                     P := No_Partial_Assign; +                  end if; +                  if P = No_Partial_Assign then +                     Seq := Get_Assign_Prev (Seq); +                     if Seq = No_Seq_Assign then +                        --  Extract from gate. +                        Net_Tables.Append +                          (Vec, Build_Extract (Ctxt, Wire.Gate, +                                               Cur_Off, Cur_Wd)); +                        exit; +                     end if; +                  end if; +               end; +            end loop; + +            Cur_Off := Cur_Off + Cur_Wd; +            Cur_Wd := Wd - (Cur_Off - Off); +            exit when Cur_Off = Off + Wd; +         end loop; + +         --  Concat +         Last := Net_Tables.Last (Vec); +         case Last is +            when Int32'First .. 0 => +               raise Internal_Error; +            when 1 => +               Res := Vec.Table (1); +            when 2 => +               Res := Build_Concat2 (Ctxt, Vec.Table (1), Vec.Table (2)); +            when 3 => +               Res := Build_Concat3 +                 (Ctxt, Vec.Table (1), Vec.Table (2), Vec.Table (3)); +            when 4 => +               Res := Build_Concat4 +                 (Ctxt, +                  Vec.Table (1), Vec.Table (2), Vec.Table (3), Vec.Table (4)); +            when 5 .. Int32'Last => +               Res := Build_Concatn (Ctxt, Wd, Uns32 (Last)); +               Inst := Get_Parent (Res); +               for I in Net_Tables.First .. Last loop +                  Connect (Get_Input (Inst, Port_Idx (I - 1)), Vec.Table (I)); +               end loop; +         end case; +         --  Free the vector and return it. +         Net_Tables.Free (Vec); +         return Res; +      end; +   end Get_Current_Assign_Value; + +   procedure Merge_Assigns (Ctxt : Builders.Context_Acc; +                            W : Wire_Id; +                            Sel : Net; +                            F_Asgns : Partial_Assign; +                            T_Asgns : Partial_Assign) +   is +      P : Partial_Assign_Array (0 .. 1); +      N : Net_Array (0 .. 1); +      Min_Off : Uns32; +      Off : Uns32; +      Wd : Width; +      Res : Net; +   begin +      P := (0 => F_Asgns, 1 => T_Asgns); + +      Min_Off := 0; +      loop +         --  Look for the partial assign with the least offset (but still +         --  greather than Min_Off).  Also extract the least width. +         Off := Uns32'Last; +         Wd := Width'Last; +         for I in P'Range loop +            if P (I) /= No_Partial_Assign then +               declare +                  Pa : Partial_Assign_Record +                    renames Partial_Assign_Table.Table (P (I)); +               begin +                  if Pa.Offset <= Off then +                     Off := Uns32'Max (Pa.Offset, Min_Off); +                     Wd := Width'Min +                       (Wd, Get_Width (Pa.Value) - (Off - Pa.Offset)); +                  end if; +               end; +            end if; +         end loop; + +         --  No more assignments. +         if Off = Uns32'Last and Wd = Width'Last then +            return; +         end if; + +         --  Get the values for that offset/width.  Update lists. +         for I in P'Range loop +            if P (I) /= No_Partial_Assign +              and then Get_Partial_Offset (P (I)) <= Off +            then +               declare +                  Val : constant Net := Get_Partial_Value (P (I)); +                  P_W : constant Width := Get_Width (Val); +                  P_Off : constant Uns32 := Get_Partial_Offset (P (I)); +               begin +                  --  There is a partial assignment. +                  if P_Off = Off and then P_W = Wd then +                     --  Full covered. +                     N (I) := Val; +                     P (I) := Get_Partial_Next (P (I)); +                  else +                     N (I) := Build_Extract (Ctxt, Val, Off - P_Off, Wd); +                     if P_Off + P_W = Off + Wd then +                        P (I) := Get_Partial_Next (P (I)); +                     end if; +                  end if; +               end; +            else +               --  No partial assignment.  Get extract previous value. +               N (I) := Get_Current_Assign_Value (Ctxt, W, Off, Wd); +            end if; +         end loop; + +         --  Build mux. +         Res := Netlists.Builders.Build_Mux2 (Ctxt, Sel, N (0), N (1)); +         Phi_Assign (Ctxt, W, Res, Off); + +         Min_Off := Off + Wd; +      end loop; +   end Merge_Assigns; +     --  Add muxes for two lists T and F of assignments.     procedure Merge_Phis (Ctxt : Builders.Context_Acc;                           Sel : Net; @@ -618,8 +792,7 @@ package body Synth.Environment is        T_Asgns : Seq_Assign;        F_Asgns : Seq_Assign;        W : Wire_Id; -      Te, Fe : Net; -      Res : Net; +      Tp, Fp : Partial_Assign;     begin        T_Asgns := Sort_Phi (T);        F_Asgns := Sort_Phi (F); @@ -632,8 +805,8 @@ package body Synth.Environment is           then              --  Has an assignment only for the false branch.              W := Get_Wire_Id (F_Asgns); -            Te := Get_Last_Assigned_Value (W); -            Fe := Get_Assign_Value (F_Asgns); +            Fp := Get_Assign_Partial (F_Asgns); +            Tp := No_Partial_Assign;              F_Asgns := Get_Assign_Chain (F_Asgns);           elsif F_Asgns = No_Seq_Assign             or else (T_Asgns /= No_Seq_Assign @@ -641,20 +814,20 @@ package body Synth.Environment is           then              --  Has an assignment only for the true branch.              W := Get_Wire_Id (T_Asgns); -            Te := Get_Assign_Value (T_Asgns); -            Fe := Get_Last_Assigned_Value (W); +            Fp := No_Partial_Assign; +            Tp := Get_Assign_Partial (T_Asgns);              T_Asgns := Get_Assign_Chain (T_Asgns);           else              --  Has assignments for both the true and the false branch.              pragma Assert (Get_Wire_Id (F_Asgns) = Get_Wire_Id (T_Asgns));              W := Get_Wire_Id (F_Asgns); -            Te := Get_Assign_Value (T_Asgns); -            Fe := Get_Assign_Value (F_Asgns); +            Fp := Get_Assign_Partial (F_Asgns); +            Tp := Get_Assign_Partial (T_Asgns);              T_Asgns := Get_Assign_Chain (T_Asgns);              F_Asgns := Get_Assign_Chain (F_Asgns);           end if; -         Res := Netlists.Builders.Build_Mux2 (Ctxt, Sel, Fe, Te); -         Phi_Assign (W, Res); +         Merge_Assigns (Ctxt, W, Sel, Fp, Tp); +        end loop;     end Merge_Phis; @@ -672,25 +845,205 @@ package body Synth.Environment is        P.Nbr := P.Nbr + 1;     end Phi_Insert_Assign; -   procedure Phi_Assign (Dest : Wire_Id; Val : Net) +   --  Check consistency: +   --  - ordered. +   --  - no overlaps. +   procedure Check (Seq : Seq_Assign) +   is +      Seq_Asgn : Seq_Assign_Record renames Assign_Table.Table (Seq); +      Prev_El : Partial_Assign; +   begin +      Prev_El := Seq_Asgn.Asgns; +      if Prev_El = No_Partial_Assign then +         --  It's empty! +         return; +      end if; +      loop +         declare +            Prev : Partial_Assign_Record +              renames Partial_Assign_Table.Table (Prev_El); +            El : constant Partial_Assign := Prev.Next; +         begin +            if El = No_Partial_Assign then +               --  Done. +               exit; +            end if; +            declare +               Cur : Partial_Assign_Record +                 renames Partial_Assign_Table.Table (El); +            begin +               --  Check no overlap. +               if Cur.Offset < Prev.Offset + Get_Width (Prev.Value) then +                  raise Internal_Error; +               end if; +            end; +            Prev_El := El; +         end; +      end loop; +   end Check; + +   --  Insert partial assignment ASGN to list SEQ. +   --  Deal with overrides.  Place it correctly. +   procedure Insert_Partial_Assign +     (Ctxt : Builders.Context_Acc; Seq : Seq_Assign; Asgn : Partial_Assign) +   is +      V : Partial_Assign_Record renames Partial_Assign_Table.Table (Asgn); +      V_Next : constant Uns32 := V.Offset + Get_Width (V.Value); +      Seq_Asgn : Seq_Assign_Record renames Assign_Table.Table (Seq); +      El, Last_El : Partial_Assign; +      Inserted : Boolean; +   begin +      Inserted := False; +      Last_El := No_Partial_Assign; +      El := Seq_Asgn.Asgns; +      while El /= No_Partial_Assign loop +         declare +            P : Partial_Assign_Record renames Partial_Assign_Table.Table (El); +            P_Next : constant Uns32 := P.Offset + Get_Width (P.Value); +         begin +            if V.Offset < P_Next and then V_Next > P.Offset then +               --  Override. +               if V.Offset <= P.Offset and then V_Next >= P_Next then +                  --  Full override: +                  --     V.Off               V.Next +                  --     |------------------|| +                  --           |----------|| +                  --          P.Off        P.Next +                  --  Remove it. +                  --  FIXME: free it. +                  if not Inserted then +                     if Last_El /= No_Partial_Assign then +                        Partial_Assign_Table.Table (Last_El).Next := Asgn; +                     else +                        Seq_Asgn.Asgns := Asgn; +                     end if; +                     V.Next := P.Next; +                     Inserted := True; +                     Last_El := Asgn; +                  else +                     pragma Assert (Last_El /= No_Partial_Assign); +                     Partial_Assign_Table.Table (Last_El).Next := P.Next; +                  end if; +               elsif V.Offset <= P.Offset and then V_Next < P_Next then +                  --  Overrides the beginning of EL. +                  --     V.Off           V.Next +                  --     |--------------|| +                  --           |----------|| +                  --          P.Off        P.Next +                  --  Shrink EL. +                  P.Value := Build_Extract (Ctxt, P.Value, +                                            Off => V_Next - P.Offset, +                                            W => P_Next - V_Next); +                  P.Offset := V_Next; +                  if not Inserted then +                     if Last_El /= No_Partial_Assign then +                        Partial_Assign_Table.Table (Last_El).Next := Asgn; +                     else +                        Seq_Asgn.Asgns := Asgn; +                     end if; +                     V.Next := El; +                     Inserted := True; +                  end if; +                  --  No more possible overlaps. +                  exit; +               elsif V.Offset > P.Offset and then P_Next <= V_Next then +                  --  Overrides the end of EL. +                  --             V.Off               V.Next +                  --             |------------------|| +                  --           |----------|| +                  --          P.Off        P.Next +                  --  Shrink EL. +                  P.Value := Build_Extract (Ctxt, P.Value, +                                            Off => 0, +                                            W => V.Offset - P.Offset); +                  pragma Assert (not Inserted); +                  V.Next := P.Next; +                  P.Next := Asgn; +                  Last_El := Asgn; +                  Inserted := True; +               elsif V.Offset > P.Offset and then V_Next < P_Next then +                  --  Contained within EL. +                  --             V.Off       V.Next +                  --             |----------|| +                  --           |---------------|| +                  --          P.Off             P.Next +                  --  Split EL. +                  pragma Assert (not Inserted); +                  Partial_Assign_Table.Append +                    ((Next => P.Next, +                      Value => Build_Extract (Ctxt, P.Value, +                                              Off => V_Next - P.Offset, +                                              W => P_Next - V_Next), +                      Offset => V_Next)); +                  V.Next := Partial_Assign_Table.Last; +                  P.Value := Build_Extract (Ctxt, P.Value, +                                            Off => 0, +                                            W => V.Offset - P.Offset); +                  P.Next := Asgn; +                  Inserted := True; +                  --  No more possible overlaps. +                  exit; +               else +                  --  No other case. +                  raise Internal_Error; +               end if; +            else +               if V.Offset < P.Offset then +                  --  Insert before P (if not already inserted). +                  if not Inserted then +                     if Last_El /= No_Partial_Assign then +                        Partial_Assign_Table.Table (Last_El).Next := Asgn; +                     else +                        Seq_Asgn.Asgns := Asgn; +                     end if; +                     V.Next := El; +                     Inserted := True; +                  end if; +                  exit; +               elsif P.Next = No_Partial_Assign then +                  if not Inserted then +                     --  Insert after P. +                     P.Next := Asgn; +                     Inserted := True; +                  end if; +                  exit; +               else +                  Last_El := El; +               end if; +            end if; + +            El := P.Next; +         end; +      end loop; +      pragma Assert (Inserted); +      pragma Debug (Check (Seq)); +   end Insert_Partial_Assign; + +   procedure Phi_Assign +     (Ctxt : Builders.Context_Acc; Dest : Wire_Id; Val : Net; Offset : Uns32)     is        Cur_Asgn : constant Seq_Assign := Wire_Id_Table.Table (Dest).Cur_Assign; +      Pasgn : Partial_Assign;     begin +      Partial_Assign_Table.Append ((Next => No_Partial_Assign, +                                    Value => Val, +                                    Offset => Offset)); +      Pasgn := Partial_Assign_Table.Last; +        if Cur_Asgn = No_Seq_Assign          or else Assign_Table.Table (Cur_Asgn).Phi < Current_Phi        then           --  Never assigned, or first assignment in that level           Assign_Table.Append ((Phi => Current_Phi, -                              Id => Dest, -                              Prev => Cur_Asgn, -                              Chain => No_Seq_Assign, -                              Value => Val)); +                               Id => Dest, +                               Prev => Cur_Asgn, +                               Chain => No_Seq_Assign, +                               Asgns => Pasgn));           Wire_Id_Table.Table (Dest).Cur_Assign := Assign_Table.Last;           Phi_Insert_Assign (Assign_Table.Last);        else           --  Overwrite. -         --  FIXME: may need to merge in case of partial assignment. -         Assign_Table.Table (Cur_Asgn).Value := Val; +         Insert_Partial_Assign (Ctxt, Cur_Asgn, Pasgn);        end if;     end Phi_Assign;  begin @@ -707,9 +1060,14 @@ begin                          Id => No_Wire_Id,                          Prev => No_Seq_Assign,                          Chain => No_Seq_Assign, -                        Value => No_Net)); +                        Asgns => No_Partial_Assign));     pragma Assert (Assign_Table.Last = No_Seq_Assign); +   Partial_Assign_Table.Append ((Next => No_Partial_Assign, +                                 Value => No_Net, +                                 Offset => 0)); +   pragma Assert (Partial_Assign_Table.Last = No_Partial_Assign); +     Phis_Table.Append ((First => No_Seq_Assign,                         Nbr => 0));     pragma Assert (Phis_Table.Last = No_Phi_Id); diff --git a/src/synth/synth-environment.ads b/src/synth/synth-environment.ads index f57ab0ab6..604991dd5 100644 --- a/src/synth/synth-environment.ads +++ b/src/synth/synth-environment.ads @@ -19,6 +19,7 @@  --  MA 02110-1301, USA.  with Types; use Types; +with Dyn_Tables;  with Tables;  with Netlists; use Netlists;  with Netlists.Builders; @@ -65,10 +66,12 @@ package Synth.Environment is     --  The current value of WID.  For variables, this is the last assigned     --  value.  For signals, this is the initial value. -   function Get_Current_Value (Wid : Wire_Id) return Net; +   function Get_Current_Value (Ctxt : Builders.Context_Acc; Wid : Wire_Id) +                              return Net;     --  The last assigned value to WID. -   function Get_Last_Assigned_Value (Wid : Wire_Id) return Net; +   function Get_Last_Assigned_Value +     (Ctxt : Builders.Context_Acc; Wid : Wire_Id) return Net;     --  Read and write the mark flag.     function Get_Wire_Mark (Wid : Wire_Id) return Boolean; @@ -79,7 +82,9 @@ package Synth.Environment is     function Get_Wire_Id (W : Seq_Assign) return Wire_Id;     function Get_Assign_Chain (Asgn : Seq_Assign) return Seq_Assign; -   function Get_Assign_Value (Asgn : Seq_Assign) return Net; + +   function Get_Assign_Value (Ctxt : Builders.Context_Acc; Asgn : Seq_Assign) +                             return Net;     type Phi_Type is private; @@ -103,15 +108,15 @@ package Synth.Environment is     function Sort_Phi (P : Phi_Type) return Seq_Assign;     --  In the current phi context, assign VAL to DEST. -   procedure Phi_Assign (Dest : Wire_Id; Val : Net); +   procedure Phi_Assign +     (Ctxt : Builders.Context_Acc; Dest : Wire_Id; Val : Net; Offset : Uns32);     --  Get current phi context.     function Current_Phi return Phi_Id;     pragma Inline (Current_Phi); -   procedure Add_Conc_Assign (Wid : Wire_Id; Val : Net; Stmt : Source.Syn_Src); -   procedure Add_Conc_Assign_Comb -     (Wid : Wire_Id; Val : Net; Stmt : Source.Syn_Src); +   procedure Add_Conc_Assign +     (Wid : Wire_Id; Val : Net; Off : Uns32; Stmt : Source.Syn_Src);     procedure Finalize_Assignments (Ctxt : Builders.Context_Acc);  private @@ -123,6 +128,11 @@ private     type Seq_Assign is new Uns32;     No_Seq_Assign : constant Seq_Assign := 0; +   type Partial_Assign is new Uns32; +   No_Partial_Assign : constant Partial_Assign := 0; + +   type Partial_Assign_Array is array (Int32 range <>) of Partial_Assign; +     type Conc_Assign is new Uns32;     No_Conc_Assign : constant Conc_Assign := 0; @@ -173,8 +183,16 @@ private        --  Next wire in the phi context.        Chain : Seq_Assign; -      --  Value assigned. +      --  Values assigned. +      Asgns : Partial_Assign; +   end record; + +   type Partial_Assign_Record is record +      Next : Partial_Assign; + +      --  Assignment at OFFSET.  The width is set by the width of the value.        Value : Net; +      Offset : Uns32;     end record;     type Conc_Assign_Record is record @@ -213,9 +231,21 @@ private        Table_Low_Bound => No_Seq_Assign,        Table_Initial => 1024); +   package Partial_Assign_Table is new Tables +     (Table_Component_Type => Partial_Assign_Record, +      Table_Index_Type => Partial_Assign, +      Table_Low_Bound => No_Partial_Assign, +      Table_Initial => 1024); +     package Conc_Assign_Table is new Tables       (Table_Component_Type => Conc_Assign_Record,        Table_Index_Type => Conc_Assign,        Table_Low_Bound => No_Conc_Assign,        Table_Initial => 1024); + +   package Net_Tables is new Dyn_Tables +     (Table_Component_Type => Net, +      Table_Index_Type => Int32, +      Table_Low_Bound => 1, +      Table_Initial => 32);  end Synth.Environment; diff --git a/src/synth/synth-expr.adb b/src/synth/synth-expr.adb index 5c0abe189..a21309b47 100644 --- a/src/synth/synth-expr.adb +++ b/src/synth/synth-expr.adb @@ -40,7 +40,6 @@ with Synth.Environment; use Synth.Environment;  with Netlists.Gates; use Netlists.Gates;  with Netlists.Builders; use Netlists.Builders; -with Netlists.Utils; use Netlists.Utils;  with Netlists.Locations; use Netlists.Locations;  package body Synth.Expr is diff --git a/src/synth/synth-expr.ads b/src/synth/synth-expr.ads index 06b824fe2..9292ab105 100644 --- a/src/synth/synth-expr.ads +++ b/src/synth/synth-expr.ads @@ -18,9 +18,11 @@  --  Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,  --  MA 02110-1301, USA. -with Ada.Unchecked_Deallocation;  with Types; use Types; +  with Netlists; use Netlists; +with Netlists.Utils; use Netlists.Utils; +  with Synth.Source;  with Synth.Values; use Synth.Values;  with Synth.Context; use Synth.Context; @@ -51,11 +53,6 @@ package Synth.Expr is     function Bit_Extract (Val : Value_Acc; Off : Uns32; Loc : Node)                          return Value_Acc; -   type Net_Array is array (Int32 range <>) of Net; -   type Net_Array_Acc is access Net_Array; -   procedure Free_Net_Array is new Ada.Unchecked_Deallocation -     (Net_Array, Net_Array_Acc); -     function Concat_Array (Arr : Net_Array_Acc) return Net;     function Synth_Expression_With_Type diff --git a/src/synth/synth-inference.adb b/src/synth/synth-inference.adb index a6b4cd094..8ff6dc1a6 100644 --- a/src/synth/synth-inference.adb +++ b/src/synth/synth-inference.adb @@ -21,7 +21,6 @@  with Netlists.Utils; use Netlists.Utils;  with Netlists.Gates; use Netlists.Gates;  with Netlists.Gates_Ports; use Netlists.Gates_Ports; -with Types; use Types;  package body Synth.Inference is     --  DFF inference. @@ -177,9 +176,32 @@ package body Synth.Inference is        end case;     end Extract_Clock; +   procedure Check_FF_Else (Els : Net; Prev_Val : Net; Off : Uns32) +   is +      Inst : Instance; +   begin +      if Els = Prev_Val then +         if Off /= 0 then +            raise Internal_Error; +         end if; +         return; +      end if; +      Inst := Get_Parent (Els); +      if Get_Id (Inst) /= Id_Extract then +         raise Internal_Error; +      end if; +      if Get_Param_Uns32 (Inst, 0) /= Off then +         raise Internal_Error; +      end if; +      if Get_Input_Net (Inst, 0) /= Prev_Val then +         raise Internal_Error; +      end if; +   end Check_FF_Else; +     procedure Infere_FF (Ctxt : Context_Acc;                          Wid : Wire_Id;                          Prev_Val : Net; +                        Off : Uns32;                          Last_Mux : Instance;                          Clk : Net;                          Enable : Net; @@ -202,10 +224,8 @@ package body Synth.Inference is        --  1. Remove the mux that creates the loop (will be replaced by the        --     dff).        Disconnect (Sel); -      if Get_Driver (I0) /= Prev_Val then -         --  There must be no 'else' part for clock expression. -         raise Internal_Error; -      end if; +      --  There must be no 'else' part for clock expression. +      Check_FF_Else (Get_Driver (I0), Prev_Val, Off);        --  Don't try to free driver of I0 as this is Prev_Val.        Disconnect (I0);        Data := Get_Driver (I1); @@ -316,12 +336,13 @@ package body Synth.Inference is        Free_Instance (Last_Mux); -      Add_Conc_Assign (Wid, Res, Stmt); +      Add_Conc_Assign (Wid, Res, Off, Stmt);     end Infere_FF;     procedure Infere (Ctxt : Context_Acc;                       Wid : Wire_Id;                       Val : Net; +                     Off : Uns32;                       Prev_Val : Net;                       Stmt : Source.Syn_Src)     is @@ -336,7 +357,7 @@ package body Synth.Inference is        Find_Longest_Loop (Val, Prev_Val, Last_Mux, Len);        if Len <= 0 then           --  No logical loop or self assignment. -         Add_Conc_Assign_Comb (Wid, Val, Stmt); +         Add_Conc_Assign (Wid, Val, Off, Stmt);        else           --  So there is a logical loop.           Sel := Get_Mux2_Sel (Last_Mux); @@ -346,7 +367,7 @@ package body Synth.Inference is              raise Internal_Error;           else              --  Clock -> FF -            Infere_FF (Ctxt, Wid, Prev_Val, Last_Mux, Clk, Enable, Stmt); +            Infere_FF (Ctxt, Wid, Prev_Val, Off, Last_Mux, Clk, Enable, Stmt);           end if;        end if;     end Infere; diff --git a/src/synth/synth-inference.ads b/src/synth/synth-inference.ads index 371932f3e..377b481ab 100644 --- a/src/synth/synth-inference.ads +++ b/src/synth/synth-inference.ads @@ -18,6 +18,7 @@  --  Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,  --  MA 02110-1301, USA. +with Types; use Types;  with Netlists; use Netlists;  with Netlists.Builders; use Netlists.Builders;  with Synth.Environment; use Synth.Environment; @@ -30,6 +31,7 @@ package Synth.Inference is     procedure Infere (Ctxt : Context_Acc;                       Wid : Wire_Id;                       Val : Net; +                     Off : Uns32;                       Prev_Val : Net;                       Stmt : Source.Syn_Src);  end Synth.Inference; diff --git a/src/synth/synth-stmts.adb b/src/synth/synth-stmts.adb index 96efdca72..f37b1388e 100644 --- a/src/synth/synth-stmts.adb +++ b/src/synth/synth-stmts.adb @@ -48,7 +48,7 @@ with Vhdl.Annotations; use Vhdl.Annotations;  with Netlists; use Netlists;  with Netlists.Builders; use Netlists.Builders;  with Netlists.Gates; -with Netlists.Utils; +with Netlists.Utils; use Netlists.Utils;  with Netlists.Locations; use Netlists.Locations;  package body Synth.Stmts is @@ -76,13 +76,15 @@ package body Synth.Stmts is        end if;     end Synth_Waveform; -   procedure Synth_Assign -     (Dest : Value_Acc; Val : Value_Acc; Loc : Source.Syn_Src) is +   procedure Synth_Assign (Dest : Value_Acc; +                           Val : Value_Acc; +                           Offset : Uns32; +                           Loc : Source.Syn_Src) is     begin        pragma Assert (Dest.Kind = Value_Wire); -      Phi_Assign -        (Dest.W, -         Get_Net (Synth_Subtype_Conversion (Val, Dest.Typ, Loc))); +      Phi_Assign (Build_Context, Dest.W, +                  Get_Net (Synth_Subtype_Conversion (Val, Dest.Typ, Loc)), +                  Offset);     end Synth_Assign;     procedure Synth_Assignment_Aggregate (Syn_Inst : Synth_Instance_Acc; @@ -136,20 +138,19 @@ package body Synth.Stmts is        Synth_Indexed_Name (Syn_Inst, Target, Targ.Typ, Voff, Mul, Off, W);        pragma Assert (Get_Type_Width (Val.Typ) = W); -      Targ_Net := Get_Last_Assigned_Value (Targ.W); -      Val_Net := Get_Net (Val);        if Voff = No_Net then           --  FIXME: check index.           pragma Assert (Mul = 0); -         V := Build_Insert (Build_Context, Targ_Net, Val_Net, Off); -         Set_Location (V, Target); +         Synth_Assign (Targ, Val, Off, Loc);        else +         Targ_Net := Get_Last_Assigned_Value (Build_Context, Targ.W); +         Val_Net := Get_Net (Val);           V := Build_Dyn_Insert             (Build_Context, Targ_Net, Val_Net, Voff, Mul, Int32 (Off));           Set_Location (V, Target); +         Synth_Assign (Targ, Create_Value_Net (V, Targ.Typ), 0, Loc);        end if; -      Synth_Assign (Targ, Create_Value_Net (V, Targ.Typ), Loc);     end Synth_Indexed_Assignment;     procedure Synth_Assignment (Syn_Inst : Synth_Instance_Acc; @@ -164,7 +165,7 @@ package body Synth.Stmts is             | Iir_Kind_Variable_Declaration             | Iir_Kind_Signal_Declaration             | Iir_Kind_Anonymous_Signal_Declaration => -            Synth_Assign (Get_Value (Syn_Inst, Target), Val, Loc); +            Synth_Assign (Get_Value (Syn_Inst, Target), Val, 0, Loc);           when Iir_Kind_Aggregate =>              Synth_Assignment_Aggregate (Syn_Inst, Target, Val, Loc);           when Iir_Kind_Indexed_Name => @@ -190,18 +191,18 @@ package body Synth.Stmts is                 end if;                 Synth_Slice_Suffix (Syn_Inst, Target, Targ.Typ.Vbound,                                     Res_Bnd, Inp, Step, Off, Wd); -               Targ_Net := Get_Last_Assigned_Value (Targ.W); -               V := Get_Net (Val);                 if Inp /= No_Net then +                  Targ_Net := Get_Last_Assigned_Value (Build_Context, Targ.W); +                  V := Get_Net (Val);                    Res := Build_Dyn_Insert                      (Build_Context, Targ_Net, V, Inp, Step, Off); +                  Set_Location (Res, Target); +                  Res_Type := Create_Vector_Type (Res_Bnd, Targ.Typ.Vec_El); +                  Synth_Assign +                    (Targ, Create_Value_Net (Res, Res_Type), 0, Loc);                 else -                  Res := Build_Insert -                    (Build_Context, Targ_Net, V, Uns32 (Off)); +                  Synth_Assign (Targ, Val, Uns32 (Off), Loc);                 end if; -               Set_Location (Res, Target); -               Res_Type := Create_Vector_Type (Res_Bnd, Targ.Typ.Vec_El); -               Synth_Assign (Targ, Create_Value_Net (Res, Res_Type), Loc);              end;           when others =>              Error_Kind ("synth_assignment", Target); @@ -750,7 +751,8 @@ package body Synth.Stmts is        for I in Wires'Range loop           declare              Wi : constant Wire_Id := Wires (I); -            Last_Val : constant Net := Get_Last_Assigned_Value (Wi); +            Last_Val : constant Net := +              Get_Last_Assigned_Value (Build_Context, Wi);              Res : Net;              Default : Net;              C : Natural; @@ -761,7 +763,7 @@ package body Synth.Stmts is                 --  value.  Otherwise, use Last_Val, ie the last assignment                 --  before the case.                 if Get_Wire_Id (Alt.Asgns) = Wi then -                  Alt.Val := Get_Assign_Value (Alt.Asgns); +                  Alt.Val := Get_Assign_Value (Build_Context, Alt.Asgns);                    Alt.Asgns := Get_Assign_Chain (Alt.Asgns);                 else                    Alt.Val := Last_Val; @@ -784,7 +786,7 @@ package body Synth.Stmts is              --  Generate the muxes tree.              Synth_Case (Sel_Net, Case_El.all, Default, Res); -            Phi_Assign (Wi, Res); +            Phi_Assign (Build_Context, Wi, Res, 0);           end;        end loop; @@ -1358,7 +1360,6 @@ package body Synth.Stmts is     function Synth_Psl_Sequence_Directive       (Syn_Inst : Synth_Instance_Acc; Stmt : Node) return Net     is -      use Netlists.Utils;        use Netlists.Gates;        Nbr_States : constant Int32 := Get_PSL_Nbr_States (Stmt);        Init : Net; @@ -1416,7 +1417,6 @@ package body Synth.Stmts is     is        use PSL.Types;        use PSL.NFAs; -      use Netlists.Utils;        use Netlists.Gates;        NFA : constant PSL_NFA := Get_PSL_NFA (Stmt);        Nbr_States : constant Int32 := Get_PSL_Nbr_States (Stmt); | 
