aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTristan Gingold <tgingold@free.fr>2020-05-06 08:21:21 +0200
committerTristan Gingold <tgingold@free.fr>2020-05-06 18:38:17 +0200
commit3b70e630543da3d42fc0cda37389312d8910e0ab (patch)
treea05f3d0030b69ecb7b3879131c5c97fd17bb10e8 /src
parent91a8e2335d1f1b3bf3232c60d84aec94fc8f85b9 (diff)
downloadghdl-3b70e630543da3d42fc0cda37389312d8910e0ab.tar.gz
ghdl-3b70e630543da3d42fc0cda37389312d8910e0ab.tar.bz2
ghdl-3b70e630543da3d42fc0cda37389312d8910e0ab.zip
synth: add support for sequential assertions. Fix #1273
Diffstat (limited to 'src')
-rw-r--r--src/synth/netlists-cleanup.adb7
-rw-r--r--src/synth/netlists-inference.adb151
-rw-r--r--src/synth/netlists-inference.ads6
-rw-r--r--src/synth/synth-environment.adb176
-rw-r--r--src/synth/synth-environment.ads13
-rw-r--r--src/synth/synth-stmts.adb17
6 files changed, 312 insertions, 58 deletions
diff --git a/src/synth/netlists-cleanup.adb b/src/synth/netlists-cleanup.adb
index 24889cd6c..c24a89031 100644
--- a/src/synth/netlists-cleanup.adb
+++ b/src/synth/netlists-cleanup.adb
@@ -126,9 +126,10 @@ package body Netlists.Cleanup is
case Get_Id (Inst) is
when Id_Output
- | Id_Ioutput
- | Id_Port
- | Id_Nop =>
+ | Id_Ioutput
+ | Id_Port
+ | Id_Enable
+ | Id_Nop =>
declare
Inp : Input;
In_Drv : Net;
diff --git a/src/synth/netlists-inference.adb b/src/synth/netlists-inference.adb
index 9079a260d..7016f1f57 100644
--- a/src/synth/netlists-inference.adb
+++ b/src/synth/netlists-inference.adb
@@ -880,4 +880,155 @@ package body Netlists.Inference is
return Res;
end Infere;
+
+ -- INST is a mux2 of a condition chain.
+ -- Return the input that is not 0. Could be either a mux2 or a const.
+ function Find_Condition_Chain_Next (Inst : Instance) return Instance
+ is
+ Mux_In0, Mux_In1 : Net;
+ In0_Inst, In1_Inst : Instance;
+ begin
+ Mux_In0 := Get_Input_Net (Inst, 1);
+ In0_Inst := Get_Net_Parent (Mux_In0);
+
+ Mux_In1 := Get_Input_Net (Inst, 2);
+ In1_Inst := Get_Net_Parent (Mux_In1);
+
+ if Get_Id (In0_Inst) /= Id_Const_UB32 then
+ -- The other input must be const 0.
+ pragma Assert (Get_Id (In1_Inst) = Id_Const_UB32
+ and then Get_Param_Uns32 (In1_Inst, 0) = 0);
+ return In0_Inst;
+ else
+ -- Either both are const, or the other input must be const 0.
+ if Get_Id (In1_Inst) = Id_Const_UB32 then
+ -- Both are const. Return the const 1.
+ if Get_Param_Uns32 (In1_Inst, 0) = 0 then
+ pragma Assert (Get_Param_Uns32 (In0_Inst, 0) = 1);
+ return In0_Inst;
+ else
+ pragma Assert (Get_Param_Uns32 (In1_Inst, 0) = 1);
+ pragma Assert (Get_Param_Uns32 (In0_Inst, 0) = 0);
+ return In1_Inst;
+ end if;
+ end if;
+
+ pragma Assert (Get_Param_Uns32 (In0_Inst, 0) = 0);
+ return In1_Inst;
+ end if;
+ end Find_Condition_Chain_Next;
+
+ -- VAL is a chain of mux2 that define the conditions to enable assertions.
+ function Infere_Assert (Ctxt : Context_Acc;
+ Val : Net;
+ En_Gate : Net;
+ Stmt : Synth.Source.Syn_Src) return Net
+ is
+ Loc : constant Location_Type := Synth.Source."+" (Stmt);
+ Inst : Instance;
+ First_Inst, Last_Inst : Instance;
+ Clk, En : Net;
+ Areset : Net;
+ Zero : Net;
+ begin
+ -- Extract clock (if any) from VAL. Return VAL is no clock.
+ First_Inst := Get_Net_Parent (Val);
+ Inst := First_Inst;
+ loop
+ if Get_Id (Inst) /= Id_Mux2 then
+ pragma Assert (Get_Id (Inst) = Id_Const_UB32);
+ return Val;
+ end if;
+ Extract_Clock (Ctxt, Get_Input_Net (Inst, 0), Clk, En);
+ exit when Clk /= No_Net;
+
+ -- No clock. Try the father.
+ Inst := Find_Condition_Chain_Next (Inst);
+ end loop;
+
+ -- Extract enable and asynchronous reset (if any).
+ Last_Inst := Inst;
+ Areset := No_Net;
+ while Last_Inst /= Inst loop
+ declare
+ Cond : Net;
+ Next_Inst : Instance;
+ begin
+ Cond := Get_Input_Net (Inst, 0);
+
+ -- Find the next mux.
+ Next_Inst := Find_Condition_Chain_Next (Inst);
+
+ -- If the next mux is in0, negate COND.
+ if Next_Inst = Get_Net_Parent (Get_Input_Net (Inst, 1)) then
+ Cond := Build_Monadic (Ctxt, Id_Not, Cond);
+ Synth.Source.Set_Location (Cond, Stmt);
+ end if;
+
+ -- 'And' COND to Areset.
+ Areset := Build2_And (Ctxt, Areset, Cond, Loc);
+
+ Inst := Next_Inst;
+ end;
+ end loop;
+
+ -- Same for LAST_INST, but check it is on in1.
+ declare
+ Next_Inst : Instance;
+ begin
+ Next_Inst := Find_Condition_Chain_Next (Last_Inst);
+ if Next_Inst /= Get_Net_Parent (Get_Input_Net (Inst, 2)) then
+ Error_Msg_Synth
+ (+Last_Inst, "assertion checked on else branch of an edge");
+ return Val;
+ end if;
+
+ En := Build2_And (Ctxt, En, Get_Output (Next_Inst, 0), Loc);
+ Zero := Get_Input_Net (Inst, 1);
+ end;
+
+ -- Build an idff/iadff for each condition of the assertions.
+ -- The caller will connect the returned value (En) to the enable gate.
+ declare
+ En_Inp : Input;
+ Assert_Inp : Input;
+ N : Net;
+ Dff : Net;
+ begin
+ En_Inp := Get_First_Sink (En_Gate);
+ pragma Assert (En_Inp /= No_Input);
+ while En_Inp /= No_Input loop
+ -- The Enable gate is connected to an implication.
+ Inst := Get_Input_Parent (En_Inp);
+ pragma Assert (Get_Id (Inst) = Id_Not);
+ N := Get_Output (Inst, 0);
+ pragma Assert (Has_One_Connection (N));
+ Inst := Get_Input_Parent (Get_First_Sink (N));
+ pragma Assert (Get_Id (Inst) = Id_Or);
+
+ N := Get_Output (Inst, 0);
+ pragma Assert (Has_One_Connection (N));
+ Inst := Get_Input_Parent (Get_First_Sink (N));
+
+ pragma Assert (Get_Id (Inst) = Id_Assert);
+
+ Assert_Inp := Get_Input (Inst, 0);
+ Disconnect (Assert_Inp);
+
+ if Areset = No_Net then
+ Dff := Build_Idff (Ctxt, Clk, N, Zero);
+ else
+ Dff := Build_Iadff (Ctxt, Clk, N, Areset, Zero, Zero);
+ end if;
+ Set_Location (Dff, Loc);
+
+ Connect (Assert_Inp, Dff);
+
+ En_Inp := Get_Next_Sink (En_Inp);
+ end loop;
+ end;
+
+ return En;
+ end Infere_Assert;
+
end Netlists.Inference;
diff --git a/src/synth/netlists-inference.ads b/src/synth/netlists-inference.ads
index 291b135bd..91ae6228b 100644
--- a/src/synth/netlists-inference.ads
+++ b/src/synth/netlists-inference.ads
@@ -41,4 +41,10 @@ package Netlists.Inference is
Prev_Val : Net;
Stmt : Synth.Source.Syn_Src;
Last_Use : Boolean) return Net;
+
+ -- Called when there is an assignment to a enable gate.
+ function Infere_Assert (Ctxt : Context_Acc;
+ Val : Net;
+ En_Gate : Net;
+ Stmt : Synth.Source.Syn_Src) return Net;
end Netlists.Inference;
diff --git a/src/synth/synth-environment.adb b/src/synth/synth-environment.adb
index 59b3938f5..4e789ef05 100644
--- a/src/synth/synth-environment.adb
+++ b/src/synth/synth-environment.adb
@@ -77,37 +77,6 @@ package body Synth.Environment is
Wire_Rec.Kind := Wire_None;
end Free_Wire;
- procedure Mark (M : out Wire_Id) is
- begin
- M := Wire_Id_Table.Last;
- end Mark;
-
- procedure Release (M : in out Wire_Id) is
- begin
- -- Check all wires to be released are free.
- for I in M + 1 .. Wire_Id_Table.Last loop
- declare
- Wire_Rec : Wire_Id_Record renames Wire_Id_Table.Table (I);
- begin
- if Wire_Rec.Kind /= Wire_None then
- raise Internal_Error;
- end if;
- end;
- end loop;
-
- -- Release.
- Wire_Id_Table.Set_Last (M);
-
- M := No_Wire_Id;
- end Release;
-
- procedure All_Released is
- begin
- if Wire_Id_Table.Last /= No_Wire_Id then
- raise Internal_Error;
- end if;
- end All_Released;
-
procedure Set_Wire_Gate (Wid : Wire_Id; Gate : Net) is
begin
-- Cannot override a gate.
@@ -203,9 +172,61 @@ package body Synth.Environment is
begin
Phis_Table.Append ((First => No_Seq_Assign,
Last => No_Seq_Assign,
- Nbr => 0));
+ Nbr => 0,
+ En => No_Wire_Id));
end Push_Phi;
+ procedure Mark (M : out Wire_Id) is
+ begin
+ M := Wire_Id_Table.Last;
+ end Mark;
+
+ procedure Release (M : in out Wire_Id)
+ is
+ Last : Wire_Id;
+ begin
+ -- Check all wires to be released are free.
+ Last := M;
+ for I in M + 1 .. Wire_Id_Table.Last loop
+ declare
+ Wire_Rec : Wire_Id_Record renames Wire_Id_Table.Table (I);
+ Asgn : Seq_Assign;
+ begin
+ case Wire_Rec.Kind is
+ when Wire_None =>
+ null;
+ when Wire_Enable =>
+ -- Keep. This renames the wire, but the only references
+ -- must be in the wire.
+ Last := Last + 1;
+ if Last /= I then
+ -- Renames.
+ Asgn := Wire_Rec.Cur_Assign;
+ while Asgn /= No_Seq_Assign loop
+ Assign_Table.Table (Asgn).Id := Last;
+ Asgn := Get_Assign_Prev (Asgn);
+ end loop;
+ Wire_Id_Table.Table (Last) := Wire_Rec;
+ end if;
+ when others =>
+ raise Internal_Error;
+ end case;
+ end;
+ end loop;
+
+ -- Release.
+ Wire_Id_Table.Set_Last (Last);
+
+ M := No_Wire_Id;
+ end Release;
+
+ procedure All_Released is
+ begin
+ if Wire_Id_Table.Last /= No_Wire_Id then
+ raise Internal_Error;
+ end if;
+ end All_Released;
+
-- Concatenate when possible partial assignments of HEAD.
procedure Merge_Partial_Assignments
(Ctxt : Context_Acc; Head : Seq_Assign_Value)
@@ -291,7 +312,10 @@ package body Synth.Environment is
Wid : Wire_Id;
begin
Asgn := Phi.First;
- Phi := (First => No_Seq_Assign, Last => No_Seq_Assign, Nbr => 0);
+ Phi := (First => No_Seq_Assign,
+ Last => No_Seq_Assign,
+ Nbr => 0,
+ En => No_Wire_Id);
while Asgn /= No_Seq_Assign loop
pragma Assert (Assign_Table.Table (Asgn).Phi = Current_Phi);
Next_Asgn := Get_Assign_Chain (Asgn);
@@ -382,6 +406,11 @@ package body Synth.Environment is
begin
if Synth.Flags.Flag_Debug_Noinference then
Res := Pa.Value;
+ elsif Wire_Rec.Kind = Wire_Enable then
+ -- Possibly infere a idff/iadff.
+ pragma Assert (Pa.Offset = 0);
+ Res := Inference.Infere_Assert
+ (Ctxt, Pa.Value, Outport, Stmt);
else
-- Note: lifetime is currently based on the kind of the
-- wire (variable -> not reused beyond this process).
@@ -407,6 +436,7 @@ package body Synth.Environment is
Asgn : Seq_Assign;
begin
Pop_Phi (Phi);
+ pragma Assert (Phis_Table.Last = No_Phi_Id);
-- It is possible that the same value is assigned to different targets.
-- Example:
@@ -939,9 +969,10 @@ package body Synth.Environment is
W : constant Width := Get_Width (Wid_Rec.Gate);
begin
case Wid_Rec.Kind is
- when Wire_Signal | Wire_Output | Wire_Inout | Wire_Variable =>
+ when Wire_Signal | Wire_Output | Wire_Inout
+ | Wire_Variable =>
null;
- when Wire_Input | Wire_None =>
+ when Wire_Input | Wire_Enable | Wire_None =>
raise Internal_Error;
end case;
@@ -981,7 +1012,8 @@ package body Synth.Environment is
else
return Get_Assign_Value (Ctxt, Wire_Rec.Cur_Assign);
end if;
- when Wire_Signal | Wire_Output | Wire_Inout | Wire_Input =>
+ when Wire_Signal | Wire_Output | Wire_Inout | Wire_Input
+ | Wire_Enable =>
-- For signals, always read the previous value.
return Wire_Rec.Gate;
when Wire_None =>
@@ -1513,13 +1545,7 @@ package body Synth.Environment is
end loop;
end Merge_Phis;
- procedure Phi_Append_Assign (Asgn : Seq_Assign)
- is
- pragma Assert (Asgn /= No_Seq_Assign);
- Asgn_Rec : Seq_Assign_Record renames Assign_Table.Table (Asgn);
- pragma Assert (Asgn_Rec.Phi = Current_Phi);
- pragma Assert (Asgn_Rec.Chain = No_Seq_Assign);
- P : Phi_Type renames Phis_Table.Table (Phis_Table.Last);
+ procedure Phi_Append_Assign (P : in out Phi_Type; Asgn : Seq_Assign) is
begin
-- Chain assignment in the current sequence.
if P.First = No_Seq_Assign then
@@ -1531,6 +1557,64 @@ package body Synth.Environment is
P.Nbr := P.Nbr + 1;
end Phi_Append_Assign;
+ procedure Phi_Append_Assign (Asgn : Seq_Assign)
+ is
+ pragma Assert (Asgn /= No_Seq_Assign);
+ Asgn_Rec : Seq_Assign_Record renames Assign_Table.Table (Asgn);
+ pragma Assert (Asgn_Rec.Phi = Current_Phi);
+ pragma Assert (Asgn_Rec.Chain = No_Seq_Assign);
+ begin
+ Phi_Append_Assign (Phis_Table.Table (Phis_Table.Last), Asgn);
+ end Phi_Append_Assign;
+
+ function Phi_Enable (Ctxt : Builders.Context_Acc; Loc : Source.Syn_Src)
+ return Net
+ is
+ Last : constant Phi_Id := Phis_Table.Last;
+ Wid : Wire_Id;
+ N : Net;
+ Asgn : Seq_Assign;
+ begin
+ if Last = No_Phi_Id then
+ -- Can be called only when a phi is created.
+ raise Internal_Error;
+ end if;
+ if Last = No_Phi_Id + 1 then
+ -- That's the first phi, which is always enabled.
+ return No_Net;
+ end if;
+
+ -- Cached value.
+ Wid := Phis_Table.Table (Last).En;
+ if Wid = No_Wire_Id then
+ Wid := Alloc_Wire (Wire_Enable, Loc);
+ Phis_Table.Table (Last).En := Wid;
+
+ -- Create the Enable gate.
+ N := Build_Enable (Ctxt);
+ Set_Location (N, Loc);
+ Set_Wire_Gate (Wid, N);
+
+ -- Initialize to '0'.
+ -- This is really cheating, as it is like assigning in the first
+ -- phi.
+ Assign_Table.Append ((Phi => No_Phi_Id + 1,
+ Id => Wid,
+ Prev => No_Seq_Assign,
+ Chain => No_Seq_Assign,
+ Val => (Is_Static => True, Val => Bit0)));
+ Asgn := Assign_Table.Last;
+ Wire_Id_Table.Table (Wid).Cur_Assign := Asgn;
+ Phi_Append_Assign (Phis_Table.Table (No_Phi_Id + 1), Asgn);
+
+ -- Assign to '1'.
+ Phi_Assign_Static (Wid, Bit1);
+ return N;
+ else
+ return Get_Current_Value (Ctxt, Wid);
+ end if;
+ end Phi_Enable;
+
-- Check consistency:
-- - ordered.
-- - no overlaps.
@@ -1753,7 +1837,8 @@ package body Synth.Environment is
Phi_Assign (Ctxt, Dest, Pasgn);
end Phi_Assign_Net;
- procedure Phi_Assign_Static (Dest : Wire_Id; Val : Memtyp) is
+ procedure Phi_Assign_Static (Dest : Wire_Id; Val : Memtyp)
+ is
Wire_Rec : Wire_Id_Record renames Wire_Id_Table.Table (Dest);
pragma Assert (Wire_Rec.Kind /= Wire_None);
Cur_Asgn : constant Seq_Assign := Wire_Rec.Cur_Assign;
@@ -1820,7 +1905,8 @@ begin
Phis_Table.Append ((First => No_Seq_Assign,
Last => No_Seq_Assign,
- Nbr => 0));
+ Nbr => 0,
+ En => No_Wire_Id));
pragma Assert (Phis_Table.Last = No_Phi_Id);
Conc_Assign_Table.Append ((Next => No_Conc_Assign,
diff --git a/src/synth/synth-environment.ads b/src/synth/synth-environment.ads
index 5d066bf43..37ab29414 100644
--- a/src/synth/synth-environment.ads
+++ b/src/synth/synth-environment.ads
@@ -61,7 +61,9 @@ package Synth.Environment is
type Wire_Kind is
(
Wire_None,
- Wire_Signal, Wire_Variable,
+ Wire_Variable,
+ Wire_Enable,
+ Wire_Signal,
Wire_Input, Wire_Output, Wire_Inout
);
@@ -145,6 +147,13 @@ package Synth.Environment is
T, F : Phi_Type;
Stmt : Source.Syn_Src);
+ -- Create or get (if already created) a net that is true iff the current
+ -- phi is selected. Used to enable sequential assertions.
+ -- Because a wire is created, inference will run on it and therefore
+ -- a dff is created if needed.
+ function Phi_Enable (Ctxt : Builders.Context_Acc; Loc : Source.Syn_Src)
+ return Net;
+
-- Lower level part.
-- Currently public to handle case statements.
@@ -348,6 +357,8 @@ private
Last : Seq_Assign;
-- Number of assignments.
Nbr : Uns32;
+ -- Enable wire created for this phi.
+ En : Wire_Id;
end record;
package Phis_Table is new Tables
diff --git a/src/synth/synth-stmts.adb b/src/synth/synth-stmts.adb
index a9f39babf..10261bfa6 100644
--- a/src/synth/synth-stmts.adb
+++ b/src/synth/synth-stmts.adb
@@ -2700,10 +2700,10 @@ package body Synth.Stmts is
procedure Synth_Dynamic_Assertion_Statement (C : Seq_Context; Stmt : Node)
is
Ctxt : constant Context_Acc := Get_Build (C.Inst);
- En : constant Net := No_Net;
Loc : constant Location_Type := Get_Location (Stmt);
Cond : Valtyp;
N : Net;
+ En : Net;
Inst : Instance;
begin
Cond := Synth_Expression
@@ -2712,15 +2712,14 @@ package body Synth.Stmts is
Set_Error (C.Inst);
return;
end if;
- if Boolean'(False) then
- N := Get_Net (Ctxt, Cond);
- if En /= No_Net then
- -- Build: En -> Cond
- N := Build2_Imp (Ctxt, En, N, Loc);
- end if;
- Inst := Build_Assert (Ctxt, Synth_Label (Stmt), N);
- Set_Location (Inst, Loc);
+ N := Get_Net (Ctxt, Cond);
+ En := Phi_Enable (Ctxt, Stmt);
+ if En /= No_Net then
+ -- Build: En -> Cond
+ N := Build2_Imp (Ctxt, En, N, Loc);
end if;
+ Inst := Build_Assert (Ctxt, Synth_Label (Stmt), N);
+ Set_Location (Inst, Loc);
end Synth_Dynamic_Assertion_Statement;
procedure Synth_Sequential_Statements