aboutsummaryrefslogtreecommitdiffstats
path: root/src/synth/netlists-memories.adb
diff options
context:
space:
mode:
authorTristan Gingold <tgingold@free.fr>2020-06-02 21:24:54 +0200
committerTristan Gingold <tgingold@free.fr>2020-06-02 21:24:54 +0200
commit2edae856b3dea3c7efd6cbab6bf93e7b86204f8b (patch)
tree267523eb212f58bfdd3572c75551b9f0fc33a3cc /src/synth/netlists-memories.adb
parentfd50e9979a29831220b479b0375d918b29e5e250 (diff)
downloadghdl-2edae856b3dea3c7efd6cbab6bf93e7b86204f8b.tar.gz
ghdl-2edae856b3dea3c7efd6cbab6bf93e7b86204f8b.tar.bz2
ghdl-2edae856b3dea3c7efd6cbab6bf93e7b86204f8b.zip
netlists-memories: simplify addresses when using a 'to' range. Fix #1348
Diffstat (limited to 'src/synth/netlists-memories.adb')
-rw-r--r--src/synth/netlists-memories.adb264
1 files changed, 264 insertions, 0 deletions
diff --git a/src/synth/netlists-memories.adb b/src/synth/netlists-memories.adb
index cb7dd244e..9fad98862 100644
--- a/src/synth/netlists-memories.adb
+++ b/src/synth/netlists-memories.adb
@@ -753,6 +753,246 @@ package body Netlists.Memories is
Remove_Instance (Dff);
end Maybe_Swap_Mux_Concat_Dff;
+ -- Generic procedure to call CB on each memory future port (dyn_insert
+ -- or dyn_extract).
+ generic
+ type Data_Type is private;
+ with procedure Cb (Inst : Instance;
+ Data : in out Data_Type;
+ Fail : out Boolean);
+ procedure Foreach_Port (Sig : Instance; Data : in out Data_Type);
+
+ procedure Foreach_Port (Sig : Instance; Data : in out Data_Type)
+ is
+ Fail : Boolean;
+ Inst, Inst2 : Instance;
+ Inp2 : Input;
+ begin
+ -- Top-level loop, for each parallel path of multiport RAMs.
+ Inp2 := Get_First_Sink (Get_Output (Sig, 0));
+ while Inp2 /= No_Input loop
+ Inst2 := Get_Input_Parent (Inp2);
+ case Get_Id (Inst2) is
+ when Id_Dyn_Extract =>
+ Cb (Inst2, Data, Fail);
+ if Fail then
+ return;
+ end if;
+ when Id_Dyn_Insert
+ | Id_Dyn_Insert_En =>
+ Cb (Inst2, Data, Fail);
+ if Fail then
+ return;
+ end if;
+ -- Walk till the signal.
+ Inst := Inst2;
+ loop
+ declare
+ Inp : Input;
+ N_Inst : Instance;
+ In_Inst : Instance;
+ begin
+ -- Check gates connected to the output.
+ Inp := Get_First_Sink (Get_Output (Inst, 0));
+ N_Inst := No_Instance;
+ while Inp /= No_Input loop
+ In_Inst := Get_Input_Parent (Inp);
+ case Get_Id (In_Inst) is
+ when Id_Dyn_Extract =>
+ Cb (In_Inst, Data, Fail);
+ if Fail then
+ return;
+ end if;
+ when Id_Dyn_Insert_En
+ | Id_Dyn_Insert =>
+ Cb (In_Inst, Data, Fail);
+ if Fail then
+ return;
+ end if;
+ pragma Assert (N_Inst = No_Instance);
+ N_Inst := In_Inst;
+ when Id_Signal
+ | Id_Isignal
+ | Id_Mem_Multiport =>
+ pragma Assert (N_Inst = No_Instance);
+ N_Inst := In_Inst;
+ when others =>
+ raise Internal_Error;
+ end case;
+ Inp := Get_Next_Sink (Inp);
+ end loop;
+ Inst := N_Inst;
+ exit when Inst = Sig;
+ end;
+ end loop;
+ when others =>
+ raise Internal_Error;
+ end case;
+ Inp2 := Get_Next_Sink (Inp2);
+ end loop;
+ end Foreach_Port;
+
+ type Gather_Ports_Type is record
+ Ports : Instance_Array_Acc;
+ Nports : Nat32;
+ end record;
+
+ procedure Gather_Ports_Cb
+ (Inst : Instance; Data : in out Gather_Ports_Type; Fail : out Boolean) is
+ begin
+ Data.Nports := Data.Nports + 1;
+ Data.Ports (Data.Nports) := Inst;
+ Fail := False;
+ end Gather_Ports_Cb;
+
+ procedure Gather_Ports_Foreach is
+ new Foreach_Port (Data_Type => Gather_Ports_Type,
+ Cb => Gather_Ports_Cb);
+
+ -- Fill PORTS with all the ports from the SIG chain.
+ procedure Gather_Ports (Sig : Instance; Ports : Instance_Array_Acc)
+ is
+ Data : Gather_Ports_Type;
+ begin
+ Data := (Ports, 0);
+ Gather_Ports_Foreach (Sig, Data);
+ pragma Assert (Data.Nports = Ports'Last);
+ end Gather_Ports;
+
+ -- Check if the index of Memidx MIDX is of the form: MAX - off,
+ -- where MAX is the maximum value of off.
+ function Is_Reverse_Range (Midx : Instance) return Boolean
+ is
+ pragma Assert (Get_Id (Midx) = Id_Memidx);
+ Sub : constant Instance := Get_Input_Instance (Midx, 0);
+ Val : Instance;
+ begin
+ if Get_Id (Sub) /= Id_Sub then
+ return False;
+ end if;
+ Val := Get_Input_Instance (Sub, 0);
+ if Get_Id (Val) /= Id_Const_UB32 then
+ return False;
+ end if;
+ return Get_Param_Uns32 (Val, 0) = Get_Param_Uns32 (Midx, 1);
+ end Is_Reverse_Range;
+
+ procedure Maybe_Remap_Address
+ (Ctxt : Context_Acc; Sig : Instance; Nbr_Ports : Nat32)
+ is
+ pragma Unreferenced (Ctxt);
+ Ports : Instance_Array_Acc;
+ begin
+ Ports := new Instance_Array (1 .. Nbr_Ports);
+
+ -- 1. Gather all ports.
+ Gather_Ports (Sig, Ports);
+
+ -- 2. From ports, get the index.
+ for I in Ports'Range loop
+ declare
+ P : constant Instance := Ports (I);
+ Idx : Input;
+ begin
+ case Get_Id (P) is
+ when Id_Dyn_Extract =>
+ Idx := Get_Input (P, 1);
+ when Id_Dyn_Insert
+ | Id_Dyn_Insert_En =>
+ Idx := Get_Input (P, 2);
+ when others =>
+ raise Internal_Error;
+ end case;
+ Ports (I) := Get_Net_Parent (Get_Driver (Idx));
+ end;
+ end loop;
+
+ -- 3. For each dimension
+ loop
+ declare
+ M : Instance;
+ Done : Boolean;
+ Idx : Net;
+ Is_Reverse : Boolean;
+ W : Width;
+ Step : Uns32;
+ Max : Uns32;
+ begin
+ Done := False;
+ for I in Ports'Range loop
+ -- Get the index (memidx gate).
+ M := Ports (I);
+ case Get_Id (M) is
+ when Id_Memidx =>
+ null;
+ when Id_Addidx =>
+ M := Get_Net_Parent (Get_Output (M, 0));
+ pragma Assert (Get_Id (M) = Id_Memidx);
+ when others =>
+ raise Internal_Error;
+ end case;
+
+ Idx := Get_Input_Net (M, 0);
+ if I = 1 then
+ W := Get_Width (Idx);
+ Step := Get_Param_Uns32 (M, 0);
+ Max := Get_Param_Uns32 (M, 1);
+ Is_Reverse := Is_Reverse_Range (M);
+ else
+ if Get_Width (Idx) /= W
+ or else Get_Param_Uns32 (M, 0) /= Step
+ or else Get_Param_Uns32 (M, 1) /= Max
+ or else Is_Reverse_Range (M) /= Is_Reverse
+ then
+ -- Different width, steps or direction.
+ Done := True;
+ exit;
+ end if;
+ end if;
+ end loop;
+
+ exit when Done;
+
+ -- Update ports.
+ for I in Ports'Range loop
+ M := Ports (I);
+ case Get_Id (M) is
+ when Id_Memidx =>
+ Ports (I) := No_Instance;
+ Done := True;
+ when Id_Addidx =>
+ M := Get_Net_Parent (Get_Output (M, 0));
+ pragma Assert (Get_Id (M) = Id_Memidx);
+ Ports (I) := Get_Net_Parent (Get_Output (M, 1));
+ when others =>
+ raise Internal_Error;
+ end case;
+
+ if Is_Reverse then
+ declare
+ Inp : constant Input := Get_Input (M, 0);
+ Sub : constant Instance :=
+ Get_Net_Parent (Get_Driver (Inp));
+ Addr_Inp : constant Input := Get_Input (Sub, 1);
+ Val : Net;
+ begin
+ -- Disconnect the sub, and connect the address directly.
+ Disconnect (Inp);
+ Connect (Inp, Disconnect_And_Get (Addr_Inp));
+ -- Remove the sub and the constant.
+ Val := Disconnect_And_Get (Get_Input (Sub, 0));
+ Remove_Instance (Get_Net_Parent (Val));
+ Remove_Instance (Sub);
+ end;
+ end if;
+ end loop;
+ exit when Done;
+ end;
+ end loop;
+
+ Free_Instance_Array (Ports);
+ end Maybe_Remap_Address;
+
-- Create a mem_rd/mem_rd_sync from a dyn_extract gate.
-- LAST is the last memory port on the chain.
-- ADDR is the address (from the dyn_extract).
@@ -1891,6 +2131,24 @@ package body Netlists.Memories is
end loop;
end Create_Memory_Ports;
+ -- Return True iff the initial value of SIG is uniform (same value for
+ -- all bits).
+ function Is_Simple_Init (Sig : Instance) return Boolean
+ is
+ pragma Assert (Get_Id (Sig) = Id_Isignal);
+ Cst : constant Instance := Get_Input_Instance (Sig, 1);
+ begin
+ case Get_Id (Cst) is
+ when Id_Const_0
+ | Id_Const_X =>
+ return True;
+ when Id_Const_UB32 =>
+ return Get_Param_Uns32 (Cst, 0) = 0;
+ when others =>
+ return False;
+ end case;
+ end Is_Simple_Init;
+
procedure Convert_To_Memory (Ctxt : Context_Acc; Sig : Instance)
is
-- Size of RAM (in bits).
@@ -1938,6 +2196,12 @@ package body Netlists.Memories is
(+Sig, "found RAM %n, width: %v bits, depth: %v",
(1 => +Sig, 2 => +Mem_W, 3 => +Mem_Depth));
+ if Get_Id (Sig) = Id_Signal
+ or else (Get_Id (Sig) = Id_Isignal and then Is_Simple_Init (Sig))
+ then
+ Maybe_Remap_Address (Ctxt, Sig, Nbr_Ports);
+ end if;
+
-- 2. Walk to extract offsets/width
Offs := new Off_Array (1 .. 2 * Nbr_Ports);
Extract_Ports_Offsets (Sig, Offs, Nbr_Offs);