aboutsummaryrefslogtreecommitdiffstats
path: root/src/synth/synth-environment.adb
diff options
context:
space:
mode:
Diffstat (limited to 'src/synth/synth-environment.adb')
-rw-r--r--src/synth/synth-environment.adb297
1 files changed, 274 insertions, 23 deletions
diff --git a/src/synth/synth-environment.adb b/src/synth/synth-environment.adb
index ce8bb6983..3443a741a 100644
--- a/src/synth/synth-environment.adb
+++ b/src/synth/synth-environment.adb
@@ -18,10 +18,14 @@
-- Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
-- MA 02110-1301, USA.
+with Netlists.Builders; use Netlists.Builders;
with Netlists.Utils; use Netlists.Utils;
with Netlists.Gates; use Netlists.Gates;
-with Netlists.Builders; use Netlists.Builders;
+with Errorout; use Errorout;
with Synth.Inference;
+with Synth.Errors; use Synth.Errors;
+with Vhdl.Nodes;
+with Vhdl.Errors; use Vhdl.Errors;
package body Synth.Environment is
procedure Set_Wire_Mark (Wid : Wire_Id; Mark : Boolean := True) is
@@ -41,7 +45,9 @@ package body Synth.Environment is
Mark_Flag => False,
Decl => Obj,
Gate => No_Net,
- Cur_Assign => No_Seq_Assign));
+ Cur_Assign => No_Seq_Assign,
+ Final_Assign => No_Conc_Assign,
+ Nbr_Final_Assign => 0));
return Wire_Id_Table.Last;
end Alloc_Wire;
@@ -105,9 +111,62 @@ package body Synth.Environment is
end loop;
end Pop_Phi;
+ function Get_Conc_Offset (Asgn : Conc_Assign) return Uns32 is
+ begin
+ return Conc_Assign_Table.Table (Asgn).Offset;
+ end Get_Conc_Offset;
+
+ function Get_Conc_Value (Asgn : Conc_Assign) return Net is
+ begin
+ return Conc_Assign_Table.Table (Asgn).Value;
+ end Get_Conc_Value;
+
+ function Get_Conc_Chain (Asgn : Conc_Assign) return Conc_Assign is
+ begin
+ return Conc_Assign_Table.Table (Asgn).Next;
+ end Get_Conc_Chain;
+
+ procedure Set_Conc_Chain (Asgn : Conc_Assign; Chain : Conc_Assign) is
+ begin
+ Conc_Assign_Table.Table (Asgn).Next := Chain;
+ end Set_Conc_Chain;
+
+ procedure Add_Conc_Assign
+ (Wire_Rec : in out Wire_Id_Record; Val : Net; Stmt : Source.Syn_Src)
+ is
+ Inst : constant Instance := Get_Parent (Val);
+ V : Net;
+ Off : Uns32;
+ Inp : Input;
+ begin
+ -- Check for partial assignment.
+ if Get_Id (Inst) = Id_Insert
+ and then Get_Input_Net (Inst, 0) = Wire_Rec.Gate
+ then
+ -- TODO: handle multiple partial assignments
+ -- (like o (1) <= x; o (3) <= y;)
+ -- TODO: handle dyn assignment (like o (i) <= x;)
+ Inp := Get_Input (Inst, 1);
+ V := Get_Driver (Inp);
+ Off := Get_Param_Uns32 (Inst, 0);
+ Disconnect (Inp);
+ Free_Instance (Inst);
+ else
+ V := Val;
+ Off := 0;
+ end if;
+ Conc_Assign_Table.Append ((Next => Wire_Rec.Final_Assign,
+ Value => V,
+ Offset => Off,
+ 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;
+
-- This procedure is called after each concurrent statement to assign
-- values to signals.
- procedure Pop_And_Merge_Phi (Ctxt : Builders.Context_Acc)
+ procedure Pop_And_Merge_Phi (Ctxt : Builders.Context_Acc;
+ Stmt : Source.Syn_Src)
is
Phi : Phi_Type;
Asgn : Seq_Assign;
@@ -118,42 +177,31 @@ package body Synth.Environment is
while Asgn /= No_Seq_Assign loop
declare
Asgn_Rec : Seq_Assign_Record renames Assign_Table.Table (Asgn);
- Outport : constant Net := Wire_Id_Table.Table (Asgn_Rec.Id).Gate;
+ Wid : constant Wire_Id := Asgn_Rec.Id;
+ Wire_Rec : Wire_Id_Record renames Wire_Id_Table.Table (Wid);
+ 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;
- New_Sig : Net;
begin
Gate_Inst := Get_Parent (Outport);
Gate_In := Get_Input (Gate_Inst, 0);
Drv := Get_Driver (Gate_In);
- case Wire_Id_Table.Table (Asgn_Rec.Id).Kind is
+ case Wire_Rec.Kind is
when Wire_Output
| Wire_Signal
| Wire_Variable =>
if Drv /= No_Net then
-- Output already assigned
raise Internal_Error;
- else
- Drv := Inference.Infere (Ctxt, Asgn_Rec.Value, Outport);
-
- if Get_Id (Gate_Inst) = Id_Isignal
- and then Get_Driver (Get_Input (Gate_Inst, 1)) = No_Net
- then
- -- Mutate Isignal to signal.
- New_Sig := Build_Signal
- (Ctxt, Get_Name (Gate_Inst), Get_Width (Outport));
- Connect (Get_Input (Get_Parent (New_Sig), 0), Drv);
- Redirect_Inputs (Outport, New_Sig);
- Wire_Id_Table.Table (Asgn_Rec.Id).Gate := New_Sig;
- Free_Instance (Gate_Inst);
- else
- Connect (Gate_In, Drv);
- end if;
end if;
+
+ Drv := Inference.Infere (Ctxt, Asgn_Rec.Value, Outport);
+
+ Add_Conc_Assign (Wire_Rec, Drv, Stmt);
when others =>
raise Internal_Error;
end case;
@@ -164,6 +212,201 @@ package body Synth.Environment is
-- FIXME: free wires.
end Pop_And_Merge_Phi;
+ -- Merge sort of conc_assign by offset.
+ procedure Sort_Conc_Assign (Chain : Conc_Assign;
+ Len : Natural;
+ First : out Conc_Assign;
+ Next : out Conc_Assign)
+ is
+ Left, Right : Conc_Assign;
+ Last : Conc_Assign;
+ El : Conc_Assign;
+ begin
+ if Len = 0 then
+ First := No_Conc_Assign;
+ Next := Chain;
+ elsif Len = 1 then
+ First := Chain;
+ Next := Get_Conc_Chain (Chain);
+ Set_Conc_Chain (Chain, No_Conc_Assign);
+ else
+ -- Divide.
+ Sort_Conc_Assign (Chain, Len / 2, Left, Right);
+ Sort_Conc_Assign (Right, Len - Len / 2, Right, Next);
+
+ First := No_Conc_Assign;
+ Last := No_Conc_Assign;
+ for I in 1 .. Len loop
+ if Left /= No_Conc_Assign
+ and then
+ (Right = No_Conc_Assign
+ or else Get_Conc_Offset (Left) <= Get_Conc_Offset (Right))
+ then
+ El := Left;
+ Left := Get_Conc_Chain (Left);
+ else
+ pragma Assert (Right /= No_Conc_Assign);
+ El := Right;
+ Right := Get_Conc_Chain (Right);
+ end if;
+ -- Append
+ if First = No_Conc_Assign then
+ First := El;
+ else
+ Set_Conc_Chain (Last, El);
+ end if;
+ Last := El;
+ end loop;
+ Set_Conc_Chain (Last, No_Conc_Assign);
+ end if;
+ end Sort_Conc_Assign;
+
+ procedure Finalize_Complex_Assignment (Ctxt : Builders.Context_Acc;
+ Wire_Rec : Wire_Id_Record;
+ Value : out Net)
+ is
+ First_Assign : Conc_Assign;
+ Asgn : Conc_Assign;
+ Last_Asgn : Conc_Assign;
+ New_Asgn : Conc_Assign;
+ Next_Off : Uns32;
+ Expected_Off : Uns32;
+ Last_Off : Uns32;
+ Nbr_Assign : Natural;
+ begin
+ Nbr_Assign := Wire_Rec.Nbr_Final_Assign;
+ -- Sort assignments by offset.
+ Asgn := Wire_Rec.Final_Assign;
+ Sort_Conc_Assign (Asgn, Nbr_Assign, Asgn, Last_Asgn);
+ First_Assign := Asgn;
+
+ -- Report overlaps and holes, count number of inputs
+ Last_Asgn := No_Conc_Assign;
+ Expected_Off := 0;
+ Last_Off := Get_Width (Wire_Rec.Gate);
+ while Expected_Off < Last_Off loop
+ if Asgn /= No_Conc_Assign then
+ Next_Off := Get_Conc_Offset (Asgn);
+ else
+ Next_Off := Last_Off;
+ end if;
+ if Next_Off = Expected_Off then
+ -- Normal case.
+ pragma Assert (Asgn /= No_Conc_Assign);
+ Expected_Off := Expected_Off + Get_Width (Get_Conc_Value (Asgn));
+ Last_Asgn := Asgn;
+ Asgn := Get_Conc_Chain (Asgn);
+ elsif Next_Off > Expected_Off then
+ if Next_Off = Expected_Off + 1 then
+ Warning_Msg_Synth
+ (+Wire_Rec.Decl, "no assignment for offset %v",
+ (1 => +Expected_Off));
+ else
+ Warning_Msg_Synth
+ (+Wire_Rec.Decl, "no assignment for offsets %v:%v",
+ (+Expected_Off, +(Next_Off - 1)));
+ end if;
+
+ -- Insert conc_assign with initial value.
+ -- FIXME: handle initial values.
+ Conc_Assign_Table.Append
+ ((Next => Asgn,
+ Value => Build_Const_Z (Ctxt, Next_Off - Expected_Off),
+ Offset => Expected_Off,
+ Stmt => Source.No_Syn_Src));
+ New_Asgn := Conc_Assign_Table.Last;
+ if Last_Asgn = No_Conc_Assign then
+ First_Assign := New_Asgn;
+ else
+ Set_Conc_Chain (Last_Asgn, New_Asgn);
+ end if;
+ Last_Asgn := New_Asgn;
+ Nbr_Assign := Nbr_Assign + 1;
+
+ Expected_Off := Next_Off;
+ else
+ pragma Assert (Next_Off < Expected_Off);
+ Error_Msg_Synth
+ (+Wire_Rec.Decl, "multiple assignments for offsets %v:%v",
+ (+Next_Off, +(Expected_Off - 1)));
+ -- TODO: insert resolver
+ pragma Assert (Asgn /= No_Conc_Assign);
+ Last_Asgn := Asgn;
+ Asgn := Get_Conc_Chain (Asgn);
+ end if;
+ end loop;
+
+ -- Create concat
+ -- Set concat inputs
+ if Nbr_Assign = 1 then
+ Value := Get_Conc_Value (First_Assign);
+ elsif Nbr_Assign = 2 then
+ Value := Build_Concat2 (Ctxt,
+ Get_Conc_Value (Last_Asgn),
+ Get_Conc_Value (First_Assign));
+ else
+ raise Internal_Error;
+ end if;
+ end Finalize_Complex_Assignment;
+
+ procedure Finalize_Assignment
+ (Ctxt : Builders.Context_Acc; Wire_Rec : Wire_Id_Record)
+ is
+ use Vhdl.Nodes;
+ Gate_Inst : constant Instance := Get_Parent (Wire_Rec.Gate);
+ Inp : constant Input := Get_Input (Gate_Inst, 0);
+ Value : Net;
+ begin
+ case Wire_Rec.Nbr_Final_Assign is
+ when 0 =>
+ -- TODO: use initial value ?
+ if Wire_Rec.Decl /= Null_Node
+ and then Wire_Rec.Kind = Wire_Output
+ then
+ Error_Msg_Synth
+ (+Wire_Rec.Decl, "no assignment for %n", +Wire_Rec.Decl);
+ end if;
+ return;
+ when 1 =>
+ declare
+ Conc_Asgn : Conc_Assign_Record renames
+ Conc_Assign_Table.Table (Wire_Rec.Final_Assign);
+ begin
+ if Conc_Asgn.Offset = 0
+ and then (Get_Width (Conc_Asgn.Value)
+ = Get_Width (Wire_Rec.Gate))
+ then
+ -- Single and full assignment.
+ Value := Conc_Asgn.Value;
+ else
+ -- Partial or multiple assignments.
+ Finalize_Complex_Assignment (Ctxt, Wire_Rec, Value);
+ end if;
+ end;
+ when others =>
+ Finalize_Complex_Assignment (Ctxt, Wire_Rec, Value);
+ end case;
+
+ Connect (Inp, Value);
+ end Finalize_Assignment;
+
+ procedure Finalize_Assignments (Ctxt : Builders.Context_Acc) is
+ begin
+ pragma Assert (Phis_Table.Last = No_Phi_Id);
+ -- pragma Assert (Assign_Table.Last = No_Seq_Assign);
+
+ for Wid in Wire_Id_Table.First + 1 .. Wire_Id_Table.Last loop
+ declare
+ Wire_Rec : Wire_Id_Record renames Wire_Id_Table.Table (Wid);
+ begin
+ pragma Assert (Wire_Rec.Cur_Assign = No_Seq_Assign);
+ Finalize_Assignment (Ctxt, Wire_Rec);
+ end;
+ end loop;
+
+ Wire_Id_Table.Set_Last (No_Wire_Id);
+ end Finalize_Assignments;
+
-- Sort the LEN first wires of chain W (linked by Chain) in Id increasing
-- values. The result is assigned to FIRST and the first non-sorted wire
-- (the one after LEN) is assigned to NEXT. The chain headed by FIRST
@@ -360,7 +603,9 @@ begin
Mark_Flag => False,
Decl => Source.No_Syn_Src,
Gate => No_Net,
- Cur_Assign => No_Seq_Assign));
+ Cur_Assign => No_Seq_Assign,
+ Final_Assign => No_Conc_Assign,
+ Nbr_Final_Assign => 0));
pragma Assert (Wire_Id_Table.Last = No_Wire_Id);
Assign_Table.Append ((Phi => No_Phi_Id,
@@ -373,4 +618,10 @@ begin
Phis_Table.Append ((First => No_Seq_Assign,
Nbr => 0));
pragma Assert (Phis_Table.Last = No_Phi_Id);
+
+ Conc_Assign_Table.Append ((Next => No_Conc_Assign,
+ Value => No_Net,
+ Offset => 0,
+ Stmt => Source.No_Syn_Src));
+ pragma Assert (Conc_Assign_Table.Last = No_Conc_Assign);
end Synth.Environment;